UNPKG

interactjs

Version:

Drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE8+)

1,723 lines (1,353 loc) 220 kB
/** * interact.js v1.3.0-alpha.3+sha.9595c40 * * Copyright (c) 2012-2017 Taye Adeyemi <dev@taye.me> * 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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict'; /* * In a (windowless) server environment this file exports a factory function * that takes the window to use. * * var interact = require('interact.js')(windowObject); * * See https://github.com/taye/interact.js/issues/187 */ if (typeof window === 'undefined') { module.exports = function (window) { require('./src/utils/window').init(window); return require('./src/index'); }; } else { module.exports = require('./src/index'); } },{"./src/index":19,"./src/utils/window":52}],2:[function(require,module,exports){ 'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var _require = require('./utils/arr'), indexOf = _require.indexOf; var extend = require('./utils/extend.js'); function fireUntilImmediateStopped(event, listeners) { for (var i = 0, len = listeners.length; i < len && !event.immediatePropagationStopped; i++) { listeners[i](event); } } var Eventable = function () { function Eventable(options) { _classCallCheck(this, Eventable); this.options = extend({}, options || {}); } Eventable.prototype.fire = function fire(event) { var listeners = void 0; var onEvent = 'on' + event.type; var global = this.global; // Interactable#on() listeners if (listeners = this[event.type]) { fireUntilImmediateStopped(event, listeners); } // interactable.onevent listener if (this[onEvent]) { this[onEvent](event); } // interact.on() listeners if (!event.propagationStopped && global && (listeners = global[event.type])) { fireUntilImmediateStopped(event, listeners); } }; Eventable.prototype.on = function on(eventType, listener) { // if this type of event was never bound if (this[eventType]) { this[eventType].push(listener); } else { this[eventType] = [listener]; } }; 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; if (index !== -1) { eventList.splice(index, 1); } if (eventList && eventList.length === 0 || !listener) { this[eventType] = listener; } }; return Eventable; }(); module.exports = Eventable; },{"./utils/arr":36,"./utils/extend.js":41}],3:[function(require,module,exports){ 'use strict'; 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) { var preEnd = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; _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.preEnd = preEnd; 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; } 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); this.timeStamp = coords.timeStamp; this.dt = interaction.pointerDelta.timeStamp; this.duration = this.timeStamp - this.t0; // 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; this.swipe = ending || phase === 'inertiastart' ? this.getSwipe() : null; signals.fire('new', signalArg); } InteractEvent.prototype.getSwipe = function getSwipe() { var interaction = this.interaction; if (interaction.prevEvent.speed < 600 || this.timeStamp - interaction.prevEvent.timeStamp > 150) { return null; } var angle = 180 * Math.atan2(interaction.prevEvent.velocityY, interaction.prevEvent.velocityX) / Math.PI; var overlap = 22.5; if (angle < 0) { angle += 360; } 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 } }; }; InteractEvent.prototype.preventDefault = function preventDefault() {}; InteractEvent.prototype.stopImmediatePropagation = function stopImmediatePropagation() { this.immediatePropagationStopped = this.propagationStopped = true; }; InteractEvent.prototype.stopPropagation = function stopPropagation() { this.propagationStopped = true; }; return InteractEvent; }(); signals.on('set-delta', function (_ref) { var iEvent = _ref.iEvent, interaction = _ref.interaction, starting = _ref.starting, deltaSource = _ref.deltaSource; var prevEvent = starting ? iEvent : interaction.prevEvent; 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; } }); InteractEvent.signals = signals; module.exports = InteractEvent; },{"./defaultOptions":18,"./utils/Signals":35,"./utils/extend":41,"./utils/getOriginXY":42}],4:[function(require,module,exports){ 'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var is = require('./utils/is'); var events = require('./utils/events'); var extend = require('./utils/extend'); var actions = require('./actions/base'); var scope = require('./scope'); var Eventable = require('./Eventable'); var defaults = require('./defaultOptions'); var signals = require('./utils/Signals').new(); var _require = require('./utils/domUtils'), getElementRect = _require.getElementRect, nodeContains = _require.nodeContains, trySelector = _require.trySelector; var _require2 = require('./utils/window'), getWindow = _require2.getWindow; var _require3 = require('./utils/arr'), indexOf = _require3.indexOf, contains = _require3.contains; var _require4 = require('./utils/browser'), wheelEvent = _require4.wheelEvent; // all set interactables scope.interactables = []; /*\ * Interactable [ property ] ** * Object type returned by @interact \*/ var Interactable = function () { function Interactable(target, options) { _classCallCheck(this, Interactable); options = options || {}; this.target = target; this.events = new Eventable(); this._context = options.context || scope.document; this._win = getWindow(trySelector(target) ? this._context : target); this._doc = this._win.document; signals.fire('new', { target: target, options: options, interactable: this, win: this._win }); scope.addDocument(this._doc, this._win); scope.interactables.push(this); this.set(options); } Interactable.prototype.setOnEvents = function setOnEvents(action, phases) { var onAction = 'on' + action; if (is.function(phases.onstart)) { this.events[onAction + 'start'] = phases.onstart; } if (is.function(phases.onmove)) { this.events[onAction + 'move'] = phases.onmove; } if (is.function(phases.onend)) { this.events[onAction + 'end'] = phases.onend; } if (is.function(phases.oninertiastart)) { this.events[onAction + 'inertiastart'] = phases.oninertiastart; } 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 (is.object(options[option])) { // duplicate the object this.options[action][option] = extend(this.options[action][option] || {}, options[option]); if (is.object(defaults.perAction[option]) && 'enabled' in defaults.perAction[option]) { this.options[action][option].enabled = options[option].enabled === false ? false : true; } } else if (is.bool(options[option]) && is.object(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]; } } } }; /*\ * 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 (is.string(this.target) && !is.element(element)) { element = this._context.querySelector(this.target); } 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 (is.function(checker)) { this.getRect = checker; return this; } if (checker === null) { delete this.options.getRect; return this; } return this.getRect; }; Interactable.prototype._backCompatOption = function _backCompatOption(optionName, newValue) { if (trySelector(newValue) || is.object(newValue)) { this.options[optionName] = newValue; 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; } var action = _ref; this.options[action][optionName] = newValue; } return this; } 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; } 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.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, options) { if (is.string(eventType) && eventType.search(' ') !== -1) { eventType = eventType.trim().split(/ +/); } if (is.array(eventType)) { for (var i = 0; i < eventType.length; i++) { this[method](eventType[i], listener, options); } return true; } if (is.object(eventType)) { for (var prop in eventType) { this[method](prop, eventType[prop], listener); } 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) - options (object | boolean) #optional options object or useCapture flag for addEventListener = (object) This Interactable \*/ Interactable.prototype.on = function on(eventType, listener, options) { if (this._onOffMultiple('on', eventType, listener, options)) { return this; } if (eventType === 'wheel') { eventType = wheelEvent; } if (contains(Interactable.eventTypes, eventType)) { this.events.on(eventType, listener); } // delegated event for selector else if (is.string(this.target)) { events.addDelegate(this.target, this._context, eventType, listener, options); } else { events.add(this.target, eventType, listener, options); } 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 - options (object | boolean) #optional options object or useCapture flag for removeEventListener = (object) This Interactable \*/ Interactable.prototype.off = function off(eventType, listener, options) { if (this._onOffMultiple('off', eventType, listener, options)) { return this; } if (eventType === 'wheel') { eventType = wheelEvent; } // if it is an action event type if (contains(Interactable.eventTypes, eventType)) { this.events.off(eventType, listener); } // delegated event else if (is.string(this.target)) { events.removeDelegate(this.target, this._context, eventType, listener, options); } // remove listener from this Interatable's element else { events.remove(this.target, eventType, listener, options); } 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 (!is.object(options)) { options = {}; } this.options = extend({}, defaults.base); var perActions = extend({}, defaults.perAction); for (var actionName in actions.methodDict) { var methodName = actions.methodDict[actionName]; this.options[actionName] = extend({}, defaults[actionName]); this.setPerAction(actionName, perActions); this[methodName](options[actionName]); } for (var _iterator2 = Interactable.settingsMethods, _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 setting = _ref2; this.options[setting] = defaults.base[setting]; if (setting in options) { this[setting](options[setting]); } } 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 (is.string(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; } } events.remove(this._context, type, events.delegateListener); events.remove(this._context, type, events.delegateUseCapture, true); } } else { events.remove(this, 'all'); } signals.fire('unset', { interactable: this }); scope.interactables.splice(indexOf(scope.interactables, this), 1); // 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 (_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.target === this && interaction.interacting()) { interaction.stop(); } } return scope.interact; }; return Interactable; }(); scope.interactables.indexOfElement = function indexOfElement(target, context) { context = context || scope.document; for (var i = 0; i < this.length; i++) { var interactable = this[i]; if (interactable.target === target && interactable._context === context) { return i; } } return -1; }; scope.interactables.get = function interactableGet(element, options, dontCheckInContext) { var ret = this[this.indexOfElement(element, options && options.context)]; return ret && (is.string(element) || dontCheckInContext || ret.inContext(element)) ? ret : null; }; scope.interactables.forEachSelector = function (callback, element) { for (var i = 0; i < this.length; i++) { var interactable = this[i]; // skip non CSS selector targets and out of context elements if (!is.string(interactable.target) || element && !interactable.inContext(element)) { continue; } var ret = callback(interactable, interactable.target, interactable._context, i, this); if (ret !== undefined) { return ret; } } }; // all interact.js eventTypes Interactable.eventTypes = scope.eventTypes = []; Interactable.signals = signals; Interactable.settingsMethods = ['deltaSource', 'origin', 'preventDefault', 'rectChecker']; module.exports = Interactable; },{"./Eventable":2,"./actions/base":6,"./defaultOptions":18,"./scope":34,"./utils/Signals":35,"./utils/arr":36,"./utils/browser":37,"./utils/domUtils":39,"./utils/events":40,"./utils/extend":41,"./utils/is":46,"./utils/window":52}],5:[function(require,module,exports){ 'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 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(); var listeners = {}; var methodNames = ['pointerDown', 'pointerMove', 'pointerUp', 'updatePointer', 'removePointer']; // for ignoring browser's simulated mouse events var prevTouchTime = 0; // all active and idle interactions scope.interactions = []; var Interaction = function () { function Interaction() { _classCallCheck(this, Interaction); this.target = null; // current interactable being interacted with this.element = null; // the target element of the interactable this.prepared = { // action that's ready to be fired on next move event name: null, axis: null, edges: null }; // keep track of added pointers this.pointers = []; this.pointerIds = []; this.downTargets = []; this.downTimes = []; // 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 }; // Starting InteractEvent pointer coordinates this.startCoords = { page: { x: 0, y: 0 }, client: { x: 0, y: 0 }, timeStamp: 0 }; // 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.downEvent = null; // pointerdown/mousedown/touchstart event this.downPointer = {}; this._eventTarget = null; this._curEventTarget = null; this.prevEvent = null; // previous action event this.pointerIsDown = false; this.pointerWasMoved = false; this._interacting = false; this.mouse = false; signals.fire('new', this); scope.interactions.push(this); } Interaction.prototype.pointerDown = function pointerDown(pointer, event, eventTarget) { var pointerIndex = this.updatePointer(pointer, event, true); 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.<action>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; } // if this interaction had been removed after stopping // add it back if (utils.indexOf(scope.interactions, this) === -1) { scope.interactions.push(this); } utils.copyAction(this.prepared, action); this.target = target; this.element = element; signals.fire('action-start', { interaction: this, event: this.downEvent }); }; Interaction.prototype.pointerMove = function pointerMove(pointer, event, eventTarget) { if (!this.simulation) { this.updatePointer(pointer); utils.setCoords(this.curCoords, this.pointers); } 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 dx = void 0; var dy = void 0; // 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.pointerWasMoved = utils.hypot(dx, dy) > Interaction.pointerMoveTolerance; } var signalArg = { pointer: pointer, pointerIndex: this.getPointerIndex(pointer), event: event, eventTarget: eventTarget, dx: dx, dy: dy, duplicate: duplicateMove, interaction: this, interactingBeforeMove: this.interacting() }; if (!duplicateMove) { // set pointer coordinate, time changes and speeds utils.setCoordDeltas(this.pointerDelta, this.prevCoords, this.curCoords); } signals.fire('move', signalArg); if (!duplicateMove) { // if interacting, fire an 'action-move' signal etc if (this.interacting()) { this.doMove(signalArg); } 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); } this._dontFireMove = false; }; // End interact move events and stop auto-scroll unless simulation is running Interaction.prototype.pointerUp = function pointerUp(pointer, event, eventTarget, curEventTarget) { var pointerIndex = this.getPointerIndex(pointer); signals.fire(/cancel$/i.test(event.type) ? 'cancel' : 'up', { pointer: pointer, pointerIndex: pointerIndex, event: event, eventTarget: eventTarget, curEventTarget: curEventTarget, interaction: this }); if (!this.simulation) { this.end(event); } this.pointerIsDown = false; this.removePointer(pointer, event); }; /*\ * 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 }); } this.stop(); }; Interaction.prototype.currentAction = function currentAction() { return this._interacting ? this.prepared.name : null; }; Interaction.prototype.interacting = function interacting() { return this._interacting; }; Interaction.prototype.stop = function stop() { signals.fire('stop', { interaction: this }); if (this._interacting) { signals.fire('stop-active', { interaction: this }); signals.fire('stop-' + this.prepared.name, { interaction: this }); } this.target = this.element = null; this._interacting = false; this.prepared.name = this.prevEvent = null; }; Interaction.prototype.getPointerIndex = function getPointerIndex(pointer) { return utils.indexOf(this.pointerIds, utils.getPointerId(pointer)); }; Interaction.prototype.updatePointer = function updatePointer(pointer, event) { var down = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : event && /(down|start)$/i.test(event.type); var id = utils.getPointerId(pointer); var index = this.getPointerIndex(pointer); if (index === -1) { index = this.pointerIds.length; this.pointerIds[index] = id; } if (down) { signals.fire('update-pointer-down', { pointer: pointer, event: event, down: down, pointerId: id, pointerIndex: index, interaction: this }); } this.pointers[index] = pointer; return index; }; Interaction.prototype.removePointer = function removePointer(pointer, event) { var id = utils.getPointerId(pointer); var index = this.mouse ? 0 : utils.indexOf(this.pointerIds, id); if (index === -1) { return; } signals.fire('remove-pointer', { pointer: pointer, event: event, pointerIndex: index, interaction: this }); this.pointers.splice(index, 1); this.pointerIds.splice(index, 1); this.downTargets.splice(index, 1); this.downTimes.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 _utils$getEventTarget = utils.getEventTargets(event), eventTarget = _utils$getEventTarget[0], curEventTarget = _utils$getEventTarget[1]; 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 _i2 = 0; _i2 < scope.interactions.length && !invalidPointer; _i2++) { invalidPointer = !scope.interactions[_i2].mouse && scope.interactions[_i2].pointerIsDown; } // try to ignore mouse events that are simulated by the browser // after a touch event invalidPointer = invalidPointer || new Date().getTime() - prevTouchTime < 500 // on iOS and Firefox Mobile, MouseEvent.timeStamp is zero if simulated || event.timeStamp === 0; } if (!invalidPointer) { var _interaction = finder.search(event, event.type, eventTarget); if (!_interaction) { _interaction = new Interaction(); _interaction.mouse = /mouse/i.test(event.pointerType || event.type) // MSPointerEvent.MSPOINTER_TYPE_MOUSE || event.pointerType === 4 || !event.pointerType; } matches.push([event, _interaction]); } } for (var _iterator = matches, _isArray = Array.isArray(_iterator), _i3 = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref; if (_isArray) { if (_i3 >= _iterator.length) break; _ref = _iterator[_i3++]; } else { _i3 = _iterator.next(); if (_i3.done) break; _ref = _i3.value; } var _ref2 = _ref, _pointer = _ref2[0], _interaction2 = _ref2[1]; _interaction2._updateEventTargets(eventTarget, curEventTarget); _interaction2[method](_pointer, event, eventTarget, curEventTarget); } }; } function endAll(event) { for (var _i4 = 0; _i4 < scope.interactions.length; _i4++) { var interaction = scope.interactions[_i4]; 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(_ref3, signalName) { var doc = _ref3.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]); } } signals.on('update-pointer-down', function (_ref4) { var interaction = _ref4.interaction, pointer = _ref4.pointer, pointerId = _ref4.pointerId, pointerIndex = _ref4.pointerIndex, event = _ref4.event, eventTarget = _ref4.eventTarget, down = _ref4.down; interaction.pointerIds[pointerIndex] = pointerId; interaction.pointers[pointerIndex] = pointer; if (down) { interaction.pointerIsDown = true; } if (!interaction.interacting()) { utils.setCoords(interaction.startCoords, interaction.pointers); utils.copyCoords(interaction.curCoords, interaction.startCoords); utils.copyCoords(interaction.prevCoords, interaction.startCoords); interaction.downEvent = event; interaction.downTimes[pointerIndex] = interaction.curCoords.timeStamp; interaction.downTargets[pointerIndex] = eventTarget || event && utils.getEventTargets(event)[0]; interaction.pointerWasMoved = false; utils.pointerExtend(interaction.downPointer, pointer); } }); 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":34,"./utils":44,"./utils/Signals":35,"./utils/browser":37,"./utils/events":40,"./utils/interactionFinder":45}],6:[function(require,module,exports){ 'use strict'; var Interaction = require('../Interaction'); var InteractEvent = require('../InteractEvent'); var actions = { firePrepared: firePrepared, names: [], methodDict: {} }; Interaction.signals.on('action-start', function (_ref) { var interaction = _ref.interaction, event = _ref.event; interaction._interacting = true; firePrepared(interaction, event, 'start'); }); Interaction.signals.on('action-move', function (_ref2) { var interaction = _ref2.interaction, event = _ref2.event, preEnd = _ref2.preEnd; firePrepared(interaction, event, 'move', preEnd); // if the action was ended in a listener if (!interaction.interacting()) { return false; } }); Interaction.signals.on('action-end', function (_ref3) { var interaction = _ref3.interaction, event = _ref3.event; firePrepared(interaction, event, 'end'); }); function firePrepared(interaction, event, phase, preEnd) { var actionName = interaction.prepared.name; var newEvent = new InteractEvent(interaction, event, actionName, phase, interaction.element, null, preEnd); interaction.target.fire(newEvent); interaction.prevEvent = newEvent; } module.exports = actions; },{"../InteractEvent":3,"../Interaction":5}],7:[function(require,module,exports){ 'use strict'; var actions = require('./base'); 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 checker(pointer, event, interactable) { var dragOptions = interactable.options.drag; return dragOptions.enabled ? { name: 'drag', axis: dragOptions.lockAxis === 'start' ? dragOptions.startAxis : dragOptions.lockAxis } : null; }, getCursor: function getCursor() { return 'move'; } }; Interaction.signals.on('before-action-move', function (_ref) { var interaction = _ref.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; } }); // dragmove InteractEvent.signals.on('new', function (_ref2) { var iEvent = _ref2.iEvent, interaction = _ref2.interaction; if (iEvent.type !== 'dragmove') { return; } var axis = interaction.prepared.axis; if (axis === 'x') { iEvent.pageY = interaction.startCoords.page.y; iEvent.clientY = interaction.startCoords.client.y; iEvent.dy = 0; } else if (axis === 'y') { iEvent.pageX = interaction.startCoords.page.x; iEvent.clientX = interaction.startCoords.client.x; iEvent.dx = 0; } }); /*\ * 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.is.object(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; } return this; } if (utils.is.bool(options)) { this.options.drag.enabled = options; if (!options) { this.ondragstart = this.ondragstart = this.ondragend = null; } return this; } return this.options.drag; }; actions.drag = drag; actions.names.push('drag'); utils.merge(Interactable.eventTypes, ['dragstart', 'dragmove', 'draginertiastart', 'draginertiaresume', 'dragend']); actions.methodDict.drag = 'draggable'; defaultOptions.drag = drag.defaults; module.exports = drag; },{"../InteractEvent":3,"../Interactable":4,"../Interaction":5,"../defaultOptions":18,"../utils":44,"./base":6}],8:[function(require,module,exports){ 'use strict'; var actions = require('./base'); 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'); var drop = { defaults: { enabled: false, accept: null, overlap: 'pointer' } }; var dynamicDrop = false; Interaction.signals.on('action-start', function (_ref) { var interaction = _ref.interaction, event = _ref.event; if (interaction.prepared.name !== 'drag') { return; } // reset active dropzones interaction.activeDrops.dropzones = []; interaction.activeDrops.elements = []; interaction.activeDrops.rects = []; interaction.dropEvents = null; if (!interaction.dynamicDrop) { setActiveDrops(interaction, interaction.element); } var dragEvent = interaction.prevEvent; var dropEvents = getDropEvents(interaction, event, dragEvent); if (dropEvents.activate) { fireActiveDrops(interaction, dropEvents.activate); } }); InteractEvent.signals.on('new', function (_ref2) { var interaction = _ref2.interaction, iEvent = _ref2.iEvent, event = _ref2.event; if (iEvent.type !== 'dragmove' && iEvent.type !== 'dragend') { return; } var draggableElement = interaction.element; var dragEvent = iEvent; var dropResult = getDrop(dragEvent, event, draggableElement); interaction.dropTarget = dropResult.dropzone; interaction.dropElement = dropResult.element; interaction.dropEvents = getDropEvents(interaction, event, dragEvent); }); Interaction.signals.on('action-move', function (_ref3) { var interaction = _ref3.interaction; if (interaction.prepared.name !== 'drag') { return; } fireDropEvents(interaction, interaction.dropEvents); }); Interaction.signals.on('action-end', function (_ref4) { var interaction = _ref4.interaction; if (interaction.prepared.name === 'drag') { fireDropEvents(interaction, interaction.dropEvents); } }); Interaction.signals.on('stop-drag', function (_ref5) { var interaction = _ref5.interaction; interaction.activeDrops.dropzones = interaction.activeDrops.elements = interaction.activeDrops.rects = interaction.dropEvents = null; }); function collectDrops(interaction, element) { var drops = []; var elements = []; element = element || interaction.element; // 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 _ref6; if (_isArray) { if (_i >= _iterator.length) break; _ref6 = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref6 = _i.value; } var current = _ref6; if (!current.options.drop.enabled) { continue; } var accept = current.options.drop.accept; // test the draggable element against the dropzone's accept setting if (utils.is.element(accept) && accept !== element || utils.is.string(accept) && !utils.matchesSelector(element, accept)) { continue; } // query for new elements if necessary var dropElements = utils.is.string(current.target) ? current._context.querySelectorAll(current.target) : [current.target]; for (var i = 0; i < dropElements.length; i++) { var currentElement = dropElements[i]; if (currentElement !== element) { drops.push(current); elements.push(currentElement); } } } return { elements: elements, dropzones: drops }; } function fireActiveDrops(interaction, event) { var prevElement = void 0; // 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) {