Sammy Guergachi
diff --git a/lib/zooming/build/zooming.js b/lib/zooming/build/zooming.js
new file mode 100644
index 0000000..b67802a
--- /dev/null
+++ b/lib/zooming/build/zooming.js
@@ -0,0 +1,1231 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.Zooming = factory());
+}(this, (function () { 'use strict';
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+} : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+};
+
+
+
+
+
+
+
+
+
+
+
+var classCallCheck = function (instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+};
+
+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);
+ }
+ }
+
+ return function (Constructor, protoProps, staticProps) {
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) defineProperties(Constructor, staticProps);
+ return Constructor;
+ };
+}();
+
+
+
+
+
+
+
+var _extends = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+
+ return target;
+};
+
+var get = function get(object, property, receiver) {
+ if (object === null) object = Function.prototype;
+ var desc = Object.getOwnPropertyDescriptor(object, property);
+
+ if (desc === undefined) {
+ var parent = Object.getPrototypeOf(object);
+
+ if (parent === null) {
+ return undefined;
+ } else {
+ return get(parent, property, receiver);
+ }
+ } else if ("value" in desc) {
+ return desc.value;
+ } else {
+ var getter = desc.get;
+
+ if (getter === undefined) {
+ return undefined;
+ }
+
+ return getter.call(receiver);
+ }
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+var set = function set(object, property, value, receiver) {
+ var desc = Object.getOwnPropertyDescriptor(object, property);
+
+ if (desc === undefined) {
+ var parent = Object.getPrototypeOf(object);
+
+ if (parent !== null) {
+ set(parent, property, value, receiver);
+ }
+ } else if ("value" in desc && desc.writable) {
+ desc.value = value;
+ } else {
+ var setter = desc.set;
+
+ if (setter !== undefined) {
+ setter.call(receiver, value);
+ }
+ }
+
+ return value;
+};
+
+var body = document.body;
+var docElm = document.documentElement;
+var isString = checkType('string');
+var isLink = checkTag('A');
+var webkitPrefix = 'WebkitAppearance' in docElm.style ? '-webkit-' : '';
+
+function checkType(typeName) {
+ return function (el) {
+ return (typeof el === 'undefined' ? 'undefined' : _typeof(el)) === typeName;
+ };
+}
+
+function checkTag(tagName) {
+ return function (el) {
+ return el.tagName === tagName;
+ };
+}
+
+function getParents(el, match) {
+ var parents = [];
+
+ for (; el && el !== document; el = el.parentNode) {
+ if (match) {
+ if (match(el)) {
+ parents.push(el);
+ }
+ } else {
+ parents.push(el);
+ }
+ }
+
+ return parents;
+}
+
+function isNotImage() {
+ return checkTag('IMG') === false;
+}
+
+function loadImage(src, cb) {
+ if (!src) return;
+
+ var img = new Image();
+ img.onload = function () {
+ if (cb) cb(img);
+ };
+
+ img.src = src;
+}
+
+function checkOriginalImage(el, cb) {
+ var srcOriginal = null;
+
+ if (el.hasAttribute('data-original')) {
+ srcOriginal = el.getAttribute('data-original');
+ } else if (isLink(el.parentNode)) {
+ srcOriginal = el.parentNode.getAttribute('href');
+ }
+
+ cb(srcOriginal);
+}
+
+var trans = sniffTransition(document.createElement('div'));
+var transformCssProp = trans.transformCssProp;
+var transEndEvent = trans.transEndEvent;
+
+function checkTrans(styles) {
+ var transitionProp = trans.transitionProp;
+ var transformProp = trans.transformProp;
+
+ var value = void 0;
+ if (styles.transition) {
+ value = styles.transition;
+ delete styles.transition;
+ styles[transitionProp] = value;
+ }
+ if (styles.transform) {
+ value = styles.transform;
+ delete styles.transform;
+ styles[transformProp] = value;
+ }
+}
+
+function sniffTransition(el) {
+ var ret = {};
+ var trans = ['webkitTransition', 'transition', 'mozTransition'];
+ var tform = ['webkitTransform', 'transform', 'mozTransform'];
+ var end = {
+ 'transition': 'transitionend',
+ 'mozTransition': 'transitionend',
+ 'webkitTransition': 'webkitTransitionEnd'
+ };
+
+ trans.some(function (prop) {
+ if (el.style[prop] !== undefined) {
+ ret.transitionProp = prop;
+ ret.transEndEvent = end[prop];
+ return true;
+ }
+ });
+
+ tform.some(function (prop) {
+ if (el.style[prop] !== undefined) {
+ ret.transformProp = prop;
+ ret.transformCssProp = prop.replace(/(.*)Transform/, '-$1-transform');
+ return true;
+ }
+ });
+
+ return ret;
+}
+
+function divide(denominator) {
+ return function (numerator) {
+ return numerator / denominator;
+ };
+}
+
+var half = divide(2);
+
+var cursor = {
+ default: 'auto',
+ zoomIn: webkitPrefix + 'zoom-in',
+ zoomOut: webkitPrefix + 'zoom-out',
+ grab: webkitPrefix + 'grab',
+ move: 'move'
+};
+
+function toggleListener(el, type, handler, add) {
+ if (add) {
+ el.addEventListener(type, handler[type], { passive: false });
+ } else {
+ el.removeEventListener(type, handler[type], { passive: false });
+ }
+}
+
+function getWindowCenter() {
+ var windowWidth = Math.min(docElm.clientWidth, window.innerWidth);
+ var windowHeight = Math.min(docElm.clientHeight, window.innerHeight);
+
+ return {
+ x: half(windowWidth),
+ y: half(windowHeight)
+ };
+}
+
+function toggleGrabListeners(el, handler, add) {
+ ['mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchmove', 'touchend'].forEach(function (type) {
+ toggleListener(el, type, handler, add);
+ });
+}
+
+function setStyle(el, styles, remember) {
+ checkTrans(styles);
+
+ var s = el.style;
+ var original = {};
+
+ for (var key in styles) {
+ if (remember) original[key] = s[key] || '';
+ s[key] = styles[key];
+ }
+
+ return original;
+}
+
+function bindAll(_this, that) {
+ var methods = Object.getOwnPropertyNames(Object.getPrototypeOf(_this));
+
+ methods.forEach(function (method) {
+ _this[method] = _this[method].bind(that);
+ });
+}
+
+var overflowHiddenParents = {
+
+ // Map from Element to its overflow:hidden parents
+ map: new Map(),
+
+ // Map from parent to its original style
+ style: new Map(),
+
+ disable: disableOverflowHiddenParents,
+ enable: enableOverflowHiddenParents
+};
+
+function isOverflowHidden(el) {
+ return getComputedStyle(el).overflow === 'hidden';
+}
+
+function getOverflowHiddenParents(el) {
+ if (overflowHiddenParents.map.has(el)) {
+ return overflowHiddenParents.map.get(el);
+ } else {
+ var parents = getParents(el.parentNode, isOverflowHidden);
+ overflowHiddenParents.map.set(el, parents);
+ return parents;
+ }
+}
+
+function disableOverflowHiddenParents(el) {
+ getOverflowHiddenParents(el).forEach(function (parent) {
+ if (overflowHiddenParents.style.has(parent)) {
+ setStyle(parent, {
+ overflow: 'visible'
+ });
+ } else {
+ overflowHiddenParents.style.set(parent, setStyle(parent, {
+ overflow: 'visible'
+ }, true));
+ }
+ });
+}
+
+function enableOverflowHiddenParents(el) {
+ if (overflowHiddenParents.map.has(el)) {
+ overflowHiddenParents.map.get(el).forEach(function (parent) {
+ setStyle(parent, overflowHiddenParents.style.get(parent));
+ });
+ }
+}
+
+var PRESS_DELAY = 200;
+var MULTITOUCH_SCALE_FACTOR = 2;
+
+var EventHandler = function () {
+ function EventHandler(instance) {
+ classCallCheck(this, EventHandler);
+
+ bindAll(this, instance);
+ }
+
+ createClass(EventHandler, [{
+ key: 'click',
+ value: function click(e) {
+ e.preventDefault();
+
+ if (this.shown) {
+ if (this.released) this.close();else this.release();
+ } else {
+ this.open(e.currentTarget);
+ }
+ }
+ }, {
+ key: 'scroll',
+ value: function scroll() {
+ var scrollTop = window.pageYOffset || (docElm || body.parentNode || body).scrollTop;
+
+ if (this.lastScrollPosition === null) {
+ this.lastScrollPosition = scrollTop;
+ }
+
+ var deltaY = this.lastScrollPosition - scrollTop;
+
+ if (Math.abs(deltaY) >= this.options.scrollThreshold) {
+ this.lastScrollPosition = null;
+ this.close();
+ }
+ }
+ }, {
+ key: 'keydown',
+ value: function keydown(e) {
+ var _this = this;
+
+ if (isEscape(e)) {
+ if (this.released) this.close();else this.release(function () {
+ return _this.close();
+ });
+ }
+ }
+ }, {
+ key: 'mousedown',
+ value: function mousedown(e) {
+ var _this2 = this;
+
+ if (isNotLeftButton(e)) return;
+ e.preventDefault();
+
+ this.pressTimer = setTimeout(function () {
+ _this2.grab(e.clientX, e.clientY);
+ }, PRESS_DELAY);
+ }
+ }, {
+ key: 'mousemove',
+ value: function mousemove(e) {
+ if (this.released) return;
+ this.move(e.clientX, e.clientY);
+ }
+ }, {
+ key: 'mouseup',
+ value: function mouseup(e) {
+ if (isNotLeftButton(e)) return;
+ clearTimeout(this.pressTimer);
+
+ if (this.released) this.close();else this.release();
+ }
+ }, {
+ key: 'touchstart',
+ value: function touchstart(e) {
+ var _this3 = this;
+
+ e.preventDefault();
+
+ this.pressTimer = setTimeout(function () {
+ processTouches(e.touches, _this3.options.scaleExtra, function (x, y, scaleExtra) {
+ _this3.grab(x, y, scaleExtra);
+ });
+ }, PRESS_DELAY);
+ }
+ }, {
+ key: 'touchmove',
+ value: function touchmove(e) {
+ var _this4 = this;
+
+ if (this.released) return;
+
+ processTouches(e.touches, this.options.scaleExtra, function (x, y, scaleExtra) {
+ _this4.move(x, y, scaleExtra);
+ });
+ }
+ }, {
+ key: 'touchend',
+ value: function touchend(e) {
+ if (isTouching(e)) return;
+ clearTimeout(this.pressTimer);
+
+ if (this.released) this.close();else this.release();
+ }
+ }]);
+ return EventHandler;
+}();
+
+function isNotLeftButton(event) {
+ return event.button !== 0;
+}
+
+function isEscape(event) {
+ var code = event.key || event.code;
+ return code === 'Escape' || event.keyCode === 27;
+}
+
+function isTouching(event) {
+ return event.targetTouches.length > 0;
+}
+
+function processTouches(touches, currScaleExtra, cb) {
+ var total = touches.length;
+ var firstTouch = touches[0];
+ var multitouch = total > 1;
+
+ var scaleExtra = currScaleExtra;
+ var i = touches.length;
+ var xs = 0,
+ ys = 0;
+
+ // keep track of the min and max of touch positions
+
+ var min = { x: firstTouch.clientX, y: firstTouch.clientY };
+ var max = { x: firstTouch.clientX, y: firstTouch.clientY };
+
+ while (i--) {
+ var t = touches[i];
+ var _ref = [t.clientX, t.clientY],
+ x = _ref[0],
+ y = _ref[1];
+
+ xs += x;
+ ys += y;
+
+ if (!multitouch) continue;
+
+ if (x < min.x) {
+ min.x = x;
+ } else if (x > max.x) {
+ max.x = x;
+ }
+
+ if (y < min.y) {
+ min.y = y;
+ } else if (y > max.y) {
+ max.y = y;
+ }
+ }
+
+ if (multitouch) {
+ // change scaleExtra dynamically
+ var distX = max.x - min.x,
+ distY = max.y - min.y;
+
+
+ if (distX > distY) {
+ scaleExtra = distX / window.innerWidth * MULTITOUCH_SCALE_FACTOR;
+ } else {
+ scaleExtra = distY / window.innerHeight * MULTITOUCH_SCALE_FACTOR;
+ }
+ }
+
+ cb(xs / total, ys / total, scaleExtra);
+}
+
+var Overlay = function () {
+ function Overlay(el, instance) {
+ classCallCheck(this, Overlay);
+
+ this.el = el;
+ this.instance = instance;
+ this.parent = document.body;
+ }
+
+ createClass(Overlay, [{
+ key: 'init',
+ value: function init(options) {
+ var _this = this;
+
+ setStyle(this.el, {
+ zIndex: 998,
+ backgroundColor: options.bgColor,
+ position: 'fixed',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ opacity: 0,
+ transition: 'opacity\n ' + options.transitionDuration + 's\n ' + options.transitionTimingFunction
+ });
+
+ this.el.addEventListener('click', function () {
+ return _this.instance.close();
+ });
+ }
+ }, {
+ key: 'updateStyle',
+ value: function updateStyle(options) {
+ setStyle(this.el, {
+ backgroundColor: options.bgColor,
+ transition: 'opacity\n ' + options.transitionDuration + 's\n ' + options.transitionTimingFunction
+ });
+ }
+ }, {
+ key: 'insert',
+ value: function insert() {
+ this.parent.appendChild(this.el);
+ }
+ }, {
+ key: 'remove',
+ value: function remove() {
+ this.parent.removeChild(this.el);
+ }
+ }, {
+ key: 'show',
+ value: function show() {
+ var _this2 = this;
+
+ setTimeout(function () {
+ return _this2.el.style.opacity = _this2.instance.options.bgOpacity;
+ }, 30);
+ }
+ }, {
+ key: 'hide',
+ value: function hide() {
+ this.el.style.opacity = 0;
+ }
+ }]);
+ return Overlay;
+}();
+
+var Target = function () {
+ function Target(el, instance) {
+ classCallCheck(this, Target);
+
+ this.el = el;
+ this.instance = instance;
+ this.translate = null;
+ this.scale = null;
+ this.srcThumbnail = this.el.getAttribute('src');
+ this.style = {
+ open: null,
+ close: null
+ };
+ }
+
+ createClass(Target, [{
+ key: 'zoomIn',
+ value: function zoomIn() {
+ var options = this.instance.options;
+ var rect = this.el.getBoundingClientRect();
+
+ // Remove overflow:hidden from target's parent nodes if any. It prevents
+ // parent nodes from hiding the target after zooming in
+ overflowHiddenParents.disable(this.el);
+
+ this.translate = calculateTranslate(rect);
+ this.scale = calculateScale(rect, options.scaleBase, options.customSize);
+
+ // force layout update
+ this.el.offsetWidth;
+
+ this.style.open = {
+ position: 'relative',
+ zIndex: 999,
+ cursor: options.enableGrab ? cursor.grab : cursor.zoomOut,
+ transition: transformCssProp + '\n ' + options.transitionDuration + 's\n ' + options.transitionTimingFunction,
+ transform: 'translate(' + this.translate.x + 'px, ' + this.translate.y + 'px)\n scale(' + this.scale.x + ',' + this.scale.y + ')',
+ width: rect.width + 'px',
+ height: rect.height + 'px'
+ };
+
+ // trigger transition
+ this.style.close = setStyle(this.el, this.style.open, true);
+ }
+ }, {
+ key: 'zoomOut',
+ value: function zoomOut() {
+ // Restore overflow:hidden to target's parent nodes if any
+ overflowHiddenParents.enable(this.el);
+
+ // force layout update
+ this.el.offsetWidth;
+
+ setStyle(this.el, { transform: 'none' });
+ }
+ }, {
+ key: 'grab',
+ value: function grab(x, y, scaleExtra) {
+ var windowCenter = getWindowCenter();
+ var dx = windowCenter.x - x,
+ dy = windowCenter.y - y;
+
+
+ setStyle(this.el, {
+ cursor: cursor.move,
+ transform: 'translate(\n ' + (this.translate.x + dx) + 'px, ' + (this.translate.y + dy) + 'px)\n scale(' + (this.scale.x + scaleExtra) + ',' + (this.scale.y + scaleExtra) + ')'
+ });
+ }
+ }, {
+ key: 'move',
+ value: function move(x, y, scaleExtra) {
+ var windowCenter = getWindowCenter();
+ var dx = windowCenter.x - x,
+ dy = windowCenter.y - y;
+
+
+ setStyle(this.el, {
+ transition: transformCssProp,
+ transform: 'translate(\n ' + (this.translate.x + dx) + 'px, ' + (this.translate.y + dy) + 'px)\n scale(' + (this.scale.x + scaleExtra) + ',' + (this.scale.y + scaleExtra) + ')'
+ });
+ }
+ }, {
+ key: 'restoreCloseStyle',
+ value: function restoreCloseStyle() {
+ setStyle(this.el, this.style.close);
+ }
+ }, {
+ key: 'restoreOpenStyle',
+ value: function restoreOpenStyle() {
+ setStyle(this.el, this.style.open);
+ }
+ }, {
+ key: 'upgradeSource',
+ value: function upgradeSource(srcOriginal) {
+ var _this = this;
+
+ if (!srcOriginal) return;
+
+ var parentNode = this.el.parentNode;
+ var temp = this.el.cloneNode(false);
+
+ // force compute the hi-res image in DOM to prevent
+ // image flickering while updating src
+ temp.setAttribute('src', srcOriginal);
+ temp.style.position = 'fixed';
+ temp.style.visibility = 'hidden';
+ parentNode.appendChild(temp);
+
+ setTimeout(function () {
+ _this.el.setAttribute('src', srcOriginal);
+ parentNode.removeChild(temp);
+ }, 100);
+ }
+ }, {
+ key: 'downgradeSource',
+ value: function downgradeSource(srcOriginal) {
+ if (!srcOriginal) return;
+
+ this.el.setAttribute('src', this.srcThumbnail);
+ }
+ }]);
+ return Target;
+}();
+
+function calculateTranslate(rect) {
+ var windowCenter = getWindowCenter();
+ var targetCenter = {
+ x: rect.left + half(rect.width),
+ y: rect.top + half(rect.height)
+ };
+
+ // The vector to translate image to the window center
+ return {
+ x: windowCenter.x - targetCenter.x,
+ y: windowCenter.y - targetCenter.y
+ };
+}
+
+function calculateScale(rect, scaleBase, customSize) {
+ if (customSize) {
+ return {
+ x: customSize.width / rect.width,
+ y: customSize.height / rect.height
+ };
+ } else {
+ var targetHalfWidth = half(rect.width);
+ var targetHalfHeight = half(rect.height);
+ var windowCenter = getWindowCenter();
+
+ // The distance between target edge and window edge
+ var targetEdgeToWindowEdge = {
+ x: windowCenter.x - targetHalfWidth,
+ y: windowCenter.y - targetHalfHeight
+ };
+
+ var scaleHorizontally = targetEdgeToWindowEdge.x / targetHalfWidth;
+ var scaleVertically = targetEdgeToWindowEdge.y / targetHalfHeight;
+
+ // The additional scale is based on the smaller value of
+ // scaling horizontally and scaling vertically
+ var scale = scaleBase + Math.min(scaleHorizontally, scaleVertically);
+
+ return {
+ x: scale,
+ y: scale
+ };
+ }
+}
+
+/**
+ * A list of options.
+ *
+ * @type {Object}
+ * @example
+ * // Default options
+ * var options = {
+ * defaultZoomable: 'img[data-action="zoom"]',
+ * enableGrab: true,
+ * preloadImage: true,
+ * transitionDuration: 0.4,
+ * transitionTimingFunction: 'cubic-bezier(0.4, 0, 0, 1)',
+ * bgColor: 'rgb(255, 255, 255)',
+ * bgOpacity: 1,
+ * scaleBase: 1.0,
+ * scaleExtra: 0.5,
+ * scrollThreshold: 40,
+ * customSize: null,
+ * onOpen: null,
+ * onClose: null,
+ * onRelease: null,
+ * onBeforeOpen: null,
+ * onBeforeClose: null,
+ * onBeforeGrab: null,
+ * onBeforeMove: null,
+ * onBeforeRelease: null
+ * }
+ */
+var OPTIONS = {
+ /**
+ * Zoomable elements by default. It can be a css selector or an element.
+ * @type {string|Element}
+ */
+ defaultZoomable: 'img[data-action="zoom"]',
+
+ /**
+ * To be able to grab and drag the image for extra zoom-in.
+ * @type {boolean}
+ */
+ enableGrab: true,
+
+ /**
+ * Preload images with attribute "data-original".
+ * @type {boolean}
+ */
+ preloadImage: true,
+
+ /**
+ * Transition duration in seconds.
+ * @type {number}
+ */
+ transitionDuration: 0.4,
+
+ /**
+ * Transition timing function.
+ * @type {string}
+ */
+ transitionTimingFunction: 'cubic-bezier(0.4, 0, 0, 1)',
+
+ /**
+ * Overlay background color.
+ * @type {string}
+ */
+ bgColor: 'rgb(255, 255, 255)',
+
+ /**
+ * Overlay background opacity.
+ * @type {number}
+ */
+ bgOpacity: 1,
+
+ /**
+ * The base scale factor for zooming. By default scale to fit the window.
+ * @type {number}
+ */
+ scaleBase: 1.0,
+
+ /**
+ * The extra scale factor when grabbing the image.
+ * @type {number}
+ */
+ scaleExtra: 0.5,
+
+ /**
+ * How much scrolling it takes before closing out.
+ * @type {number}
+ */
+ scrollThreshold: 40,
+
+ /**
+ * Scale (zoom in) to given width and height. Ignore scaleBase if set.
+ * @type {Object}
+ * @example
+ * customSize: { width: 800, height: 400 }
+ */
+ customSize: null,
+
+ /**
+ * A callback function that will be called when a target is opened and
+ * transition has ended. It will get the target element as the argument.
+ * @type {Function}
+ */
+ onOpen: null,
+
+ /**
+ * Same as above, except fired when closed.
+ * @type {Function}
+ */
+ onClose: null,
+
+ /**
+ * Same as above, except fired when released.
+ * @type {Function}
+ */
+ onRelease: null,
+
+ /**
+ * A callback function that will be called before open.
+ * @type {Function}
+ */
+ onBeforeOpen: null,
+
+ /**
+ * A callback function that will be called before close.
+ * @type {Function}
+ */
+ onBeforeClose: null,
+
+ /**
+ * A callback function that will be called before grab.
+ * @type {Function}
+ */
+ onBeforeGrab: null,
+
+ /**
+ * A callback function that will be called before move.
+ * @type {Function}
+ */
+ onBeforeMove: null,
+
+ /**
+ * A callback function that will be called before release.
+ * @type {Function}
+ */
+ onBeforeRelease: null
+};
+
+var Zooming$1 = function () {
+
+ /**
+ * @param {Object} [options] Update default options if provided.
+ */
+ function Zooming(options) {
+ classCallCheck(this, Zooming);
+
+
+ // elements
+ this.target = null;
+ this.overlay = new Overlay(document.createElement('div'), this);
+ this.eventHandler = new EventHandler(this);
+ this.body = document.body;
+
+ // state
+ this.shown = false; // target is open
+ this.lock = false; // target is in transform
+ this.released = true; // mouse/finger is not pressing down
+ this.lastScrollPosition = null;
+ this.pressTimer = null;
+
+ // init
+ this.options = _extends({}, OPTIONS);
+ this.config(options);
+ this.listen(this.options.defaultZoomable);
+ this.overlay.init(this.options);
+ }
+
+ /**
+ * Make element(s) zoomable.
+ * @param {string|Element} el A css selector or an Element.
+ * @return {this}
+ */
+
+
+ createClass(Zooming, [{
+ key: 'listen',
+ value: function listen(el) {
+ if (isString(el)) {
+ var els = document.querySelectorAll(el),
+ i = els.length;
+
+ while (i--) {
+ this.listen(els[i]);
+ }
+
+ return this;
+ }
+
+ if (isNotImage(el)) return;
+
+ el.style.cursor = cursor.zoomIn;
+ el.addEventListener('click', this.eventHandler.click, { passive: false });
+
+ if (this.options.preloadImage) {
+ checkOriginalImage(el, loadImage);
+ }
+
+ return this;
+ }
+
+ /**
+ * Update options.
+ * @param {Object} options An Object that contains this.options.
+ * @return {this}
+ */
+
+ }, {
+ key: 'config',
+ value: function config(options) {
+ if (!options) return this.options;
+
+ _extends(this.options, options);
+ this.overlay.updateStyle(this.options);
+
+ return this;
+ }
+
+ /**
+ * Open (zoom in) the Element.
+ * @param {Element} el The Element to open.
+ * @param {Function} [cb=this.options.onOpen] A callback function that will
+ * be called when a target is opened and transition has ended. It will get
+ * the target element as the argument.
+ * @return {this}
+ */
+
+ }, {
+ key: 'open',
+ value: function open(el) {
+ var _this = this;
+
+ var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.onOpen;
+
+ if (this.shown || this.lock) return;
+
+ var target = isString(el) ? document.querySelector(el) : el;
+
+ if (isNotImage(target)) return;
+
+ // onBeforeOpen event
+ if (this.options.onBeforeOpen) this.options.onBeforeOpen(target);
+
+ if (!this.options.preloadImage) {
+ checkOriginalImage(target, loadImage);
+ }
+
+ this.target = new Target(target, this);
+
+ this.shown = true;
+ this.lock = true;
+
+ this.target.zoomIn();
+ this.overlay.insert();
+ this.overlay.show();
+
+ document.addEventListener('scroll', this.eventHandler.scroll);
+ document.addEventListener('keydown', this.eventHandler.keydown);
+
+ var onEnd = function onEnd() {
+ target.removeEventListener(transEndEvent, onEnd);
+
+ _this.lock = false;
+
+ checkOriginalImage(target, function (srcOriginal) {
+ return _this.target.upgradeSource(srcOriginal);
+ });
+
+ if (_this.options.enableGrab) {
+ toggleGrabListeners(document, _this.eventHandler, true);
+ }
+
+ if (cb) cb(target);
+ };
+
+ target.addEventListener(transEndEvent, onEnd);
+
+ return this;
+ }
+
+ /**
+ * Close (zoom out) the Element currently opened.
+ * @param {Function} [cb=this.options.onClose] A callback function that will
+ * be called when a target is closed and transition has ended. It will get
+ * the target element as the argument.
+ * @return {this}
+ */
+
+ }, {
+ key: 'close',
+ value: function close() {
+ var _this2 = this;
+
+ var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.onClose;
+
+ if (!this.shown || this.lock) return;
+
+ var target = this.target.el;
+
+ // onBeforeClose event
+ if (this.options.onBeforeClose) this.options.onBeforeClose(target);
+
+ this.lock = true;
+
+ this.body.style.cursor = cursor.default;
+ this.overlay.hide();
+ this.target.zoomOut();
+
+ document.removeEventListener('scroll', this.eventHandler.scroll);
+ document.removeEventListener('keydown', this.eventHandler.keydown);
+
+ var onEnd = function onEnd() {
+ target.removeEventListener(transEndEvent, onEnd);
+
+ _this2.shown = false;
+ _this2.lock = false;
+
+ checkOriginalImage(target, function (srcOriginal) {
+ return _this2.target.downgradeSource(srcOriginal);
+ });
+
+ if (_this2.options.enableGrab) {
+ toggleGrabListeners(document, _this2.eventHandler, false);
+ }
+
+ _this2.target.restoreCloseStyle();
+ _this2.overlay.remove();
+
+ if (cb) cb(target);
+ };
+
+ target.addEventListener(transEndEvent, onEnd);
+
+ return this;
+ }
+
+ /**
+ * Grab the Element currently opened given a position and apply extra zoom-in.
+ * @param {number} x The X-axis of where the press happened.
+ * @param {number} y The Y-axis of where the press happened.
+ * @param {number} scaleExtra Extra zoom-in to apply.
+ * @param {Function} [cb=this.options.scaleExtra] A callback function that
+ * will be called when a target is grabbed and transition has ended. It
+ * will get the target element as the argument.
+ * @return {this}
+ */
+
+ }, {
+ key: 'grab',
+ value: function grab(x, y) {
+ var scaleExtra = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.options.scaleExtra;
+ var cb = arguments[3];
+
+ if (!this.shown || this.lock) return;
+
+ var target = this.target.el;
+
+ // onBeforeGrab event
+ if (this.options.onBeforeGrab) this.options.onBeforeGrab(target);
+
+ this.released = false;
+ this.target.grab(x, y, scaleExtra);
+
+ var onEnd = function onEnd() {
+ target.removeEventListener(transEndEvent, onEnd);
+ if (cb) cb(target);
+ };
+
+ target.addEventListener(transEndEvent, onEnd);
+ }
+
+ /**
+ * Move the Element currently grabbed given a position and apply extra zoom-in.
+ * @param {number} x The X-axis of where the press happened.
+ * @param {number} y The Y-axis of where the press happened.
+ * @param {number} scaleExtra Extra zoom-in to apply.
+ * @param {Function} [cb=this.options.scaleExtra] A callback function that
+ * will be called when a target is moved and transition has ended. It will
+ * get the target element as the argument.
+ * @return {this}
+ */
+
+ }, {
+ key: 'move',
+ value: function move(x, y) {
+ var scaleExtra = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.options.scaleExtra;
+ var cb = arguments[3];
+
+ if (!this.shown || this.lock) return;
+
+ var target = this.target.el;
+
+ // onBeforeMove event
+ if (this.options.onBeforeMove) this.options.onBeforeMove(target);
+
+ this.released = false;
+
+ this.target.move(x, y, scaleExtra);
+ this.body.style.cursor = cursor.move;
+
+ var onEnd = function onEnd() {
+ target.removeEventListener(transEndEvent, onEnd);
+ if (cb) cb(target);
+ };
+
+ target.addEventListener(transEndEvent, onEnd);
+ }
+
+ /**
+ * Release the Element currently grabbed.
+ * @param {Function} [cb=this.options.onRelease] A callback function that
+ * will be called when a target is released and transition has ended. It
+ * will get the target element as the argument.
+ * @return {this}
+ */
+
+ }, {
+ key: 'release',
+ value: function release() {
+ var _this3 = this;
+
+ var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.onRelease;
+
+ if (!this.shown || this.lock) return;
+
+ var target = this.target.el;
+
+ // onBeforeRelease event
+ if (this.options.onBeforeRelease) this.options.onBeforeRelease(target);
+
+ this.lock = true;
+
+ this.target.restoreOpenStyle();
+ this.body.style.cursor = cursor.default;
+
+ var onEnd = function onEnd() {
+ target.removeEventListener(transEndEvent, onEnd);
+
+ _this3.lock = false;
+ _this3.released = true;
+
+ if (cb) cb(target);
+ };
+
+ target.addEventListener(transEndEvent, onEnd);
+
+ return this;
+ }
+ }]);
+ return Zooming;
+}();
+
+document.addEventListener('DOMContentLoaded', function () {
+ new Zooming$1();
+});
+
+return Zooming$1;
+
+})));
+//# sourceMappingURL=zooming.js.map
diff --git a/lib/zooming/build/zooming.js.map b/lib/zooming/build/zooming.js.map
new file mode 100644
index 0000000..13f8f3a
--- /dev/null
+++ b/lib/zooming/build/zooming.js.map
@@ -0,0 +1 @@
+{"version":3,"file":null,"sources":["../src/util/_dom.js","../src/util/_image.js","../src/util/_trans.js","../src/util/_math.js","../src/util/_helpers.js","../src/EventHandler.js","../src/Overlay.js","../src/Target.js","../src/_options.js","../src/Zooming.js","../src/main.js"],"sourcesContent":["export const body = document.body\nexport const docElm = document.documentElement\nexport const isString = checkType('string')\nexport const isLink = checkTag('A')\nexport const webkitPrefix = 'WebkitAppearance' in docElm.style\n ? '-webkit-'\n : ''\n\nexport function checkType (typeName) {\n return function (el) {\n return typeof el === typeName\n }\n}\n\nexport function checkTag (tagName) {\n return function (el) {\n return el.tagName === tagName\n }\n}\n\nexport function getParents (el, match) {\n let parents = []\n\n for (; el && el !== document; el = el.parentNode) {\n if (match) {\n if (match(el)) {\n parents.push(el)\n }\n } else {\n parents.push(el)\n }\n }\n\n return parents\n}\n","import { checkTag, isLink } from './_dom'\n\nexport function isNotImage () {\n return checkTag('IMG') === false\n}\n\nexport function loadImage (src, cb) {\n if (!src) return\n\n const img = new Image()\n img.onload = function () {\n if (cb) cb(img)\n }\n \n img.src = src\n}\n\nexport function checkOriginalImage (el, cb) {\n let srcOriginal = null\n\n if (el.hasAttribute('data-original')) {\n srcOriginal = el.getAttribute('data-original')\n } else if (isLink(el.parentNode)) {\n srcOriginal = el.parentNode.getAttribute('href')\n }\n\n cb(srcOriginal)\n}\n","const trans = sniffTransition(document.createElement('div'))\nexport const transformCssProp = trans.transformCssProp\nexport const transEndEvent = trans.transEndEvent\n\nexport function checkTrans (styles) {\n const transitionProp = trans.transitionProp\n const transformProp = trans.transformProp\n\n let value\n if (styles.transition) {\n value = styles.transition\n delete styles.transition\n styles[transitionProp] = value\n }\n if (styles.transform) {\n value = styles.transform\n delete styles.transform\n styles[transformProp] = value\n }\n}\n\nexport function sniffTransition (el) {\n let ret = {}\n const trans = ['webkitTransition', 'transition', 'mozTransition']\n const tform = ['webkitTransform', 'transform', 'mozTransform']\n const end = {\n 'transition' : 'transitionend',\n 'mozTransition' : 'transitionend',\n 'webkitTransition' : 'webkitTransitionEnd'\n }\n\n trans.some(prop => {\n if (el.style[prop] !== undefined) {\n ret.transitionProp = prop\n ret.transEndEvent = end[prop]\n return true\n }\n })\n\n tform.some(prop => {\n if (el.style[prop] !== undefined) {\n ret.transformProp = prop\n ret.transformCssProp = prop.replace(/(.*)Transform/, '-$1-transform')\n return true\n }\n })\n\n return ret\n}\n","function divide (denominator) {\n return (numerator) => {\n return numerator / denominator\n }\n}\n\nexport const half = divide(2)\n","import { docElm, webkitPrefix, getParents } from './_dom'\nimport { checkTrans } from './_trans'\nimport { half } from './_math'\n\nexport const cursor = {\n default: 'auto',\n zoomIn: `${webkitPrefix}zoom-in`,\n zoomOut: `${webkitPrefix}zoom-out`,\n grab: `${webkitPrefix}grab`,\n move: 'move'\n}\n\nexport function toggleListener (el, type, handler, add) {\n if (add) {\n el.addEventListener(type, handler[type], { passive: false })\n } else {\n el.removeEventListener(type, handler[type], { passive: false })\n }\n}\n\nexport function getWindowCenter () {\n const windowWidth = Math.min(docElm.clientWidth, window.innerWidth)\n const windowHeight = Math.min(docElm.clientHeight, window.innerHeight)\n\n return {\n x: half(windowWidth),\n y: half(windowHeight)\n }\n}\n\nexport function toggleGrabListeners (el, handler, add) {\n ['mousedown', 'mousemove', 'mouseup','touchstart', 'touchmove', 'touchend']\n .forEach(type => {\n toggleListener(el, type, handler, add)\n })\n}\n\nexport function setStyle (el, styles, remember) {\n checkTrans(styles)\n\n let s = el.style\n let original = {}\n\n for (let key in styles) {\n if (remember) original[key] = s[key] || ''\n s[key] = styles[key]\n }\n\n return original\n}\n\nexport function bindAll (_this, that) {\n const methods = (\n Object.getOwnPropertyNames(\n Object.getPrototypeOf(_this)\n )\n )\n\n methods.forEach(method => {\n _this[method] = _this[method].bind(that)\n })\n}\n\nexport const overflowHiddenParents = {\n\n // Map from Element to its overflow:hidden parents\n map: new Map(),\n\n // Map from parent to its original style\n style: new Map(),\n\n disable: disableOverflowHiddenParents,\n enable: enableOverflowHiddenParents\n}\n\nfunction isOverflowHidden (el) {\n return getComputedStyle(el).overflow === 'hidden'\n}\n\nfunction getOverflowHiddenParents (el) {\n if (overflowHiddenParents.map.has(el)) {\n return overflowHiddenParents.map.get(el)\n } else {\n const parents = getParents(el.parentNode, isOverflowHidden)\n overflowHiddenParents.map.set(el, parents)\n return parents\n }\n}\n\nfunction disableOverflowHiddenParents (el) {\n getOverflowHiddenParents(el).forEach(parent => {\n if (overflowHiddenParents.style.has(parent)) {\n setStyle(parent, {\n overflow: 'visible'\n })\n } else {\n overflowHiddenParents.style.set(parent, setStyle(parent, {\n overflow: 'visible'\n }, true))\n }\n })\n}\n\nfunction enableOverflowHiddenParents (el) {\n if (overflowHiddenParents.map.has(el)) {\n overflowHiddenParents.map.get(el).forEach(parent => {\n setStyle(parent, overflowHiddenParents.style.get(parent))\n })\n }\n}\n","import { docElm, body } from './util/_dom'\nimport { bindAll } from './util/_helpers'\n\nconst PRESS_DELAY = 200\nconst MULTITOUCH_SCALE_FACTOR = 2\n\nexport default class EventHandler {\n\n constructor (instance) {\n bindAll(this, instance)\n }\n\n click (e) {\n e.preventDefault()\n\n if (this.shown) {\n if (this.released) this.close()\n else this.release()\n } else {\n this.open(e.currentTarget)\n }\n }\n\n scroll () {\n const scrollTop = window.pageYOffset ||\n (docElm || body.parentNode || body).scrollTop\n\n if (this.lastScrollPosition === null) {\n this.lastScrollPosition = scrollTop\n }\n\n const deltaY = this.lastScrollPosition - scrollTop\n\n if (Math.abs(deltaY) >= this.options.scrollThreshold) {\n this.lastScrollPosition = null\n this.close()\n }\n }\n\n keydown (e) {\n if (isEscape(e)) {\n if (this.released) this.close()\n else this.release(() => this.close())\n }\n }\n\n mousedown (e) {\n if (isNotLeftButton(e)) return\n e.preventDefault()\n\n this.pressTimer = setTimeout(() => {\n this.grab(e.clientX, e.clientY)\n }, PRESS_DELAY)\n }\n\n mousemove (e) {\n if (this.released) return\n this.move(e.clientX, e.clientY)\n }\n\n mouseup (e) {\n if (isNotLeftButton(e)) return\n clearTimeout(this.pressTimer)\n\n if (this.released) this.close()\n else this.release()\n }\n\n touchstart (e) {\n e.preventDefault()\n\n this.pressTimer = setTimeout(() => {\n processTouches(e.touches, this.options.scaleExtra,\n (x, y, scaleExtra) => {\n this.grab(x, y, scaleExtra)\n })\n }, PRESS_DELAY)\n }\n\n touchmove (e) {\n if (this.released) return\n\n processTouches(e.touches, this.options.scaleExtra,\n (x, y, scaleExtra) => {\n this.move(x, y, scaleExtra)\n })\n }\n\n touchend (e) {\n if (isTouching(e)) return\n clearTimeout(this.pressTimer)\n\n if (this.released) this.close()\n else this.release()\n }\n}\n\nfunction isNotLeftButton (event) {\n return event.button !== 0\n}\n\nfunction isEscape (event) {\n const code = event.key || event.code\n return code === 'Escape' || event.keyCode === 27\n}\n\nfunction isTouching (event) {\n return event.targetTouches.length > 0\n}\n\nfunction processTouches (touches, currScaleExtra, cb) {\n const total = touches.length\n const firstTouch = touches[0]\n const multitouch = total > 1\n\n let scaleExtra = currScaleExtra\n let i = touches.length\n let [xs, ys] = [0, 0]\n\n // keep track of the min and max of touch positions\n let min = { x: firstTouch.clientX, y: firstTouch.clientY }\n let max = { x: firstTouch.clientX, y: firstTouch.clientY }\n\n while (i--) {\n const t = touches[i]\n const [x, y] = [t.clientX, t.clientY]\n xs += x\n ys += y\n\n if (!multitouch) continue\n\n if (x < min.x) {\n min.x = x\n } else if (x > max.x) {\n max.x = x\n }\n\n if (y < min.y) {\n min.y = y\n } else if (y > max.y) {\n max.y = y\n }\n }\n\n if (multitouch) {\n // change scaleExtra dynamically\n const [distX, distY] = [max.x - min.x, max.y - min.y]\n\n if (distX > distY) {\n scaleExtra = (distX / window.innerWidth) * MULTITOUCH_SCALE_FACTOR\n } else {\n scaleExtra = (distY / window.innerHeight) * MULTITOUCH_SCALE_FACTOR\n }\n }\n\n cb(xs / total, ys / total, scaleExtra)\n}\n","import { setStyle } from './util/_helpers'\n\nexport default class Overlay {\n\n constructor (el, instance) {\n this.el = el\n this.instance = instance\n this.parent = document.body\n }\n\n init (options) {\n setStyle(this.el, {\n zIndex: 998,\n backgroundColor: options.bgColor,\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n opacity: 0,\n transition: `opacity\n ${options.transitionDuration}s\n ${options.transitionTimingFunction}`\n })\n\n this.el.addEventListener('click', () => this.instance.close())\n }\n\n updateStyle (options) {\n setStyle(this.el, {\n backgroundColor: options.bgColor,\n transition: `opacity\n ${options.transitionDuration}s\n ${options.transitionTimingFunction}`\n })\n }\n\n insert () {\n this.parent.appendChild(this.el)\n }\n\n remove () {\n this.parent.removeChild(this.el)\n }\n\n show () {\n setTimeout(() => this.el.style.opacity = this.instance.options.bgOpacity, 30)\n }\n\n hide () {\n this.el.style.opacity = 0\n }\n}\n","import { cursor, setStyle, getWindowCenter, overflowHiddenParents } from './util/_helpers'\nimport { transformCssProp } from './util/_trans'\nimport { half } from './util/_math'\n\nexport default class Target {\n\n constructor (el, instance) {\n this.el = el\n this.instance = instance\n this.translate = null\n this.scale = null\n this.srcThumbnail = this.el.getAttribute('src')\n this.style = {\n open: null,\n close: null\n }\n }\n\n zoomIn () {\n const options = this.instance.options\n const rect = this.el.getBoundingClientRect()\n\n // Remove overflow:hidden from target's parent nodes if any. It prevents\n // parent nodes from hiding the target after zooming in\n overflowHiddenParents.disable(this.el)\n\n this.translate = calculateTranslate(rect)\n this.scale = calculateScale(rect, options.scaleBase, options.customSize)\n\n // force layout update\n this.el.offsetWidth\n\n this.style.open = {\n position: 'relative',\n zIndex: 999,\n cursor: options.enableGrab\n ? cursor.grab\n : cursor.zoomOut,\n transition: `${transformCssProp}\n ${options.transitionDuration}s\n ${options.transitionTimingFunction}`,\n transform: `translate(${this.translate.x}px, ${this.translate.y}px)\n scale(${this.scale.x},${this.scale.y})`,\n width: `${rect.width}px`,\n height: `${rect.height}px`\n }\n\n // trigger transition\n this.style.close = setStyle(this.el, this.style.open, true)\n }\n\n zoomOut () {\n // Restore overflow:hidden to target's parent nodes if any\n overflowHiddenParents.enable(this.el)\n\n // force layout update\n this.el.offsetWidth\n\n setStyle(this.el, { transform: 'none' })\n }\n\n grab (x, y, scaleExtra) {\n const windowCenter = getWindowCenter()\n const [dx, dy] = [windowCenter.x - x, windowCenter.y - y]\n\n setStyle(this.el, {\n cursor: cursor.move,\n transform: `translate(\n ${this.translate.x + dx}px, ${this.translate.y + dy}px)\n scale(${this.scale.x + scaleExtra},${this.scale.y + scaleExtra})`\n })\n }\n\n move (x, y, scaleExtra) {\n const windowCenter = getWindowCenter()\n const [dx, dy] = [windowCenter.x - x, windowCenter.y - y]\n\n setStyle(this.el, {\n transition: transformCssProp,\n transform: `translate(\n ${this.translate.x + dx}px, ${this.translate.y + dy}px)\n scale(${this.scale.x + scaleExtra},${this.scale.y + scaleExtra})`\n })\n }\n\n restoreCloseStyle () {\n setStyle(this.el, this.style.close)\n }\n\n restoreOpenStyle () {\n setStyle(this.el, this.style.open)\n }\n\n upgradeSource (srcOriginal) {\n if (!srcOriginal) return\n\n const parentNode = this.el.parentNode\n const temp = this.el.cloneNode(false)\n\n // force compute the hi-res image in DOM to prevent\n // image flickering while updating src\n temp.setAttribute('src', srcOriginal)\n temp.style.position = 'fixed'\n temp.style.visibility = 'hidden'\n parentNode.appendChild(temp)\n\n setTimeout(() => {\n this.el.setAttribute('src', srcOriginal)\n parentNode.removeChild(temp)\n }, 100)\n }\n\n downgradeSource (srcOriginal) {\n if (!srcOriginal) return\n\n this.el.setAttribute('src', this.srcThumbnail)\n }\n}\n\nfunction calculateTranslate (rect) {\n const windowCenter = getWindowCenter()\n const targetCenter = {\n x: rect.left + half(rect.width),\n y: rect.top + half(rect.height)\n }\n\n // The vector to translate image to the window center\n return {\n x: windowCenter.x - targetCenter.x,\n y: windowCenter.y - targetCenter.y\n }\n}\n\nfunction calculateScale (rect, scaleBase, customSize) {\n if (customSize) {\n return {\n x: customSize.width / rect.width,\n y: customSize.height / rect.height\n }\n } else {\n const targetHalfWidth = half(rect.width)\n const targetHalfHeight = half(rect.height)\n const windowCenter = getWindowCenter()\n\n // The distance between target edge and window edge\n const targetEdgeToWindowEdge = {\n x: windowCenter.x - targetHalfWidth,\n y: windowCenter.y - targetHalfHeight\n }\n\n const scaleHorizontally = targetEdgeToWindowEdge.x / targetHalfWidth\n const scaleVertically = targetEdgeToWindowEdge.y / targetHalfHeight\n\n // The additional scale is based on the smaller value of\n // scaling horizontally and scaling vertically\n const scale = scaleBase + Math.min(scaleHorizontally, scaleVertically)\n\n return {\n x: scale,\n y: scale\n }\n }\n}\n","/**\n * A list of options.\n *\n * @type {Object}\n * @example\n * // Default options\n * var options = {\n * defaultZoomable: 'img[data-action=\"zoom\"]',\n * enableGrab: true,\n * preloadImage: true,\n * transitionDuration: 0.4,\n * transitionTimingFunction: 'cubic-bezier(0.4, 0, 0, 1)',\n * bgColor: 'rgb(255, 255, 255)',\n * bgOpacity: 1,\n * scaleBase: 1.0,\n * scaleExtra: 0.5,\n * scrollThreshold: 40,\n * customSize: null,\n * onOpen: null,\n * onClose: null,\n * onRelease: null,\n * onBeforeOpen: null,\n * onBeforeClose: null,\n * onBeforeGrab: null,\n * onBeforeMove: null,\n * onBeforeRelease: null\n * }\n */\nconst OPTIONS = {\n /**\n * Zoomable elements by default. It can be a css selector or an element.\n * @type {string|Element}\n */\n defaultZoomable: 'img[data-action=\"zoom\"]',\n\n /**\n * To be able to grab and drag the image for extra zoom-in.\n * @type {boolean}\n */\n enableGrab: true,\n\n /**\n * Preload images with attribute \"data-original\".\n * @type {boolean}\n */\n preloadImage: true,\n\n /**\n * Transition duration in seconds.\n * @type {number}\n */\n transitionDuration: 0.4,\n\n /**\n * Transition timing function.\n * @type {string}\n */\n transitionTimingFunction: 'cubic-bezier(0.4, 0, 0, 1)',\n\n /**\n * Overlay background color.\n * @type {string}\n */\n bgColor: 'rgb(255, 255, 255)',\n\n /**\n * Overlay background opacity.\n * @type {number}\n */\n bgOpacity: 1,\n\n /**\n * The base scale factor for zooming. By default scale to fit the window.\n * @type {number}\n */\n scaleBase: 1.0,\n\n /**\n * The extra scale factor when grabbing the image.\n * @type {number}\n */\n scaleExtra: 0.5,\n\n /**\n * How much scrolling it takes before closing out.\n * @type {number}\n */\n scrollThreshold: 40,\n\n /**\n * Scale (zoom in) to given width and height. Ignore scaleBase if set.\n * @type {Object}\n * @example\n * customSize: { width: 800, height: 400 }\n */\n customSize: null,\n\n /**\n * A callback function that will be called when a target is opened and\n * transition has ended. It will get the target element as the argument.\n * @type {Function}\n */\n onOpen: null,\n\n /**\n * Same as above, except fired when closed.\n * @type {Function}\n */\n onClose: null,\n\n /**\n * Same as above, except fired when released.\n * @type {Function}\n */\n onRelease: null,\n\n /**\n * A callback function that will be called before open.\n * @type {Function}\n */\n onBeforeOpen: null,\n\n /**\n * A callback function that will be called before close.\n * @type {Function}\n */\n onBeforeClose: null,\n\n /**\n * A callback function that will be called before grab.\n * @type {Function}\n */\n onBeforeGrab: null,\n\n /**\n * A callback function that will be called before move.\n * @type {Function}\n */\n onBeforeMove: null,\n\n /**\n * A callback function that will be called before release.\n * @type {Function}\n */\n onBeforeRelease: null\n}\n\nexport default OPTIONS\n","import { isNotImage, loadImage, checkOriginalImage } from './util/_image'\nimport { cursor, toggleGrabListeners } from './util/_helpers'\nimport { transEndEvent } from './util/_trans'\nimport { isString } from './util/_dom'\n\nimport EventHandler from './EventHandler'\nimport Overlay from './Overlay'\nimport Target from './Target'\n\nimport DEFAULT_OPTIONS from './_options'\n\n/**\n * Zooming instance.\n */\nexport default class Zooming {\n\n /**\n * @param {Object} [options] Update default options if provided.\n */\n constructor (options) {\n\n // elements\n this.target = null\n this.overlay = new Overlay(document.createElement('div'), this)\n this.eventHandler = new EventHandler(this)\n this.body = document.body\n\n // state\n this.shown = false // target is open\n this.lock = false // target is in transform\n this.released = true // mouse/finger is not pressing down\n this.lastScrollPosition = null\n this.pressTimer = null\n\n // init\n this.options = Object.assign({}, DEFAULT_OPTIONS)\n this.config(options)\n this.listen(this.options.defaultZoomable)\n this.overlay.init(this.options)\n }\n\n /**\n * Make element(s) zoomable.\n * @param {string|Element} el A css selector or an Element.\n * @return {this}\n */\n listen (el) {\n if (isString(el)) {\n let els = document.querySelectorAll(el), i = els.length\n\n while (i--) {\n this.listen(els[i])\n }\n\n return this\n }\n\n if (isNotImage(el)) return\n\n el.style.cursor = cursor.zoomIn\n el.addEventListener('click', this.eventHandler.click, { passive: false })\n\n if (this.options.preloadImage) {\n checkOriginalImage(el, loadImage)\n }\n\n return this\n }\n\n /**\n * Update options.\n * @param {Object} options An Object that contains this.options.\n * @return {this}\n */\n config (options) {\n if (!options) return this.options\n\n Object.assign(this.options, options)\n this.overlay.updateStyle(this.options)\n\n return this\n }\n\n /**\n * Open (zoom in) the Element.\n * @param {Element} el The Element to open.\n * @param {Function} [cb=this.options.onOpen] A callback function that will\n * be called when a target is opened and transition has ended. It will get\n * the target element as the argument.\n * @return {this}\n */\n open (el, cb = this.options.onOpen) {\n if (this.shown || this.lock) return\n\n const target = isString(el)\n ? document.querySelector(el)\n : el\n\n if (isNotImage(target)) return\n\n // onBeforeOpen event\n if (this.options.onBeforeOpen) this.options.onBeforeOpen(target)\n\n if (!this.options.preloadImage) {\n checkOriginalImage(target, loadImage)\n }\n\n this.target = new Target(target, this)\n\n this.shown = true\n this.lock = true\n\n this.target.zoomIn()\n this.overlay.insert()\n this.overlay.show()\n\n document.addEventListener('scroll', this.eventHandler.scroll)\n document.addEventListener('keydown', this.eventHandler.keydown)\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n\n this.lock = false\n\n checkOriginalImage(target, srcOriginal => this.target.upgradeSource(srcOriginal))\n\n if (this.options.enableGrab) {\n toggleGrabListeners(document, this.eventHandler, true)\n }\n\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n\n return this\n }\n\n /**\n * Close (zoom out) the Element currently opened.\n * @param {Function} [cb=this.options.onClose] A callback function that will\n * be called when a target is closed and transition has ended. It will get\n * the target element as the argument.\n * @return {this}\n */\n close (cb = this.options.onClose) {\n if (!this.shown || this.lock) return\n\n const target = this.target.el\n\n // onBeforeClose event\n if (this.options.onBeforeClose) this.options.onBeforeClose(target)\n\n this.lock = true\n\n this.body.style.cursor = cursor.default\n this.overlay.hide()\n this.target.zoomOut()\n\n document.removeEventListener('scroll', this.eventHandler.scroll)\n document.removeEventListener('keydown', this.eventHandler.keydown)\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n\n this.shown = false\n this.lock = false\n\n checkOriginalImage(target, srcOriginal => this.target.downgradeSource(srcOriginal))\n\n if (this.options.enableGrab) {\n toggleGrabListeners(document, this.eventHandler, false)\n }\n\n this.target.restoreCloseStyle()\n this.overlay.remove()\n\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n\n return this\n }\n\n /**\n * Grab the Element currently opened given a position and apply extra zoom-in.\n * @param {number} x The X-axis of where the press happened.\n * @param {number} y The Y-axis of where the press happened.\n * @param {number} scaleExtra Extra zoom-in to apply.\n * @param {Function} [cb=this.options.scaleExtra] A callback function that\n * will be called when a target is grabbed and transition has ended. It\n * will get the target element as the argument.\n * @return {this}\n */\n grab (x, y, scaleExtra = this.options.scaleExtra, cb) {\n if (!this.shown || this.lock) return\n\n const target = this.target.el\n\n // onBeforeGrab event\n if (this.options.onBeforeGrab) this.options.onBeforeGrab(target)\n\n this.released = false\n this.target.grab(x, y, scaleExtra)\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n }\n\n /**\n * Move the Element currently grabbed given a position and apply extra zoom-in.\n * @param {number} x The X-axis of where the press happened.\n * @param {number} y The Y-axis of where the press happened.\n * @param {number} scaleExtra Extra zoom-in to apply.\n * @param {Function} [cb=this.options.scaleExtra] A callback function that\n * will be called when a target is moved and transition has ended. It will\n * get the target element as the argument.\n * @return {this}\n */\n move (x, y, scaleExtra = this.options.scaleExtra, cb) {\n if (!this.shown || this.lock) return\n\n const target = this.target.el\n\n // onBeforeMove event\n if (this.options.onBeforeMove) this.options.onBeforeMove(target)\n\n this.released = false\n\n this.target.move(x, y, scaleExtra)\n this.body.style.cursor = cursor.move\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n }\n\n /**\n * Release the Element currently grabbed.\n * @param {Function} [cb=this.options.onRelease] A callback function that\n * will be called when a target is released and transition has ended. It\n * will get the target element as the argument.\n * @return {this}\n */\n release (cb = this.options.onRelease) {\n if (!this.shown || this.lock) return\n\n const target = this.target.el\n\n // onBeforeRelease event\n if (this.options.onBeforeRelease) this.options.onBeforeRelease(target)\n\n this.lock = true\n\n this.target.restoreOpenStyle()\n this.body.style.cursor = cursor.default\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n\n this.lock = false\n this.released = true\n\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n\n return this\n }\n}\n","import Zooming from './Zooming'\n\ndocument.addEventListener('DOMContentLoaded', () => {\n new Zooming()\n})\n\nexport default Zooming\n"],"names":["body","document","docElm","documentElement","isString","checkType","isLink","checkTag","webkitPrefix","style","typeName","el","tagName","getParents","match","parents","parentNode","push","isNotImage","loadImage","src","cb","img","Image","onload","checkOriginalImage","srcOriginal","hasAttribute","getAttribute","trans","sniffTransition","createElement","transformCssProp","transEndEvent","checkTrans","styles","transitionProp","transformProp","value","transition","transform","ret","tform","end","some","prop","undefined","replace","divide","denominator","numerator","half","cursor","toggleListener","type","handler","add","addEventListener","passive","removeEventListener","getWindowCenter","windowWidth","Math","min","clientWidth","window","innerWidth","windowHeight","clientHeight","innerHeight","toggleGrabListeners","forEach","setStyle","remember","s","original","key","bindAll","_this","that","methods","Object","getOwnPropertyNames","getPrototypeOf","method","bind","overflowHiddenParents","Map","disableOverflowHiddenParents","enableOverflowHiddenParents","isOverflowHidden","getComputedStyle","overflow","getOverflowHiddenParents","map","has","get","set","parent","PRESS_DELAY","MULTITOUCH_SCALE_FACTOR","EventHandler","instance","e","preventDefault","shown","released","close","release","open","currentTarget","scrollTop","pageYOffset","lastScrollPosition","deltaY","abs","options","scrollThreshold","isEscape","isNotLeftButton","pressTimer","setTimeout","grab","clientX","clientY","move","touches","scaleExtra","x","y","isTouching","event","button","code","keyCode","targetTouches","length","processTouches","currScaleExtra","total","firstTouch","multitouch","i","xs","ys","max","t","distX","distY","Overlay","bgColor","transitionDuration","transitionTimingFunction","appendChild","removeChild","opacity","bgOpacity","Target","translate","scale","srcThumbnail","rect","getBoundingClientRect","disable","calculateTranslate","calculateScale","scaleBase","customSize","offsetWidth","enableGrab","zoomOut","width","height","enable","windowCenter","dx","dy","temp","cloneNode","setAttribute","position","visibility","targetCenter","left","top","targetHalfWidth","targetHalfHeight","targetEdgeToWindowEdge","scaleHorizontally","scaleVertically","OPTIONS","Zooming","target","overlay","eventHandler","lock","babelHelpers.extends","DEFAULT_OPTIONS","config","listen","defaultZoomable","init","els","querySelectorAll","zoomIn","click","preloadImage","updateStyle","onOpen","querySelector","onBeforeOpen","insert","show","scroll","keydown","onEnd","upgradeSource","onClose","onBeforeClose","default","hide","downgradeSource","restoreCloseStyle","remove","onBeforeGrab","onBeforeMove","onRelease","onBeforeRelease","restoreOpenStyle"],"mappings":"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"}
\ No newline at end of file
diff --git a/lib/zooming/build/zooming.min.js b/lib/zooming/build/zooming.min.js
new file mode 100644
index 0000000..5b15a07
--- /dev/null
+++ b/lib/zooming/build/zooming.min.js
@@ -0,0 +1,2 @@
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.Zooming=e()}(this,function(){"use strict";function t(t){return function(e){return("undefined"==typeof e?"undefined":T(e))===t}}function e(t){return function(e){return e.tagName===t}}function n(t,e){for(var n=[];t&&t!==document;t=t.parentNode)e?e(t)&&n.push(t):n.push(t);return n}function i(){return e("IMG")===!1}function o(t,e){if(t){var n=new Image;n.onload=function(){e&&e(n)},n.src=t}}function s(t,e){var n=null;t.hasAttribute("data-original")?n=t.getAttribute("data-original"):P(t.parentNode)&&(n=t.parentNode.getAttribute("href")),e(n)}function r(t){var e=I.transitionProp,n=I.transformProp,i=void 0;t.transition&&(i=t.transition,delete t.transition,t[e]=i),t.transform&&(i=t.transform,delete t.transform,t[n]=i)}function a(t){var e={},n=["webkitTransition","transition","mozTransition"],i=["webkitTransform","transform","mozTransform"],o={transition:"transitionend",mozTransition:"transitionend",webkitTransition:"webkitTransitionEnd"};return n.some(function(n){if(void 0!==t.style[n])return e.transitionProp=n,e.transEndEvent=o[n],!0}),i.some(function(n){if(void 0!==t.style[n])return e.transformProp=n,e.transformCssProp=n.replace(/(.*)Transform/,"-$1-transform"),!0}),e}function l(t){return function(e){return e/t}}function u(t,e,n,i){i?t.addEventListener(e,n[e],{passive:!1}):t.removeEventListener(e,n[e],{passive:!1})}function c(){var t=Math.min(z.clientWidth,window.innerWidth),e=Math.min(z.clientHeight,window.innerHeight);return{x:N(t),y:N(e)}}function h(t,e,n){["mousedown","mousemove","mouseup","touchstart","touchmove","touchend"].forEach(function(i){u(t,i,e,n)})}function f(t,e,n){r(e);var i=t.style,o={};for(var s in e)n&&(o[s]=i[s]||""),i[s]=e[s];return o}function d(t,e){var n=Object.getOwnPropertyNames(Object.getPrototypeOf(t));n.forEach(function(n){t[n]=t[n].bind(e)})}function v(t){return"hidden"===getComputedStyle(t).overflow}function p(t){if(G.map.has(t))return G.map.get(t);var e=n(t.parentNode,v);return G.map.set(t,e),e}function y(t){p(t).forEach(function(t){G.style.has(t)?f(t,{overflow:"visible"}):G.style.set(t,f(t,{overflow:"visible"},!0))})}function m(t){G.map.has(t)&&G.map.get(t).forEach(function(t){f(t,G.style.get(t))})}function g(t){return 0!==t.button}function b(t){var e=t.key||t.code;return"Escape"===e||27===t.keyCode}function k(t){return t.targetTouches.length>0}function w(t,e,n){for(var i=t.length,o=t[0],s=i>1,r=e,a=t.length,l=0,u=0,c={x:o.clientX,y:o.clientY},h={x:o.clientX,y:o.clientY};a--;){var f=t[a],d=[f.clientX,f.clientY],v=d[0],p=d[1];l+=v,u+=p,s&&(vh.x&&(h.x=v),ph.y&&(h.y=p))}if(s){var y=h.x-c.x,m=h.y-c.y;r=y>m?y/window.innerWidth*R:m/window.innerHeight*R}n(l/i,u/i,r)}function x(t){var e=c(),n={x:t.left+N(t.width),y:t.top+N(t.height)};return{x:e.x-n.x,y:e.y-n.y}}function E(t,e,n){if(n)return{x:n.width/t.width,y:n.height/t.height};var i=N(t.width),o=N(t.height),s=c(),r={x:s.x-i,y:s.y-o},a=r.x/i,l=r.y/o,u=e+Math.min(a,l);return{x:u,y:u}}var T="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},S=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},C=function(){function t(t,e){for(var n=0;n=this.options.scrollThreshold&&(this.lastScrollPosition=null,this.close())}},{key:"keydown",value:function(t){var e=this;b(t)&&(this.released?this.close():this.release(function(){return e.close()}))}},{key:"mousedown",value:function(t){var e=this;g(t)||(t.preventDefault(),this.pressTimer=setTimeout(function(){e.grab(t.clientX,t.clientY)},j))}},{key:"mousemove",value:function(t){this.released||this.move(t.clientX,t.clientY)}},{key:"mouseup",value:function(t){g(t)||(clearTimeout(this.pressTimer),this.released?this.close():this.release())}},{key:"touchstart",value:function(t){var e=this;t.preventDefault(),this.pressTimer=setTimeout(function(){w(t.touches,e.options.scaleExtra,function(t,n,i){e.grab(t,n,i)})},j)}},{key:"touchmove",value:function(t){var e=this;this.released||w(t.touches,this.options.scaleExtra,function(t,n,i){e.move(t,n,i)})}},{key:"touchend",value:function(t){k(t)||(clearTimeout(this.pressTimer),this.released?this.close():this.release())}}]),t}(),Y=function(){function t(e,n){S(this,t),this.el=e,this.instance=n,this.parent=document.body}return C(t,[{key:"init",value:function(t){var e=this;f(this.el,{zIndex:998,backgroundColor:t.bgColor,position:"fixed",top:0,left:0,right:0,bottom:0,opacity:0,transition:"opacity\n "+t.transitionDuration+"s\n "+t.transitionTimingFunction}),this.el.addEventListener("click",function(){return e.instance.close()})}},{key:"updateStyle",value:function(t){f(this.el,{backgroundColor:t.bgColor,transition:"opacity\n "+t.transitionDuration+"s\n "+t.transitionTimingFunction})}},{key:"insert",value:function(){this.parent.appendChild(this.el)}},{key:"remove",value:function(){this.parent.removeChild(this.el)}},{key:"show",value:function(){var t=this;setTimeout(function(){return t.el.style.opacity=t.instance.options.bgOpacity},30)}},{key:"hide",value:function(){this.el.style.opacity=0}}]),t}(),X=function(){function t(e,n){S(this,t),this.el=e,this.instance=n,this.translate=null,this.scale=null,this.srcThumbnail=this.el.getAttribute("src"),this.style={open:null,close:null}}return C(t,[{key:"zoomIn",value:function(){var t=this.instance.options,e=this.el.getBoundingClientRect();G.disable(this.el),this.translate=x(e),this.scale=E(e,t.scaleBase,t.customSize),this.el.offsetWidth,this.style.open={position:"relative",zIndex:999,cursor:t.enableGrab?D.grab:D.zoomOut,transition:M+"\n "+t.transitionDuration+"s\n "+t.transitionTimingFunction,transform:"translate("+this.translate.x+"px, "+this.translate.y+"px)\n scale("+this.scale.x+","+this.scale.y+")",width:e.width+"px",height:e.height+"px"},this.style.close=f(this.el,this.style.open,!0)}},{key:"zoomOut",value:function(){G.enable(this.el),this.el.offsetWidth,f(this.el,{transform:"none"})}},{key:"grab",value:function(t,e,n){var i=c(),o=i.x-t,s=i.y-e;f(this.el,{cursor:D.move,transform:"translate(\n "+(this.translate.x+o)+"px, "+(this.translate.y+s)+"px)\n scale("+(this.scale.x+n)+","+(this.scale.y+n)+")"})}},{key:"move",value:function(t,e,n){var i=c(),o=i.x-t,s=i.y-e;f(this.el,{transition:M,transform:"translate(\n "+(this.translate.x+o)+"px, "+(this.translate.y+s)+"px)\n scale("+(this.scale.x+n)+","+(this.scale.y+n)+")"})}},{key:"restoreCloseStyle",value:function(){f(this.el,this.style.close)}},{key:"restoreOpenStyle",value:function(){f(this.el,this.style.open)}},{key:"upgradeSource",value:function(t){var e=this;if(t){var n=this.el.parentNode,i=this.el.cloneNode(!1);i.setAttribute("src",t),i.style.position="fixed",i.style.visibility="hidden",n.appendChild(i),setTimeout(function(){e.el.setAttribute("src",t),n.removeChild(i)},100)}}},{key:"downgradeSource",value:function(t){t&&this.el.setAttribute("src",this.srcThumbnail)}}]),t}(),F={defaultZoomable:'img[data-action="zoom"]',enableGrab:!0,preloadImage:!0,transitionDuration:.4,transitionTimingFunction:"cubic-bezier(0.4, 0, 0, 1)",bgColor:"rgb(255, 255, 255)",bgOpacity:1,scaleBase:1,scaleExtra:.5,scrollThreshold:40,customSize:null,onOpen:null,onClose:null,onRelease:null,onBeforeOpen:null,onBeforeClose:null,onBeforeGrab:null,onBeforeMove:null,onBeforeRelease:null},Z=function(){function t(e){S(this,t),this.target=null,this.overlay=new Y(document.createElement("div"),this),this.eventHandler=new W(this),this.body=document.body,this.shown=!1,this.lock=!1,this.released=!0,this.lastScrollPosition=null,this.pressTimer=null,this.options=O({},F),this.config(e),this.listen(this.options.defaultZoomable),this.overlay.init(this.options)}return C(t,[{key:"listen",value:function(t){if(B(t)){for(var e=document.querySelectorAll(t),n=e.length;n--;)this.listen(e[n]);return this}if(!i(t))return t.style.cursor=D.zoomIn,t.addEventListener("click",this.eventHandler.click,{passive:!1}),this.options.preloadImage&&s(t,o),this}},{key:"config",value:function(t){return t?(O(this.options,t),this.overlay.updateStyle(this.options),this):this.options}},{key:"open",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.options.onOpen;if(!this.shown&&!this.lock){var r=B(t)?document.querySelector(t):t;if(!i(r)){this.options.onBeforeOpen&&this.options.onBeforeOpen(r),this.options.preloadImage||s(r,o),this.target=new X(r,this),this.shown=!0,this.lock=!0,this.target.zoomIn(),this.overlay.insert(),this.overlay.show(),document.addEventListener("scroll",this.eventHandler.scroll),document.addEventListener("keydown",this.eventHandler.keydown);var a=function t(){r.removeEventListener(A,t),e.lock=!1,s(r,function(t){return e.target.upgradeSource(t)}),e.options.enableGrab&&h(document,e.eventHandler,!0),n&&n(r)};return r.addEventListener(A,a),this}}}},{key:"close",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.options.onClose;if(this.shown&&!this.lock){var n=this.target.el;this.options.onBeforeClose&&this.options.onBeforeClose(n),this.lock=!0,this.body.style.cursor=D.default,this.overlay.hide(),this.target.zoomOut(),document.removeEventListener("scroll",this.eventHandler.scroll),document.removeEventListener("keydown",this.eventHandler.keydown);var i=function i(){n.removeEventListener(A,i),t.shown=!1,t.lock=!1,s(n,function(e){return t.target.downgradeSource(e)}),t.options.enableGrab&&h(document,t.eventHandler,!1),t.target.restoreCloseStyle(),t.overlay.remove(),e&&e(n)};return n.addEventListener(A,i),this}}},{key:"grab",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.options.scaleExtra,i=arguments[3];if(this.shown&&!this.lock){var o=this.target.el;this.options.onBeforeGrab&&this.options.onBeforeGrab(o),this.released=!1,this.target.grab(t,e,n);var s=function t(){o.removeEventListener(A,t),i&&i(o)};o.addEventListener(A,s)}}},{key:"move",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.options.scaleExtra,i=arguments[3];if(this.shown&&!this.lock){var o=this.target.el;this.options.onBeforeMove&&this.options.onBeforeMove(o),this.released=!1,this.target.move(t,e,n),this.body.style.cursor=D.move;var s=function t(){o.removeEventListener(A,t),i&&i(o)};o.addEventListener(A,s)}}},{key:"release",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.options.onRelease;if(this.shown&&!this.lock){var n=this.target.el;this.options.onBeforeRelease&&this.options.onBeforeRelease(n),this.lock=!0,this.target.restoreOpenStyle(),this.body.style.cursor=D.default;var i=function i(){n.removeEventListener(A,i),t.lock=!1,t.released=!0,e&&e(n)};return n.addEventListener(A,i),this}}}]),t}();return document.addEventListener("DOMContentLoaded",function(){new Z}),Z});
+//# sourceMappingURL=zooming.min.js.map
diff --git a/lib/zooming/build/zooming.min.js.map b/lib/zooming/build/zooming.min.js.map
new file mode 100644
index 0000000..f171866
--- /dev/null
+++ b/lib/zooming/build/zooming.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":null,"sources":["../src/util/_dom.js","../src/util/_image.js","../src/util/_trans.js","../src/util/_math.js","../src/util/_helpers.js","../src/EventHandler.js","../src/Target.js","../src/Overlay.js","../src/_options.js","../src/Zooming.js","../src/main.js"],"sourcesContent":["export const body = document.body\nexport const docElm = document.documentElement\nexport const isString = checkType('string')\nexport const isLink = checkTag('A')\nexport const webkitPrefix = 'WebkitAppearance' in docElm.style\n ? '-webkit-'\n : ''\n\nexport function checkType (typeName) {\n return function (el) {\n return typeof el === typeName\n }\n}\n\nexport function checkTag (tagName) {\n return function (el) {\n return el.tagName === tagName\n }\n}\n\nexport function getParents (el, match) {\n let parents = []\n\n for (; el && el !== document; el = el.parentNode) {\n if (match) {\n if (match(el)) {\n parents.push(el)\n }\n } else {\n parents.push(el)\n }\n }\n\n return parents\n}\n","import { checkTag, isLink } from './_dom'\n\nexport function isNotImage () {\n return checkTag('IMG') === false\n}\n\nexport function loadImage (src, cb) {\n if (!src) return\n\n const img = new Image()\n img.onload = function () {\n if (cb) cb(img)\n }\n \n img.src = src\n}\n\nexport function checkOriginalImage (el, cb) {\n let srcOriginal = null\n\n if (el.hasAttribute('data-original')) {\n srcOriginal = el.getAttribute('data-original')\n } else if (isLink(el.parentNode)) {\n srcOriginal = el.parentNode.getAttribute('href')\n }\n\n cb(srcOriginal)\n}\n","const trans = sniffTransition(document.createElement('div'))\nexport const transformCssProp = trans.transformCssProp\nexport const transEndEvent = trans.transEndEvent\n\nexport function checkTrans (styles) {\n const transitionProp = trans.transitionProp\n const transformProp = trans.transformProp\n\n let value\n if (styles.transition) {\n value = styles.transition\n delete styles.transition\n styles[transitionProp] = value\n }\n if (styles.transform) {\n value = styles.transform\n delete styles.transform\n styles[transformProp] = value\n }\n}\n\nexport function sniffTransition (el) {\n let ret = {}\n const trans = ['webkitTransition', 'transition', 'mozTransition']\n const tform = ['webkitTransform', 'transform', 'mozTransform']\n const end = {\n 'transition' : 'transitionend',\n 'mozTransition' : 'transitionend',\n 'webkitTransition' : 'webkitTransitionEnd'\n }\n\n trans.some(prop => {\n if (el.style[prop] !== undefined) {\n ret.transitionProp = prop\n ret.transEndEvent = end[prop]\n return true\n }\n })\n\n tform.some(prop => {\n if (el.style[prop] !== undefined) {\n ret.transformProp = prop\n ret.transformCssProp = prop.replace(/(.*)Transform/, '-$1-transform')\n return true\n }\n })\n\n return ret\n}\n","function divide (denominator) {\n return (numerator) => {\n return numerator / denominator\n }\n}\n\nexport const half = divide(2)\n","import { docElm, webkitPrefix, getParents } from './_dom'\nimport { checkTrans } from './_trans'\nimport { half } from './_math'\n\nexport const cursor = {\n default: 'auto',\n zoomIn: `${webkitPrefix}zoom-in`,\n zoomOut: `${webkitPrefix}zoom-out`,\n grab: `${webkitPrefix}grab`,\n move: 'move'\n}\n\nexport function toggleListener (el, type, handler, add) {\n if (add) {\n el.addEventListener(type, handler[type], { passive: false })\n } else {\n el.removeEventListener(type, handler[type], { passive: false })\n }\n}\n\nexport function getWindowCenter () {\n const windowWidth = Math.min(docElm.clientWidth, window.innerWidth)\n const windowHeight = Math.min(docElm.clientHeight, window.innerHeight)\n\n return {\n x: half(windowWidth),\n y: half(windowHeight)\n }\n}\n\nexport function toggleGrabListeners (el, handler, add) {\n ['mousedown', 'mousemove', 'mouseup','touchstart', 'touchmove', 'touchend']\n .forEach(type => {\n toggleListener(el, type, handler, add)\n })\n}\n\nexport function setStyle (el, styles, remember) {\n checkTrans(styles)\n\n let s = el.style\n let original = {}\n\n for (let key in styles) {\n if (remember) original[key] = s[key] || ''\n s[key] = styles[key]\n }\n\n return original\n}\n\nexport function bindAll (_this, that) {\n const methods = (\n Object.getOwnPropertyNames(\n Object.getPrototypeOf(_this)\n )\n )\n\n methods.forEach(method => {\n _this[method] = _this[method].bind(that)\n })\n}\n\nexport const overflowHiddenParents = {\n\n // Map from Element to its overflow:hidden parents\n map: new Map(),\n\n // Map from parent to its original style\n style: new Map(),\n\n disable: disableOverflowHiddenParents,\n enable: enableOverflowHiddenParents\n}\n\nfunction isOverflowHidden (el) {\n return getComputedStyle(el).overflow === 'hidden'\n}\n\nfunction getOverflowHiddenParents (el) {\n if (overflowHiddenParents.map.has(el)) {\n return overflowHiddenParents.map.get(el)\n } else {\n const parents = getParents(el.parentNode, isOverflowHidden)\n overflowHiddenParents.map.set(el, parents)\n return parents\n }\n}\n\nfunction disableOverflowHiddenParents (el) {\n getOverflowHiddenParents(el).forEach(parent => {\n if (overflowHiddenParents.style.has(parent)) {\n setStyle(parent, {\n overflow: 'visible'\n })\n } else {\n overflowHiddenParents.style.set(parent, setStyle(parent, {\n overflow: 'visible'\n }, true))\n }\n })\n}\n\nfunction enableOverflowHiddenParents (el) {\n if (overflowHiddenParents.map.has(el)) {\n overflowHiddenParents.map.get(el).forEach(parent => {\n setStyle(parent, overflowHiddenParents.style.get(parent))\n })\n }\n}\n","import { docElm, body } from './util/_dom'\nimport { bindAll } from './util/_helpers'\n\nconst PRESS_DELAY = 200\nconst MULTITOUCH_SCALE_FACTOR = 2\n\nexport default class EventHandler {\n\n constructor (instance) {\n bindAll(this, instance)\n }\n\n click (e) {\n e.preventDefault()\n\n if (this.shown) {\n if (this.released) this.close()\n else this.release()\n } else {\n this.open(e.currentTarget)\n }\n }\n\n scroll () {\n const scrollTop = window.pageYOffset ||\n (docElm || body.parentNode || body).scrollTop\n\n if (this.lastScrollPosition === null) {\n this.lastScrollPosition = scrollTop\n }\n\n const deltaY = this.lastScrollPosition - scrollTop\n\n if (Math.abs(deltaY) >= this.options.scrollThreshold) {\n this.lastScrollPosition = null\n this.close()\n }\n }\n\n keydown (e) {\n if (isEscape(e)) {\n if (this.released) this.close()\n else this.release(() => this.close())\n }\n }\n\n mousedown (e) {\n if (isNotLeftButton(e)) return\n e.preventDefault()\n\n this.pressTimer = setTimeout(() => {\n this.grab(e.clientX, e.clientY)\n }, PRESS_DELAY)\n }\n\n mousemove (e) {\n if (this.released) return\n this.move(e.clientX, e.clientY)\n }\n\n mouseup (e) {\n if (isNotLeftButton(e)) return\n clearTimeout(this.pressTimer)\n\n if (this.released) this.close()\n else this.release()\n }\n\n touchstart (e) {\n e.preventDefault()\n\n this.pressTimer = setTimeout(() => {\n processTouches(e.touches, this.options.scaleExtra,\n (x, y, scaleExtra) => {\n this.grab(x, y, scaleExtra)\n })\n }, PRESS_DELAY)\n }\n\n touchmove (e) {\n if (this.released) return\n\n processTouches(e.touches, this.options.scaleExtra,\n (x, y, scaleExtra) => {\n this.move(x, y, scaleExtra)\n })\n }\n\n touchend (e) {\n if (isTouching(e)) return\n clearTimeout(this.pressTimer)\n\n if (this.released) this.close()\n else this.release()\n }\n}\n\nfunction isNotLeftButton (event) {\n return event.button !== 0\n}\n\nfunction isEscape (event) {\n const code = event.key || event.code\n return code === 'Escape' || event.keyCode === 27\n}\n\nfunction isTouching (event) {\n return event.targetTouches.length > 0\n}\n\nfunction processTouches (touches, currScaleExtra, cb) {\n const total = touches.length\n const firstTouch = touches[0]\n const multitouch = total > 1\n\n let scaleExtra = currScaleExtra\n let i = touches.length\n let [xs, ys] = [0, 0]\n\n // keep track of the min and max of touch positions\n let min = { x: firstTouch.clientX, y: firstTouch.clientY }\n let max = { x: firstTouch.clientX, y: firstTouch.clientY }\n\n while (i--) {\n const t = touches[i]\n const [x, y] = [t.clientX, t.clientY]\n xs += x\n ys += y\n\n if (!multitouch) continue\n\n if (x < min.x) {\n min.x = x\n } else if (x > max.x) {\n max.x = x\n }\n\n if (y < min.y) {\n min.y = y\n } else if (y > max.y) {\n max.y = y\n }\n }\n\n if (multitouch) {\n // change scaleExtra dynamically\n const [distX, distY] = [max.x - min.x, max.y - min.y]\n\n if (distX > distY) {\n scaleExtra = (distX / window.innerWidth) * MULTITOUCH_SCALE_FACTOR\n } else {\n scaleExtra = (distY / window.innerHeight) * MULTITOUCH_SCALE_FACTOR\n }\n }\n\n cb(xs / total, ys / total, scaleExtra)\n}\n","import { cursor, setStyle, getWindowCenter, overflowHiddenParents } from './util/_helpers'\nimport { transformCssProp } from './util/_trans'\nimport { half } from './util/_math'\n\nexport default class Target {\n\n constructor (el, instance) {\n this.el = el\n this.instance = instance\n this.translate = null\n this.scale = null\n this.srcThumbnail = this.el.getAttribute('src')\n this.style = {\n open: null,\n close: null\n }\n }\n\n zoomIn () {\n const options = this.instance.options\n const rect = this.el.getBoundingClientRect()\n\n // Remove overflow:hidden from target's parent nodes if any. It prevents\n // parent nodes from hiding the target after zooming in\n overflowHiddenParents.disable(this.el)\n\n this.translate = calculateTranslate(rect)\n this.scale = calculateScale(rect, options.scaleBase, options.customSize)\n\n // force layout update\n this.el.offsetWidth\n\n this.style.open = {\n position: 'relative',\n zIndex: 999,\n cursor: options.enableGrab\n ? cursor.grab\n : cursor.zoomOut,\n transition: `${transformCssProp}\n ${options.transitionDuration}s\n ${options.transitionTimingFunction}`,\n transform: `translate(${this.translate.x}px, ${this.translate.y}px)\n scale(${this.scale.x},${this.scale.y})`,\n width: `${rect.width}px`,\n height: `${rect.height}px`\n }\n\n // trigger transition\n this.style.close = setStyle(this.el, this.style.open, true)\n }\n\n zoomOut () {\n // Restore overflow:hidden to target's parent nodes if any\n overflowHiddenParents.enable(this.el)\n\n // force layout update\n this.el.offsetWidth\n\n setStyle(this.el, { transform: 'none' })\n }\n\n grab (x, y, scaleExtra) {\n const windowCenter = getWindowCenter()\n const [dx, dy] = [windowCenter.x - x, windowCenter.y - y]\n\n setStyle(this.el, {\n cursor: cursor.move,\n transform: `translate(\n ${this.translate.x + dx}px, ${this.translate.y + dy}px)\n scale(${this.scale.x + scaleExtra},${this.scale.y + scaleExtra})`\n })\n }\n\n move (x, y, scaleExtra) {\n const windowCenter = getWindowCenter()\n const [dx, dy] = [windowCenter.x - x, windowCenter.y - y]\n\n setStyle(this.el, {\n transition: transformCssProp,\n transform: `translate(\n ${this.translate.x + dx}px, ${this.translate.y + dy}px)\n scale(${this.scale.x + scaleExtra},${this.scale.y + scaleExtra})`\n })\n }\n\n restoreCloseStyle () {\n setStyle(this.el, this.style.close)\n }\n\n restoreOpenStyle () {\n setStyle(this.el, this.style.open)\n }\n\n upgradeSource (srcOriginal) {\n if (!srcOriginal) return\n\n const parentNode = this.el.parentNode\n const temp = this.el.cloneNode(false)\n\n // force compute the hi-res image in DOM to prevent\n // image flickering while updating src\n temp.setAttribute('src', srcOriginal)\n temp.style.position = 'fixed'\n temp.style.visibility = 'hidden'\n parentNode.appendChild(temp)\n\n setTimeout(() => {\n this.el.setAttribute('src', srcOriginal)\n parentNode.removeChild(temp)\n }, 100)\n }\n\n downgradeSource (srcOriginal) {\n if (!srcOriginal) return\n\n this.el.setAttribute('src', this.srcThumbnail)\n }\n}\n\nfunction calculateTranslate (rect) {\n const windowCenter = getWindowCenter()\n const targetCenter = {\n x: rect.left + half(rect.width),\n y: rect.top + half(rect.height)\n }\n\n // The vector to translate image to the window center\n return {\n x: windowCenter.x - targetCenter.x,\n y: windowCenter.y - targetCenter.y\n }\n}\n\nfunction calculateScale (rect, scaleBase, customSize) {\n if (customSize) {\n return {\n x: customSize.width / rect.width,\n y: customSize.height / rect.height\n }\n } else {\n const targetHalfWidth = half(rect.width)\n const targetHalfHeight = half(rect.height)\n const windowCenter = getWindowCenter()\n\n // The distance between target edge and window edge\n const targetEdgeToWindowEdge = {\n x: windowCenter.x - targetHalfWidth,\n y: windowCenter.y - targetHalfHeight\n }\n\n const scaleHorizontally = targetEdgeToWindowEdge.x / targetHalfWidth\n const scaleVertically = targetEdgeToWindowEdge.y / targetHalfHeight\n\n // The additional scale is based on the smaller value of\n // scaling horizontally and scaling vertically\n const scale = scaleBase + Math.min(scaleHorizontally, scaleVertically)\n\n return {\n x: scale,\n y: scale\n }\n }\n}\n","import { setStyle } from './util/_helpers'\n\nexport default class Overlay {\n\n constructor (el, instance) {\n this.el = el\n this.instance = instance\n this.parent = document.body\n }\n\n init (options) {\n setStyle(this.el, {\n zIndex: 998,\n backgroundColor: options.bgColor,\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n opacity: 0,\n transition: `opacity\n ${options.transitionDuration}s\n ${options.transitionTimingFunction}`\n })\n\n this.el.addEventListener('click', () => this.instance.close())\n }\n\n updateStyle (options) {\n setStyle(this.el, {\n backgroundColor: options.bgColor,\n transition: `opacity\n ${options.transitionDuration}s\n ${options.transitionTimingFunction}`\n })\n }\n\n insert () {\n this.parent.appendChild(this.el)\n }\n\n remove () {\n this.parent.removeChild(this.el)\n }\n\n show () {\n setTimeout(() => this.el.style.opacity = this.instance.options.bgOpacity, 30)\n }\n\n hide () {\n this.el.style.opacity = 0\n }\n}\n","/**\n * A list of options.\n *\n * @type {Object}\n * @example\n * // Default options\n * var options = {\n * defaultZoomable: 'img[data-action=\"zoom\"]',\n * enableGrab: true,\n * preloadImage: true,\n * transitionDuration: 0.4,\n * transitionTimingFunction: 'cubic-bezier(0.4, 0, 0, 1)',\n * bgColor: 'rgb(255, 255, 255)',\n * bgOpacity: 1,\n * scaleBase: 1.0,\n * scaleExtra: 0.5,\n * scrollThreshold: 40,\n * customSize: null,\n * onOpen: null,\n * onClose: null,\n * onRelease: null,\n * onBeforeOpen: null,\n * onBeforeClose: null,\n * onBeforeGrab: null,\n * onBeforeMove: null,\n * onBeforeRelease: null\n * }\n */\nconst OPTIONS = {\n /**\n * Zoomable elements by default. It can be a css selector or an element.\n * @type {string|Element}\n */\n defaultZoomable: 'img[data-action=\"zoom\"]',\n\n /**\n * To be able to grab and drag the image for extra zoom-in.\n * @type {boolean}\n */\n enableGrab: true,\n\n /**\n * Preload images with attribute \"data-original\".\n * @type {boolean}\n */\n preloadImage: true,\n\n /**\n * Transition duration in seconds.\n * @type {number}\n */\n transitionDuration: 0.4,\n\n /**\n * Transition timing function.\n * @type {string}\n */\n transitionTimingFunction: 'cubic-bezier(0.4, 0, 0, 1)',\n\n /**\n * Overlay background color.\n * @type {string}\n */\n bgColor: 'rgb(255, 255, 255)',\n\n /**\n * Overlay background opacity.\n * @type {number}\n */\n bgOpacity: 1,\n\n /**\n * The base scale factor for zooming. By default scale to fit the window.\n * @type {number}\n */\n scaleBase: 1.0,\n\n /**\n * The extra scale factor when grabbing the image.\n * @type {number}\n */\n scaleExtra: 0.5,\n\n /**\n * How much scrolling it takes before closing out.\n * @type {number}\n */\n scrollThreshold: 40,\n\n /**\n * Scale (zoom in) to given width and height. Ignore scaleBase if set.\n * @type {Object}\n * @example\n * customSize: { width: 800, height: 400 }\n */\n customSize: null,\n\n /**\n * A callback function that will be called when a target is opened and\n * transition has ended. It will get the target element as the argument.\n * @type {Function}\n */\n onOpen: null,\n\n /**\n * Same as above, except fired when closed.\n * @type {Function}\n */\n onClose: null,\n\n /**\n * Same as above, except fired when released.\n * @type {Function}\n */\n onRelease: null,\n\n /**\n * A callback function that will be called before open.\n * @type {Function}\n */\n onBeforeOpen: null,\n\n /**\n * A callback function that will be called before close.\n * @type {Function}\n */\n onBeforeClose: null,\n\n /**\n * A callback function that will be called before grab.\n * @type {Function}\n */\n onBeforeGrab: null,\n\n /**\n * A callback function that will be called before move.\n * @type {Function}\n */\n onBeforeMove: null,\n\n /**\n * A callback function that will be called before release.\n * @type {Function}\n */\n onBeforeRelease: null\n}\n\nexport default OPTIONS\n","import { isNotImage, loadImage, checkOriginalImage } from './util/_image'\nimport { cursor, toggleGrabListeners } from './util/_helpers'\nimport { transEndEvent } from './util/_trans'\nimport { isString } from './util/_dom'\n\nimport EventHandler from './EventHandler'\nimport Overlay from './Overlay'\nimport Target from './Target'\n\nimport DEFAULT_OPTIONS from './_options'\n\n/**\n * Zooming instance.\n */\nexport default class Zooming {\n\n /**\n * @param {Object} [options] Update default options if provided.\n */\n constructor (options) {\n\n // elements\n this.target = null\n this.overlay = new Overlay(document.createElement('div'), this)\n this.eventHandler = new EventHandler(this)\n this.body = document.body\n\n // state\n this.shown = false // target is open\n this.lock = false // target is in transform\n this.released = true // mouse/finger is not pressing down\n this.lastScrollPosition = null\n this.pressTimer = null\n\n // init\n this.options = Object.assign({}, DEFAULT_OPTIONS)\n this.config(options)\n this.listen(this.options.defaultZoomable)\n this.overlay.init(this.options)\n }\n\n /**\n * Make element(s) zoomable.\n * @param {string|Element} el A css selector or an Element.\n * @return {this}\n */\n listen (el) {\n if (isString(el)) {\n let els = document.querySelectorAll(el), i = els.length\n\n while (i--) {\n this.listen(els[i])\n }\n\n return this\n }\n\n if (isNotImage(el)) return\n\n el.style.cursor = cursor.zoomIn\n el.addEventListener('click', this.eventHandler.click, { passive: false })\n\n if (this.options.preloadImage) {\n checkOriginalImage(el, loadImage)\n }\n\n return this\n }\n\n /**\n * Update options.\n * @param {Object} options An Object that contains this.options.\n * @return {this}\n */\n config (options) {\n if (!options) return this.options\n\n Object.assign(this.options, options)\n this.overlay.updateStyle(this.options)\n\n return this\n }\n\n /**\n * Open (zoom in) the Element.\n * @param {Element} el The Element to open.\n * @param {Function} [cb=this.options.onOpen] A callback function that will\n * be called when a target is opened and transition has ended. It will get\n * the target element as the argument.\n * @return {this}\n */\n open (el, cb = this.options.onOpen) {\n if (this.shown || this.lock) return\n\n const target = isString(el)\n ? document.querySelector(el)\n : el\n\n if (isNotImage(target)) return\n\n // onBeforeOpen event\n if (this.options.onBeforeOpen) this.options.onBeforeOpen(target)\n\n if (!this.options.preloadImage) {\n checkOriginalImage(target, loadImage)\n }\n\n this.target = new Target(target, this)\n\n this.shown = true\n this.lock = true\n\n this.target.zoomIn()\n this.overlay.insert()\n this.overlay.show()\n\n document.addEventListener('scroll', this.eventHandler.scroll)\n document.addEventListener('keydown', this.eventHandler.keydown)\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n\n this.lock = false\n\n checkOriginalImage(target, srcOriginal => this.target.upgradeSource(srcOriginal))\n\n if (this.options.enableGrab) {\n toggleGrabListeners(document, this.eventHandler, true)\n }\n\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n\n return this\n }\n\n /**\n * Close (zoom out) the Element currently opened.\n * @param {Function} [cb=this.options.onClose] A callback function that will\n * be called when a target is closed and transition has ended. It will get\n * the target element as the argument.\n * @return {this}\n */\n close (cb = this.options.onClose) {\n if (!this.shown || this.lock) return\n\n const target = this.target.el\n\n // onBeforeClose event\n if (this.options.onBeforeClose) this.options.onBeforeClose(target)\n\n this.lock = true\n\n this.body.style.cursor = cursor.default\n this.overlay.hide()\n this.target.zoomOut()\n\n document.removeEventListener('scroll', this.eventHandler.scroll)\n document.removeEventListener('keydown', this.eventHandler.keydown)\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n\n this.shown = false\n this.lock = false\n\n checkOriginalImage(target, srcOriginal => this.target.downgradeSource(srcOriginal))\n\n if (this.options.enableGrab) {\n toggleGrabListeners(document, this.eventHandler, false)\n }\n\n this.target.restoreCloseStyle()\n this.overlay.remove()\n\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n\n return this\n }\n\n /**\n * Grab the Element currently opened given a position and apply extra zoom-in.\n * @param {number} x The X-axis of where the press happened.\n * @param {number} y The Y-axis of where the press happened.\n * @param {number} scaleExtra Extra zoom-in to apply.\n * @param {Function} [cb=this.options.scaleExtra] A callback function that\n * will be called when a target is grabbed and transition has ended. It\n * will get the target element as the argument.\n * @return {this}\n */\n grab (x, y, scaleExtra = this.options.scaleExtra, cb) {\n if (!this.shown || this.lock) return\n\n const target = this.target.el\n\n // onBeforeGrab event\n if (this.options.onBeforeGrab) this.options.onBeforeGrab(target)\n\n this.released = false\n this.target.grab(x, y, scaleExtra)\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n }\n\n /**\n * Move the Element currently grabbed given a position and apply extra zoom-in.\n * @param {number} x The X-axis of where the press happened.\n * @param {number} y The Y-axis of where the press happened.\n * @param {number} scaleExtra Extra zoom-in to apply.\n * @param {Function} [cb=this.options.scaleExtra] A callback function that\n * will be called when a target is moved and transition has ended. It will\n * get the target element as the argument.\n * @return {this}\n */\n move (x, y, scaleExtra = this.options.scaleExtra, cb) {\n if (!this.shown || this.lock) return\n\n const target = this.target.el\n\n // onBeforeMove event\n if (this.options.onBeforeMove) this.options.onBeforeMove(target)\n\n this.released = false\n\n this.target.move(x, y, scaleExtra)\n this.body.style.cursor = cursor.move\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n }\n\n /**\n * Release the Element currently grabbed.\n * @param {Function} [cb=this.options.onRelease] A callback function that\n * will be called when a target is released and transition has ended. It\n * will get the target element as the argument.\n * @return {this}\n */\n release (cb = this.options.onRelease) {\n if (!this.shown || this.lock) return\n\n const target = this.target.el\n\n // onBeforeRelease event\n if (this.options.onBeforeRelease) this.options.onBeforeRelease(target)\n\n this.lock = true\n\n this.target.restoreOpenStyle()\n this.body.style.cursor = cursor.default\n\n const onEnd = () => {\n target.removeEventListener(transEndEvent, onEnd)\n\n this.lock = false\n this.released = true\n\n if (cb) cb(target)\n }\n\n target.addEventListener(transEndEvent, onEnd)\n\n return this\n }\n}\n","import Zooming from './Zooming'\n\ndocument.addEventListener('DOMContentLoaded', () => {\n new Zooming()\n})\n\nexport default Zooming\n"],"names":["checkType","typeName","el","checkTag","tagName","getParents","match","parents","document","parentNode","push","isNotImage","loadImage","src","cb","img","Image","onload","checkOriginalImage","srcOriginal","hasAttribute","getAttribute","isLink","checkTrans","styles","transitionProp","trans","transformProp","value","transition","transform","sniffTransition","ret","tform","end","some","undefined","style","prop","transEndEvent","transformCssProp","replace","divide","denominator","numerator","toggleListener","type","handler","add","addEventListener","passive","removeEventListener","getWindowCenter","windowWidth","Math","min","docElm","clientWidth","window","innerWidth","windowHeight","clientHeight","innerHeight","half","toggleGrabListeners","forEach","setStyle","remember","s","original","key","bindAll","_this","that","methods","Object","getOwnPropertyNames","getPrototypeOf","method","bind","isOverflowHidden","getComputedStyle","overflow","getOverflowHiddenParents","overflowHiddenParents","map","has","get","set","disableOverflowHiddenParents","parent","enableOverflowHiddenParents","isNotLeftButton","event","button","isEscape","code","keyCode","isTouching","targetTouches","length","processTouches","touches","currScaleExtra","total","firstTouch","multitouch","scaleExtra","i","xs","ys","x","clientX","y","clientY","max","t","distX","distY","MULTITOUCH_SCALE_FACTOR","calculateTranslate","rect","windowCenter","targetCenter","left","width","top","height","calculateScale","scaleBase","customSize","targetHalfWidth","targetHalfHeight","targetEdgeToWindowEdge","scaleHorizontally","scaleVertically","scale","body","documentElement","isString","webkitPrefix","createElement","cursor","Map","PRESS_DELAY","EventHandler","instance","this","e","preventDefault","shown","released","close","release","open","currentTarget","scrollTop","pageYOffset","lastScrollPosition","deltaY","abs","options","scrollThreshold","pressTimer","setTimeout","grab","move","_this3","Overlay","bgColor","transitionDuration","transitionTimingFunction","appendChild","removeChild","_this2","opacity","bgOpacity","Target","translate","srcThumbnail","getBoundingClientRect","disable","offsetWidth","enableGrab","zoomOut","enable","dx","dy","temp","cloneNode","setAttribute","position","visibility","OPTIONS","Zooming","target","overlay","eventHandler","lock","babelHelpers.extends","DEFAULT_OPTIONS","config","listen","defaultZoomable","init","els","querySelectorAll","zoomIn","click","preloadImage","updateStyle","onOpen","querySelector","onBeforeOpen","insert","show","scroll","keydown","onEnd","upgradeSource","onClose","onBeforeClose","default","hide","downgradeSource","restoreCloseStyle","remove","onBeforeGrab","onBeforeMove","onRelease","onBeforeRelease","restoreOpenStyle"],"mappings":"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"}
\ No newline at end of file
diff --git a/lib/zooming/scripts/build.js b/lib/zooming/scripts/build.js
new file mode 100644
index 0000000..4bf120b
--- /dev/null
+++ b/lib/zooming/scripts/build.js
@@ -0,0 +1,82 @@
+const rollup = require('rollup')
+const watch = require('rollup-watch')
+const babel = require('rollup-plugin-babel')
+const eslint = require('rollup-plugin-eslint')
+const uglify = require('rollup-plugin-uglify')
+const open = require('open')
+
+const defaultPlugins = [
+ babel({
+ exclude: 'node_modules/**'
+ }),
+ eslint({
+ env: {
+ browser: true
+ },
+ extends: 'eslint:recommended',
+ parserOptions: {
+ ecmaVersion: 6,
+ sourceType: 'module'
+ },
+ rules: {
+ indent: ['off', 2],
+ quotes: ['error', 'single'],
+ semi: ['off', 'never']
+ }
+ })
+]
+
+const config = (dest, plugins) => {
+ return {
+ entry: 'src/main.js',
+ dest: dest,
+ format: 'umd',
+ moduleName: 'Zooming',
+ sourceMap: true,
+ plugins: plugins,
+ onwarn: (message) => {
+ // https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined
+ if (/The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten./.test(message)) {
+ return
+ }
+ }
+ }
+}
+
+const stderr = console.error.bind(console) // eslint-disable-line no-console
+let opened = false
+
+const eventHandler = (event) => {
+ switch (event.code) {
+ case 'STARTING':
+ stderr('checking rollup-watch version...')
+ break
+ case 'BUILD_START':
+ stderr('bundling...')
+ break
+ case 'BUILD_END':
+ stderr('bundled in ' + event.duration + 'ms. Watching for changes...')
+ if (!opened) {
+ open(__dirname + '/../index.html')
+ opened = true
+ }
+ break
+ case 'ERROR':
+ stderr('error: ' + event.error)
+ break
+ default:
+ stderr('unknown event', event)
+ }
+}
+
+const watcherDefault = watch(rollup, config(
+ 'build/zooming.js',
+ defaultPlugins
+))
+
+watch(rollup, config(
+ 'build/zooming.min.js',
+ defaultPlugins.concat([uglify({})])
+))
+
+watcherDefault.on('event', eventHandler)
diff --git a/lib/zooming/scripts/test.js b/lib/zooming/scripts/test.js
new file mode 100644
index 0000000..03685d0
--- /dev/null
+++ b/lib/zooming/scripts/test.js
@@ -0,0 +1,3 @@
+const open = require('open')
+
+open(__dirname + '/../test.html')
diff --git a/pages/auroraGameHub.html b/pages/auroraGameHub.html
index 9cd7835..eb5d6d8 100644
--- a/pages/auroraGameHub.html
+++ b/pages/auroraGameHub.html
@@ -4,7 +4,8 @@
-
+
+
Sammy Guergachi - Aurora Game Hub
@@ -12,16 +13,41 @@
-
Aurora Game Hub is a project I spent 5 years of my free time devoted to
-
+
Aurora Game Hub was my software design playground
+
+
The Idea
+
Aurora Game Hub was a project I started in August 2010, the summer before grade 12 high school. I remember knowing just enough Java to be able to make something cool.
+
The problem I wanted to tackle was one I felt very passionately about at the time. Being a gamer and deeply interested in user interface/user experience development, I was keenly aware of a problem PC gamers faced that Console gamers didn’t. At the time I had an Xbox 360, and loved the experience around the console game, from the point of putting a disc in to the console and instantly being able to play a game, to the sleek integration of the Xbox dashboard. I wanted something like that for PC gamers.
+
[Image of Xbox 360 Dashboard]
+
I was angry, because the PC was more powerful than any console at the time and subjectively had great games on the platform, my view was the only thing missing was creating some sort of immersive experience where gamers could launch one application and have access to all things gaming without worrying about all the other stuff the PC did. The PC can do many things and because of that you get a very fractured experience where the way you play or access your games is the same as the way you access or open a PDF document. The other issue was that you had to use so many different applications from different digital distributors to play your various games.
+
[Image of PC Gamer Desktop]
+
My philosophy to side projects has primarily been to do something that hasn’t already been done already or done well enough. I’m not interested in putting a lot of work to make something marginally better than a competing product. So I make sure to put a lot of time into searching and analyzing competing services. Because to be honest, I much rather use someone else’s product which solves a problem I’m facing good enough than build something that is no more of a remix of a well made competing product. In this case the competition was miserable. There was nothing that could figure out this problem.
+
Most services that did something like this focused more around a social component. Whether it be a chat service, a gaming social network, they all had the ability to launch and manage your games library but none had it nailed down. Examples of these services/tools include Xfire, Raptr, and Curse. You also had digital distribution platforms like Steam and Origin, but the had a poor to no ability to add game’s that weren’t sold in their stores. My aim was to focus on the common denominator and do a good job at managing all your games, regardless of where you got it from. I also wanted no social features to start with, from my personal experience these social features have already been done well enough by many companies, not something I had any interest in.
+
Tackling this problem wasn’t my only goal. I really wanted to experiment with UI and UX design. I had so many ideas of un-conventional UIs that I wanted to see come alive. The accumulation of all these little UI details is really why I love this project so much.
+
Rev 09
+
The first build of Aurora released did nothing but show a loading screen. The same summer I started working on Aurora, I had decided to open source my project and use Source Forge as the host of my svn repository to manage my code. I released revision 9 as the first version of Aurora available to run. All that happened when you ran it was see a full screen window with a loop of green hexagons slowly moving horizontally. You saw the logo at the top and a spinning progress wheel at the bottom with messages that would slide and fade in and out of view.
+
+
On the surface, it seemed like nothing, but a lot of decisions I had made in this first, unusable, version was carried on until the very end.
+
First of all, the layout of this screen was inspired by a game I used to play called EVE Online. I loved the login screens in that game, it felt very sci-fi without feeling unnecessarily gaudy. The subtle sci-fi music in the background and the idle animation in the center really wowed me. With each expansion of EVE a new animation and theme song would greet you on login. A lot of other people were fans of this login screen by the looks of a search on youtube.
+
[Image of Eve Login Screen]
+
I didn’t have the ability to make an idle animation with as much fidelity as EVE did, so I settled for a simple horizontally moving animation of green hexagons. Why green hexagons? Because hexagons are the coolest shape. These green hexagons later lead me to build the piece of UI I am most proud of in this project.
+
The animated login text was inspired by the Office 2010 splash screen. I had never seen text being presented in that way anywhere else. It was a really digestible and nice way of delivering segmented pieces of information to the user. This component was reused many times throughout the project.
The last important thing I had done was a bit more philosophical. I was convinced that what made applications boring and unattractive was how static they were. I realize now with maturity that this philosophy was shortsighted, but it resulted in some interesting experiments. My goal was to make Aurora dynamic in subtle ways. The way I did this was to randomly pick different strings of text to use for the same message. So for example if I wanted to say “welcome” to the user, I would randomly pick from “hello” and “greetings” as possible synonyms to send the same message. This system was later extended to support me dynamically changing the dictionary of synonyms to support seasonal or holiday messages which would be dynamically updated in the background. I tried to apply this dynamic messaging everywhere I could, but I soon realized later in development it wasn’t very scalable for me to write variations of each message we needed to display to the user.
+
A Partner
+
In 2011 I had gone to University. By then I had done a lot more work on Aurora. Most notably I had implemented the dashboard UI, the info bar which would later turn into a live news ticker for PC gaming news, and the foundation of what would become the game library. But the most important thing to happen that year for the project was when I met Carlos online.
+
I had decided that I needed a partner if I was going to really turn this UI playground into something really functional in a realistic timeline. It turns out SourceForge had a form dedicated to “job” postings for open source projects on the site. It was a really great way to get exposure on this project. I got a bunch of replies, most of which went nowhere. I made sure to convey to the few promising candidates how passionate I was about the project and made it clear I was looking for a “creative programmer”, which was my way of saying someone who has good programing skills but also had the taste to appreciate my focus on the immersive UI design I was envisioning for the project.
+
One person who ended up being a partner on the project for years since was Carlos. Someone who I found out happened to live in a city called Aurora near me which was quite the coincidence. In this case Carlos was a huge help to me, and I will forever be grateful to him. I have never met Carlos in person, but he’s been like a friend to me.