New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

panzoom2

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

panzoom2 - npm Package Compare versions

Comparing version 1.0.0-beta.4 to 1.0.0-beta.5

.gitattributes

2063

dist/panzoom.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.panzoom = {})));
}(this, (function (exports) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.panzoom = factory());
}(this, (function () { 'use strict';
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
// Copyright (C) 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
// See https://github.com/traitsjs/traits.js#readme
// for background on traits and a description of this library
var Trait = (function(){
// == Ancillary functions ==
var SUPPORTS_DEFINEPROP = (function() {
try {
var test = {};
Object.defineProperty(test, 'x', {get: function() { return 0; } } );
return test.x === 0;
} catch(e) {
return false;
}
})();
// IE8 implements Object.defineProperty and Object.getOwnPropertyDescriptor
// only for DOM objects. These methods don't work on plain objects.
// Hence, we need a more elaborate feature-test to see whether the
// browser truly supports these methods:
function supportsGOPD() {
try {
if (Object.getOwnPropertyDescriptor) {
var test = {x:0};
return !!Object.getOwnPropertyDescriptor(test,'x');
}
} catch(e) {}
return false;
} function supportsDP() {
try {
if (Object.defineProperty) {
var test = {};
Object.defineProperty(test,'x',{value:0});
return test.x === 0;
}
} catch(e) {}
return false;
}
var call = Function.prototype.call;
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
/**
* An ad hoc version of bind that only binds the 'this' parameter.
*/
var bindThis = Function.prototype.bind ?
function(fun, self) { return Function.prototype.bind.call(fun, self); } :
function(fun, self) {
function funcBound(var_args) {
return fun.apply(self, arguments);
}
return funcBound;
};
var hasOwnProperty = bindThis(call, Object.prototype.hasOwnProperty);
var slice = bindThis(call, Array.prototype.slice);
// feature testing such that traits.js runs on both ES3 and ES5
var forEach = function(arr, fun) {
for (var i = 0, len = arr.length; i < len; i++) { fun(arr[i]); }
};
}();
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
var freeze = Object.freeze || function(obj) { return obj; };
var getOwnPropertyNames = Object.getOwnPropertyNames ||
function(obj) {
var props = [];
for (var p in obj) { if (hasOwnProperty(obj,p)) { props.push(p); } }
return props;
};
var getOwnPropertyDescriptor = supportsGOPD() ?
Object.getOwnPropertyDescriptor :
function(obj, name) {
return {
value: obj[name],
enumerable: true,
writable: true,
configurable: true
};
};
var defineProperty = supportsDP() ? Object.defineProperty :
function(obj, name, pd) {
obj[name] = pd.value;
};
var defineProperties = Object.defineProperties ||
function(obj, propMap) {
for (var name in propMap) {
if (hasOwnProperty(propMap, name)) {
defineProperty(obj, name, propMap[name]);
}
}
};
var Object_create = Object.create ||
function(proto, propMap) {
var self;
function dummy() {} dummy.prototype = proto || Object.prototype;
self = new dummy();
if (propMap) {
defineProperties(self, propMap);
}
return self;
};
var getOwnProperties = Object.getOwnProperties ||
function(obj) {
var map = {};
forEach(getOwnPropertyNames(obj), function (name) {
map[name] = getOwnPropertyDescriptor(obj, name);
});
return map;
};
// end of ES3 - ES5 compatibility functions
function makeConflictAccessor(name) {
var accessor = function(var_args) {
throw new Error("Conflicting property: "+name);
};
freeze(accessor.prototype);
return freeze(accessor);
}
function makeRequiredPropDesc(name) {
return freeze({
value: undefined,
enumerable: false,
required: true
});
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
function makeConflictingPropDesc(name) {
var conflict = makeConflictAccessor(name);
if (SUPPORTS_DEFINEPROP) {
return freeze({
get: conflict,
set: conflict,
enumerable: false,
conflict: true
});
} else {
return freeze({
value: conflict,
enumerable: false,
conflict: true
});
}
}
/**
* Are x and y not observably distinguishable?
*/
function identical(x, y) {
if (x === y) {
// 0 === -0, but they are not identical
return x !== 0 || 1/x === 1/y;
} else {
// NaN !== NaN, but they are identical.
// NaNs are the only non-reflexive value, i.e., if x !== x,
// then x is a NaN.
return x !== x && y !== y;
}
}
// Note: isSameDesc should return true if both
// desc1 and desc2 represent a 'required' property
// (otherwise two composed required properties would be turned into
// a conflict)
function isSameDesc(desc1, desc2) {
// for conflicting properties, don't compare values because
// the conflicting property values are never equal
if (desc1.conflict && desc2.conflict) {
return true;
} else {
return ( desc1.get === desc2.get
&& desc1.set === desc2.set
&& identical(desc1.value, desc2.value)
&& desc1.enumerable === desc2.enumerable
&& desc1.required === desc2.required
&& desc1.conflict === desc2.conflict);
}
}
function freezeAndBind(meth, self) {
return freeze(bindThis(meth, self));
}
/* makeSet(['foo', ...]) => { foo: true, ...}
*
* makeSet returns an object whose own properties represent a set.
*
* Each string in the names array is added to the set.
*
* To test whether an element is in the set, perform:
* hasOwnProperty(set, element)
*/
function makeSet(names) {
var set = {};
forEach(names, function (name) {
set[name] = true;
});
return freeze(set);
}
// == singleton object to be used as the placeholder for a required
// property ==
var required = freeze({
toString: function() { return '<Trait.required>'; }
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
// == The public API methods ==
/**
* var newTrait = trait({ foo:required, ... })
*
* @param object an object record (in principle an object literal)
* @returns a new trait describing all of the own properties of the object
* (both enumerable and non-enumerable)
*
* As a general rule, 'trait' should be invoked with an object
* literal, since the object merely serves as a record
* descriptor. Both its identity and its prototype chain are
* irrelevant.
*
* Data properties bound to function objects in the argument will be
* flagged as 'method' properties. The prototype of these function
* objects is frozen.
*
* Data properties bound to the 'required' singleton exported by
* this module will be marked as 'required' properties.
*
* The <tt>trait</tt> function is pure if no other code can witness
* the side-effects of freezing the prototypes of the methods. If
* <tt>trait</tt> is invoked with an object literal whose methods
* are represented as in-place anonymous functions, this should
* normally be the case.
*/
function trait(obj) {
var map = {};
forEach(getOwnPropertyNames(obj), function (name) {
var pd = getOwnPropertyDescriptor(obj, name);
if (pd.value === required) {
pd = makeRequiredPropDesc(name);
} else if (typeof pd.value === 'function') {
pd.method = true;
pd.enumerable = false;
if ('prototype' in pd.value) {
freeze(pd.value.prototype);
}
} else {
if (pd.get && pd.get.prototype) { freeze(pd.get.prototype); }
if (pd.set && pd.set.prototype) { freeze(pd.set.prototype); }
}
map[name] = pd;
});
return map;
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
/**
* var newTrait = compose(trait_1, trait_2, ..., trait_N)
*
* @param trait_i a trait object
* @returns a new trait containing the combined own properties of
* all the trait_i.
*
* If two or more traits have own properties with the same name, the new
* trait will contain a 'conflict' property for that name. 'compose' is
* a commutative and associative operation, and the order of its
* arguments is not significant.
*
* If 'compose' is invoked with < 2 arguments, then:
* compose(trait_1) returns a trait equivalent to trait_1
* compose() returns an empty trait
*/
function compose(var_args) {
var traits = slice(arguments, 0);
var newTrait = {};
var LengthUnit = function LengthUnit(name) {
classCallCheck(this, LengthUnit);
forEach(traits, function (trait) {
forEach(getOwnPropertyNames(trait), function (name) {
var pd = trait[name];
if (hasOwnProperty(newTrait, name) &&
!newTrait[name].required) {
this.name = name;
};
// a non-required property with the same name was previously
// defined this is not a conflict if pd represents a
// 'required' property itself:
if (pd.required) {
return; // skip this property, the required property is
// now present
}
var PixelUnit = function (_LengthUnit) {
inherits(PixelUnit, _LengthUnit);
if (!isSameDesc(newTrait[name], pd)) {
// a distinct, non-required property with the same name
// was previously defined by another trait => mark as
// conflicting property
newTrait[name] = makeConflictingPropDesc(name);
} // else,
// properties are not in conflict if they refer to the same value
function PixelUnit() {
classCallCheck(this, PixelUnit);
return possibleConstructorReturn(this, (PixelUnit.__proto__ || Object.getPrototypeOf(PixelUnit)).call(this, 'px'));
} else {
newTrait[name] = pd;
}
});
});
return freeze(newTrait);
}
createClass(PixelUnit, [{
key: 'valueOf',
value: function valueOf() {
return this.name;
/* var newTrait = exclude(['name', ...], trait)
*
* @param names a list of strings denoting property names.
* @param trait a trait some properties of which should be excluded.
* @returns a new trait with the same own properties as the original trait,
* except that all property names appearing in the first argument
* are replaced by required property descriptors.
*
* Note: exclude(A, exclude(B,t)) is equivalent to exclude(A U B, t)
*/
function exclude(names, trait) {
var exclusions = makeSet(names);
var newTrait = {};
forEach(getOwnPropertyNames(trait), function (name) {
// required properties are not excluded but ignored
if (!hasOwnProperty(exclusions, name) || trait[name].required) {
newTrait[name] = trait[name];
} else {
// excluded properties are replaced by required properties
newTrait[name] = makeRequiredPropDesc(name);
}
});
return freeze(newTrait);
}
/**
* var newTrait = override(trait_1, trait_2, ..., trait_N)
*
* @returns a new trait with all of the combined properties of the
* argument traits. In contrast to 'compose', 'override'
* immediately resolves all conflicts resulting from this
* composition by overriding the properties of later
* traits. Trait priority is from left to right. I.e. the
* properties of the leftmost trait are never overridden.
*
* override is associative:
* override(t1,t2,t3) is equivalent to override(t1, override(t2, t3)) or
* to override(override(t1, t2), t3)
* override is not commutative: override(t1,t2) is not equivalent
* to override(t2,t1)
*
* override() returns an empty trait
* override(trait_1) returns a trait equivalent to trait_1
*/
function override(var_args) {
var traits = slice(arguments, 0);
var newTrait = {};
forEach(traits, function (trait) {
forEach(getOwnPropertyNames(trait), function (name) {
var pd = trait[name];
// add this trait's property to the composite trait only if
// - the trait does not yet have this property
// - or, the trait does have the property, but it's a required property
if (!hasOwnProperty(newTrait, name) || newTrait[name].required) {
newTrait[name] = pd;
}
});
});
return freeze(newTrait);
}
/**
* var newTrait = override(dominantTrait, recessiveTrait)
*
* @returns a new trait with all of the properties of dominantTrait
* and all of the properties of recessiveTrait not in dominantTrait
*
* Note: override is associative:
* override(t1, override(t2, t3)) is equivalent to
* override(override(t1, t2), t3)
*/
/*function override(frontT, backT) {
var newTrait = {};
// first copy all of backT's properties into newTrait
forEach(getOwnPropertyNames(backT), function (name) {
newTrait[name] = backT[name];
});
// now override all these properties with frontT's properties
forEach(getOwnPropertyNames(frontT), function (name) {
var pd = frontT[name];
// frontT's required property does not override the provided property
if (!(pd.required && hasOwnProperty(newTrait, name))) {
newTrait[name] = pd;
}
});
return freeze(newTrait);
}*/
/**
* var newTrait = rename(map, trait)
*
* @param map an object whose own properties serve as a mapping from
old names to new names.
* @param trait a trait object
* @returns a new trait with the same properties as the original trait,
* except that all properties whose name is an own property
* of map will be renamed to map[name], and a 'required' property
* for name will be added instead.
*
* rename({a: 'b'}, t) eqv compose(exclude(['a'],t),
* { a: { required: true },
* b: t[a] })
*
* For each renamed property, a required property is generated. If
* the map renames two properties to the same name, a conflict is
* generated. If the map renames a property to an existing
* unrenamed property, a conflict is generated.
*
* Note: rename(A, rename(B, t)) is equivalent to rename(\n ->
* A(B(n)), t) Note: rename({...},exclude([...], t)) is not eqv to
* exclude([...],rename({...}, t))
*/
function rename(map, trait) {
var renamedTrait = {};
forEach(getOwnPropertyNames(trait), function (name) {
// required props are never renamed
if (hasOwnProperty(map, name) && !trait[name].required) {
var alias = map[name]; // alias defined in map
if (hasOwnProperty(renamedTrait, alias) &&
!renamedTrait[alias].required) {
// could happen if 2 props are mapped to the same alias
renamedTrait[alias] = makeConflictingPropDesc(alias);
} else {
// add the property under an alias
renamedTrait[alias] = trait[name];
}
// add a required property under the original name
// but only if a property under the original name does not exist
// such a prop could exist if an earlier prop in the trait was
// previously aliased to this name
if (!hasOwnProperty(renamedTrait, name)) {
renamedTrait[name] = makeRequiredPropDesc(name);
}
} else { // no alias defined
if (hasOwnProperty(renamedTrait, name)) {
// could happen if another prop was previously aliased to name
if (!trait[name].required) {
renamedTrait[name] = makeConflictingPropDesc(name);
}
// else required property overridden by a previously aliased
// property and otherwise ignored
} else {
renamedTrait[name] = trait[name];
}
}
});
return freeze(renamedTrait);
}
/**
* var newTrait = resolve({ oldName: 'newName', excludeName:
* undefined, ... }, trait)
*
* This is a convenience function combining renaming and
* exclusion. It can be implemented as <tt>rename(map,
* exclude(exclusions, trait))</tt> where map is the subset of
* mappings from oldName to newName and exclusions is an array of
* all the keys that map to undefined (or another falsy value).
*
* @param resolutions an object whose own properties serve as a
mapping from old names to new names, or to undefined if
the property should be excluded
* @param trait a trait object
* @returns a resolved trait with the same own properties as the
* original trait.
*
* In a resolved trait, all own properties whose name is an own property
* of resolutions will be renamed to resolutions[name] if it is truthy,
* or their value is changed into a required property descriptor if
* resolutions[name] is falsy.
*
* Note, it's important to _first_ exclude, _then_ rename, since exclude
* and rename are not associative, for example:
* rename({a: 'b'}, exclude(['b'], trait({ a:1,b:2 }))) eqv trait({b:1})
* exclude(['b'], rename({a: 'b'}, trait({ a:1,b:2 }))) eqv
* trait({b:Trait.required})
*
* writing resolve({a:'b', b: undefined},trait({a:1,b:2})) makes it
* clear that what is meant is to simply drop the old 'b' and rename
* 'a' to 'b'
*/
function resolve(resolutions, trait) {
var renames = {};
var exclusions = [];
// preprocess renamed and excluded properties
for (var name in resolutions) {
if (hasOwnProperty(resolutions, name)) {
if (resolutions[name]) { // old name -> new name
renames[name] = resolutions[name];
} else { // name -> undefined
exclusions.push(name);
}
}
}
}]);
return PixelUnit;
}(LengthUnit);
return rename(renames, exclude(exclusions, trait));
}
var Translate3d = function () {
function Translate3d() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { tx: 0, ty: 0, tz: 0 },
tx = _ref.tx,
ty = _ref.ty,
tz = _ref.tz;
/**
* var obj = create(proto, trait)
*
* @param proto denotes the prototype of the completed object
* @param trait a trait object to be turned into a complete object
* @returns an object with all of the properties described by the trait.
* @throws 'Missing required property' the trait still contains a
* required property.
* @throws 'Remaining conflicting property' if the trait still
* contains a conflicting property.
*
* Trait.create is like Object.create, except that it generates
* high-integrity or final objects. In addition to creating a new object
* from a trait, it also ensures that:
* - an exception is thrown if 'trait' still contains required properties
* - an exception is thrown if 'trait' still contains conflicting
* properties
* - the object is and all of its accessor and method properties are frozen
* - the 'this' pseudovariable in all accessors and methods of
* the object is bound to the composed object.
*
* Use Object.create instead of Trait.create if you want to create
* abstract or malleable objects. Keep in mind that for such objects:
* - no exception is thrown if 'trait' still contains required properties
* (the properties are simply dropped from the composite object)
* - no exception is thrown if 'trait' still contains conflicting
* properties (these properties remain as conflicting
* properties in the composite object)
* - neither the object nor its accessor and method properties are frozen
* - the 'this' pseudovariable in all accessors and methods of
* the object is left unbound.
*/
function create(proto, trait) {
var self = Object_create(proto);
var properties = {};
classCallCheck(this, Translate3d);
forEach(getOwnPropertyNames(trait), function (name) {
var pd = trait[name];
// check for remaining 'required' properties
// Note: it's OK for the prototype to provide the properties
if (pd.required) {
if (!(name in proto)) {
throw new Error('Missing required property: '+name);
}
} else if (pd.conflict) { // check for remaining conflicting properties
throw new Error('Remaining conflicting property: '+name);
} else if ('value' in pd) { // data property
// freeze all function properties and their prototype
if (pd.method) { // the property is meant to be used as a method
// bind 'this' in trait method to the composite object
properties[name] = {
value: freezeAndBind(pd.value, self),
enumerable: pd.enumerable,
configurable: pd.configurable,
writable: pd.writable
};
} else {
properties[name] = pd;
}
} else { // accessor property
properties[name] = {
get: pd.get ? freezeAndBind(pd.get, self) : undefined,
set: pd.set ? freezeAndBind(pd.set, self) : undefined,
enumerable: pd.enumerable,
configurable: pd.configurable
};
}
});
this.tx = parseFloat(tx);
this.ty = parseFloat(ty);
this.tz = parseFloat(tz);
defineProperties(self, properties);
return freeze(self);
}
this.unit = new PixelUnit();
/** A shorthand for create(Object.prototype, trait({...}), options) */
function object(record, options) {
return create(Object.prototype, trait(record), options);
}
createClass(Translate3d, [{
key: 'setUnit',
value: function setUnit(unit) {
this.unit = unit;
/**
* Tests whether two traits are equivalent. T1 is equivalent to T2 iff
* both describe the same set of property names and for all property
* names n, T1[n] is equivalent to T2[n]. Two property descriptors are
* equivalent if they have the same value, accessors and attributes.
*
* @return a boolean indicating whether the two argument traits are
* equivalent.
*/
function eqv(trait1, trait2) {
var names1 = getOwnPropertyNames(trait1);
var names2 = getOwnPropertyNames(trait2);
var name;
if (names1.length !== names2.length) {
return false;
}
}, {
key: 'toString',
value: function toString() {
return 'translate3d(' + (this.tx + this.unit) + ', ' + (this.ty + this.unit) + ', ' + (this.tz + this.unit) + ');';
for (var i = 0; i < names1.length; i++) {
name = names1[i];
if (!trait2[name] || !isSameDesc(trait1[name], trait2[name])) {
return false;
}
}
}]);
return Translate3d;
}();
return true;
}
// if this code is ran in ES3 without an Object.create function, this
// library will define it on Object:
if (!Object.create) {
Object.create = Object_create;
}
// ES5 does not by default provide Object.getOwnProperties
// if it's not defined, the Traits library defines this utility
// function on Object
if(!Object.getOwnProperties) {
Object.getOwnProperties = getOwnProperties;
}
// expose the public API of this module
function Trait(record) {
// calling Trait as a function creates a new atomic trait
return trait(record);
}
Trait.required = freeze(required);
Trait.compose = freeze(compose);
Trait.resolve = freeze(resolve);
Trait.override = freeze(override);
Trait.create = freeze(create);
Trait.eqv = freeze(eqv);
Trait.object = freeze(object); // not essential, cf. create + trait
return freeze(Trait);
}());
/**
* Returns a new function that is a composition of supplied functions from right to left.
* @example c = compose(x => x + 1, x => x.val), c({ val: 1 }) -> 2
* @param {...function} fs
* @returns {function}
*/
function compose() {
for (var _len = arguments.length, fs = Array(_len), _key = 0; _key < _len; _key++) {
fs[_key] = arguments[_key];
}
return function (x) {
return reduceRight(function (x, a) {
return a(x);
}, x, fs);
};
}
function reduceRight(f, init, list) {
return reduce(f, init, list.reverse());
}
/**
* Returns a new function where some of the arguments are pre defined.
* @param {function} f The function to partially apply arguments to
* @param {number} [arity] Optionally specify how many arguments the function f will take, before being called. Useful for variadic functions
* @returns {function}
*/
function partial(f, context) {
var arity = f.length;
return function partial() {
for (var _len2 = arguments.length, xs = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
xs[_key2] = arguments[_key2];
}
if (arity < xs.length) throw new TypeError((f.name || 'anonymous') + ' does not accept ' + xs.length + ' arguments');
return arity === xs.length ? f.apply(context, xs) : partial.bind.apply(partial, [context].concat(xs));
};
}
function reduce(f, init, list) {
var result = init;
each(function (value, key) {
result = f(result, value, key);
}, list);
return result;
}
function filter(f, list) {
var seq = [];
each(function (value, key) {
if (f(value, key)) seq.push(value);
}, list);
return seq;
}
/**
* map
* @param {function} f
* @param {array|object} list
* @returns {array}
*/
function map$1(f, list) {
var seq = [];
each(function (value, key) {
seq.push(f(value, key));
}, list);
return seq;
}
/**
*
* @param {function} f
* @param {array|object} list
* @returns {array}
*/
function each(f, list) {
for (var key in list) {
f(list[key], key);
}
}
var mapReduce = compose(partial(reduce)(function (a, b) {
return a.indexOf(b) > -1 ? a : a.concat([b]);
}, []), partial(map$1)(function (x) {
return x[0];
}));
function Observer() {

@@ -113,7 +752,9 @@ var listeners = [];

on: function on(eventName, f, reject) {
if (!(f instanceof Function)) throw new TypeError('event handler is not a function');
if (!(reject instanceof Function)) reject = f;
listeners.push([eventName, f, reject]);
},
fire: function fire(eventName) {
var _this = this;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {

@@ -123,6 +764,7 @@ args[_key - 1] = arguments[_key];

listeners.forEach(function (listener) {
each(function (listener) {
if (listener[0] === eventName) {
try {
listener[1].apply(listener[1], args);
listener[1].apply(_this, args);
// listener[1](...args)
} catch (error) {

@@ -132,12 +774,24 @@ listener[2](error);

}
});
}, listeners);
},
/**
*
* @param {string} eventName The event type you want to remove
* @param {function} f The function listening for the event type to
* distinguish from other listeners for the same event type
*/
off: function off(eventName, f) {
listeners = listeners.filter(function (listener) {
var notRemoved = true;
listeners = filter(function (listener) {
if (listener[0] === eventName) {
// if f is not defined then remove all listeners with the eventName
if (!f) return false;else return listener[1] === f ? false : true;
if (!f) return false;else return listener[1] === f ? notRemoved = false : true;
}
return true;
});
}, listeners);
return !notRemoved;
},

@@ -152,3 +806,3 @@ once: function once(eventName, f, errorHandler) {

promise: function promise(eventName) {
var _this = this;
var _this2 = this;

@@ -158,3 +812,3 @@ var self = this;

return new Promise(function (resolve, reject) {
_this.on(eventName, function wrapper() {
_this2.on(eventName, function wrapper() {
resolve.apply(undefined, arguments);

@@ -172,2 +826,7 @@ self.off(eventName, wrapper);

listeners = null;
},
get currentListenerTypes() {
return mapReduce(listeners);
}

@@ -177,652 +836,882 @@ };

var Point = function Point(_ref) {
var _ref$x = _ref.x,
x = _ref$x === undefined ? 0 : _ref$x,
_ref$y = _ref.y,
y = _ref$y === undefined ? 0 : _ref$y;
classCallCheck(this, Point);
var eventTypes = ['wheel', 'mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchmove', 'touchend'];
this.x = parseFloat(x);
this.y = parseFloat(y);
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var Swipe = function () {
function Swipe(options) {
classCallCheck(this, Swipe);
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
this.el = null;
this.lastTouches = null;
this.detecting = false;
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var defineProperty = function (obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
createClass(Swipe, [{
key: 'listen',
value: function listen(action) {
// console.log('Swipe::listen')
if (!(action instanceof Function)) throw new TypeError('action must be a function');
return obj;
};
this.action = Swipe.action(this, action);
var Point = function () {
function Point(_ref) {
var _ref$x = _ref.x,
x = _ref$x === undefined ? 0 : _ref$x,
_ref$y = _ref.y,
y = _ref$y === undefined ? 0 : _ref$y;
classCallCheck(this, Point);
this.el.addEventListener('mousedown', this.action);
this.el.addEventListener('touchstart', this.action);
}
}, {
key: 'unlisten',
value: function unlisten() {
// console.log('Swipe::unlisten')
this.el.removeEventListener('mousedown', this.action);
this.el.removeEventListener('touchstart', this.action);
}
}, {
key: 'setElement',
value: function setElement(el) {
// TODO: unlisten on el before changing
this.el = el;
}
}, {
key: 'destroy',
value: function destroy() {
this.el = null;
this.action = null;
this.lastTouches = null;
}
}], [{
key: 'action',
value: function action(swipe, _action) {
var _this = this;
this.x = parseFloat(x);
this.y = parseFloat(y);
}
return function (event) {
swipe.unlisten();
/**
* Substract point from this point
* @param {Point} point
* @returns {Point}
*/
var startEvent = normalizeEvent(event);
swipe.lastTouches = startEvent;
// console.log(startEvent)
_this.detecting = true;
createClass(Point, [{
key: "delta",
value: function delta(point) {
return new Point({
x: this.x - point.x,
y: this.y - point.y
});
}
swipe.el.addEventListener(startEvent.type.move, moveHandler);
swipe.el.addEventListener(startEvent.type.end, endHandler); // removing event listeners from DOM via this
/**
* Calculate the distance bewteen 2 points
* @param {Point} point
* @returns {number}
*/
function moveHandler(event) {
var currentEvent = normalizeEvent(event);
// console.log('getting moves!', currentEvent)
}, {
key: "distance",
value: function distance(point) {
var delta = this.delta(point);
return Math.sqrt(Math.pow(delta.x, 2) + Math.pow(delta.y, 2));
}
}]);
return Point;
}();
// TODO: take timestamp into consideration - call endHandler if enough time has passed
var GestureEvent = function () {
function GestureEvent(nativeEvent) {
classCallCheck(this, GestureEvent);
var distance = Math.sqrt( // TODO: abstract this somewhere
Math.pow(currentEvent.touches[0].x - swipe.lastTouches.touches[0].x, 2) + Math.pow(currentEvent.touches[0].y - swipe.lastTouches.touches[0].y, 2));
var event = GestureEvent.normalizeEvent(nativeEvent);
this.touches = event.touches;
this.page = event.page;
this.type = event.type;
this.deltaY = event.deltaY;
this.target = event.target;
this.preventDefault = event.preventDefault;
}
// console.log(distance)
createClass(GestureEvent, [{
key: 'getDirection',
value: function getDirection(event) {
// debugger
// https://stackoverflow.com/questions/2264072/detect-a-finger-swipe-through-javascript-on-the-iphone-and-android
var delta = this.touches[0].delta(event.touches[0]);
if (distance > 100) {
// TODO: make 100 a relative value and consider zoom
var direction = diff(swipe.lastTouches, currentEvent);
// console.log(direction)
// TODO: return an enum instead
// console.log(delta.x, delta.y)
if (Math.abs(delta.x) > Math.abs(delta.y)) {
if (delta.x > 0) return 'right';else return 'left';
} else {
if (delta.y > 0) return 'down';else return 'up';
}
}
// tell subscribers
_action('swipe', direction);
/**
* Detect mouse or touch move and end event names.
* getEventTypeNames will return an
* object, where move is touchmove/mousemove and
* end is touchend/mouseup.
* @returns {object} { move: String, end: String }
*/
// tell event listeners
var swipeEvent = new CustomEvent('swipe', { detail: direction });
if (!swipe.el.dispatchEvent(swipeEvent)) {
console.log('Swipe::action - event was cancelled');
}
}, {
key: 'getEventTypeNames',
value: function getEventTypeNames() {
return this.type === 'touchstart' ? { move: 'touchmove', end: 'touchend' } : { move: 'mousemove', end: 'mouseup' };
}
}], [{
key: 'normalizeEvent',
value: function normalizeEvent(nativeEvent) {
var event = {
touches: nativeEvent.touches ? map(function (t) {
return new Point({ x: t.clientX, y: t.clientY });
}, nativeEvent.touches) : [new Point({ x: nativeEvent.clientX, y: nativeEvent.clientY })],
// debugger
endHandler(); // removing event listeners from DOM via this (before touchend/mouseup)
}
page: nativeEvent.touches ? map(function (t) {
return new Point({ x: t.pageX, y: t.pageY });
}, nativeEvent.touches) : [new Point({ x: nativeEvent.pageX, y: nativeEvent.pageY })],
type: nativeEvent.type,
deltaY: nativeEvent.deltaY,
target: nativeEvent.target,
preventDefault: function preventDefault() {
nativeEvent.preventDefault();
},
stopPropagation: function stopPropagation() {
nativeEvent.stopPropagation();
}
};
function endHandler() {
swipe.el.removeEventListener(startEvent.type.move, moveHandler);
swipe.el.removeEventListener(startEvent.type.end, endHandler);
swipe.detecting = false;
swipe.listen(_action);
}
};
return event;
}
}, {
key: 'addEvent',
value: function addEvent(el, type, listener, options) {
el.addEventListener(type, listener, options || true);
}
}, {
key: 'removeEvent',
value: function removeEvent(el, type, listener) {
el.removeEventListener(type, listener, true);
}
}]);
return Swipe;
return GestureEvent;
}();
function normalizeEvent(ev) {
var event = {};
function percentToPixel(percentage) {
var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'width';
// console.log(ev)
var box = this.el.getBoundingClientRect();
return box[side] * percentage * .01;
}
event.touches = [ev.touches ? new Point({ x: ev.touches[0].pageX, y: ev.touches[0].pageY }) : new Point({ x: ev.pageX, y: ev.pageY })];
event.type = ev.type === 'touchstart' // TODO: use a proper enum
? { move: 'touchmove', end: 'touchend' } : { move: 'mousemove', end: 'mouseup' };
event.timeStamp = Date.now();
function percentToPixelMatrix() {
var box = this.el.getBoundingClientRect();
return {
tx: box.width * (this.tx * .01),
ty: box.height * (this.ty * .01),
tz: this.tz
};
}
return event;
function remToPixel(rem) {
var fontSize = window.getComputedStyle(documentElement).fontSize;
return rem * fontSize;
}
// TODO: might also be useful in Pan.js
function diff(event1, event2) {
// https://stackoverflow.com/questions/2264072/detect-a-finger-swipe-through-javascript-on-the-iphone-and-android
var deltaX = event1.touches[0].x - event2.touches[0].x; // TODO: make addition and substraction easier for Point
var deltaY = event1.touches[0].y - event2.touches[0].y;
function emToPixel(em) {
var fontSize = window.getComputedStyle(this.el).fontSize;
return em * fontSize;
}
// TODO: return an enum instead
// console.log(deltaX, deltaY)
if (Math.abs(deltaX) > Math.abs(deltaY)) {
if (deltaX > 0) return 'left';else return 'right';
} else {
if (deltaY > 0) return 'up';else return 'down';
}
function cmToPixel(cm) {
return cm * 96 / 2.54;
}
var Pinch = function () {
function Pinch(options) {
classCallCheck(this, Pinch);
function mmToPixel(mm) {
return cmToPixel(mm) / 10;
}
this.threshold = options.threshold;
this.el = null;
this.lastTouches = null;
this.lastDistance = null;
this.detecting = false;
}
function getUnit(value) {
return String(value).indexOf('%') > -1 ? '%' : 'px';
}
createClass(Pinch, [{
key: 'listen',
value: function listen(action) {
// console.log('Pinch::listen')
if (!(action instanceof Function)) throw new TypeError('action must be a function');
var units = /*#__PURE__*/Object.freeze({
percentToPixel: percentToPixel,
percentToPixelMatrix: percentToPixelMatrix,
remToPixel: remToPixel,
emToPixel: emToPixel,
cmToPixel: cmToPixel,
mmToPixel: mmToPixel,
getUnit: getUnit
});
this.action = Pinch.action(this, action);
this.el.addEventListener('touchstart', this.action);
/* detect passive option for event listeners */
var supportsPassiveOption = false;
try {
var opts = Object.defineProperty({}, 'passive', {
get: function get$$1() {
supportsPassiveOption = true;
}
}, {
key: 'unlisten',
value: function unlisten() {
// console.log('Pinch::unlisten')
this.el.removeEventListener('touchstart', this.action);
}
}, {
key: 'setElement',
value: function setElement(el) {
this.el = el;
}
}, {
key: 'destroy',
value: function destroy() {
this.el = null;
this.action = null;
this.lastTouches = null;
}
}], [{
key: 'action',
value: function action(pinch, _action) {
var _this = this;
});
window.addEventListener('test', null, opts);
} catch (e) {}
/* end detect */
// console.log('supportsPassiveOption', supportsPassiveOption)
return function (event) {
event.preventDefault();
var matchPassive = /\.passive$/;
function isPassive(eventName) {
return matchPassive.test(eventName);
}
pinch.unlisten();
function isValidEventType(eventName) {
return eventTypes.indexOf(eventName.replace(matchPassive, '')) > -1;
}
var startEvent = normalizeEvent$1(event);
// console.log(startEvent)
if (startEvent.touches.length < 2) {
endHandler();
return;
}
pinch.lastTouches = startEvent;
/**
* Returns the correct eventName and options object.
* @param {string} eventName Removes '.passive' from the eventName if passive is not supported
* @return {object} passive options if supported or true for options to use capture.
*/
function normalisePassive(eventName) {
return supportsPassiveOption && isPassive(eventName) ? {
realEventName: eventName.replace(matchPassive, ''),
options: {
capture: true,
passive: true
}
} : {
realEventName: eventName.replace(matchPassive, ''),
options: true
};
}
_this.detecting = true;
function NativeEvents() {
var _Trait;
pinch.el.addEventListener(startEvent.type.move, moveHandler);
pinch.el.addEventListener(startEvent.type.end, endHandler); // removing event listeners from DOM via this
var nativeListeners = new Map();
function moveHandler(event) {
event.preventDefault(); // prevent native zoom TODO: perhaps we should not make that decision...
var currentEvent = normalizeEvent$1(event);
// console.log('getting moves!', currentEvent)
return Trait.compose(Trait.resolve({
on: 'observerOn',
off: 'observerOff',
destroy: 'observerDestroy'
}, Trait(Observer())), Trait((_Trait = {
el: Trait.required,
currentListenerTypes: Trait.required,
on: Trait.required,
off: Trait.required,
fire: Trait.required,
// TODO: take timestamp into consideration - call endHandler if enough time has passed
eventNotifier: function eventNotifier(event) {
this.fire(event.type, new GestureEvent(event));
},
eventNotifierPassive: function eventNotifierPassive(event) {
this.fire(event.type + '.passive', new GestureEvent(event));
}
}, defineProperty(_Trait, 'on', function on(eventName, f) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
// movement (translate)
var distanceFromFirstTouch = Math.sqrt( // TODO: abstract this somewhere
Math.pow(currentEvent.touches[0].x - pinch.lastTouches.touches[0].x, 2) + Math.pow(currentEvent.touches[0].y - pinch.lastTouches.touches[0].y, 2));
// distance between two first fingers
var distanceBetweenTwoFingers = Math.sqrt( // TODO: abstract this somewhere
Math.pow(currentEvent.touches[0].x - pinch.lastTouches.touches[1].x, 2) + Math.pow(currentEvent.touches[0].y - pinch.lastTouches.touches[1].y, 2));
this.observerOn(eventName, f, options.reject);
var pinchOutwards = pinch.lastDistance && distanceBetweenTwoFingers > pinch.lastDistance ? true : false;
// console.log(pinchOutwards ? 'zoom in' : 'zoom out')
if (!isValidEventType(eventName)) return;
pinch.lastDistance = distanceBetweenTwoFingers;
var realEventName = void 0;
// console.log('distance', distanceFromFirstTouch, distanceBetweenTwoFingers)
var _normalisePassive = normalisePassive(eventName);
var scale = distanceFromFirstTouch / distanceBetweenTwoFingers;
// console.log('scale', scale)
options = _normalisePassive.options;
realEventName = _normalisePassive.realEventName;
if (scale > pinch.threshold) {
// Focus formular ported from svg.panzoom.js - ask Ulrich why it's like that
var currentFocus = new Point({
x: currentEvent.touches[0].x + .5 * (currentEvent.touches[1].x - currentEvent.touches[0].x),
y: currentEvent.touches[0].y + .5 * (currentEvent.touches[1].y - currentEvent.touches[0].y)
});
var lastFocus = new Point({
x: pinch.lastTouches.touches[0].x + 0.5 * (pinch.lastTouches.touches[1].x - pinch.lastTouches.touches[0].x),
y: pinch.lastTouches.touches[0].y + 0.5 * (pinch.lastTouches.touches[1].y - pinch.lastTouches.touches[0].y)
});
var eventNotifier = options.passive ? this.eventNotifierPassive : this.eventNotifier;
// console.log(scale)
var pinchEventData = {
focus: currentFocus,
scale: pinchOutwards ? scale : -scale,
focusAfterScale: new Point({ x: -lastFocus.x, y: -lastFocus.y })
var counter = nativeListeners.get(eventName);
if (counter) {
nativeListeners.set(eventName, counter + 1);
} else {
GestureEvent.addEvent(this.el, realEventName, eventNotifier, options);
nativeListeners.set(eventName, 1);
}
}), defineProperty(_Trait, 'off', function off(eventName, f) {
if (!this.observerOff(eventName, f)) return;
// console.dir(pinchEventData)
if (!isValidEventType(eventName)) return;
// tell subscribers
};_action('pinch', pinchEventData);
var _normalisePassive2 = normalisePassive(eventName),
options = _normalisePassive2.options,
realEventName = _normalisePassive2.realEventName;
// tell event listeners
var pinchEvent = new CustomEvent('pinch', { detail: pinchEventData });
if (!pinch.el.dispatchEvent(pinchEvent)) {
endHandler();
console.log('Pinch::action - event was cancelled');
}
}
}
function endHandler() {
pinch.el.removeEventListener(startEvent.type.move, moveHandler);
pinch.el.removeEventListener(startEvent.type.end, endHandler);
pinch.detecting = false;
pinch.listen(_action);
}
};
var counter = void 0;
if (counter = nativeListeners.get(eventName)) {
nativeListeners.set(eventName, counter - 1);
if (counter === 1) GestureEvent.removeEvent(this.el, realEventName, this.eventNotifier, options);
} else {
GestureEvent.removeEvent(this.el, realEventName, this.eventNotifier, options);
}
}]);
return Pinch;
}();
}), defineProperty(_Trait, 'destroy', function destroy() {
this.removeNativeEventHandlers();
nativeListeners = null;
this.observerDestroy();
}), defineProperty(_Trait, 'addNativeEventHandlers', function addNativeEventHandlers() {
var _this = this;
function normalizeEvent$1(ev) {
var event = {};
each(function (eventName) {
if (isValidEventType(eventName) && !nativeListeners.has(eventName)) {
var _normalisePassive3 = normalisePassive(eventName),
_normalisePassive3$op = _normalisePassive3.options,
options = _normalisePassive3$op === undefined ? {} : _normalisePassive3$op,
realEventName = _normalisePassive3.realEventName;
// console.log(ev)
var eventNotifier = options.passive ? _this.eventNotifierPassive : _this.eventNotifier;
// debugger
GestureEvent.addEvent(_this.el, realEventName, eventNotifier, options);
nativeListeners.set(eventName, 1);
}
}, this.currentListenerTypes);
}), defineProperty(_Trait, 'removeNativeEventHandlers', function removeNativeEventHandlers() {
var _this2 = this;
event.touches = Array.prototype.map.call(ev.touches, function (t) {
return new Point({ x: t.pageX, y: t.pageY });
});
event.type = { move: 'touchmove', end: 'touchend' // TODO: use a proper enum
};event.timeStamp = Date.now();
each(function (eventName) {
if (isValidEventType(eventName) && nativeListeners.has(eventName)) {
var _normalisePassive4 = normalisePassive(eventName),
options = _normalisePassive4.options,
realEventName = _normalisePassive4.realEventName;
return event;
GestureEvent.removeEvent(_this2.el, realEventName, _this2.eventNotifier, options);
nativeListeners.delete(eventName);
}
}, this.currentListenerTypes);
}), _Trait)));
}
var Pan = function () {
function Pan(options) {
classCallCheck(this, Pan);
var FLOATING = '(\\-?[\\d\\.e]+)';
var COMMA_SPACE = '\\,?\\s*';
var R_MATRIX = new RegExp('^matrix\\(' + FLOATING + COMMA_SPACE + FLOATING + COMMA_SPACE + FLOATING + COMMA_SPACE + FLOATING + COMMA_SPACE + FLOATING + COMMA_SPACE + FLOATING + '\\)$');
var Translate3d = function () {
/**
* @param {number} tx
* @param {number} tx
* @param {number} tz Is a <length> representing the z
* component of the translating vector. It can't be a
* <percentage> value; in that case the property
* containing the transform is considered invalid.
*/
function Translate3d(tx, ty, tz) {
classCallCheck(this, Translate3d);
Object.assign(this, units);
this.tx = parseFloat(tx || 0);
this.ty = parseFloat(ty || 0);
this.tz = parseFloat(tz || 1);
this.unit = this.getUnit(tx);
this.el = null;
this.lastTouches = null;
this.detecting = false;
}
createClass(Pan, [{
key: 'listen',
value: function listen(action) {
// console.log('Pan::listen')
if (!(action instanceof Function)) throw new TypeError('action must be a function');
this.action = Pan.action(this, action);
this.el.addEventListener('mousedown', this.action);
this.el.addEventListener('touchstart', this.action);
createClass(Translate3d, [{
key: 'setUnit',
value: function setUnit(unit) {
this.unit = this.getUnit(unit);
}
}, {
key: 'unlisten',
value: function unlisten() {
// console.log('Pan::unlisten')
this.el.removeEventListener('mousedown', this.action);
this.el.removeEventListener('touchstart', this.action);
}
}, {
key: 'setElement',
value: function setElement(el) {
// TODO: unlisten on el before changing
this.el = el;
}
}, {
key: 'destroy',
value: function destroy() {
this.el = null;
this.action = null;
this.lastTouches = null;
key: 'toString',
value: function toString() {
return Translate3d.getMatrixString(this.toObject());
}
}, {
key: 'toObject',
value: function toObject() {
var matrix = {
tx: this.tx,
ty: this.ty,
tz: this.tz
};
return this.unit === '%' ? this.percentToPixelMatrix() : matrix;
}
}], [{
key: 'action',
value: function action(pan, _action) {
var _this = this;
key: 'getMatrixString',
value: function getMatrixString(transform) {
return 'matrix(' + transform.tz + ', 0, 0, ' + transform.tz + ', ' + transform.tx + ', ' + transform.ty + ')';
}
}, {
key: 'parse',
value: function parse(cssTransform) {
var matrix = R_MATRIX.exec(cssTransform);
return matrix ? new Translate3d(matrix[5], matrix[6], matrix[1]) : null;
}
}]);
return Translate3d;
}();
return function (event) {
event.preventDefault();
var lastTouches = null;
var lastDistance = null;
var eventNames = null;
pan.unlisten();
var Pinch = {
// required custom properties from option object
options: {
pinchThreshold: .2,
preventDefault: true
},
var startEvent = normalizeEvent$2(event);
pan.lastTouches = startEvent;
// life cycle handlers
listen: function listen() {
this.on('touchstart.passive', this.startHandler, { reject: errorHandler });
console.log('Pinch::listen');
},
unlisten: function unlisten() {
this.off('touchstart.passive', this.startHandler, { reject: errorHandler });
console.log('Pinch::unlisten');
},
// console.log(startEvent)
_this.detecting = true;
pan.el.addEventListener(startEvent.type.move, moveHandler);
pan.el.addEventListener(startEvent.type.end, endHandler); // removing event listeners from DOM via this
// custom methods
startHandler: function startHandler(event) {
console.log('Pinch::moveHandler', event, this);
function moveHandler(event) {
var currentEvent = normalizeEvent$2(event);
// console.log('getting moves!', currentEvent)
this.unlisten();
// TODO: take timestamp into consideration - call endHandler if enough time has passed
if (event.touches.length < 2) {
this.endHandler();
return;
}
// TODO: abstract this somewhere
var delta = new Point({
x: currentEvent.touches[0].x - pan.lastTouches.touches[0].x,
y: currentEvent.touches[0].y - pan.lastTouches.touches[0].y
});
// console.log(delta)
lastTouches = event;
eventNames = event.getEventTypeNames();
// tell subscribers
_action('pan', delta);
this.on(preventDefault ? eventNames.move : eventNames.move + '.passive', this.moveHandler);
this.on(eventNames.end, this.endHandler);
},
moveHandler: function moveHandler(event) {
// TODO: take timestamp into consideration - call endHandler if enough time has passed
// tell event listeners
var panEvent = new CustomEvent('pan', { detail: delta });
if (!pan.el.dispatchEvent(panEvent)) {
endHandler();
console.log('Pan::action - event was cancelled');
}
}
// movement (translate)
var distanceFromFirstTouch = event.touches[0].distance(lastTouches.touches[0]);
function endHandler() {
pan.el.removeEventListener(startEvent.type.move, moveHandler);
pan.el.removeEventListener(startEvent.type.end, endHandler);
pan.detecting = false;
pan.listen(_action);
}
};
}
}]);
return Pan;
}();
// distance between two first fingers
var distanceBetweenTwoFingers = event.touches[0].distance(lastTouches.touches[1]);
function normalizeEvent$2(ev) {
var event = {};
var pinchOutwards = lastDistance && distanceBetweenTwoFingers > lastDistance ? true : false;
// console.log(pinchOutwards ? 'zoom in' : 'zoom out')
// console.log(ev)
lastDistance = distanceBetweenTwoFingers;
event.touches = [ev.touches ? new Point({ x: ev.touches[0].pageX, y: ev.touches[0].pageY }) : new Point({ x: ev.pageX, y: ev.pageY })];
event.type = ev.type === 'touchstart' // TODO: use a proper enum
? { move: 'touchmove', end: 'touchend' } : { move: 'mousemove', end: 'mouseup' };
event.timeStamp = Date.now();
var scale = distanceFromFirstTouch / distanceBetweenTwoFingers;
// console.log('scale', scale)
return event;
if (scale > this.options.pinchThreshold) {
// Focus formular ported from svg.panzoom.js - ask Ulrich why it's like that
var currentFocus = new Point({
x: event.touches[0].x + .5 * (event.touches[1].x - event.touches[0].x),
y: event.touches[0].y + .5 * (event.touches[1].y - event.touches[0].y)
});
var lastFocus = new Point({
x: lastTouches.touches[0].x + 0.5 * (lastTouches.touches[1].x - lastTouches.touches[0].x),
y: lastTouches.touches[0].y + 0.5 * (lastTouches.touches[1].y - lastTouches.touches[0].y)
});
// console.log(scale)
event.point = currentFocus;
event.scale = pinchOutwards ? scale : -scale, event.focusAfterScale = new Point({ x: -lastFocus.x, y: -lastFocus.y });
this.fire('pinch', event);
}
},
endHandler: function endHandler() {
this.off(preventDefault ? eventNames.move : eventNames.move + '.passive', this.moveHandler);
this.off(eventNames.end, this.endHandler);
this.listen();
}
};
function errorHandler(error) {
console.error(error, 'error happen in listener');
}
var Wheel = function () {
function Wheel(options) {
classCallCheck(this, Wheel);
var Wheel = {
options: {
preventDefault: false
},
this.el = null;
this.lastTouches = null;
this.detecting = false;
this.zoomFactor = options.zoomFactor;
// life cycle handlers
listen: function listen() {
this.on(this.options.preventDefault ? 'wheel' : 'wheel.passive', this.moveHandler);
console.log('Wheel::listen');
},
unlisten: function unlisten() {
console.log('Wheel::unlisten');
this.off(this.options.preventDefault ? 'wheel' : 'wheel.passive', this.moveHandler);
},
moveHandler: function moveHandler(event) {
// touchpads can give event.deltaY == 0, which is something we never want to handle
if (event.deltaY === 0) return;
event.point = event.touches[0];
this.fire('wheelEvent', event);
}
};
createClass(Wheel, [{
key: 'listen',
value: function listen(action) {
// console.log('Wheel::listen')
if (!(action instanceof Function)) throw new TypeError('action must be a function');
var translate3d = null;
this.action = Wheel.action(this, action);
var Zoom = {
/**
* @property gestures
* Supplied gestures can be an object or array
* but note that gestures are changed into
* observable gestures and the gestures property
* will always be an array after initialization.
*/
gestures: [Pinch, Wheel],
this.el.addEventListener('wheel', this.action, true);
}
}, {
key: 'unlisten',
value: function unlisten() {
// console.log('Wheel::unlisten')
this.el.removeEventListener('wheel', this.action, true);
}
}, {
key: 'setElement',
value: function setElement(el) {
// TODO: unlisten on el before changing
this.el = el;
}
}, {
key: 'destroy',
value: function destroy() {
this.el = null;
this.action = null;
this.lastTouches = null;
}
}], [{
key: 'action',
value: function action(wheel, _action) {
var _this = this;
// default options
options: {
zoomFactor: 0.03,
domEvents: false,
preventDefault: true
},
return function (event) {
// event.preventDefault()
// event.stopImmediatePropagation()
// event.stopPropagation()
// event.returnValue = false
// life cycle handlers
listen: function listen() {
console.log('zoom::listen');
// touchpads can give event.deltaY == 0, which is something we never want to handle
if (event.deltaY === 0) return;
translate3d = Translate3d.parse(this.el.style.transform);
if (!translate3d) translate3d = new Translate3d();
wheel.unlisten();
_this.detecting = true;
translate3d.setElement(this.el); // enable percentage stuff
var wheelEventData = {
point: normalizeEvent$3(event).touches[0],
scale: wheel.zoomFactor * -event.deltaY
// console.log(wheelEventData)
this.el.style.transformOrigin = 'top left';
};_action('wheelDelta', wheelEventData);
this.on('wheelEvent', this.eventHandler, function (error) {
console.error(error);
});
},
unlisten: function unlisten() {
console.log('zoom::unlisten');
// tell event listeners
var wheelEvent = new CustomEvent('wheelDelta', { detail: wheelEventData });
if (!wheel.el.dispatchEvent(wheelEvent)) {
endHandler();
console.log('Wheel::action - event was cancelled');
}
this.off('wheelEvent', this.eventHandler);
},
destroy: function destroy() {
translate3d = null;
},
eventHandler: function eventHandler(event) {
if (this.options.domEvents) {
var zoomEvent = new CustomEvent('zoom', {
detail: event,
bubbles: true,
cancelable: true
});
endHandler();
function endHandler() {
wheel.detecting = false;
wheel.listen(_action);
}
};
if (!this.el.dispatchEvent(zoomEvent)) {
console.log('Zoom::eventHandler - event was cancelled');
return;
}
}
}]);
return Wheel;
}();
function normalizeEvent$3(ev) {
var event = {};
if (this.options.preventDefault) event.preventDefault();
// TODO: add async zoom operation here?
this.zoom(event.point, getScaleMultiplier(event.deltaY, this.options.zoomFactor));
},
zoom: function zoom(point, multiplier) {
translate3d.tx = point.x - multiplier * (point.x - translate3d.tx);
translate3d.ty = point.y - multiplier * (point.y - translate3d.ty);
translate3d.tz *= multiplier;
this.el.style.transform = Translate3d.getMatrixString(translate3d);
}
};
// console.log(ev)
function getScaleMultiplier(delta, zoomFactor) {
var scaleMultiplier = 1;
if (delta > 0) {
// zoom out
scaleMultiplier = 1 - zoomFactor;
} else if (delta < 0) {
// zoom in
scaleMultiplier = 1 + zoomFactor;
}
event.touches = [new Point({ x: ev.pageX, y: ev.pageY })];
event.type = null;
event.timeStamp = Date.now();
return scaleMultiplier;
}
return event;
var lastTouches$1 = null;
var eventNames$1 = null;
function errorHandler$1(error) {
console.error(error);
}
var Options = function () {
function Options(_ref) {
var Pan = {
options: {
preventDefault: false
},
// life cycle handlers
listen: function listen() {
this.on('mousedown', this.startHandler, { reject: errorHandler$1 });
this.on('touchstart.passive', this.startHandler, { reject: errorHandler$1 });
console.log('Pan::listen');
},
unlisten: function unlisten() {
this.off('mousedown', this.startHandler);
this.off('touchstart.passive', this.startHandler);
console.log('Pan::unlisten');
},
// custom methods
startHandler: function startHandler(event) {
var _this = this;
var gestures = _ref.gestures,
_ref$min = _ref.min,
min = _ref$min === undefined ? .5 : _ref$min,
_ref$max = _ref.max,
max = _ref$max === undefined ? 20 : _ref$max;
classCallCheck(this, Options);
this.unlisten();
this.min = parseFloat(min);
this.max = parseFloat(max);
// TODO: take timestamp into consideration - call endHandler if enough time has passed
lastTouches$1 = event;
if (gestures && !Array.isArray(gestures)) throw new TypeError('gestures most be an array');
eventNames$1 = event.getEventTypeNames();
this.configurations = gestures ? new Map(gestures) : new Map([['swipe', {}], ['pinch', { threshold: .2 }], ['pan', {}], ['wheel', { zoomFactor: 0.03 }]]);
this.on(this.options.preventDefault ? eventNames$1.move : eventNames$1.move + '.passive', this.moveHandler, function (error) {
console.error(error);
_this.unlisten();
});
this.on(eventNames$1.end, this.endHandler, { reject: errorHandler$1 });
},
moveHandler: function moveHandler(event) {
// TODO: take timestamp into consideration - call endHandler if enough time has passed
this.factories = new Map([['swipe', function (opt) {
return new Swipe(opt || _this.configurations.get('swipe'));
}], ['pinch', function (opt) {
return new Pinch(opt || _this.configurations.get('pinch'));
}], ['pan', function (opt) {
return new Pan(opt || _this.configurations.get('pan'));
}], ['wheel', function (opt) {
return new Wheel(opt || _this.configurations.get('wheel'));
}]]);
event.delta = event.touches[0].delta(lastTouches$1.touches[0]);
event.direction = event.getDirection(lastTouches$1);
// console.log(event.delta)
this.fire('pan', event);
},
endHandler: function endHandler() {
this.off(this.options.preventDefault ? eventNames$1.move : eventNames$1.move + '.passive', this.moveHandler);
this.off(eventNames$1.end, this.endHandler);
this.listen();
}
};
createClass(Options, [{
key: 'getFactory',
value: function getFactory(name) {
return this.factories.get(name);
var minDistance$1 = '';
var lastTouches$2 = null;
var eventNames$2 = null;
var isPassive$1 = false;
function getEventName(eventName) {
return isPassive$1 ? eventName + '.passive' : eventName;
}
var Swipe = Object.assign(percentToPixel, {
// required option(s)
options: {
distance: {
required: true
},
get preventDefault() {
return !isPassive$1;
},
set preventDefault(bool) {
isPassive$1 = !bool;
}
}]);
return Options;
}();
},
/* detect passive option for event listeners */
var supportsPassiveOption = false;
try {
var opts = Object.defineProperty({}, 'passive', {
get: function get$$1() {
supportsPassiveOption = true;
// life cycle handlers
listen: function listen() {
if (getUnit(this.options.distance) === '%') {
minDistance$1 = Math.min(this.percentToPixel(parseFloat(this.options.distance), 'width'), this.percentToPixel(parseFloat(this.options.distance), 'height'));
}
});
window.addEventListener('test', null, opts);
} catch (e) {}
/* end detect */
console.log('supportsPassiveOption', supportsPassiveOption);
var PanZoom = function () {
function PanZoom(options) {
classCallCheck(this, PanZoom);
this.on('mousedown', this.startHandler, { reject: errorHandler$2 });
this.on('touchstart.passive', this.startHandler, { reject: errorHandler$2 });
console.log('Swipe::listen');
},
unlisten: function unlisten() {
this.off('mousedown', this.startHandler);
this.off('touchstart.passive', this.startHandler);
console.log('Swipe::unlisten');
},
this.options = options;
this.el = null;
this.swipe = null;
this.pinch = null;
this.pan = null;
this.isListening = false;
}
createClass(PanZoom, [{
key: 'listen',
value: function listen() {
// console.log('PanZoom::listen')
// custom methods
/**
*
* @param {GestureEvent} event
*/
startHandler: function startHandler(event) {
this.unlisten();
if (this.isListening) return;
// TODO: take timestamp into consideration - call endHandler if enough time has passed
lastTouches$2 = event;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
eventNames$2 = event.getEventTypeNames();
try {
for (var _iterator = this.options.configurations.keys()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var name = _step.value;
this.on(getEventName(eventNames$2.move), this.moveHandler, { reject: errorHandler$2 });
this.on(eventNames$2.end, this.endHandler, { reject: errorHandler$2 });
},
var factory = this.options.getFactory(name);
this[name] = new factory();
this[name].setElement(this.el);
this[name].listen(this.fire);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
/**
*
* @param {GestureEvent} event
*/
moveHandler: function moveHandler(event) {
var distance = event.touches[0].distance(lastTouches$2.touches[0]);
// console.log(distance)
this.isListening = true;
if (distance > minDistance$1) {
// TODO: consider zoom in distance?
event.direction = event.getDirection(lastTouches$2);
this.fire('swipe', event);
this.endHandler();
}
}, {
key: 'unlisten',
value: function unlisten() {
// console.log('PanZoom::unlisten')
if (!this.isListening) return;
},
endHandler: function endHandler() {
this.off(getEventName(eventNames$2.move), this.moveHandler);
this.off(eventNames$2.end, this.endHandler);
this.listen();
}
});
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
function errorHandler$2(error) {
console.error(error, 'error happen in listener');
}
try {
for (var _iterator2 = this.options.configurations.keys()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var name = _step2.value;
/**
*
* @param {HTMLElement} el
* @param {object} referent
* @param {object} options
*/
function panzoom(el) {
var referent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Zoom;
var options = arguments[2];
this.off(name);
this[name].unlisten();
this[name] = null;
if (!el) throw new TypeError('the first argument to panzoom must be an Element');
var initializedReferent = initReferent(referent, el, options);
console.log(initializedReferent);
initializedReferent.listen();
return initializedReferent;
}
function initReferent(referent, el, options) {
if (!(referent.gestures && Object.values(referent.gestures)[0])) {
throw new Error('Referent must have gestures');
}
// mixin options from referent with option argument
options = Object.assign({}, referent.options, options);
// if (referent.isInitialized) {
// throw new Error('Referent is initialized - use .listen() or destroy()')
// }
// referent.isInitialized = true
// shared observer between a referent and all of its gestures
var hivemind = NativeEvents();
var isListening = false;
var proxy = Trait.create(Object.prototype, Trait.compose(Trait.resolve({ destroy: 'destroyListeners' }, hivemind), Trait.resolve({
'listen': 'listenReferent',
'unlisten': 'unlistenReferent',
'destroy': 'destroyReferent',
'options': undefined,
'gestures': undefined
}, Trait(referent)), Trait({
el: el,
options: options,
get isListening() {
return isListening;
},
gestures: map$1(function (gesture, name) {
gesture.el = el;
var defaultOptions = {};
each(function (value, key) {
// better error message than the one from traits.js
if (value.required && !options[key]) {
throw new Error('options is missing ' + key + ' - required by ' + (isNaN(name) ? name : 'a') + ' gesture');
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
if (value.required) defaultOptions[key] = Trait.required;else defaultOptions[key] = value;
}, gesture.options);
// convert a gesture's options record into a trait instance where
// `options` overwrite a gesture's default options
try {
gesture.options = Trait.object(Object.assign(defaultOptions, options));
} catch (error) {
throw new Error(error.message + ' in options');
}
this.isListening = false;
}
}, {
key: 'setElement',
value: function setElement(el) {
this.el = el;
}
}]);
return PanZoom;
}();
// convert gesture record into a trait instance
var t = Trait.create(Object.prototype, Trait.compose(Trait(gesture), hivemind));
return t;
}, referent.gestures),
function createPanzoom(el) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
listen: function listen(arg) {
if (this.isListening) return;
if (!el) throw new TypeError('the first argument to createPanzoom must be an Element');
each(function (gesture) {
gesture.listen(arg);
}, this.gestures);
var panzoom = new PanZoom(new Options(options));
Object.assign(panzoom, Observer()); // add mixins
panzoom.setElement(el);
panzoom.listen();
this.listenReferent(arg);
return panzoom;
// TODO: maybe not needed since we call listen on all gestures and they add the native event handler via NativeEvents.js
this.addNativeEventHandlers();
isListening = true;
},
unlisten: function unlisten(arg) {
this.removeNativeEventHandlers();
each(function (gesture) {
gesture.unlisten(arg);
}, this.gestures);
if (this.unlistenReferent) this.unlistenReferent(arg);
isListening = false;
},
destroy: function destroy() {
this.destroyListeners();
if (this.destroyReferent) this.destroyReferent();
return proxy = hivemind = null;
}
})));
return proxy;
}
exports.createPanzoom = createPanzoom;
exports.PanZoom = PanZoom;
exports.Translate3d = Translate3d;
exports.Observer = Observer;
panzoom.Point = Point;
panzoom.Translate3d = Translate3d;
panzoom.Observer = Observer;
panzoom.gestures = {};
panzoom.gestures.Pinch = Pinch;
panzoom.gestures.Pan = Pan;
panzoom.gestures.Wheel = Wheel;
panzoom.gestures.Swipe = Swipe;
panzoom.referents = {};
panzoom.referents.Zoom = Zoom;
Object.defineProperty(exports, '__esModule', { value: true });
return panzoom;
})));
//# sourceMappingURL=panzoom.js.map
'use strict'
// const Translate3d = panzoom.Translate3d
const scene = panzoom.createPanzoom(document.querySelector('.scene'))
// scene.unlisten()
const listenButton = document.getElementById('listenButton')
const initializeButton = document.getElementById('initializeButton')
const destroyButton = document.getElementById('destroyButton')
// const translate3d = new Translate3d()
// console.log(String(translate3d))
// console.log(translate3d)
const elements = {
container: document.querySelector('.container'),
scene: document.querySelector('.scene')
}
let zoom
initializeButton.onclick = initialize
function initialize () {
const el = getEl()
// el.addEventListener('zoom', zoomHandler)
// zoom = panzoom(el, panzoom.referents.Zoom, { domEvents: true })
zoom = panzoom(el)
listenButton.textContent = getButtonText(zoom)
}
// function zoomHandler (event) {
// event.preventDefault()
// }
listenButton.onclick = function () {
if (zoom && zoom.isListening) {
zoom.unlisten()
} else if (zoom && !zoom.isListening) {
zoom.listen()
} else {
initialize()
}
listenButton.textContent = getButtonText(zoom)
}
destroyButton.onclick = function () {
// const el = getEl()
// el.removeEventListener('zoom', zoomHandler)
zoom.destroy()
zoom = null
listenButton.textContent = 'Listen'
}
function getEl () {
const elValue = document.querySelector('input[type="radio"]:checked').value
return elements[elValue]
}
function getButtonText (referent) {
return referent.isListening ? 'Unlisten' : 'Listen'
}
/* global panzoom */
const allGesturesReferent = {
gestures: {
Pinch: panzoom.gestures.Pinch,
Pan: panzoom.gestures.Pan,
Wheel: panzoom.gestures.Wheel,
Swipe: panzoom.gestures.Swipe
},
options: {
domEvents: true,
distance: '200px'
},
listen: function () {
if (this.options.domEvents) {
this.on('pinch', this.fireEvent('pinch'), { reject: function (error) { console.log(error) } })
this.on('pan', this.fireEvent('pan'), { reject: function (error) { console.log(error) } })
this.on('wheelEvent', this.fireEvent('wheelEvent'), { reject: function (error) { console.log(error) } })
this.on('swipe', this.fireEvent('swipe'), { reject: function (error) { console.log(error) } })
}
},
fireEvent: function (name) {
return function (event) {
const customEvent = new CustomEvent(name, {
detail: event,
bubbles: true,
cancelable: true,
})
if (!this.el.dispatchEvent(customEvent)) {
console.log(name + ' - event was cancelled')
return
}
}
}
}
const listenButton = document.getElementById('listenButton')
const domMessages = document.querySelectorAll('.messages__message')
const scene = panzoom.createPanzoom(document.querySelector('.scene'))
const scene = panzoom(document.querySelector('.scene'), allGesturesReferent)

@@ -17,3 +50,3 @@ const messages = setupMessages()

if (messageEl.dataset.method === 'event') {
switch (gesture) {
switch (gesture) { // for DOM events
case 'pinch':

@@ -25,9 +58,12 @@ unpack = pinchEventUnpack

break
case 'wheelDelta':
case 'wheelEvent':
unpack = wheelEventUnpack
break
case 'swipe':
unpack = swipeEventUnpack
break
default:
unpack = eventDetail
}
} else {
} else { // for panzoom2 events
switch (gesture) {

@@ -40,5 +76,8 @@ case 'pinch':

break
case 'wheelDelta':
case 'wheelEvent':
unpack = wheelUnpack
break
case 'swipe':
unpack = swipeUnpack
break
default:

@@ -67,3 +106,3 @@ unpack = identity

return pinchEvent.scale.toFixed(2)
+ ' (' + pinchEvent.focus.x.toFixed(1) + ', ' + pinchEvent.focus.y.toFixed(1) + ') ('
+ ' (' + pinchEvent.point.x.toFixed(1) + ', ' + pinchEvent.point.y.toFixed(1) + ') ('
+ pinchEvent.focusAfterScale.x.toFixed(1) + ', ' + pinchEvent.focusAfterScale.y.toFixed(1) + ')'

@@ -77,3 +116,3 @@ }

function panUnpack (panEvent) {
return '(' + panEvent.x.toFixed(2) + ', ' + panEvent.y.toFixed(2) + ')'
return '(' + panEvent.touches[0].x.toFixed(2) + ', ' + panEvent.touches[0].y.toFixed(2) + ')'
}

@@ -86,5 +125,13 @@

function wheelUnpack (wheelEvent) {
return 'scale: ' + wheelEvent.scale.toFixed(2) + ' (' + wheelEvent.point.x + ', ' + wheelEvent.point.y + ')'
return 'deltaY: ' + wheelEvent.deltaY.toFixed(2) + ' (' + wheelEvent.point.x + ', ' + wheelEvent.point.y + ') in page (' + wheelEvent.page[0].x + ', ' + wheelEvent.page[0].y + ')'
}
function swipeEventUnpack (event) {
return swipeUnpack(eventDetail(event))
}
function swipeUnpack (swipeEvent) {
return 'direction: ' + swipeEvent.direction
}
function messagesFactory (gesture, method, title, messageEl, unpack) {

@@ -97,3 +144,3 @@ const message = {

handler: function (payload) {
// if (gesture === 'wheelDelta') console.log('payload', payload)
// if (gesture === 'wheelEvent') console.log('payload', payload)
message.messageEl.textContent = title + ' ' + ++message.counter + ': ' + unpack(payload)

@@ -133,4 +180,4 @@ }

// If you have unlisten then you have to enable listen again
// , but it's irrelevant when you start to listen again.
// If you have unlisten then you have to enable listen again,
// but it's irrelevant when you start to listen again.
// .listen() is idempotent

@@ -137,0 +184,0 @@ scene.listen()

'use strict'
const listenButton = document.getElementById('listenButton')
const messages = document.querySelectorAll('.message')
const scene = panzoom.createPanzoom(document.querySelector('.scene'))
// our custom referent
const catchSwipe = {
gestures: [panzoom.gestures.Swipe],
listen()
// default options to referent and gestures
// - can be overriden via the options argument to panzoom()
options: {
distance: '70%'
},
function listen () {
// life-cycle method
listen: function () {
// use promise
this.promise('swipe').then(swipeHandler1)
// use promise
scene.promise('swipe').then(swipeHandler1)
// listen to event
document.body.addEventListener('swipe', swipeHandler2, true)
// listen to event
document.body.addEventListener('swipe', swipeHandler2, true)
// subscribe
this.on('swipe', swipeHandler3)
// subscribe
scene.on('swipe', swipeHandler3)
// subscribe once
this.once('swipe', swipeHandler4)
// subscribe once
scene.once('swipe', swipeHandler4)
// If you have unlisten then you have to enable listen again
// , but it's irrelevant when you start to listen again.
scene.listen()
listenButton.textContent = getButtonText(scene)
listenButton.onclick = function () {
// unlisten will remove all event listeners and nullify the swipe module
scene.unlisten('swipe')
if (this.options.domEvents) {
this.on('swipe', this.delegateEvent)
}
},
// life-cycle method
unlisten: function () {
document.body.removeEventListener('swipe', swipeHandler2, true)
listenButton.textContent = getButtonText(scene)
listenButton.onclick = listen
// scene.off('swipe) // will remove all event listeners for the 'swipe' event
this.off('swipe') // will remove all event listeners for the 'swipe' event
// scene.off('swipe', swipeHandler1) // promise can not be unlisten to - off all swipe event listeners or turn off swipe completely
// scene.off('swipe', swipeHandler3) // this will work
// scene.off('swipe', swipeHandler4) // once can not be unlisten to - off all swipe event listeners or turn off swipe completely
},
// custom method
delegateEvent: function (event) {
const swipeEvent = new CustomEvent('swipe', { detail: event.direction })
if (!this.el.dispatchEvent(swipeEvent)) {
console.log('Swipe::action - event was cancelled')
}
}
}
const initializeButton = document.getElementById('initializeButton')
const listenButton = document.getElementById('listenButton')
const messages = document.querySelectorAll('.message')
let scene
initializeButton.onclick = function () {
if (scene) {
scene.destroy()
console.log(scene)
scene = null
initializeButton.textContent = 'Initialize'
listenButton.style.display = 'none'
} else {
scene = panzoom(document.querySelector('.scene'), catchSwipe, { domEvents: true, preventDefault: true })
// scene.listen() is called automatically
initializeButton.textContent = 'Destroy'
listenButton.style.display = 'inline-block'
}
}
listenButton.onclick = function () {
// unlisten will remove all event listeners
if (scene.isListening) scene.unlisten()
else scene.listen()
listenButton.textContent = getButtonText(scene)
}
function getButtonText (scene) {

@@ -47,8 +82,9 @@ return scene.isListening ? 'Unlisten' : 'Listen'

let counter1 = 0
const title1 = 'promise (once)'
messages[0].textContent = title1
function swipeHandler1 (direction) {
messages[0].textContent = title1 + ' ' + ++counter1 + ' - ' + direction
console.log(direction)
function swipeHandler1 (event) {
messages[0].textContent = title1 + ' ' + ++counter1 + ' - ' + event.direction
console.log(event)
}

@@ -69,5 +105,5 @@

messages[2].textContent = title3
function swipeHandler3(direction) {
messages[2].textContent = title3 + ' ' + ++counter3 + ' - ' + direction
console.log(direction)
function swipeHandler3(event) {
messages[2].textContent = title3 + ' ' + ++counter3 + ' - ' + event.direction
console.log(event)
}

@@ -78,5 +114,5 @@

messages[3].textContent = title4
function swipeHandler4(direction) {
messages[3].textContent = title4 + ' ' + ++counter4 + ' - ' + direction
console.log(direction)
function swipeHandler4(event) {
messages[3].textContent = title4 + ' ' + ++counter4 + ' - ' + event.direction
console.log(event)
}
{
"name": "panzoom2",
"version": "1.0.0-beta.4",
"version": "1.0.0-beta.5",
"description": "Work In Progess - Hopefully a panzoom lib you can finally depend on",
"main": "dist/panzoom.js",
"scripts": {
"test": "tap tests/*.test.js",
"test": "tap tests/*.test.*",
"dev": "parallelshell -v 'npm run build -- --watch' 'npm run browsersync'",
"build": "rollup --config rollup.config.js",
"browsersync": "browser-sync start --server --files 'dist/*.js' --startPath docs/"
"browsersync": "browser-sync start --server --files 'dist/*.js' --startPath docs/",
"debug": "node --inspect-brk"
},

@@ -23,15 +24,18 @@ "keywords": [

"devDependencies": {
"babel-core": "^6.26.0",
"babel-core": "^6.26.3",
"babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.6.1",
"browser-sync": "^2.23.6",
"browser-sync": "^2.24.1",
"eslint": "^4.19.1",
"jsdom": "^11.6.2",
"jsdom": "^11.10.0",
"parallelshell": "^3.0.2",
"rollup": "^0.57.1",
"rollup-plugin-babel": "^3.0.3",
"rollup-plugin-commonjs": "^9.1.0",
"rollup-plugin-babel": "^3.0.4",
"rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-node-resolve": "^3.3.0",
"tap": "^11.1.3"
"tap": "^11.1.4"
},
"dependencies": {
"traits.js": "^1.1.4"
}
}

@@ -13,2 +13,7 @@ [![Build Status](https://travis-ci.org/dotnetCarpenter/panzoom2.svg?branch=master)](https://travis-ci.org/dotnetCarpenter/panzoom2)

## Supported browsers
The project aim to support every [browser that has more than 1% market share, plus IE11](http://browserl.ist/?q=IE+11%2C+%3E+1%25).
## Internet Explorer 11

@@ -31,3 +36,3 @@

- [swipe](https://dotnetcarpenter.github.io/panzoom2/swipe) `Showcase swipe detection and all the ways you can listen and unlisten to an event with panzoom.`
- [gestures](https://dotnetcarpenter.github.io/panzoom2/gestures) `Showcase gesture recognition. - INCOMPLETE`
- [gestures](https://dotnetcarpenter.github.io/panzoom2/gestures) `Showcase gesture recognition.`

@@ -39,1 +44,2 @@ ## Inspiration drawn from

- Eight Media's [HAMMER.JS](http://hammerjs.github.io/)
- [User-defined gestures for surface computing](https://faculty.washington.edu/wobbrock/pubs/chi-09.02.pdf)

@@ -15,3 +15,6 @@ // rollup.config.js

plugins: [
resolve(),
resolve({
extensions: ['.js', '.mjs'],
module: true
}),
babel({

@@ -22,2 +25,2 @@ // only transpile our source code

]
}
}
import Point from '../models/Point'
class Pan {
constructor (options) {
this.el = null
this.lastTouches = null
this.detecting = false
}
let lastTouches = null
let eventNames = null
listen (action) {
// console.log('Pan::listen')
if (!(action instanceof Function)) throw new TypeError('action must be a function')
function errorHandler (error) {
console.error(error)
}
this.action = Pan.action(this, action)
export default {
options: {
preventDefault: false
},
this.el.addEventListener('mousedown', this.action)
this.el.addEventListener('touchstart', this.action)
}
// life cycle handlers
listen () {
this.on('mousedown', this.startHandler, { reject: errorHandler })
this.on('touchstart.passive', this.startHandler, { reject: errorHandler })
console.log('Pan::listen')
},
unlisten () {
// console.log('Pan::unlisten')
this.el.removeEventListener('mousedown', this.action)
this.el.removeEventListener('touchstart', this.action)
}
this.off('mousedown', this.startHandler)
this.off('touchstart.passive', this.startHandler)
console.log('Pan::unlisten')
},
setElement (el) {
// TODO: unlisten on el before changing
this.el = el
}
// custom methods
startHandler (event) {
this.unlisten()
destroy () {
this.el = null
this.action = null
this.lastTouches = null
}
// TODO: take timestamp into consideration - call endHandler if enough time has passed
lastTouches = event
static action (pan, action) {
return event => {
event.preventDefault()
eventNames = event.getEventTypeNames()
pan.unlisten()
this.on(this.options.preventDefault ? eventNames.move : eventNames.move + '.passive', this.moveHandler, error => {
console.error(error)
this.unlisten()
})
this.on(eventNames.end, this.endHandler, { reject: errorHandler })
},
const startEvent = normalizeEvent(event)
pan.lastTouches = startEvent
moveHandler (event) {
// TODO: take timestamp into consideration - call endHandler if enough time has passed
// console.log(startEvent)
this.detecting = true
event.delta = event.touches[0].delta(lastTouches.touches[0])
event.direction = event.getDirection(lastTouches)
// console.log(event.delta)
pan.el.addEventListener(startEvent.type.move, moveHandler)
pan.el.addEventListener(startEvent.type.end, endHandler) // removing event listeners from DOM via this
this.fire('pan', event)
},
function moveHandler (event) {
const currentEvent = normalizeEvent(event)
// console.log('getting moves!', currentEvent)
// TODO: take timestamp into consideration - call endHandler if enough time has passed
// TODO: abstract this somewhere
const delta = new Point({
x: currentEvent.touches[0].x - pan.lastTouches.touches[0].x,
y: currentEvent.touches[0].y - pan.lastTouches.touches[0].y
})
// console.log(delta)
// tell subscribers
action('pan', delta)
// tell event listeners
const panEvent = new CustomEvent('pan', { detail: delta })
if (!pan.el.dispatchEvent(panEvent)) {
endHandler()
console.log('Pan::action - event was cancelled')
}
}
function endHandler () {
pan.el.removeEventListener(startEvent.type.move, moveHandler)
pan.el.removeEventListener(startEvent.type.end, endHandler)
pan.detecting = false
pan.listen(action)
}
}
endHandler () {
this.off(this.options.preventDefault ? eventNames.move : eventNames.move + '.passive', this.moveHandler)
this.off(eventNames.end, this.endHandler)
this.listen()
}
}
function normalizeEvent(ev) {
const event = {}
// console.log(ev)
event.touches = [
ev.touches
? new Point({ x: ev.touches[0].pageX, y: ev.touches[0].pageY })
: new Point({ x: ev.pageX, y: ev.pageY })
]
event.type = ev.type === 'touchstart' // TODO: use a proper enum
? { move: 'touchmove', end: 'touchend' }
: { move: 'mousemove', end: 'mouseup' }
event.timeStamp = Date.now()
return event
}
export default Pan
import Point from '../models/Point'
class Pinch {
constructor (options) {
this.threshold = options.threshold
this.el = null
this.lastTouches = null
this.lastDistance = null
this.detecting = false
}
let minDistance = ''
let lastTouches = null
let lastDistance = null
let eventNames = null
listen (action) {
// console.log('Pinch::listen')
if (!(action instanceof Function)) throw new TypeError('action must be a function')
export default {
// required custom properties from option object
options: {
pinchThreshold: .2,
preventDefault: true
},
this.action = Pinch.action(this, action)
this.el.addEventListener('touchstart', this.action)
}
// life cycle handlers
listen () {
this.on('touchstart.passive', this.startHandler, { reject: errorHandler })
console.log('Pinch::listen')
},
unlisten () {
// console.log('Pinch::unlisten')
this.el.removeEventListener('touchstart', this.action)
}
this.off('touchstart.passive', this.startHandler, { reject: errorHandler })
console.log('Pinch::unlisten')
},
setElement (el) {
this.el = el
}
// custom methods
startHandler (event) {
console.log('Pinch::moveHandler', event, this)
destroy () {
this.el = null
this.action = null
this.lastTouches = null
}
this.unlisten()
static action (pinch, action) {
return event => {
event.preventDefault()
if (event.touches.length < 2) {
this.endHandler()
return
}
pinch.unlisten()
lastTouches = event
eventNames = event.getEventTypeNames()
const startEvent = normalizeEvent(event)
// console.log(startEvent)
if (startEvent.touches.length < 2) {
endHandler()
return
}
pinch.lastTouches = startEvent
this.on(preventDefault ? eventNames.move : eventNames.move + '.passive', this.moveHandler)
this.on(eventNames.end, this.endHandler)
},
this.detecting = true
moveHandler (event) {
// TODO: take timestamp into consideration - call endHandler if enough time has passed
pinch.el.addEventListener(startEvent.type.move, moveHandler)
pinch.el.addEventListener(startEvent.type.end, endHandler) // removing event listeners from DOM via this
// movement (translate)
const distanceFromFirstTouch = event.touches[0].distance(lastTouches.touches[0])
function moveHandler (event) {
event.preventDefault() // prevent native zoom TODO: perhaps we should not make that decision...
const currentEvent = normalizeEvent(event)
// console.log('getting moves!', currentEvent)
// distance between two first fingers
const distanceBetweenTwoFingers = event.touches[0].distance(lastTouches.touches[1])
// TODO: take timestamp into consideration - call endHandler if enough time has passed
const pinchOutwards = lastDistance && distanceBetweenTwoFingers > lastDistance ? true : false
// console.log(pinchOutwards ? 'zoom in' : 'zoom out')
// movement (translate)
const distanceFromFirstTouch = Math.sqrt( // TODO: abstract this somewhere
(currentEvent.touches[0].x - pinch.lastTouches.touches[0].x) ** 2
+
(currentEvent.touches[0].y - pinch.lastTouches.touches[0].y) ** 2
)
// distance between two first fingers
const distanceBetweenTwoFingers = Math.sqrt( // TODO: abstract this somewhere
(currentEvent.touches[0].x - pinch.lastTouches.touches[1].x) ** 2
+
(currentEvent.touches[0].y - pinch.lastTouches.touches[1].y) ** 2
)
lastDistance = distanceBetweenTwoFingers
const pinchOutwards = pinch.lastDistance && distanceBetweenTwoFingers > pinch.lastDistance ? true : false
// console.log(pinchOutwards ? 'zoom in' : 'zoom out')
pinch.lastDistance = distanceBetweenTwoFingers
// console.log('distance', distanceFromFirstTouch, distanceBetweenTwoFingers)
const scale = distanceFromFirstTouch / distanceBetweenTwoFingers
const scale = distanceFromFirstTouch / distanceBetweenTwoFingers
// console.log('scale', scale)
if (scale > pinch.threshold) {
// Focus formular ported from svg.panzoom.js - ask Ulrich why it's like that
const currentFocus = new Point({
x: currentEvent.touches[0].x + .5 * (currentEvent.touches[1].x - currentEvent.touches[0].x),
y: currentEvent.touches[0].y + .5 * (currentEvent.touches[1].y - currentEvent.touches[0].y)
})
if (scale > this.options.pinchThreshold) {
// Focus formular ported from svg.panzoom.js - ask Ulrich why it's like that
const currentFocus = new Point({
x: event.touches[0].x + .5 * (event.touches[1].x - event.touches[0].x),
y: event.touches[0].y + .5 * (event.touches[1].y - event.touches[0].y)
})
const lastFocus = new Point({
x: pinch.lastTouches.touches[0].x + 0.5 * (pinch.lastTouches.touches[1].x - pinch.lastTouches.touches[0].x),
y: pinch.lastTouches.touches[0].y + 0.5 * (pinch.lastTouches.touches[1].y - pinch.lastTouches.touches[0].y)
})
const lastFocus = new Point({
x: lastTouches.touches[0].x + 0.5 * (lastTouches.touches[1].x - lastTouches.touches[0].x),
y: lastTouches.touches[0].y + 0.5 * (lastTouches.touches[1].y - lastTouches.touches[0].y)
})
// console.log(scale)
const pinchEventData = {
focus: currentFocus,
scale: pinchOutwards ? scale : -scale,
focusAfterScale: new Point({ x: -lastFocus.x, y: -lastFocus.y })
}
// console.log(scale)
event.point = currentFocus
event.scale = pinchOutwards ? scale : -scale,
event.focusAfterScale = new Point({ x: -lastFocus.x, y: -lastFocus.y })
// console.dir(pinchEventData)
// tell subscribers
action('pinch', pinchEventData)
// tell event listeners
const pinchEvent = new CustomEvent('pinch', { detail: pinchEventData })
if (!pinch.el.dispatchEvent(pinchEvent)) {
endHandler()
console.log('Pinch::action - event was cancelled')
}
}
}
function endHandler () {
pinch.el.removeEventListener(startEvent.type.move, moveHandler)
pinch.el.removeEventListener(startEvent.type.end, endHandler)
pinch.detecting = false
pinch.listen(action)
}
this.fire('pinch', event)
}
},
endHandler () {
this.off(preventDefault ? eventNames.move : eventNames.move + '.passive', this.moveHandler)
this.off(eventNames.end, this.endHandler)
this.listen()
}
}
function normalizeEvent(ev) {
const event = {}
// console.log(ev)
event.touches = Array.prototype.map.call(
ev.touches,
t => new Point({ x: t.pageX, y: t.pageY })
)
event.type = { move: 'touchmove', end: 'touchend' } // TODO: use a proper enum
event.timeStamp = Date.now()
return event
function errorHandler (error) {
console.error(error, 'error happen in listener')
}
export default Pinch
import Point from '../models/Point'
import { percentToPixel, getUnit } from '../mixins/LengthUnits'
import GestureEvent from '../models/GestureEvent';
class Swipe {
constructor (options) {
this.el = null
this.lastTouches = null
this.detecting = false
}
let minDistance = ''
let lastTouches = null
let eventNames = null
let isPassive = false
listen (action) {
// console.log('Swipe::listen')
if (!(action instanceof Function)) throw new TypeError('action must be a function')
function getEventName(eventName) {
return isPassive ? eventName + '.passive' : eventName
}
this.action = Swipe.action(this, action)
export default Object.assign(percentToPixel, {
// required option(s)
options: {
distance: {
required: true
},
get preventDefault () {
return !isPassive
},
set preventDefault (bool) {
isPassive = !bool
}
},
this.el.addEventListener('mousedown', this.action)
this.el.addEventListener('touchstart', this.action)
}
// life cycle handlers
listen () {
if (getUnit(this.options.distance) === '%') {
minDistance = Math.min(
this.percentToPixel(parseFloat(this.options.distance), 'width'),
this.percentToPixel(parseFloat(this.options.distance), 'height')
)
}
this.on('mousedown', this.startHandler, { reject: errorHandler })
this.on('touchstart.passive', this.startHandler, { reject: errorHandler })
console.log('Swipe::listen')
},
unlisten () {
// console.log('Swipe::unlisten')
this.el.removeEventListener('mousedown', this.action)
this.el.removeEventListener('touchstart', this.action)
}
this.off('mousedown', this.startHandler)
this.off('touchstart.passive', this.startHandler)
console.log('Swipe::unlisten')
},
setElement (el) {
// TODO: unlisten on el before changing
this.el = el
}
// custom methods
/**
*
* @param {GestureEvent} event
*/
startHandler (event) {
this.unlisten()
destroy () {
this.el = null
this.action = null
this.lastTouches = null
}
// TODO: take timestamp into consideration - call endHandler if enough time has passed
lastTouches = event
static action (swipe, action) {
return event => {
swipe.unlisten()
eventNames = event.getEventTypeNames()
const startEvent = normalizeEvent(event)
swipe.lastTouches = startEvent
this.on(getEventName(eventNames.move), this.moveHandler, { reject: errorHandler })
this.on(eventNames.end, this.endHandler, { reject: errorHandler })
},
// console.log(startEvent)
this.detecting = true
/**
*
* @param {GestureEvent} event
*/
moveHandler (event) {
const distance = event.touches[0].distance(lastTouches.touches[0])
// console.log(distance)
swipe.el.addEventListener(startEvent.type.move, moveHandler)
swipe.el.addEventListener(startEvent.type.end, endHandler) // removing event listeners from DOM via this
if (distance > minDistance) { // TODO: consider zoom in distance?
event.direction = event.getDirection(lastTouches)
function moveHandler (event) {
const currentEvent = normalizeEvent(event)
// console.log('getting moves!', currentEvent)
this.fire('swipe', event)
// TODO: take timestamp into consideration - call endHandler if enough time has passed
this.endHandler()
}
},
const distance = Math.sqrt( // TODO: abstract this somewhere
(currentEvent.touches[0].x - swipe.lastTouches.touches[0].x) ** 2
+
(currentEvent.touches[0].y - swipe.lastTouches.touches[0].y) ** 2
)
// console.log(distance)
if (distance > 100) { // TODO: make 100 a relative value and consider zoom
const direction = diff(swipe.lastTouches, currentEvent)
// console.log(direction)
// tell subscribers
action('swipe', direction)
// tell event listeners
const swipeEvent = new CustomEvent('swipe', { detail: direction })
if (!swipe.el.dispatchEvent(swipeEvent)) {
console.log('Swipe::action - event was cancelled')
}
// debugger
endHandler() // removing event listeners from DOM via this (before touchend/mouseup)
}
}
function endHandler () {
swipe.el.removeEventListener(startEvent.type.move, moveHandler)
swipe.el.removeEventListener(startEvent.type.end, endHandler)
swipe.detecting = false
swipe.listen(action)
}
}
endHandler () {
this.off(getEventName(eventNames.move), this.moveHandler)
this.off(eventNames.end, this.endHandler)
this.listen()
}
}
})
function normalizeEvent(ev) {
const event = {}
// console.log(ev)
event.touches = [
ev.touches
? new Point({ x: ev.touches[0].pageX, y: ev.touches[0].pageY })
: new Point({ x: ev.pageX, y: ev.pageY })
]
event.type = ev.type === 'touchstart' // TODO: use a proper enum
? { move: 'touchmove', end: 'touchend' }
: { move: 'mousemove', end: 'mouseup' }
event.timeStamp = Date.now()
return event
function errorHandler (error) {
console.error(error, 'error happen in listener')
}
// TODO: might also be useful in Pan.js
function diff (event1, event2) {
// https://stackoverflow.com/questions/2264072/detect-a-finger-swipe-through-javascript-on-the-iphone-and-android
const deltaX = event1.touches[0].x - event2.touches[0].x // TODO: make addition and substraction easier for Point
const deltaY = event1.touches[0].y - event2.touches[0].y
// TODO: return an enum instead
// console.log(deltaX, deltaY)
if (Math.abs(deltaX) > Math.abs(deltaY)) {
if (deltaX > 0) return 'left'
else return 'right'
} else {
if (deltaY > 0) return 'up'
else return 'down'
}
}
export default Swipe
import Point from '../models/Point'
class Wheel {
constructor (options) {
this.el = null
this.lastTouches = null
this.detecting = false
this.zoomFactor = options.zoomFactor
}
export default {
options: {
preventDefault: false
},
listen (action) {
// console.log('Wheel::listen')
if (!(action instanceof Function)) throw new TypeError('action must be a function')
this.action = Wheel.action(this, action)
this.el.addEventListener('wheel', this.action, true)
}
// life cycle handlers
listen () {
this.on(this.options.preventDefault ? 'wheel' : 'wheel.passive', this.moveHandler)
console.log('Wheel::listen')
},
unlisten () {
// console.log('Wheel::unlisten')
this.el.removeEventListener('wheel', this.action, true)
}
console.log('Wheel::unlisten')
this.off(this.options.preventDefault ? 'wheel' : 'wheel.passive', this.moveHandler)
},
setElement (el) {
// TODO: unlisten on el before changing
this.el = el
}
moveHandler (event) {
// touchpads can give event.deltaY == 0, which is something we never want to handle
if (event.deltaY === 0) return
destroy () {
this.el = null
this.action = null
this.lastTouches = null
event.point = event.touches[0]
this.fire('wheelEvent', event)
}
static action (wheel, action) {
return event => {
// event.preventDefault()
// event.stopImmediatePropagation()
// event.stopPropagation()
// event.returnValue = false
// touchpads can give event.deltaY == 0, which is something we never want to handle
if (event.deltaY === 0) return
wheel.unlisten()
this.detecting = true
const wheelEventData = {
point: normalizeEvent(event).touches[0],
scale: wheel.zoomFactor * -event.deltaY
}
// console.log(wheelEventData)
action('wheelDelta', wheelEventData)
// tell event listeners
const wheelEvent = new CustomEvent('wheelDelta', { detail: wheelEventData })
if (!wheel.el.dispatchEvent(wheelEvent)) {
endHandler()
console.log('Wheel::action - event was cancelled')
}
endHandler()
function endHandler () {
wheel.detecting = false
wheel.listen(action)
}
}
}
}
function normalizeEvent(ev) {
const event = {}
// console.log(ev)
event.touches = [
new Point({ x: ev.pageX, y: ev.pageY })
]
event.type = null
event.timeStamp = Date.now()
return event
}
export default Wheel

@@ -0,10 +1,157 @@

import Trait from 'traits.js'
import NativeEvents from './mixins/NativeEvents'
import Translate3d from './models/Translate3d'
import Point from './models/Point'
import { map, each } from './utils'
import Zoom from './referents/Zoom'
// import Move from './referents/Move'
// library exports
import Observer from './mixins/Observer'
import { createPanzoom, PanZoom } from './PanZoom'
import Pinch from './gestures/Pinch'
import Pan from './gestures/Pan'
import Wheel from './gestures/Wheel'
import Swipe from './gestures/Swipe'
export {
createPanzoom,
PanZoom, // only exposed to test
Translate3d, // only exposed to test
Observer // only exposed to test
/**
*
* @param {HTMLElement} el
* @param {object} referent
* @param {object} options
*/
function panzoom (el, referent = Zoom, options) {
if (!el) throw new TypeError('the first argument to panzoom must be an Element')
const initializedReferent = initReferent(referent, el, options)
console.log(initializedReferent)
initializedReferent.listen()
return initializedReferent
}
function initReferent (referent, el, options) {
if (!(referent.gestures && Object.values(referent.gestures)[0])) {
throw new Error('Referent must have gestures')
}
// mixin options from referent with option argument
options = Object.assign({}, referent.options, options)
// if (referent.isInitialized) {
// throw new Error('Referent is initialized - use .listen() or destroy()')
// }
// referent.isInitialized = true
// shared observer between a referent and all of its gestures
let hivemind = NativeEvents()
let isListening = false
let proxy = Trait.create(Object.prototype,
Trait.compose(
Trait.resolve({ destroy: 'destroyListeners' }, hivemind),
Trait.resolve(
{
'listen': 'listenReferent',
'unlisten': 'unlistenReferent',
'destroy': 'destroyReferent',
'options': undefined,
'gestures': undefined
},
Trait(referent)
),
Trait({
el,
options,
get isListening () { return isListening },
gestures: map(
(gesture, name) => {
gesture.el = el
const defaultOptions = {}
each((value, key) => {
// better error message than the one from traits.js
if (value.required && !options[key]) {
throw new Error(`options is missing ${key} - required by ${isNaN(name) ? name : 'a'} gesture`)
}
if (value.required) defaultOptions[key] = Trait.required
else defaultOptions[key] = value
}, gesture.options)
// convert a gesture's options record into a trait instance where
// `options` overwrite a gesture's default options
try {
gesture.options = Trait.object(Object.assign((defaultOptions), options))
} catch (error) {
throw new Error(`${error.message} in options`)
}
// convert gesture record into a trait instance
const t = Trait.create(
Object.prototype,
Trait.compose(Trait(gesture), hivemind)
)
return t
},
referent.gestures
),
listen (arg) {
if (this.isListening) return
each(gesture => {
gesture.listen(arg)
}, this.gestures)
this.listenReferent(arg)
// TODO: maybe not needed since we call listen on all gestures and they add the native event handler via NativeEvents.js
this.addNativeEventHandlers()
isListening = true
},
unlisten (arg) {
this.removeNativeEventHandlers()
each(gesture => {
gesture.unlisten(arg)
}, this.gestures)
if (this.unlistenReferent) this.unlistenReferent(arg)
isListening = false
},
destroy () {
this.destroyListeners()
if (this.destroyReferent) this.destroyReferent()
return proxy = hivemind = null
}
})
)
)
return proxy
}
panzoom.Point = Point
panzoom.Translate3d = Translate3d
panzoom.Observer = Observer
panzoom.gestures = {}
panzoom.gestures.Pinch = Pinch
panzoom.gestures.Pan = Pan
panzoom.gestures.Wheel = Wheel
panzoom.gestures.Swipe = Swipe
panzoom.referents = {}
panzoom.referents.Zoom = Zoom
export default panzoom

@@ -1,4 +0,9 @@

'use strict'
import { compose, partial, each, filter, reduce, map } from '../utils'
function Observer () {
const mapReduce = compose(
partial(reduce)((a, b) => a.indexOf(b) > -1 ? a : a.concat([b]), []),
partial(map)(x => x[0])
)
export default function Observer () {
let listeners = []

@@ -8,4 +13,4 @@

on (eventName, f, reject) {
if (!(f instanceof Function)) throw new TypeError('event handler is not a function')
if (!(reject instanceof Function)) reject = f
listeners.push([eventName, f, reject])

@@ -15,6 +20,7 @@ },

fire (eventName, ...args) {
listeners.forEach(listener => {
each(listener => {
if (listener[0] === eventName) {
try {
listener[1].apply(listener[1], args)
listener[1].apply(this, args)
// listener[1](...args)
} catch (error) {

@@ -24,7 +30,15 @@ listener[2](error)

}
})
}, listeners)
},
/**
*
* @param {string} eventName The event type you want to remove
* @param {function} f The function listening for the event type to
* distinguish from other listeners for the same event type
*/
off (eventName, f) {
listeners = listeners.filter(
let notRemoved = true
listeners = filter(
listener => {

@@ -34,7 +48,9 @@ if (listener[0] === eventName) {

if (!f) return false
else return listener[1] === f ? false : true
else return listener[1] === f ? notRemoved = false : true
}
return true
}
)
, listeners)
return !notRemoved
},

@@ -72,6 +88,8 @@

listeners = null
},
get currentListenerTypes () {
return mapReduce(listeners)
}
}
}
export default Observer

@@ -1,2 +0,1 @@

class Point {

@@ -7,4 +6,28 @@ constructor ({x = 0, y = 0}) {

}
/**
* Substract point from this point
* @param {Point} point
* @returns {Point}
*/
delta (point) {
return new Point({
x: this.x - point.x,
y: this.y - point.y
})
}
/**
* Calculate the distance bewteen 2 points
* @param {Point} point
* @returns {number}
*/
distance (point) {
const delta = this.delta(point)
return Math.sqrt(
delta.x ** 2 + delta.y ** 2
)
}
}
export default Point

@@ -1,21 +0,70 @@

import { PixelUnit } from './LengthUnit'
import * as units from '../mixins/LengthUnits'
const FLOATING = '(\\-?[\\d\\.e]+)'
const COMMA_SPACE = '\\,?\\s*'
const R_MATRIX = new RegExp(
'^matrix\\(' +
FLOATING + COMMA_SPACE +
FLOATING + COMMA_SPACE +
FLOATING + COMMA_SPACE +
FLOATING + COMMA_SPACE +
FLOATING + COMMA_SPACE +
FLOATING + '\\)$'
)
class Translate3d {
constructor ({tx, ty, tz} = {tx: 0, ty: 0, tz: 0}) {
this.tx = parseFloat(tx)
this.ty = parseFloat(ty)
this.tz = parseFloat(tz)
/**
* @param {number} tx
* @param {number} tx
* @param {number} tz Is a <length> representing the z
* component of the translating vector. It can't be a
* <percentage> value; in that case the property
* containing the transform is considered invalid.
*/
constructor (tx, ty, tz) {
Object.assign(this, units)
this.unit = new PixelUnit()
this.tx = parseFloat(tx || 0)
this.ty = parseFloat(ty || 0)
this.tz = parseFloat(tz || 1)
this.unit = this.getUnit(tx)
this.el = null
}
setUnit (unit) {
this.unit = unit
this.unit = this.getUnit(unit)
}
setElement (el) {
this.el = el
}
toString () {
return `translate3d(${this.tx + this.unit}, ${this.ty + this.unit}, ${this.tz + this.unit});`
return Translate3d.getMatrixString(this.toObject())
}
toObject () {
const matrix = {
tx: this.tx,
ty: this.ty,
tz: this.tz
}
return this.unit === '%'
? this.percentToPixelMatrix(/* this.el, matrix */)
: matrix
}
static getMatrixString (transform) {
return `matrix(${transform.tz}, 0, 0, ${transform.tz}, ${transform.tx}, ${transform.ty})`
}
static parse (cssTransform) {
const matrix = R_MATRIX.exec(cssTransform)
return matrix
? new Translate3d(matrix[5], matrix[6], matrix[1])
: null
}
}
export default Translate3d

@@ -1,2 +0,1 @@

class ZoomLevel {

@@ -3,0 +2,0 @@ constructor (levels = []) {

@@ -118,1 +118,20 @@ const test = require('tap').test

})
test('Observer returns a list of listener types via currentListenerTypes', t => {
t.plan(3)
const observer = createObserver()
const expected = ['wheel']
observer.on('wheel', noop)
t.match(observer.currentListenerTypes, expected, 'should match [wheel]')
observer.on('mousedown', noop)
expected.push('mousedown')
t.match(observer.currentListenerTypes.sort(), expected.sort(), 'should match [wheel, mousedown]')
observer.on('wheel', noop)
t.strictSame(observer.currentListenerTypes.sort(), expected.sort(), 'should match [wheel, mousedown]')
function noop () {}
})
const test = require('tap').test
const JsDom = require('jsdom').JSDOM
const JSDOM = require('jsdom').JSDOM
const panzoom = require('../dist/panzoom')
const globalDom = new JsDom('', { pretendToBeVisual: true })
global.window = globalDom.window;
global.document = globalDom.window.document;
global.HTMLElement = globalDom.window.HTMLElement;
const globalDom = new JSDOM('', { pretendToBeVisual: true })
global.window = globalDom.window
global.document = globalDom.window.document
global.HTMLElement = globalDom.window.HTMLElement

@@ -21,7 +21,7 @@ const PanZoom = panzoom.PanZoom

const dom = new JsDom(`<body><div class='content'></div></body>`);
const document = dom.window.document;
const content = document.querySelector('.content');
const dom = new JSDOM(`<body><div class='content'></div></body>`)
const document = dom.window.document
const content = document.querySelector('.content')
t.ok(panzoom.createPanzoom(content) instanceof PanZoom)
})
}, {skip: true})
const test = require('tap').test
const panzoom = require('../dist/panzoom')
const JSDOM = require('jsdom').JSDOM
const Translate3d = require('../dist/panzoom').Translate3d
const Translate3d = panzoom.Translate3d
// const test = test
// const JSDOM = jsdom.default.JSDOM
// console.log(tap)
const globalDom = new JSDOM('', { pretendToBeVisual: true })
global.window = globalDom.window
global.document = globalDom.window.document
test('Translate3d returns a CSS string', t => {
test('Translate3d returns a CSS string width default values', t => {
t.plan(1)
const translate3d = new Translate3d()
const expected = 'translate3d(0px, 0px, 0px);'
const expected = 'matrix(1, 0, 0, 1, 0, 0)'
const actual = String(translate3d)

@@ -15,1 +21,81 @@

})
test('Translate3d returns a CSS string with parameter values', t => {
t.plan(1)
const translate3d = new Translate3d(100, 200, 0.5)
const expected = 'matrix(0.5, 0, 0, 0.5, 100, 200)'
const actual = String(translate3d)
t.equal(actual, expected)
})
test('Translate3d will throw if you ask for percentage to pixel values and have not set a container', t => {
t.plan(1)
const translate3d = new Translate3d('100%', '200%', 0.5)
t.throws(() => { String(translate3d) }, Error)
})
test('Translate3d returns a CSS string with percentage to pixel values', t => {
t.plan(1)
const div = createMockDiv(200, 200)
const translate3d = new Translate3d('100%', '200%', 0.5)
translate3d.setElement(div)
const expected = 'matrix(0.5, 0, 0, 0.5, 200, 400)'
const actual = String(translate3d)
t.equal(actual, expected)
})
test('Translate3d can translate percentage values to pixel values', t => {
t.plan(1)
const div = createMockDiv(200, 200)
const translate3d = new Translate3d('50%', '100%', '0.5')
translate3d.setElement(div)
const expected = { tx: 100, ty: 200, tz: .5 }
const actual = translate3d.toObject()
t.match(actual, expected)
})
test('Translate3d can parse a css style string', t => {
t.plan(3)
const translate3d = Translate3d.parse('matrix(1.3, 0, 0, 1.3, 0, 0)')
t.ok(translate3d instanceof Translate3d, '.parse() should return an instance of Translate3d')
let expected = { tx: 0, ty: 0, tz: 1.3 }
let actual = translate3d
t.match(actual, expected, ' should return a string identical to the parsed string')
expected = null
actual = Translate3d.parse('matrix(something very wrong)')
t.equal(actual, expected, '.parse() should return null for incorrect transform matrix')
})
function createMockDiv (width, height) {
const div = global.document.createElement('div')
Object.assign(div.style, {
width: width + '',
height: height + '',
})
// we have to mock this for jsdom.
div.getBoundingClientRect = () => ({
width,
height,
top: 0,
left: 0,
right: width,
bottom: height,
})
return div
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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