Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@interactjs/core

Package Overview
Dependencies
Maintainers
2
Versions
137
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@interactjs/core - npm Package Compare versions

Comparing version 1.4.0-alpha.17 to 1.4.0-alpha.18

defaultOptions.d.ts

21

defaultOptions.js

@@ -1,11 +0,12 @@

export default {
base: {
preventDefault: 'auto',
deltaSource : 'page',
},
perAction: {
enabled : false,
origin: { x: 0, y: 0 },
},
export const defaults = {
base: {
preventDefault: 'auto',
deltaSource: 'page',
},
perAction: {
enabled: false,
origin: { x: 0, y: 0 },
},
};
export default defaults;
//# sourceMappingURL=defaultOptions.js.map

@@ -1,63 +0,54 @@

import * as arr from '@interactjs/utils/arr';
import extend from '@interactjs/utils/extend';
import * as arr from '@interactjs/utils/arr';
import extend from '@interactjs/utils/extend';
import normalize from '@interactjs/utils/normalizeListeners';
function fireUntilImmediateStopped (event, listeners) {
for (const listener of listeners) {
if (event.immediatePropagationStopped) { break; }
listener(event);
}
function fireUntilImmediateStopped(event, listeners) {
for (const listener of listeners) {
if (event.immediatePropagationStopped) {
break;
}
listener(event);
}
}
class Eventable {
constructor (options) {
this.options = extend({}, options || {});
this.types = {};
this.propagationStopped = this.immediatePropagationStopped = false;
}
fire (event) {
let listeners;
const global = this.global;
// Interactable#on() listeners
if ((listeners = this.types[event.type])) {
fireUntilImmediateStopped(event, listeners);
constructor(options) {
this.types = {};
this.propagationStopped = false;
this.immediatePropagationStopped = false;
this.options = extend({}, options || {});
}
// interact.on() listeners
if (!event.propagationStopped && global && (listeners = global[event.type])) {
fireUntilImmediateStopped(event, listeners);
fire(event) {
let listeners;
const global = this.global;
// Interactable#on() listeners
if ((listeners = this.types[event.type])) {
fireUntilImmediateStopped(event, listeners);
}
// interact.on() listeners
if (!event.propagationStopped && global && (listeners = global[event.type])) {
fireUntilImmediateStopped(event, listeners);
}
}
}
on (type, listener) {
const listeners = normalize(type, listener);
for (type in listeners) {
this.types[type] = arr.merge(this.types[type] || [], listeners[type]);
on(type, listener) {
const listeners = normalize(type, listener);
for (type in listeners) {
this.types[type] = arr.merge(this.types[type] || [], listeners[type]);
}
}
}
off (type, listener) {
const listeners = normalize(type, listener);
for (type in listeners) {
const eventList = this.types[type];
if (!eventList || !eventList.length) { continue; }
for (listener of listeners[type]) {
const index = eventList.indexOf(listener);
if (index !== -1) {
eventList.splice(index, 1);
off(type, listener) {
const listeners = normalize(type, listener);
for (type in listeners) {
const eventList = this.types[type];
if (!eventList || !eventList.length) {
continue;
}
for (listener of listeners[type]) {
const index = eventList.indexOf(listener);
if (index !== -1) {
eventList.splice(index, 1);
}
}
}
}
}
}
}
export default Eventable;
//# sourceMappingURL=Eventable.js.map

@@ -1,342 +0,297 @@

import clone from '@interactjs/utils/clone';
import * as is from '@interactjs/utils/is';
import events from '@interactjs/utils/events';
import extend from '@interactjs/utils/extend';
import * as arr from '@interactjs/utils/arr';
import * as arr from '@interactjs/utils/arr';
import browser from '@interactjs/utils/browser';
import clone from '@interactjs/utils/clone';
import { getElementRect, nodeContains, trySelector } from '@interactjs/utils/domUtils';
import events from '@interactjs/utils/events';
import extend from '@interactjs/utils/extend';
import * as is from '@interactjs/utils/is';
import normalizeListeners from '@interactjs/utils/normalizeListeners';
import { getWindow } from '@interactjs/utils/window';
import Eventable from './Eventable';
import {
getElementRect,
nodeContains,
trySelector,
} from '@interactjs/utils/domUtils';
import { getWindow } from '@interactjs/utils/window';
import browser from '@interactjs/utils/browser';
class Interactable {
get _defaults () {
return {
base: {},
perAction: {},
};
}
/** */
constructor (target, options, defaultContext) {
this._actions = options.actions;
this.target = target;
this.events = new Eventable();
this._context = options.context || defaultContext;
this._win = getWindow(trySelector(target)? this._context : target);
this._doc = this._win.document;
this.set(options);
}
setOnEvents (actionName, phases) {
if (is.func(phases.onstart) ) { this.on(`${actionName}start` , phases.onstart ); }
if (is.func(phases.onmove) ) { this.on(`${actionName}move` , phases.onmove ); }
if (is.func(phases.onend) ) { this.on(`${actionName}end` , phases.onend ); }
if (is.func(phases.oninertiastart)) { this.on(`${actionName}inertiastart`, phases.oninertiastart); }
return this;
}
updatePerActionListeners (actionName, prev, cur) {
if (is.array(prev)) {
this.off(actionName, prev);
/** */
export class Interactable {
/** */
constructor(target, options, defaultContext) {
this.events = new Eventable();
this._actions = options.actions;
this.target = target;
this._context = options.context || defaultContext;
this._win = getWindow(trySelector(target) ? this._context : target);
this._doc = this._win.document;
this.set(options);
}
if (is.array(cur)) {
this.on(actionName, cur);
get _defaults() {
return {
base: {},
perAction: {},
};
}
}
setPerAction (actionName, options) {
const defaults = this._defaults;
// for all the default per-action options
for (const optionName in options) {
const actionOptions = this.options[actionName];
const optionValue = options[optionName];
const isArray = is.array(optionValue);
// remove old event listeners and add new ones
if (optionName === 'listeners') {
this.updatePerActionListeners(actionName, actionOptions.listeners, optionValue);
}
// if the option value is an array
if (isArray) {
actionOptions[optionName] = arr.from(optionValue);
}
// if the option value is an object
else if (!isArray && is.plainObject(optionValue)) {
// copy the object
actionOptions[optionName] = extend(
actionOptions[optionName] || {},
clone(optionValue));
// set anabled field to true if it exists in the defaults
if (is.object(defaults.perAction[optionName]) && 'enabled' in defaults.perAction[optionName]) {
actionOptions[optionName].enabled = optionValue.enabled === false? false : true;
setOnEvents(actionName, phases) {
if (is.func(phases.onstart)) {
this.on(`${actionName}start`, phases.onstart);
}
}
// if the option value is a boolean and the default is an object
else if (is.bool(optionValue) && is.object(defaults.perAction[optionName])) {
actionOptions[optionName].enabled = optionValue;
}
// if it's anything else, do a plain assignment
else {
actionOptions[optionName] = optionValue;
}
if (is.func(phases.onmove)) {
this.on(`${actionName}move`, phases.onmove);
}
if (is.func(phases.onend)) {
this.on(`${actionName}end`, phases.onend);
}
if (is.func(phases.oninertiastart)) {
this.on(`${actionName}inertiastart`, phases.oninertiastart);
}
return this;
}
}
/**
* The default function to get an Interactables bounding rect. Can be
* overridden using {@link Interactable.rectChecker}.
*
* @param {Element} [element] The element to measure.
* @return {object} The object's bounding rectangle.
*/
getRect (element) {
element = element || this.target;
if (is.string(this.target) && !(is.element(element))) {
element = this._context.querySelector(this.target);
updatePerActionListeners(actionName, prev, cur) {
if (is.array(prev)) {
this.off(actionName, prev);
}
if (is.array(cur)) {
this.on(actionName, cur);
}
}
return getElementRect(element);
}
/**
* Returns or sets the function used to calculate the interactable's
* element's rectangle
*
* @param {function} [checker] A function which returns this Interactable's
* bounding rectangle. See {@link Interactable.getRect}
* @return {function | object} The checker function or this Interactable
*/
rectChecker (checker) {
if (is.func(checker)) {
this.getRect = checker;
return this;
setPerAction(actionName, options) {
const defaults = this._defaults;
// for all the default per-action options
for (const optionName in options) {
const actionOptions = this.options[actionName];
const optionValue = options[optionName];
const isArray = is.array(optionValue);
// remove old event listeners and add new ones
if (optionName === 'listeners') {
this.updatePerActionListeners(actionName, actionOptions.listeners, optionValue);
}
// if the option value is an array
if (isArray) {
actionOptions[optionName] = arr.from(optionValue);
}
// if the option value is an object
else if (!isArray && is.plainObject(optionValue)) {
// copy the object
actionOptions[optionName] = extend(actionOptions[optionName] || {}, clone(optionValue));
// set anabled field to true if it exists in the defaults
if (is.object(defaults.perAction[optionName]) && 'enabled' in defaults.perAction[optionName]) {
actionOptions[optionName].enabled = optionValue.enabled === false ? false : true;
}
}
// if the option value is a boolean and the default is an object
else if (is.bool(optionValue) && is.object(defaults.perAction[optionName])) {
actionOptions[optionName].enabled = optionValue;
}
// if it's anything else, do a plain assignment
else {
actionOptions[optionName] = optionValue;
}
}
}
if (checker === null) {
delete this.options.getRect;
return this;
/**
* The default function to get an Interactables bounding rect. Can be
* overridden using {@link Interactable.rectChecker}.
*
* @param {Element} [element] The element to measure.
* @return {object} The object's bounding rectangle.
*/
getRect(element) {
element = element
? element
: is.element(this.target)
? this.target
: null;
if (is.string(this.target)) {
element = element || this._context.querySelector(this.target);
}
return getElementRect(element);
}
return this.getRect;
}
_backCompatOption (optionName, newValue) {
if (trySelector(newValue) || is.object(newValue)) {
this.options[optionName] = newValue;
for (const action of this._actions.names) {
this.options[action][optionName] = newValue;
}
return this;
/**
* Returns or sets the function used to calculate the interactable's
* element's rectangle
*
* @param {function} [checker] A function which returns this Interactable's
* bounding rectangle. See {@link Interactable.getRect}
* @return {function | object} The checker function or this Interactable
*/
rectChecker(checker) {
if (is.func(checker)) {
this.getRect = checker;
return this;
}
if (checker === null) {
delete this.options.getRect;
return this;
}
return this.getRect;
}
return this.options[optionName];
}
/**
* Gets or sets the origin of the Interactable's element. The x and y
* of the origin will be subtracted from action event coordinates.
*
* @param {Element | object | string} [origin] An HTML or SVG Element whose
* rect will be used, an object eg. { x: 0, y: 0 } or string 'parent', 'self'
* or any CSS selector
*
* @return {object} The current origin or this Interactable
*/
origin (newValue) {
return this._backCompatOption('origin', newValue);
}
/**
* Returns or sets the mouse coordinate types used to calculate the
* movement of the pointer.
*
* @param {string} [newValue] Use 'client' if you will be scrolling while
* interacting; Use 'page' if you want autoScroll to work
* @return {string | object} The current deltaSource or this Interactable
*/
deltaSource (newValue) {
if (newValue === 'page' || newValue === 'client') {
this.options.deltaSource = newValue;
return this;
_backCompatOption(optionName, newValue) {
if (trySelector(newValue) || is.object(newValue)) {
this.options[optionName] = newValue;
for (const action of this._actions.names) {
this.options[action][optionName] = newValue;
}
return this;
}
return this.options[optionName];
}
return this.options.deltaSource;
}
/**
* Gets the selector context Node of the Interactable. The default is
* `window.document`.
*
* @return {Node} The context Node of this Interactable
*/
context () {
return this._context;
}
inContext (element) {
return (this._context === element.ownerDocument
/**
* Gets or sets the origin of the Interactable's element. The x and y
* of the origin will be subtracted from action event coordinates.
*
* @param {Element | object | string} [origin] An HTML or SVG Element whose
* rect will be used, an object eg. { x: 0, y: 0 } or string 'parent', 'self'
* or any CSS selector
*
* @return {object} The current origin or this Interactable
*/
origin(newValue) {
return this._backCompatOption('origin', newValue);
}
/**
* Returns or sets the mouse coordinate types used to calculate the
* movement of the pointer.
*
* @param {string} [newValue] Use 'client' if you will be scrolling while
* interacting; Use 'page' if you want autoScroll to work
* @return {string | object} The current deltaSource or this Interactable
*/
deltaSource(newValue) {
if (newValue === 'page' || newValue === 'client') {
this.options.deltaSource = newValue;
return this;
}
return this.options.deltaSource;
}
/**
* Gets the selector context Node of the Interactable. The default is
* `window.document`.
*
* @return {Node} The context Node of this Interactable
*/
context() {
return this._context;
}
inContext(element) {
return (this._context === element.ownerDocument
|| nodeContains(this._context, element));
}
/**
* Calls listeners for the given InteractEvent type bound globally
* and directly to this Interactable
*
* @param {InteractEvent} iEvent The InteractEvent object to be fired on this
* Interactable
* @return {Interactable} this Interactable
*/
fire (iEvent) {
this.events.fire(iEvent);
return this;
}
_onOff (method, typeArg, listenerArg, options) {
if (is.object(typeArg) && !is.array(typeArg)) {
options = listenerArg;
listenerArg = null;
}
const addRemove = method === 'on' ? 'add' : 'remove';
const listeners = normalizeListeners(typeArg, listenerArg);
for (let type in listeners) {
if (type === 'wheel') { type = browser.wheelEvent; }
for (const listener of listeners[type]) {
// if it is an action event type
if (arr.contains(this._actions.eventTypes, type)) {
this.events[method](type, listener);
/**
* Calls listeners for the given InteractEvent type bound globally
* and directly to this Interactable
*
* @param {InteractEvent} iEvent The InteractEvent object to be fired on this
* Interactable
* @return {Interactable} this Interactable
*/
fire(iEvent) {
this.events.fire(iEvent);
return this;
}
_onOff(method, typeArg, listenerArg, options) {
if (is.object(typeArg) && !is.array(typeArg)) {
options = listenerArg;
listenerArg = null;
}
// delegated event
else if (is.string(this.target)) {
events[`${addRemove}Delegate`](this.target, this._context, type, listener, options);
const addRemove = method === 'on' ? 'add' : 'remove';
const listeners = normalizeListeners(typeArg, listenerArg);
for (let type in listeners) {
if (type === 'wheel') {
type = browser.wheelEvent;
}
for (const listener of listeners[type]) {
// if it is an action event type
if (arr.contains(this._actions.eventTypes, type)) {
this.events[method](type, listener);
}
// delegated event
else if (is.string(this.target)) {
events[`${addRemove}Delegate`](this.target, this._context, type, listener, options);
}
// remove listener from this Interatable's element
else {
events[addRemove](this.target, type, listener, options);
}
}
}
// remove listener from this Interatable's element
else {
events[addRemove](this.target, type, listener, options);
}
}
return this;
}
return this;
}
/**
* Binds a listener for an InteractEvent, pointerEvent or DOM event.
*
* @param {string | array | object} types The types of events to listen
* for
* @param {function | array | object} [listener] The event listener function(s)
* @param {object | boolean} [options] options object or useCapture flag for
* addEventListener
* @return {Interactable} This Interactable
*/
on (types, listener, options) {
return this._onOff('on', types, listener, options);
}
/**
* Removes an InteractEvent, pointerEvent or DOM event listener.
*
* @param {string | array | object} types The types of events that were
* listened for
* @param {function | array | object} [listener] The event listener function(s)
* @param {object | boolean} [options] options object or useCapture flag for
* removeEventListener
* @return {Interactable} This Interactable
*/
off (types, listener, options) {
return this._onOff('off', types, listener, options);
}
/**
* Reset the options of this Interactable
*
* @param {object} options The new settings to apply
* @return {object} This Interactable
*/
set (options) {
const defaults = this._defaults;
if (!is.object(options)) {
options = {};
/**
* Binds a listener for an InteractEvent, pointerEvent or DOM event.
*
* @param {string | array | object} types The types of events to listen
* for
* @param {function | array | object} [listener] The event listener function(s)
* @param {object | boolean} [options] options object or useCapture flag for
* addEventListener
* @return {Interactable} This Interactable
*/
on(types, listener, options) {
return this._onOff('on', types, listener, options);
}
this.options = clone(defaults.base);
for (const actionName in this._actions.methodDict) {
const methodName = this._actions.methodDict[actionName];
this.options[actionName] = {};
this.setPerAction(actionName, extend(extend({}, defaults.perAction), defaults[actionName]));
this[methodName](options[actionName]);
/**
* Removes an InteractEvent, pointerEvent or DOM event listener.
*
* @param {string | array | object} types The types of events that were
* listened for
* @param {function | array | object} [listener] The event listener function(s)
* @param {object | boolean} [options] options object or useCapture flag for
* removeEventListener
* @return {Interactable} This Interactable
*/
off(types, listener, options) {
return this._onOff('off', types, listener, options);
}
for (const setting in options) {
if (is.func(this[setting])) {
this[setting](options[setting]);
}
/**
* Reset the options of this Interactable
*
* @param {object} options The new settings to apply
* @return {object} This Interactable
*/
set(options) {
const defaults = this._defaults;
if (!is.object(options)) {
options = {};
}
this.options = clone(defaults.base);
for (const actionName in this._actions.methodDict) {
const methodName = this._actions.methodDict[actionName];
this.options[actionName] = {};
this.setPerAction(actionName, extend(extend({}, defaults.perAction), defaults[actionName]));
this[methodName](options[actionName]);
}
for (const setting in options) {
if (is.func(this[setting])) {
this[setting](options[setting]);
}
}
return this;
}
return this;
}
/**
* Remove this interactable from the list of interactables and remove it's
* action capabilities and event listeners
*
* @return {interact}
*/
unset () {
events.remove(this.target, 'all');
if (is.string(this.target)) {
// remove delegated events
for (const type in events.delegatedEvents) {
const 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;
}
/**
* Remove this interactable from the list of interactables and remove it's
* action capabilities and event listeners
*
* @return {interact}
*/
unset() {
events.remove(this.target, 'all');
if (is.string(this.target)) {
// remove delegated events
for (const type in events.delegatedEvents) {
const 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);
}
}
events.remove(this._context, type, events.delegateListener);
events.remove(this._context, type, events.delegateUseCapture, true);
}
else {
events.remove(this.target, 'all');
}
}
else {
events.remove(this, 'all');
}
}
}
export default Interactable;
//# sourceMappingURL=Interactable.js.map

@@ -0,105 +1,87 @@

import { matchesSelector, nodeContains } from '@interactjs/utils/domUtils';
import events from '@interactjs/utils/events';
import * as is from '@interactjs/utils/is';
import events from '@interactjs/utils/events';
import { nodeContains, matchesSelector } from '@interactjs/utils/domUtils';
import { getWindow } from '@interactjs/utils/window';
function preventDefault (interactable, newValue) {
if (/^(always|never|auto)$/.test(newValue)) {
interactable.options.preventDefault = newValue;
return interactable;
}
if (is.bool(newValue)) {
interactable.options.preventDefault = newValue? 'always' : 'never';
return interactable;
}
return interactable.options.preventDefault;
function preventDefault(interactable, newValue) {
if (/^(always|never|auto)$/.test(newValue)) {
interactable.options.preventDefault = newValue;
return interactable;
}
if (is.bool(newValue)) {
interactable.options.preventDefault = newValue ? 'always' : 'never';
return interactable;
}
return interactable.options.preventDefault;
}
function checkAndPreventDefault (interactable, scope, event) {
const setting = interactable.options.preventDefault;
if (setting === 'never') { return; }
if (setting === 'always') {
function checkAndPreventDefault(interactable, scope, event) {
const setting = interactable.options.preventDefault;
if (setting === 'never') {
return;
}
if (setting === 'always') {
event.preventDefault();
return;
}
// setting === 'auto'
// if the browser supports passive event listeners and isn't running on iOS,
// don't preventDefault of touch{start,move} events. CSS touch-action and
// user-select should be used instead of calling event.preventDefault().
if (events.supportsPassive && /^touch(start|move)$/.test(event.type)) {
const doc = getWindow(event.target).document;
const docOptions = scope.getDocOptions(doc);
if (!(docOptions && docOptions.events) || docOptions.events.passive !== false) {
return;
}
}
// don't preventDefault of pointerdown events
if (/^(mouse|pointer|touch)*(down|start)/i.test(event.type)) {
return;
}
// don't preventDefault on editable elements
if (is.element(event.target)
&& matchesSelector(event.target, 'input,select,textarea,[contenteditable=true],[contenteditable=true] *')) {
return;
}
event.preventDefault();
return;
}
// setting === 'auto'
// if the browser supports passive event listeners and isn't running on iOS,
// don't preventDefault of touch{start,move} events. CSS touch-action and
// user-select should be used instead of calling event.preventDefault().
if (events.supportsPassive && /^touch(start|move)$/.test(event.type)) {
const doc = getWindow(event.target).document;
const docOptions = scope.getDocOptions(doc);
if (!(docOptions && docOptions.events) || docOptions.events.passive !== false) {
return;
}
}
// don't preventDefault of pointerdown events
if (/^(mouse|pointer|touch)*(down|start)/i.test(event.type)) {
return;
}
// don't preventDefault on editable elements
if (is.element(event.target)
&& matchesSelector(event.target, 'input,select,textarea,[contenteditable=true],[contenteditable=true] *')) {
return;
}
event.preventDefault();
}
function onInteractionEvent ({ interaction, event }) {
if (interaction.target) {
interaction.target.checkAndPreventDefault(event);
}
}
export function install (scope) {
/** @lends Interactable */
const Interactable = scope.Interactable;
/**
* 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
*
* @param {string} [newValue] `'always'`, `'never'` or `'auto'`
* @return {string | Interactable} The current setting or this Interactable
*/
Interactable.prototype.preventDefault = function (newValue) {
return preventDefault(this, newValue);
};
Interactable.prototype.checkAndPreventDefault = function (event) {
return checkAndPreventDefault(this, scope, event);
};
for (const eventSignal of ['down', 'move', 'up', 'cancel']) {
scope.interactions.signals.on(eventSignal, onInteractionEvent);
}
// prevent native HTML5 drag on interact.js target elements
scope.interactions.eventMap.dragstart = function preventNativeDrag (event) {
for (const interaction of scope.interactions.list) {
if (interaction.element
&& (interaction.element === event.target
|| nodeContains(interaction.element, event.target))) {
function onInteractionEvent({ interaction, event }) {
if (interaction.target) {
interaction.target.checkAndPreventDefault(event);
return;
}
}
};
}
export function install(scope) {
/** @lends Interactable */
const Interactable = scope.Interactable;
/**
* 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
*
* @param {string} [newValue] `'always'`, `'never'` or `'auto'`
* @return {string | Interactable} The current setting or this Interactable
*/
Interactable.prototype.preventDefault = function (newValue) {
return preventDefault(this, newValue);
};
Interactable.prototype.checkAndPreventDefault = function (event) {
return checkAndPreventDefault(this, scope, event);
};
for (const eventSignal of ['down', 'move', 'up', 'cancel']) {
scope.interactions.signals.on(eventSignal, onInteractionEvent);
}
// prevent native HTML5 drag on interact.js target elements
scope.interactions.eventMap.dragstart = function preventNativeDrag(event) {
for (const interaction of scope.interactions.list) {
if (interaction.element
&& (interaction.element === event.target
|| nodeContains(interaction.element, event.target))) {
interaction.target.checkAndPreventDefault(event);
return;
}
}
};
}
export default { install };
//# sourceMappingURL=interactablePreventDefault.js.map

@@ -1,146 +0,126 @@

import extend from '@interactjs/utils/extend';
import extend from '@interactjs/utils/extend';
import getOriginXY from '@interactjs/utils/getOriginXY';
import defaults from './defaultOptions';
import hypot from '@interactjs/utils/hypot';
import hypot from '@interactjs/utils/hypot';
import defaults from './defaultOptions';
class InteractEvent {
/** */
constructor (interaction, event, actionName, phase, element, related, preEnd, type) {
element = element || interaction.element;
const target = interaction.target;
const deltaSource = (target && target.options || defaults).deltaSource;
const origin = getOriginXY(target, element, actionName);
const starting = phase === 'start';
const ending = phase === 'end';
const prevEvent = starting? this : interaction.prevEvent;
const coords = starting
? interaction.coords.start
: ending
? { page: prevEvent.page, client: prevEvent.client, timeStamp: interaction.coords.cur.timeStamp }
: interaction.coords.cur;
this.page = extend({}, coords.page);
this.client = extend({}, coords.client);
this.timeStamp = coords.timeStamp;
if (!ending) {
this.page.x -= origin.x;
this.page.y -= origin.y;
this.client.x -= origin.x;
this.client.y -= origin.y;
/** */
constructor(interaction, event, actionName, phase, element, related, preEnd, type) {
this.immediatePropagationStopped = false;
this.propagationStopped = false;
element = element || interaction.element;
const target = interaction.target;
const deltaSource = (target && target.options || defaults).deltaSource;
const origin = getOriginXY(target, element, actionName);
const starting = phase === 'start';
const ending = phase === 'end';
const prevEvent = starting ? this : interaction.prevEvent;
const coords = starting
? interaction.coords.start
: ending
? { page: prevEvent.page, client: prevEvent.client, timeStamp: interaction.coords.cur.timeStamp }
: interaction.coords.cur;
this.page = extend({}, coords.page);
this.client = extend({}, coords.client);
this.timeStamp = coords.timeStamp;
if (!ending) {
this.page.x -= origin.x;
this.page.y -= origin.y;
this.client.x -= origin.x;
this.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 = type || (actionName + (phase || ''));
this.interaction = interaction;
this.interactable = target;
this.t0 = starting
? interaction.pointers[interaction.pointers.length - 1].downTime
: prevEvent.t0;
this.x0 = interaction.coords.start.page.x - origin.x;
this.y0 = interaction.coords.start.page.y - origin.y;
this.clientX0 = interaction.coords.start.client.x - origin.x;
this.clientY0 = interaction.coords.start.client.y - origin.y;
if (starting || ending) {
this.delta = { x: 0, y: 0 };
}
else {
this.delta = {
x: this[deltaSource].x - prevEvent[deltaSource].x,
y: this[deltaSource].y - prevEvent[deltaSource].y,
};
}
this.dt = interaction.coords.delta.timeStamp;
this.duration = this.timeStamp - this.t0;
// velocity and speed in pixels per second
this.velocity = extend({}, interaction.coords.velocity[deltaSource]);
this.speed = hypot(this.velocity.x, this.velocity.y);
this.swipe = (ending || phase === 'inertiastart') ? this.getSwipe() : null;
}
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 = type || (actionName + (phase || ''));
this.interaction = interaction;
this.interactable = target;
this.t0 = starting
? interaction.pointers[interaction.pointers.length - 1].downTime
: prevEvent.t0;
this.x0 = interaction.coords.start.page.x - origin.x;
this.y0 = interaction.coords.start.page.y - origin.y;
this.clientX0 = interaction.coords.start.client.x - origin.x;
this.clientY0 = interaction.coords.start.client.y - origin.y;
if (starting || ending) {
this.delta = { x: 0, y: 0 };
get pageX() { return this.page.x; }
get pageY() { return this.page.y; }
set pageX(value) { this.page.x = value; }
set pageY(value) { this.page.y = value; }
get clientX() { return this.client.x; }
get clientY() { return this.client.y; }
set clientX(value) { this.client.x = value; }
set clientY(value) { this.client.y = value; }
get dx() { return this.delta.x; }
get dy() { return this.delta.y; }
set dx(value) { this.delta.x = value; }
set dy(value) { this.delta.y = value; }
get velocityX() { return this.velocity.x; }
get velocityY() { return this.velocity.y; }
set velocityX(value) { this.velocity.x = value; }
set velocityY(value) { this.velocity.y = value; }
getSwipe() {
const interaction = this.interaction;
if (interaction.prevEvent.speed < 600
|| this.timeStamp - interaction.prevEvent.timeStamp > 150) {
return null;
}
let angle = 180 * Math.atan2(interaction.prevEvent.velocityY, interaction.prevEvent.velocityX) / Math.PI;
const overlap = 22.5;
if (angle < 0) {
angle += 360;
}
const left = 135 - overlap <= angle && angle < 225 + overlap;
const up = 225 - overlap <= angle && angle < 315 + overlap;
const right = !left && (315 - overlap <= angle || angle < 45 + overlap);
const down = !up && 45 - overlap <= angle && angle < 135 + overlap;
return {
up,
down,
left,
right,
angle,
speed: interaction.prevEvent.speed,
velocity: {
x: interaction.prevEvent.velocityX,
y: interaction.prevEvent.velocityY,
},
};
}
else {
this.delta = {
x: this[deltaSource].x - prevEvent[deltaSource].x,
y: this[deltaSource].y - prevEvent[deltaSource].y,
};
preventDefault() { }
/**
* Don't call listeners on the remaining targets
*/
stopImmediatePropagation() {
this.immediatePropagationStopped = this.propagationStopped = true;
}
this.dt = interaction.coords.delta.timeStamp;
this.duration = this.timeStamp - this.t0;
// velocity and speed in pixels per second
this.velocity = extend({}, interaction.coords.velocity[deltaSource]);
this.speed = hypot(this.velocity.x, this.velocity.y);
this.swipe = (ending || phase === 'inertiastart')? this.getSwipe() : null;
}
get pageX () { return this.page.x; }
get pageY () { return this.page.y; }
set pageX (value) { this.page.x = value; }
set pageY (value) { this.page.y = value; }
get clientX () { return this.client.x; }
get clientY () { return this.client.y; }
set clientX (value) { this.client.x = value; }
set clientY (value) { this.client.y = value; }
get dx () { return this.delta.x; }
get dy () { return this.delta.y; }
set dx (value) { this.delta.x = value; }
set dy (value) { this.delta.y = value; }
get velocityX () { return this.velocity.x; }
get velocityY () { return this.velocity.y; }
set velocityX (value) { this.velocity.x = value; }
set velocityY (value) { this.velocity.y = value; }
getSwipe () {
const interaction = this.interaction;
if (interaction.prevEvent.speed < 600
|| this.timeStamp - interaction.prevEvent.timeStamp > 150) {
return null;
/**
* Don't call any other listeners (even on the current target)
*/
stopPropagation() {
this.propagationStopped = true;
}
let angle = 180 * Math.atan2(interaction.prevEvent.velocityY, interaction.prevEvent.velocityX) / Math.PI;
const overlap = 22.5;
if (angle < 0) {
angle += 360;
}
const left = 135 - overlap <= angle && angle < 225 + overlap;
const up = 225 - overlap <= angle && angle < 315 + overlap;
const right = !left && (315 - overlap <= angle || angle < 45 + overlap);
const down = !up && 45 - overlap <= angle && angle < 135 + overlap;
return {
up,
down,
left,
right,
angle,
speed: interaction.prevEvent.speed,
velocity: {
x: interaction.prevEvent.velocityX,
y: interaction.prevEvent.velocityY,
},
};
}
preventDefault () {}
/** */
stopImmediatePropagation () {
this.immediatePropagationStopped = this.propagationStopped = true;
}
/** */
stopPropagation () {
this.propagationStopped = true;
}
}
export default InteractEvent;
//# sourceMappingURL=InteractEvent.js.map

@@ -0,429 +1,360 @@

import * as utils from '@interactjs/utils';
import InteractEvent from './InteractEvent';
import * as utils from '@interactjs/utils';
class Interaction {
get pointerMoveTolerance () {
return 1;
}
/** */
constructor ({ pointerType, signals }) {
this._signals = signals;
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 = [/* { id, pointer, event, target, downTime }*/];
this.coords = {
// Starting InteractEvent pointer coordinates
start: utils.pointer.newCoords(),
// Previous native pointer move event coordinates
prev: utils.pointer.newCoords(),
// current native pointer move event coordinates
cur: utils.pointer.newCoords(),
// Change in coordinates and time of the pointer
delta: utils.pointer.newCoords(),
// pointer velocity
velocity: utils.pointer.newCoords(),
};
this.downEvent = null; // pointerdown/mousedown/touchstart event
this.downPointer = {};
this._latestPointer = {
pointer: null,
event: null,
eventTarget : null,
};
this.prevEvent = null; // previous action event
this.pointerIsDown = false;
this.pointerWasMoved = false;
this._interacting = false;
this._ending = false;
this.pointerType = pointerType;
this._signals.fire('new', this);
}
pointerDown (pointer, event, eventTarget) {
const pointerIndex = this.updatePointer(pointer, event, eventTarget, true);
this._signals.fire('down', {
pointer,
event,
eventTarget,
pointerIndex,
interaction: this,
});
}
/**
* ```js
* 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 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)
*
* @param {object} action The action to be performed - drag, resize, etc.
* @param {Interactable} target The Interactable to target
* @param {Element} element The DOM Element to target
* @return {object} interact
*/
start (action, target, element) {
if (this.interacting()
|| !this.pointerIsDown
|| this.pointers.length < (action.name === 'gesture'? 2 : 1)) {
return;
export class Interaction {
/** */
constructor({ pointerType, signals }) {
// current interactable being interacted with
this.target = null;
// the target element of the interactable
this.element = null;
// action that's ready to be fired on next move event
this.prepared = {
name: null,
axis: null,
edges: null,
};
// keep track of added pointers
this.pointers = [];
// pointerdown/mousedown/touchstart event
this.downEvent = null;
this.downPointer = {};
this._latestPointer = {
pointer: null,
event: null,
eventTarget: null,
};
// previous action event
this.prevEvent = null;
this.pointerIsDown = false;
this.pointerWasMoved = false;
this._interacting = false;
this._ending = false;
this.simulation = null;
/**
* @alias Interaction.prototype.move
*/
this.doMove = utils.warnOnce(function (signalArg) {
this.move(signalArg);
}, 'The interaction.doMove() method has been renamed to interaction.move()');
this.coords = {
// Starting InteractEvent pointer coordinates
start: utils.pointer.newCoords(),
// Previous native pointer move event coordinates
prev: utils.pointer.newCoords(),
// current native pointer move event coordinates
cur: utils.pointer.newCoords(),
// Change in coordinates and time of the pointer
delta: utils.pointer.newCoords(),
// pointer velocity
velocity: utils.pointer.newCoords(),
};
this._signals = signals;
this.pointerType = pointerType;
this._signals.fire('new', this);
}
utils.copyAction(this.prepared, action);
this.target = target;
this.element = element;
this._interacting = this._doPhase({
interaction: this,
event: this.downEvent,
phase: 'start',
});
}
pointerMove (pointer, event, eventTarget) {
if (!this.simulation) {
this.updatePointer(pointer, event, eventTarget, false);
utils.pointer.setCoords(this.coords.cur, this.pointers.map(p => p.pointer));
get pointerMoveTolerance() {
return 1;
}
const duplicateMove = (this.coords.cur.page.x === this.coords.prev.page.x
&& this.coords.cur.page.y === this.coords.prev.page.y
&& this.coords.cur.client.x === this.coords.prev.client.x
&& this.coords.cur.client.y === this.coords.prev.client.y);
let dx;
let dy;
// register movement greater than pointerMoveTolerance
if (this.pointerIsDown && !this.pointerWasMoved) {
dx = this.coords.cur.client.x - this.coords.start.client.x;
dy = this.coords.cur.client.y - this.coords.start.client.y;
this.pointerWasMoved = utils.hypot(dx, dy) > this.pointerMoveTolerance;
pointerDown(pointer, event, eventTarget) {
const pointerIndex = this.updatePointer(pointer, event, eventTarget, true);
this._signals.fire('down', {
pointer,
event,
eventTarget,
pointerIndex,
interaction: this,
});
}
const signalArg = {
pointer,
pointerIndex: this.getPointerIndex(pointer),
event,
eventTarget,
dx,
dy,
duplicate: duplicateMove,
interaction: this,
};
if (!duplicateMove) {
// set pointer coordinate, time changes and velocity
utils.pointer.setCoordDeltas(this.coords.delta, this.coords.prev, this.coords.cur);
utils.pointer.setCoordVelocity(this.coords.velocity, this.coords.delta);
/**
* ```js
* 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 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)
*
* @param {object} action The action to be performed - drag, resize, etc.
* @param {Interactable} target The Interactable to target
* @param {Element} element The DOM Element to target
* @return {object} interact
*/
start(action, target, element) {
if (this.interacting()
|| !this.pointerIsDown
|| this.pointers.length < (action.name === 'gesture' ? 2 : 1)) {
return;
}
utils.copyAction(this.prepared, action);
this.target = target;
this.element = element;
this._interacting = this._doPhase({
interaction: this,
event: this.downEvent,
phase: 'start',
});
}
this._signals.fire('move', signalArg);
if (!duplicateMove) {
// if interacting, fire an 'action-move' signal etc
if (this.interacting()) {
this.move(signalArg);
}
if (this.pointerWasMoved) {
utils.pointer.copyCoords(this.coords.prev, this.coords.cur);
}
pointerMove(pointer, event, eventTarget) {
if (!this.simulation) {
this.updatePointer(pointer, event, eventTarget, false);
utils.pointer.setCoords(this.coords.cur, this.pointers.map(p => p.pointer));
}
const duplicateMove = (this.coords.cur.page.x === this.coords.prev.page.x
&& this.coords.cur.page.y === this.coords.prev.page.y
&& this.coords.cur.client.x === this.coords.prev.client.x
&& this.coords.cur.client.y === this.coords.prev.client.y);
let dx;
let dy;
// register movement greater than pointerMoveTolerance
if (this.pointerIsDown && !this.pointerWasMoved) {
dx = this.coords.cur.client.x - this.coords.start.client.x;
dy = this.coords.cur.client.y - this.coords.start.client.y;
this.pointerWasMoved = utils.hypot(dx, dy) > this.pointerMoveTolerance;
}
const signalArg = {
pointer,
pointerIndex: this.getPointerIndex(pointer),
event,
eventTarget,
dx,
dy,
duplicate: duplicateMove,
interaction: this,
};
if (!duplicateMove) {
// set pointer coordinate, time changes and velocity
utils.pointer.setCoordDeltas(this.coords.delta, this.coords.prev, this.coords.cur);
utils.pointer.setCoordVelocity(this.coords.velocity, this.coords.delta);
}
this._signals.fire('move', signalArg);
if (!duplicateMove) {
// if interacting, fire an 'action-move' signal etc
if (this.interacting()) {
this.move(signalArg);
}
if (this.pointerWasMoved) {
utils.pointer.copyCoords(this.coords.prev, this.coords.cur);
}
}
}
}
/**
* ```js
* 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.move();
* }
* });
* ```
*
* 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.
*/
move (signalArg) {
signalArg = utils.extend({
pointer: this._latestPointer.pointer,
event: this._latestPointer.event,
eventTarget: this._latestPointer.eventTarget,
interaction: this,
noBefore: false,
}, signalArg || {});
signalArg.phase = 'move';
this._doPhase(signalArg);
}
// End interact move events and stop auto-scroll unless simulation is running
pointerUp (pointer, event, eventTarget, curEventTarget) {
let pointerIndex = this.getPointerIndex(pointer);
if (pointerIndex === -1) {
pointerIndex = this.updatePointer(pointer, event, eventTarget, false);
/**
* ```js
* 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.move();
* }
* });
* ```
*
* 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.
*/
move(signalArg) {
signalArg = utils.extend({
pointer: this._latestPointer.pointer,
event: this._latestPointer.event,
eventTarget: this._latestPointer.eventTarget,
interaction: this,
noBefore: false,
}, signalArg || {});
signalArg.phase = 'move';
this._doPhase(signalArg);
}
this._signals.fire(/cancel$/i.test(event.type)? 'cancel' : 'up', {
pointer,
pointerIndex,
event,
eventTarget,
curEventTarget,
interaction: this,
});
if (!this.simulation) {
this.end(event);
// End interact move events and stop auto-scroll unless simulation is running
pointerUp(pointer, event, eventTarget, curEventTarget) {
let pointerIndex = this.getPointerIndex(pointer);
if (pointerIndex === -1) {
pointerIndex = this.updatePointer(pointer, event, eventTarget, false);
}
this._signals.fire(/cancel$/i.test(event.type) ? 'cancel' : 'up', {
pointer,
pointerIndex,
event,
eventTarget,
curEventTarget,
interaction: this,
});
if (!this.simulation) {
this.end(event);
}
this.pointerIsDown = false;
this.removePointer(pointer, event);
}
this.pointerIsDown = false;
this.removePointer(pointer, event);
}
documentBlur (event) {
this.end(event);
this._signals.fire('blur', { event, interaction: this });
}
/**
* ```js
* 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();
* }
* });
* ```
*
* @param {PointerEvent} [event]
*/
end (event) {
this._ending = true;
event = event || this._latestPointer.event;
let endPhaseResult;
if (this.interacting()) {
endPhaseResult = this._doPhase({
event,
interaction: this,
phase: 'end',
});
documentBlur(event) {
this.end(event);
this._signals.fire('blur', { event, interaction: this });
}
this._ending = false;
if (endPhaseResult === true) {
this.stop();
/**
* ```js
* 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();
* }
* });
* ```
*
* @param {PointerEvent} [event]
*/
end(event) {
this._ending = true;
event = event || this._latestPointer.event;
let endPhaseResult;
if (this.interacting()) {
endPhaseResult = this._doPhase({
event,
interaction: this,
phase: 'end',
});
}
this._ending = false;
if (endPhaseResult === true) {
this.stop();
}
}
}
currentAction () {
return this._interacting? this.prepared.name: null;
}
interacting () {
return this._interacting;
}
/** */
stop () {
this._signals.fire('stop', { interaction: this });
this.target = this.element = null;
this._interacting = false;
this.prepared.name = this.prevEvent = null;
}
getPointerIndex (pointer) {
const pointerId = utils.pointer.getPointerId(pointer);
// mouse and pen interactions may have only one pointer
return (this.pointerType === 'mouse' || this.pointerType === 'pen')
? this.pointers.length - 1
: utils.arr.findIndex(this.pointers, curPointer => curPointer.id === pointerId);
}
getPointerInfo (pointer) {
return this.pointers[this.getPointerIndex(pointer)];
}
updatePointer (pointer, event, eventTarget, down) {
const id = utils.pointer.getPointerId(pointer);
let pointerIndex = this.getPointerIndex(pointer);
let pointerInfo = this.pointers[pointerIndex];
down = down === false
? false
: down || /(down|start)$/i.test(event.type);
if (!pointerInfo) {
pointerInfo = {
id,
pointer,
event,
downTime: null,
downTarget: null,
};
pointerIndex = this.pointers.length;
this.pointers.push(pointerInfo);
currentAction() {
return this._interacting ? this.prepared.name : null;
}
else {
pointerInfo.pointer = pointer;
interacting() {
return this._interacting;
}
if (down) {
this.pointerIsDown = true;
if (!this.interacting()) {
utils.pointer.setCoords(this.coords.start, this.pointers.map(p => p.pointer));
utils.pointer.copyCoords(this.coords.cur , this.coords.start);
utils.pointer.copyCoords(this.coords.prev, this.coords.start);
utils.pointer.pointerExtend(this.downPointer, pointer);
this.downEvent = event;
pointerInfo.downTime = this.coords.cur.timeStamp;
pointerInfo.downTarget = eventTarget;
this.pointerWasMoved = false;
}
/** */
stop() {
this._signals.fire('stop', { interaction: this });
this.target = this.element = null;
this._interacting = false;
this.prepared.name = this.prevEvent = null;
}
this._updateLatestPointer(pointer, event, eventTarget);
this._signals.fire('update-pointer', {
pointer,
event,
eventTarget,
down,
pointerInfo,
pointerIndex,
interaction: this,
});
return pointerIndex;
}
removePointer (pointer, event) {
const pointerIndex = this.getPointerIndex(pointer);
if (pointerIndex === -1) { return; }
const pointerInfo = this.pointers[pointerIndex];
this._signals.fire('remove-pointer', {
pointer,
event,
pointerIndex,
pointerInfo,
interaction: this,
});
this.pointers.splice(pointerIndex, 1);
}
_updateLatestPointer (pointer, event, eventTarget) {
this._latestPointer.pointer = pointer;
this._latestPointer.event = event;
this._latestPointer.eventTarget = eventTarget;
}
_createPreparedEvent (event, phase, preEnd, type) {
const actionName = this.prepared.name;
return new InteractEvent(this, event, actionName, phase, this.element, null, preEnd, type);
}
_fireEvent (iEvent) {
this.target.fire(iEvent);
if (!this.prevEvent || iEvent.timeStamp >= this.prevEvent.timeStamp) {
this.prevEvent = iEvent;
getPointerIndex(pointer) {
const pointerId = utils.pointer.getPointerId(pointer);
// mouse and pen interactions may have only one pointer
return (this.pointerType === 'mouse' || this.pointerType === 'pen')
? this.pointers.length - 1
: utils.arr.findIndex(this.pointers, curPointer => curPointer.id === pointerId);
}
}
_doPhase (signalArg) {
const { event, phase, preEnd, type } = signalArg;
if (!signalArg.noBefore) {
const beforeResult = this._signals.fire(`before-action-${phase}`, signalArg);
if (beforeResult === false) {
return false;
}
getPointerInfo(pointer) {
return this.pointers[this.getPointerIndex(pointer)];
}
const iEvent = signalArg.iEvent = this._createPreparedEvent(event, phase, preEnd, type);
this._signals.fire(`action-${phase}`, signalArg);
this._fireEvent(iEvent);
this._signals.fire(`after-action-${phase}`, signalArg);
return true;
}
updatePointer(pointer, event, eventTarget, down) {
const id = utils.pointer.getPointerId(pointer);
let pointerIndex = this.getPointerIndex(pointer);
let pointerInfo = this.pointers[pointerIndex];
down = down === false
? false
: down || /(down|start)$/i.test(event.type);
if (!pointerInfo) {
pointerInfo = new PointerInfo(id, pointer, event, null, null);
pointerIndex = this.pointers.length;
this.pointers.push(pointerInfo);
}
else {
pointerInfo.pointer = pointer;
}
if (down) {
this.pointerIsDown = true;
if (!this.interacting()) {
utils.pointer.setCoords(this.coords.start, this.pointers.map(p => p.pointer));
utils.pointer.copyCoords(this.coords.cur, this.coords.start);
utils.pointer.copyCoords(this.coords.prev, this.coords.start);
utils.pointer.pointerExtend(this.downPointer, pointer);
this.downEvent = event;
pointerInfo.downTime = this.coords.cur.timeStamp;
pointerInfo.downTarget = eventTarget;
this.pointerWasMoved = false;
}
}
this._updateLatestPointer(pointer, event, eventTarget);
this._signals.fire('update-pointer', {
pointer,
event,
eventTarget,
down,
pointerInfo,
pointerIndex,
interaction: this,
});
return pointerIndex;
}
removePointer(pointer, event) {
const pointerIndex = this.getPointerIndex(pointer);
if (pointerIndex === -1) {
return;
}
const pointerInfo = this.pointers[pointerIndex];
this._signals.fire('remove-pointer', {
pointer,
event,
pointerIndex,
pointerInfo,
interaction: this,
});
this.pointers.splice(pointerIndex, 1);
}
_updateLatestPointer(pointer, event, eventTarget) {
this._latestPointer.pointer = pointer;
this._latestPointer.event = event;
this._latestPointer.eventTarget = eventTarget;
}
_createPreparedEvent(event, phase, preEnd, type) {
const actionName = this.prepared.name;
return new InteractEvent(this, event, actionName, phase, this.element, null, preEnd, type);
}
_fireEvent(iEvent) {
this.target.fire(iEvent);
if (!this.prevEvent || iEvent.timeStamp >= this.prevEvent.timeStamp) {
this.prevEvent = iEvent;
}
}
_doPhase(signalArg) {
const { event, phase, preEnd, type } = signalArg;
if (!signalArg.noBefore) {
const beforeResult = this._signals.fire(`before-action-${phase}`, signalArg);
if (beforeResult === false) {
return false;
}
}
const iEvent = signalArg.iEvent = this._createPreparedEvent(event, phase, preEnd, type);
this._signals.fire(`action-${phase}`, signalArg);
this._fireEvent(iEvent);
this._signals.fire(`after-action-${phase}`, signalArg);
return true;
}
}
/**
* @alias Interaction.prototype.move
*/
Interaction.prototype.doMove = utils.warnOnce(
function (signalArg) {
this.move(signalArg);
},
'The interaction.doMove() method has been renamed to interaction.move()');
export class PointerInfo {
constructor(id, pointer, event, downTime, downTarget) {
this.id = id;
this.pointer = pointer;
this.event = event;
this.downTime = downTime;
this.downTarget = downTarget;
}
}
export default Interaction;
//# sourceMappingURL=Interaction.js.map

@@ -0,210 +1,174 @@

import browser from '@interactjs/utils/browser';
import domObjects from '@interactjs/utils/domObjects';
import events from '@interactjs/utils/events';
import finder from '@interactjs/utils/interactionFinder';
import pointerUtils from '@interactjs/utils/pointerUtils';
import Signals from '@interactjs/utils/Signals';
import InteractionBase from './Interaction';
import events from '@interactjs/utils/events';
import finder from '@interactjs/utils/interactionFinder';
import browser from '@interactjs/utils/browser';
import domObjects from '@interactjs/utils/domObjects';
import pointerUtils from '@interactjs/utils/pointerUtils';
import Signals from '@interactjs/utils/Signals';
const methodNames = [
'pointerDown', 'pointerMove', 'pointerUp',
'updatePointer', 'removePointer', 'windowBlur',
'pointerDown', 'pointerMove', 'pointerUp',
'updatePointer', 'removePointer', 'windowBlur',
];
function install (scope) {
const signals = new Signals();
const listeners = {};
for (const method of methodNames) {
listeners[method] = doOnInteractions(method, scope);
}
const eventMap = { /* 'eventType': listenerFunc */ };
const pEventTypes = browser.pEventTypes;
if (domObjects.PointerEvent) {
eventMap[pEventTypes.down ] = listeners.pointerDown;
eventMap[pEventTypes.move ] = listeners.pointerMove;
eventMap[pEventTypes.up ] = listeners.pointerUp;
eventMap[pEventTypes.cancel] = listeners.pointerUp;
}
else {
eventMap.mousedown = listeners.pointerDown;
eventMap.mousemove = listeners.pointerMove;
eventMap.mouseup = listeners.pointerUp;
eventMap.touchstart = listeners.pointerDown;
eventMap.touchmove = listeners.pointerMove;
eventMap.touchend = listeners.pointerUp;
eventMap.touchcancel = listeners.pointerUp;
}
eventMap.blur = event => {
for (const interaction of scope.interactions.list) {
interaction.documentBlur(event);
function install(scope) {
const signals = new Signals();
const listeners = {};
for (const method of methodNames) {
listeners[method] = doOnInteractions(method, scope);
}
};
scope.signals.on('add-document' , onDocSignal);
scope.signals.on('remove-document', onDocSignal);
// for ignoring browser's simulated mouse events
scope.prevTouchTime = 0;
scope.Interaction = class Interaction extends InteractionBase {
get pointerMoveTolerance () {
return scope.interactions.pointerMoveTolerance;
const pEventTypes = browser.pEventTypes;
const eventMap = {};
if (domObjects.PointerEvent) {
eventMap[pEventTypes.down] = listeners.pointerDown;
eventMap[pEventTypes.move] = listeners.pointerMove;
eventMap[pEventTypes.up] = listeners.pointerUp;
eventMap[pEventTypes.cancel] = listeners.pointerUp;
}
set pointerMoveTolerance (value) {
scope.interactions.pointerMoveTolerance = value;
else {
eventMap.mousedown = listeners.pointerDown;
eventMap.mousemove = listeners.pointerMove;
eventMap.mouseup = listeners.pointerUp;
eventMap.touchstart = listeners.pointerDown;
eventMap.touchmove = listeners.pointerMove;
eventMap.touchend = listeners.pointerUp;
eventMap.touchcancel = listeners.pointerUp;
}
};
scope.interactions = {
signals,
// all active and idle interactions
list: [],
new (options) {
options.signals = signals;
return new scope.Interaction(options);
},
listeners,
eventMap,
pointerMoveTolerance: 1,
};
scope.actions = {
names: [],
methodDict: {},
eventTypes: [],
};
eventMap.blur = event => {
for (const interaction of scope.interactions.list) {
interaction.documentBlur(event);
}
};
scope.signals.on('add-document', onDocSignal);
scope.signals.on('remove-document', onDocSignal);
// for ignoring browser's simulated mouse events
scope.prevTouchTime = 0;
scope.Interaction = class Interaction extends InteractionBase {
get pointerMoveTolerance() {
return scope.interactions.pointerMoveTolerance;
}
set pointerMoveTolerance(value) {
scope.interactions.pointerMoveTolerance = value;
}
};
scope.interactions = {
signals,
// all active and idle interactions
list: [],
new(options) {
options.signals = signals;
return new scope.Interaction(options);
},
listeners,
eventMap,
pointerMoveTolerance: 1,
};
scope.actions = {
names: [],
methodDict: {},
eventTypes: [],
};
}
function doOnInteractions (method, scope) {
return (function (event) {
const interactions = scope.interactions.list;
const pointerType = pointerUtils.getPointerType(event);
const [eventTarget, curEventTarget] = pointerUtils.getEventTargets(event);
const matches = []; // [ [pointer, interaction], ...]
if (browser.supportsTouch && /touch/.test(event.type)) {
scope.prevTouchTime = new Date().getTime();
for (const changedTouch of event.changedTouches) {
const pointer = changedTouch;
const pointerId = pointerUtils.getPointerId(pointer);
const searchDetails = {
pointer,
pointerId,
pointerType,
eventType: event.type,
eventTarget,
curEventTarget,
scope,
};
const interaction = getInteraction(searchDetails);
matches.push([
searchDetails.pointer,
searchDetails.eventTarget,
searchDetails.curEventTarget,
interaction,
]);
}
}
else {
let invalidPointer = false;
if (!browser.supportsPointerEvent && /mouse/.test(event.type)) {
// ignore mouse events while touch interactions are active
for (let i = 0; i < interactions.length && !invalidPointer; i++) {
invalidPointer = interactions[i].pointerType !== 'mouse' && interactions[i].pointerIsDown;
function doOnInteractions(method, scope) {
return (function (event) {
const interactions = scope.interactions.list;
const pointerType = pointerUtils.getPointerType(event);
const [eventTarget, curEventTarget] = pointerUtils.getEventTargets(event);
const matches = []; // [ [pointer, interaction], ...]
if (browser.supportsTouch && /touch/.test(event.type)) {
scope.prevTouchTime = new Date().getTime();
for (const changedTouch of event.changedTouches) {
const pointer = changedTouch;
const pointerId = pointerUtils.getPointerId(pointer);
const searchDetails = {
pointer,
pointerId,
pointerType,
eventType: event.type,
eventTarget,
curEventTarget,
scope,
};
const interaction = getInteraction(searchDetails);
matches.push([
searchDetails.pointer,
searchDetails.eventTarget,
searchDetails.curEventTarget,
interaction,
]);
}
}
// try to ignore mouse events that are simulated by the browser
// after a touch event
invalidPointer = invalidPointer
|| (new Date().getTime() - scope.prevTouchTime < 500)
// on iOS and Firefox Mobile, MouseEvent.timeStamp is zero if simulated
|| event.timeStamp === 0;
}
if (!invalidPointer) {
const searchDetails = {
pointer: event,
pointerId: pointerUtils.getPointerId(event),
pointerType,
eventType: event.type,
curEventTarget,
eventTarget,
scope,
};
const interaction = getInteraction(searchDetails);
matches.push([
searchDetails.pointer,
searchDetails.eventTarget,
searchDetails.curEventTarget,
interaction,
]);
}
}
// eslint-disable-next-line no-shadow
for (const [pointer, eventTarget, curEventTarget, interaction] of matches) {
interaction[method](pointer, event, eventTarget, curEventTarget);
}
});
else {
let invalidPointer = false;
if (!browser.supportsPointerEvent && /mouse/.test(event.type)) {
// ignore mouse events while touch interactions are active
for (let i = 0; i < interactions.length && !invalidPointer; i++) {
invalidPointer = interactions[i].pointerType !== 'mouse' && interactions[i].pointerIsDown;
}
// try to ignore mouse events that are simulated by the browser
// after a touch event
invalidPointer = invalidPointer
|| (new Date().getTime() - scope.prevTouchTime < 500)
// on iOS and Firefox Mobile, MouseEvent.timeStamp is zero if simulated
|| event.timeStamp === 0;
}
if (!invalidPointer) {
const searchDetails = {
pointer: event,
pointerId: pointerUtils.getPointerId(event),
pointerType,
eventType: event.type,
curEventTarget,
eventTarget,
scope,
};
const interaction = getInteraction(searchDetails);
matches.push([
searchDetails.pointer,
searchDetails.eventTarget,
searchDetails.curEventTarget,
interaction,
]);
}
}
// eslint-disable-next-line no-shadow
for (const [pointer, eventTarget, curEventTarget, interaction] of matches) {
interaction[method](pointer, event, eventTarget, curEventTarget);
}
});
}
function getInteraction (searchDetails) {
const { pointerType, scope } = searchDetails;
const foundInteraction = finder.search(searchDetails);
const signalArg = { interaction: foundInteraction, searchDetails };
scope.interactions.signals.fire('find', signalArg);
return signalArg.interaction || newInteraction({ pointerType }, scope);
function getInteraction(searchDetails) {
const { pointerType, scope } = searchDetails;
const foundInteraction = finder.search(searchDetails);
const signalArg = { interaction: foundInteraction, searchDetails };
scope.interactions.signals.fire('find', signalArg);
return signalArg.interaction || newInteraction({ pointerType }, scope);
}
export function newInteraction (options, scope) {
const interaction = scope.interactions.new(options);
scope.interactions.list.push(interaction);
return interaction;
export function newInteraction(options, scope) {
const interaction = scope.interactions.new(options);
scope.interactions.list.push(interaction);
return interaction;
}
function onDocSignal ({ doc, scope, options }, signalName) {
const { eventMap } = scope.interactions;
const eventMethod = signalName.indexOf('add') === 0
? events.add : events.remove;
if (scope.browser.isIOS && !options.events) {
options.events = { passive: false };
}
// delegate event listener
for (const eventType in events.delegatedEvents) {
eventMethod(doc, eventType, events.delegateListener);
eventMethod(doc, eventType, events.delegateUseCapture, true);
}
const eventOptions = options && options.events;
for (const eventType in eventMap) {
eventMethod(doc, eventType, eventMap[eventType], eventOptions);
}
function onDocSignal({ doc, scope, options }, signalName) {
const { eventMap } = scope.interactions;
const eventMethod = signalName.indexOf('add') === 0
? events.add : events.remove;
if (scope.browser.isIOS && !options.events) {
options.events = { passive: false };
}
// delegate event listener
for (const eventType in events.delegatedEvents) {
eventMethod(doc, eventType, events.delegateListener);
eventMethod(doc, eventType, events.delegateUseCapture, true);
}
const eventOptions = options && options.events;
for (const eventType in eventMap) {
eventMethod(doc, eventType, eventMap[eventType], eventOptions);
}
}
export default {
install,
onDocSignal,
doOnInteractions,
newInteraction,
methodNames,
install,
onDocSignal,
doOnInteractions,
newInteraction,
methodNames,
};
//# sourceMappingURL=interactions.js.map
{
"name": "@interactjs/core",
"version": "1.4.0-alpha.17+sha.793e5fa",
"version": "1.4.0-alpha.18+sha.a8adfbf",
"peerDependencies": {
"@interactjs/utils": "1.4.0-alpha.17+sha.793e5fa"
"@interactjs/utils": "1.4.0-alpha.18+sha.a8adfbf"
},
"devDependencies": {
"@interactjs/_dev": "1.4.0-alpha.17+sha.793e5fa",
"@interactjs/utils": "1.4.0-alpha.17+sha.793e5fa"
"@interactjs/_dev": "1.4.0-alpha.18+sha.a8adfbf",
"@interactjs/utils": "1.4.0-alpha.18+sha.a8adfbf"
},

@@ -11,0 +11,0 @@ "publishConfig": {

@@ -1,197 +0,153 @@

import Eventable from './Eventable';
import defaults from './defaultOptions';
import * as utils from '@interactjs/utils';
import domObjects from '@interactjs/utils/domObjects';
import defaults from './defaultOptions';
import Eventable from './Eventable';
import InteractableBase from './Interactable';
import InteractEvent from './InteractEvent';
import interactions from './interactions';
import InteractEvent from './InteractEvent';
import InteractableBase from './Interactable';
const {
win,
browser,
raf,
Signals,
events,
} = utils;
export function createScope () {
const scope = {
Signals,
signals: new Signals(),
browser,
events,
utils,
defaults: utils.clone(defaults),
Eventable,
InteractEvent: InteractEvent,
Interactable: class Interactable extends InteractableBase {
get _defaults () { return scope.defaults; }
set (options) {
super.set(options);
scope.interactables.signals.fire('set', {
options,
interactable: this,
});
return this;
}
unset () {
super.unset();
scope.interactables.signals.fire('unset', { interactable: this });
}
},
interactables: {
// all set interactables
list: [],
new (target, options) {
const { win, browser, raf, Signals, events, } = utils;
export function createScope() {
return new Scope();
}
export class Scope {
constructor() {
// FIXME Signals
this.signals = new Signals();
this.browser = browser;
this.events = events;
this.utils = utils;
this.defaults = utils.clone(defaults);
this.Eventable = Eventable;
this.InteractEvent = InteractEvent;
this.interactables = new InteractableSet(this);
// main document
this.document = null;
// all documents being listened to
this.documents = [ /* { doc, options } */];
const scope = this;
this.Interactable = class Interactable extends InteractableBase {
get _defaults() { return scope.defaults; }
set(options) {
super.set(options);
scope.interactables.signals.fire('set', {
options,
interactable: this,
});
return this;
}
unset() {
super.unset();
scope.interactables.signals.fire('unset', { interactable: this });
}
};
}
init(window) {
return initScope(this, window);
}
addDocument(doc, options) {
// do nothing if document is already known
if (this.getDocIndex(doc) !== -1) {
return false;
}
const window = win.getWindow(doc);
options = options ? utils.extend({}, options) : {};
this.documents.push({ doc, options });
events.documents.push(doc);
// don't add an unload event for the main document
// so that the page may be cached in browser history
if (doc !== this.document) {
events.add(window, 'unload', this.onWindowUnload);
}
this.signals.fire('add-document', { doc, window, scope: this, options });
}
removeDocument(doc) {
const index = this.getDocIndex(doc);
const window = win.getWindow(doc);
const options = this.documents[index].options;
events.remove(window, 'unload', this.onWindowUnload);
this.documents.splice(index, 1);
events.documents.splice(index, 1);
this.signals.fire('remove-document', { doc, window, scope: this, options });
}
onWindowUnload(event) {
this.removeDocument(event.currentTarget.document);
}
getDocIndex(doc) {
for (let i = 0; i < this.documents.length; i++) {
if (this.documents[i].doc === doc) {
return i;
}
}
return -1;
}
getDocOptions(doc) {
const docIndex = this.getDocIndex(doc);
return docIndex === -1 ? null : this.documents[docIndex].options;
}
}
class InteractableSet {
constructor(scope) {
this.scope = scope;
this.signals = new utils.Signals();
// all set interactables
this.list = [];
}
new(target, options) {
options = utils.extend(options || {}, {
actions: scope.actions,
actions: this.scope.actions,
});
const interactable = new scope.Interactable(target, options, scope.document);
scope.addDocument(interactable._doc);
scope.interactables.list.push(interactable);
scope.interactables.signals.fire('new', {
target,
options,
interactable: interactable,
win: this._win,
const interactable = new this.scope.Interactable(target, options, this.scope.document);
this.scope.addDocument(interactable._doc);
this.scope.interactables.list.push(interactable);
this.scope.interactables.signals.fire('new', {
target,
options,
interactable: interactable,
win: this.scope._win,
});
return interactable;
},
indexOfElement (target, context) {
context = context || scope.document;
}
indexOfElement(target, context) {
context = context || this.scope.document;
const list = this.list;
for (let i = 0; i < list.length; i++) {
const interactable = list[i];
if (interactable.target === target && interactable._context === context) {
return i;
}
const interactable = list[i];
if (interactable.target === target && interactable._context === context) {
return i;
}
}
return -1;
},
get (element, options, dontCheckInContext) {
}
get(element, options, dontCheckInContext) {
const ret = this.list[this.indexOfElement(element, options && options.context)];
return ret && (utils.is.string(element) || dontCheckInContext || ret.inContext(element))? ret : null;
},
forEachMatch (element, callback) {
return ret && (utils.is.string(element) || dontCheckInContext || ret.inContext(element)) ? ret : null;
}
forEachMatch(element, callback) {
for (const interactable of this.list) {
let ret;
if ((utils.is.string(interactable.target)
// target is a selector and the element matches
? (utils.is.element(element) && utils.dom.matchesSelector(element, interactable.target))
// target is the element
: element === interactable.target)
// the element is in context
&& (interactable.inContext(element))) {
ret = callback(interactable);
}
if (ret !== undefined) {
return ret;
}
let ret;
if ((utils.is.string(interactable.target)
// target is a selector and the element matches
? (utils.is.element(element) && utils.dom.matchesSelector(element, interactable.target))
// target is the element
: element === interactable.target)
// the element is in context
&& (interactable.inContext(element))) {
ret = callback(interactable);
}
if (ret !== undefined) {
return ret;
}
}
},
signals: new utils.Signals(),
},
// main document
document: null,
// all documents being listened to
documents: [/* { doc, options } */],
init (window) {
return initScope(scope, window);
},
addDocument (doc, options) {
// do nothing if document is already known
if (scope.getDocIndex(doc) !== -1) { return false; }
const window = win.getWindow(doc);
options = options ? utils.extend({}, options) : {};
scope.documents.push({ doc, options });
events.documents.push(doc);
// 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(window, 'unload', scope.onWindowUnload);
}
scope.signals.fire('add-document', { doc, window, scope, options });
},
removeDocument (doc) {
const index = scope.getDocIndex(doc);
const window = win.getWindow(doc);
const options = scope.documents[index].options;
events.remove(window, 'unload', scope.onWindowUnload);
scope.documents.splice(index, 1);
events.documents.splice(index, 1);
scope.signals.fire('remove-document', { doc, window, scope, options });
},
onWindowUnload (event) {
scope.removeDocument(event.currentTarget.document);
},
getDocIndex (doc) {
for (let i = 0; i < scope.documents.length; i++) {
if (scope.documents[i].doc === doc) {
return i;
}
}
return -1;
},
getDocOptions (doc) {
const docIndex = scope.getDocIndex(doc);
return docIndex === -1 ? null : scope.documents[docIndex].options;
},
};
return scope;
}
}
export function initScope (scope, window) {
win.init(window);
domObjects.init(window);
browser.init(window);
raf.init(window);
events.init(window);
interactions.install(scope);
scope.document = window.document;
return scope;
export function initScope(scope, window) {
win.init(window);
domObjects.init(window);
browser.init(window);
raf.init(window);
events.init(window);
interactions.install(scope);
scope.document = window.document;
return scope;
}
//# sourceMappingURL=scope.js.map
import test from '@interactjs/_dev/test/test';
import Eventable from '../Eventable';
import Eventable from '../Eventable.ts';

@@ -4,0 +4,0 @@ test('Eventable', t => {

@@ -1,5 +0,5 @@

import * as utils from '@interactjs/utils';
import Signals from '@interactjs/utils/Signals';
import { createScope } from '../scope';
import Eventable from '../Eventable';
import * as utils from '@interactjs/utils/index.ts';
import Signals from '@interactjs/utils/Signals.ts';
import { createScope } from '../scope.ts';
import Eventable from '../Eventable.ts';
import { doc } from '@interactjs/_dev/test/domator';

@@ -6,0 +6,0 @@

@@ -5,3 +5,3 @@ import test from '@interactjs/_dev/test/test';

import * as helpers from './helpers';
import Interactable from '../Interactable';
import Interactable from '../Interactable.ts';

@@ -8,0 +8,0 @@ test('Interactable copies and extends defaults', t => {

import test from '@interactjs/_dev/test/test';
import pointerUtils from '@interactjs/utils/pointerUtils';
import pointerUtils from '@interactjs/utils/pointerUtils.ts';
import * as helpers from './helpers';
import Signals from '@interactjs/utils/Signals';
import Interaction from '../Interaction';
import InteractEvent from '../InteractEvent';
import interactions from '../interactions';
import Signals from '@interactjs/utils/Signals.ts';
import Interaction from '../Interaction.ts';
import InteractEvent from '../InteractEvent.ts';
import interactions from '../interactions.ts';

@@ -10,0 +10,0 @@ const makeInteractionAndSignals = () => new Interaction({ signals: new Signals });

import test from '@interactjs/_dev/test/test';
import * as helpers from './helpers';
import Signals from '@interactjs/utils/Signals';
import Interaction from '../Interaction';
import interactions from '../interactions';
import Signals from '@interactjs/utils/Signals.ts';
import Interaction from '../Interaction.ts';
import interactions from '../interactions.ts';

@@ -8,0 +8,0 @@ test('interactions', t => {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc