(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.photoviewer = factory()); })(this, (function () { 'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } // Class D var D = function (selector, context) { return new D.fn.init(selector, context); }; var document$1 = window.document, emptyArray = [], concat = emptyArray.concat, filter = emptyArray.filter, slice = emptyArray.slice, elementDisplay = {}, classCache = {}, cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1, 'opacity': 1, 'z-index': 1, 'zoom': 1 }, fragmentRE = /^\s*<(\w+|!)[^>]*>/, singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, rootNodeRE = /^(?:body|html)$/i, // special attributes that should be get/set via method calls methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], table = document$1.createElement('table'), tableRow = document$1.createElement('tr'), containers = { 'tr': document$1.createElement('tbody'), 'tbody': table, 'thead': table, 'tfoot': table, 'td': tableRow, 'th': tableRow, '*': document$1.createElement('div') }, simpleSelectorRE = /^[\w-]*$/, class2type = {}, toString = class2type.toString, tempParent = document$1.createElement('div'), isArray = Array.isArray || function (arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }, contains = document$1.documentElement.contains ? function (parent, node) { return parent !== node && parent.contains(node); } : function (parent, node) { while (node && (node = node.parentNode)) if (node === parent) return true; return false; }; function type(obj) { return obj == null ? String(obj) : class2type[toString.call(obj)] || 'object'; } function isFunction(value) { return type(value) == 'function'; } function isWindow(obj) { return obj != null && obj == obj.window; } function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE; } function isObject(obj) { return type(obj) == 'object'; } function isPlainObject(obj) { return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype; } function likeArray(obj) { var length = !!obj && 'length' in obj && obj.length, typeRes = type(obj); return 'function' != typeRes && !isWindow(obj) && ( 'array' == typeRes || length === 0 || (typeof length == 'number' && length > 0 && (length - 1) in obj) ); } function compact(array) { return filter.call(array, function (item) { return item != null; }); } function dasherize$1(str) { return str.replace(/::/g, '/') .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') .replace(/([a-z\d])([A-Z])/g, '$1_$2') .replace(/_/g, '-') .toLowerCase(); } function maybeAddPx(name, value) { return (typeof value == 'number' && !cssNumber[dasherize$1(name)]) ? value + 'px' : value; } function camelize(str) { return str.replace(/-+(.)?/g, function (match, chr) { return chr ? chr.toUpperCase() : ''; }); } function classRE(name) { return name in classCache ? classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')); } function defaultDisplay(nodeName) { var element, display; if (!elementDisplay[nodeName]) { element = document$1.createElement(nodeName); document$1.body.appendChild(element); display = getComputedStyle(element, '').getPropertyValue('display'); element.parentNode.removeChild(element); display == 'none' && (display = 'block'); elementDisplay[nodeName] = display; } return elementDisplay[nodeName]; } function flatten(array) { return array.length > 0 ? D.fn.concat.apply([], array) : array; } function isD(object) { return object instanceof D; } function funcArg(context, arg, idx, payload) { return isFunction(arg) ? arg.call(context, idx, payload) : arg; } function setAttribute(node, name, value) { value == null ? node.removeAttribute(name) : node.setAttribute(name, value); } // access className property while respecting SVGAnimatedString function className(node, value) { var klass = node.className || '', svg = klass && klass.baseVal !== undefined; if (value === undefined) return svg ? klass.baseVal : klass; svg ? (klass.baseVal = value) : (node.className = value); } D.fn = D.prototype = { constuctor: D, length: 0, // Because a collection acts like an array // copy over these useful array functions. forEach: emptyArray.forEach, reduce: emptyArray.reduce, push: emptyArray.push, sort: emptyArray.sort, splice: emptyArray.splice, indexOf: emptyArray.indexOf, // D's counterpart to jQuery's `$.fn.init` and // takes a CSS selector and an optional context (and handles various // special cases). init: function (selector, context) { var dom; // If nothing given, return an empty D collection if (!selector) { return this; } // Optimize for string selectors else if (typeof selector == 'string') { selector = selector.trim(); // If it's a html fragment, create nodes from it // Note: In both Chrome 21 and Firefox 15, DOM error 12 // is thrown if the fragment doesn't begin with < if (selector[0] == '<' && fragmentRE.test(selector)) { dom = D.fragment(selector, RegExp.$1, context); selector = null; } // If there's a context, create a collection on that context first, and select // nodes from there else if (context !== undefined) { return D(context).find(selector); } // If it's a CSS selector, use it to select nodes. else { dom = D.qsa(document$1, selector); } } // If a function is given, call it when the DOM is ready else if (isFunction(selector)) { return D(document$1).ready(selector); } // If a D collection is given, just return it else if (isD(selector)) { return selector; } // normalize array if an array of nodes is given else if (isArray(selector)) { dom = compact(selector); } // Wrap DOM nodes. else if (isObject(selector)) { dom = [selector], selector = null; } // If there's a context, create a collection on that context first, and select // nodes from there else if (context !== undefined) { return D(context).find(selector); } // And last but no least, if it's a CSS selector, use it to select nodes. else { dom = D.qsa(document$1, selector); } // create a new D collection from the nodes found return D.makeArray(dom, selector, this); }, // Modify the collection by adding elements to it concat: function () { var i, value, args = []; for (i = 0; i < arguments.length; i++) { value = arguments[i]; args[i] = isD(value) ? value.toArray() : value; } return concat.apply(isD(this) ? this.toArray() : this, args); }, // `pluck` is borrowed from Prototype.js pluck: function (property) { return D.map(this, function (el) { return el[property]; }); }, toArray: function () { return this.get(); }, get: function (idx) { return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]; }, size: function () { return this.length; }, each: function (callback) { emptyArray.every.call(this, function (el, idx) { return callback.call(el, idx, el) !== false; }); return this; }, map: function (fn) { return D(D.map(this, function (el, i) { return fn.call(el, i, el); })); }, slice: function () { return D(slice.apply(this, arguments)); }, first: function () { var el = this[0]; return el && !isObject(el) ? el : D(el); }, last: function () { var el = this[this.length - 1]; return el && !isObject(el) ? el : D(el); }, eq: function (idx) { return idx === -1 ? this.slice(idx) : this.slice(idx, +idx + 1); } }; D.extend = D.fn.extend = function () { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if (typeof target === 'boolean') { deep = target; // Skip the boolean and the target target = arguments[i] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) if (typeof target !== 'object' && !isFunction(target)) { target = {}; } // Extend D itself if only one argument is passed if (i === length) { target = this; i--; } for (; i < length; i++) { // Only deal with non-null/undefined values if ((options = arguments[i]) != null) { // Extend the base object for (name in options) { src = target[name]; copy = options[name]; // Prevent never-ending loop if (target === copy) { continue; } // Recurse if we're merging plain objects or arrays if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && isArray(src) ? src : []; } else { clone = src && isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[name] = D.extend(deep, clone, copy); // Don't bring in undefined values } else if (copy !== undefined) { target[name] = copy; } } } } // Return the modified object return target; }; D.extend({ // Make DOM Array makeArray: function (dom, selector, me) { var i, len = dom ? dom.length : 0; for (i = 0; i < len; i++) me[i] = dom[i]; me.length = len; me.selector = selector || ''; return me; }, // D's CSS selector qsa: function (element, selector) { var found, maybeID = selector[0] == '#', maybeClass = !maybeID && selector[0] == '.', // Ensure that a 1 char tag name still gets checked nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, isSimple = simpleSelectorRE.test(nameOnly); return ( // Safari DocumentFragment doesn't have getElementById element.getElementById && isSimple && maybeID) // eslint-disable-next-line no-cond-assign ? (found = element.getElementById(nameOnly)) ? [found] : [] : element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11 ? [] : slice.call( // DocumentFragment doesn't have getElementsByClassName/TagName isSimple && !maybeID && element.getElementsByClassName ? maybeClass // If it's simple, it could be a class ? element.getElementsByClassName(nameOnly) // Or a tag : element.getElementsByTagName(selector) // Or it's not simple, and we need to query all : element.querySelectorAll(selector) ); }, // Html -> Node fragment: function (html, name, properties) { var dom, nodes, container; // A special case optimization for a single tag if (singleTagRE.test(html)) dom = D(document$1.createElement(RegExp.$1)); if (!dom) { if (html.replace) html = html.replace(tagExpanderRE, '<$1>'); if (name === undefined) name = fragmentRE.test(html) && RegExp.$1; if (!(name in containers)) name = '*'; container = containers[name]; container.innerHTML = '' + html; dom = D.each(slice.call(container.childNodes), function () { container.removeChild(this); }); } if (isPlainObject(properties)) { nodes = D(dom); D.each(properties, function (key, value) { if (methodAttributes.indexOf(key) > -1) nodes[key](value); else nodes.attr(key, value); }); } return dom; }, matches: function (element, selector) { if (!selector || !element || element.nodeType !== 1) return false; var matchesSelector = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.matchesSelector; if (matchesSelector) return matchesSelector.call(element, selector); // fall back to performing a selector: var match, parent = element.parentNode, temp = !parent; if (temp) (parent = tempParent).appendChild(element); match = ~D.qsa(parent, selector).indexOf(element); temp && tempParent.removeChild(element); return match; }, each: function (elements, callback) { var i, key; if (likeArray(elements)) { for (i = 0; i < elements.length; i++) if (callback.call(elements[i], i, elements[i]) === false) return elements; } else { for (key in elements) if (callback.call(elements[key], key, elements[key]) === false) return elements; } return elements; }, map: function (elements, callback) { var value, values = [], i, key; if (likeArray(elements)) for (i = 0; i < elements.length; i++) { value = callback(elements[i], i); if (value != null) values.push(value); } else for (key in elements) { value = callback(elements[key], key); if (value != null) values.push(value); } return flatten(values); } }); // Populate the class2type map D.each('Boolean Number String Function Array Date RegExp Object Error'.split(' '), function (i, name) { class2type['[object ' + name + ']'] = name.toLowerCase(); }); D.fn.init.prototype = D.fn; function noop() { } function css(property, value) { if (arguments.length < 2) { var element = this[0]; if (typeof property == 'string') { if (!element) return; return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property); } else if (isArray(property)) { if (!element) return; var props = {}; var computedStyle = getComputedStyle(element, ''); D.each(property, function (_, prop) { props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop)); }); return props; } } var css = ''; if (type(property) == 'string') { if (!value && value !== 0) { this.each(function () { this.style.removeProperty(dasherize$1(property)); }); } else { css = dasherize$1(property) + ':' + maybeAddPx(property, value); } } else { for (var key in property) { if (!property[key] && property[key] !== 0) { this.each(function () { this.style.removeProperty(dasherize$1(key)); }); } else { css += dasherize$1(key) + ':' + maybeAddPx(key, property[key]) + ';'; } } } return this.each(function () { this.style.cssText += ';' + css; }); } function hasClass(name) { if (!name) return false; return emptyArray.some.call(this, function (el) { return this.test(className(el)); }, classRE(name)); } function addClass(name) { var classList = []; if (!name) return this; return this.each(function (idx) { if (!('className' in this)) return; classList = []; var cls = className(this), newName = funcArg(this, name, idx, cls); newName.split(/\s+/g).forEach(function (klass) { if (!D(this).hasClass(klass)) classList.push(klass); }, this); classList.length && className(this, cls + (cls ? ' ' : '') + classList.join(' ')); }); } function removeClass(name) { var classList = []; return this.each(function (idx) { if (!('className' in this)) return; if (name === undefined) return className(this, ''); classList = className(this); funcArg(this, name, idx, classList).split(/\s+/g).forEach(function (klass) { classList = classList.replace(classRE(klass), ' '); }); className(this, classList.trim()); }); } function offset(coordinates) { if (coordinates) return this.each(function (index) { var $this = D(this), coords = funcArg(this, coordinates, index, $this.offset()), parentOffset = $this.offsetParent().offset(), props = { top: coords.top - parentOffset.top, left: coords.left - parentOffset.left }; if ($this.css('position') == 'static') props['position'] = 'relative'; $this.css(props); }); if (!this.length) return null; if (document$1.documentElement !== this[0] && !contains(document$1.documentElement, this[0])) return { top: 0, left: 0 }; var obj = this[0].getBoundingClientRect(); return { left: obj.left + window.pageXOffset, top: obj.top + window.pageYOffset, width: Math.round(obj.width), height: Math.round(obj.height) }; } function position() { if (!this.length) return; var elem = this[0], offset, // Get *real* offset parent offsetParent = this.offsetParent(), parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); // `position: fixed` elements are offset from the viewport, which itself always has zero offset if (D(elem).css('position') === 'fixed') { // Assume `position: fixed` implies availability of getBoundingClientRect offset = elem.getBoundingClientRect(); } else { offset = this.offset(); // Incorporate borders into its offset, since they are outside its content origin parentOffset.top += parseFloat(D(offsetParent[0]).css('border-top-width')) || 0; parentOffset.left += parseFloat(D(offsetParent[0]).css('border-left-width')) || 0; } // Subtract parent offsets and element margins // note: when an element has `margin: auto` the offsetLeft and marginLeft // are the same in Safari causing `offset.left` to incorrectly be 0 return { top: offset.top - parentOffset.top - parseFloat(D(elem).css('margin-top')) || 0, left: offset.left - parentOffset.left - parseFloat(D(elem).css('margin-left')) || 0 }; } function scrollTop(value) { if (!this.length) return; var hasScrollTop = 'scrollTop' in this[0]; if (value === undefined) return hasScrollTop ? this[0].scrollTop : isWindow(this[0]) ? this[0].pageYOffset : this[0].defaultView.pageYOffset; return this.each(hasScrollTop ? function () { this.scrollTop = value; } : function () { this.scrollTo(this.scrollX, value); }); } function scrollLeft(value) { if (!this.length) return; var hasScrollLeft = 'scrollLeft' in this[0]; if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : isWindow(this[0]) ? this[0].pageXOffset : this[0].defaultView.pageXOffset; return this.each(hasScrollLeft ? function () { this.scrollLeft = value; } : function () { this.scrollTo(value, this.scrollY); }); } function offsetParent() { return this.map(function () { var parent = this.offsetParent || document$1.body; while (parent && !rootNodeRE.test(parent.nodeName) && D(parent).css('position') == 'static') parent = parent.offsetParent; return parent; }); } function attr(name, value) { var result; return (typeof name == 'string' && !(1 in arguments)) ? (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) : this.each(function (idx) { if (this.nodeType !== 1) return; if (isObject(name)) for (var key in name) setAttribute(this, key, name[key]); else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))); }); } function removeAttr(name) { return this.each(function () { this.nodeType === 1 && name.split(' ').forEach(function (attribute) { setAttribute(this, attribute); }, this); }); } function find(selector) { var result, $this = this; if (!selector) result = D(); else if (typeof selector == 'object') result = D(selector).filter(function () { var node = this; return emptyArray.some.call($this, function (parent) { return contains(parent, node); }); }); else if (this.length == 1) result = D(D.qsa(this[0], selector)); else result = this.map(function () { return D.qsa(this, selector); }); return result; } function closest(selector, context) { var nodes = [], collection = typeof selector == 'object' && D(selector); this.each(function (_, node) { while (node && !(collection ? collection.indexOf(node) >= 0 : D.matches(node, selector))) node = node !== context && !isDocument(node) && node.parentNode; if (node && nodes.indexOf(node) < 0) nodes.push(node); }); return D(nodes); } function isIE() { var ua = window.navigator.userAgent; var msie = ua.indexOf('MSIE '); return msie > 0 || !!navigator.userAgent.match(/Trident.*rv:11\./); } function subtract(el, dimen) { var offsetMap = { width: ['padding-left', 'padding-right', 'border-left-width', 'border-right-width'], height: ['padding-top', 'padding-bottom', 'border-top-width', 'border-bottom-width'] }; return el.css('box-sizing') === 'border-box' && !isIE() ? parseFloat(el.css(dimen)) - parseFloat(el.css(offsetMap[dimen][0])) - parseFloat(el.css(offsetMap[dimen][1])) - parseFloat(el.css(offsetMap[dimen][2])) - parseFloat(el.css(offsetMap[dimen][3])) : parseFloat(el.css(dimen)); } function calc(dimension, value) { var dimensionProperty = dimension.replace(/./, function (m) { return m[0].toUpperCase(); }); var el = this[0]; if (value === undefined) return isWindow(el) ? el.document.documentElement['client' + dimensionProperty] : isDocument(el) ? el.documentElement['scroll' + dimensionProperty] : subtract(this, dimension); else return this.each(function (idx) { el = D(this); el.css(dimension, funcArg(this, value, idx, el[dimension]())); }); } // Export function width(value) { return calc.call(this, 'width', value); } function height(value) { return calc.call(this, 'height', value); } var traverseNode = function (node, fn) { fn(node); for (var i = 0, len = node.childNodes.length; i < len; i++) traverseNode(node.childNodes[i], fn); }; // inside => append, prepend var domMani = function (elem, args, fn, inside) { // arguments can be nodes, arrays of nodes, D objects and HTML strings var argType, nodes = D.map(args, function (arg) { var arr = []; argType = type(arg); if (argType == 'array') { arg.forEach(function (el) { if (el.nodeType !== undefined) return arr.push(el); else if (isD(el)) return arr = arr.concat(el.get()); arr = arr.concat(D.fragment(el)); }); return arr; } return argType == 'object' || arg == null ? arg : D.fragment(arg); }), parent, copyByClone = elem.length > 1; if (nodes.length < 1) return elem; return elem.each(function (_, target) { parent = inside ? target : target.parentNode; var parentInDocument = contains(document$1.documentElement, parent); nodes.forEach(function (node) { if (copyByClone) node = node.cloneNode(true); else if (!parent) return D(node).remove(); fn.call(target, node); if (parentInDocument) { traverseNode(node, function (el) { if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && (!el.type || el.type === 'text/javascript') && !el.src) { var target = el.ownerDocument ? el.ownerDocument.defaultView : window; target['eval'].call(target, el.innerHTML); } }); } }); }); }; // Export function remove$1() { return this.each(function () { if (this.parentNode != null) this.parentNode.removeChild(this); }); } function empty() { return this.each(function () { this.innerHTML = ''; }); } function html(html) { return 0 in arguments ? this.each(function (idx) { var originHtml = this.innerHTML; D(this).empty().append(funcArg(this, html, idx, originHtml)); }) : (0 in this ? this[0].innerHTML : null); } function append() { return domMani(this, arguments, function (elem) { this.insertBefore(elem, null); }, true); } var _zid = 1; function zid(element) { return element._zid || (element._zid = _zid++); } function isString(obj) { return typeof obj == 'string'; } var returnTrue = function () { return true; }, returnFalse = function () { return false; }, eventMethods = { preventDefault: 'isDefaultPrevented', stopImmediatePropagation: 'isImmediatePropagationStopped', stopPropagation: 'isPropagationStopped' }; function compatible(event, source) { if (source || !event.isDefaultPrevented) { source || (source = event); D.each(eventMethods, function (name, predicate) { var sourceMethod = source[name]; event[name] = function () { this[predicate] = returnTrue; return sourceMethod && sourceMethod.apply(source, arguments); }; event[predicate] = returnFalse; }); try { event.timeStamp || (event.timeStamp = Date.now()); } catch (ignored) { console.warn(ignored); } if (source.defaultPrevented !== undefined ? source.defaultPrevented : 'returnValue' in source ? source.returnValue === false : source.getPreventDefault && source.getPreventDefault()) event.isDefaultPrevented = returnTrue; } return event; } var handlers = {}, focusinSupported = 'onfocusin' in window, focus = { focus: 'focusin', blur: 'focusout' }, hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }, ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/; function parse(event) { var parts = ('' + event).split('.'); return { e: parts[0], ns: parts.slice(1).sort().join(' ') }; } function matcherFor(ns) { return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)'); } function findHandlers(element, event, fn, selector) { event = parse(event); if (event.ns) var matcher = matcherFor(event.ns); return (handlers[zid(element)] || []).filter(function (handler) { return handler && (!event.e || handler.e == event.e) && (!event.ns || matcher.test(handler.ns)) && (!fn || zid(handler.fn) === zid(fn)) && (!selector || handler.sel == selector); }); } function eventCapture(handler, captureSetting) { return handler.del && (!focusinSupported && (handler.e in focus)) || !!captureSetting; } function realEvent(type) { return hover[type] || (focusinSupported && focus[type]) || type; } function add(element, events, fn, data, selector, delegator, capture) { var id = zid(element), set = (handlers[id] || (handlers[id] = [])); events.split(/\s/).forEach(function (event) { if (event == 'ready') return D(document$1).ready(fn); var handler = parse(event); handler.fn = fn; handler.sel = selector; // emulate mouseenter, mouseleave if (handler.e in hover) fn = function (e) { var related = e.relatedTarget; if (!related || (related !== this && !contains(this, related))) return handler.fn.apply(this, arguments); }; handler.del = delegator; var callback = delegator || fn; handler.proxy = function (e) { e = compatible(e); if (e.isImmediatePropagationStopped()) return; e.data = data; var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)); if (result === false) e.preventDefault(), e.stopPropagation(); return result; }; handler.i = set.length; set.push(handler); if ('addEventListener' in element) element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)); }); } function remove(element, events, fn, selector, capture) { var id = zid(element); (events || '').split(/\s/).forEach(function (event) { findHandlers(element, event, fn, selector).forEach(function (handler) { delete handlers[id][handler.i]; if ('removeEventListener' in element) element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)); }); }); } function createProxy(event) { var key, proxy = { originalEvent: event }; for (key in event) if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]; return compatible(proxy, event); } var on = function (event, selector, data, callback, one) { var autoRemove, delegator, $this = this; if (event && !isString(event)) { D.each(event, function (type, fn) { $this.on(type, selector, data, fn, one); }); return $this; } if (!isString(selector) && !isFunction(callback) && callback !== false) callback = data, data = selector, selector = undefined; if (callback === undefined || data === false) callback = data, data = undefined; if (callback === false) callback = returnFalse; return $this.each(function (_, element) { if (one) autoRemove = function (e) { remove(element, e.type, callback); return callback.apply(this, arguments); }; if (selector) delegator = function (e) { var evt, match = D(e.target).closest(selector, element).get(0); if (match && match !== element) { evt = D.extend(createProxy(e), { currentTarget: match, liveFired: element }); return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))); } }; add(element, event, callback, data, selector, delegator || autoRemove); }); }; var off = function (event, selector, callback) { var $this = this; if (event && !isString(event)) { D.each(event, function (type, fn) { $this.off(type, selector, fn); }); return $this; } if (!isString(selector) && !isFunction(callback) && callback !== false) callback = selector, selector = undefined; if (callback === false) callback = returnFalse; return $this.each(function () { remove(this, event, callback, selector); }); }; var prefix = '', eventPrefix, vendors = { Webkit: 'webkit', Moz: '', O: 'o' }, testEl = document$1.createElement('div'), testTransitionProperty = testEl.style.transitionProperty; if (testEl.style.transform === undefined) D.each(vendors, function (vendor, event) { if (testEl.style[vendor + 'TransitionProperty'] !== undefined) { prefix = '-' + vendor.toLowerCase() + '-'; eventPrefix = event; return false; } }); testEl = null; // fx cannot seperate function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : name.toLowerCase(); } D.fx = { off: (eventPrefix === undefined && testTransitionProperty === undefined), speeds: { _default: 400, fast: 200, slow: 600 }, cssPrefix: prefix, transitionEnd: normalizeEvent('TransitionEnd'), animationEnd: normalizeEvent('AnimationEnd') }; var supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i, transform, transitionProperty, transitionDuration, transitionTiming, transitionDelay, animationName, animationDuration, animationTiming, animationDelay, cssReset = {}; function dasherize(str) { return str.replace(/([A-Z])/g, '-$1').toLowerCase(); } transform = prefix + 'transform'; cssReset[transitionProperty = prefix + 'transition-property'] = cssReset[transitionDuration = prefix + 'transition-duration'] = cssReset[transitionDelay = prefix + 'transition-delay'] = cssReset[transitionTiming = prefix + 'transition-timing-function'] = cssReset[animationName = prefix + 'animation-name'] = cssReset[animationDuration = prefix + 'animation-duration'] = cssReset[animationDelay = prefix + 'animation-delay'] = cssReset[animationTiming = prefix + 'animation-timing-function'] = ''; var anim$1 = function (properties, duration, ease, callback, delay) { var key, cssValues = {}, cssProperties, transforms = '', that = this, wrappedCallback, endEvent = D.fx.transitionEnd, fired = false; if (duration === undefined) duration = D.fx.speeds._default / 1000; if (delay === undefined) delay = 0; if (D.fx.off) duration = 0; if (typeof properties == 'string') { // keyframe animation cssValues[animationName] = properties; cssValues[animationDuration] = duration + 's'; cssValues[animationDelay] = delay + 's'; cssValues[animationTiming] = (ease || 'linear'); endEvent = D.fx.animationEnd; } else { cssProperties = []; // CSS transitions for (key in properties) if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') '; else cssValues[key] = properties[key], cssProperties.push(dasherize(key)); if (transforms) cssValues[transform] = transforms, cssProperties.push(transform); if (duration > 0 && typeof properties === 'object') { cssValues[transitionProperty] = cssProperties.join(', '); cssValues[transitionDuration] = duration + 's'; cssValues[transitionDelay] = delay + 's'; cssValues[transitionTiming] = (ease || 'linear'); } } wrappedCallback = function (event) { if (typeof event !== 'undefined') { if (event.target !== event.currentTarget) return; // makes sure the event didn't bubble from "below" D(event.target).off(endEvent, wrappedCallback); } else D(this).off(endEvent, wrappedCallback); // triggered by setTimeout fired = true; D(this).css(cssReset); callback && callback.call(this); }; if (duration > 0) { this.on(endEvent, wrappedCallback); // transitionEnd is not always firing on older Android phones // so make sure it gets fired setTimeout(function () { if (fired) return; wrappedCallback.call(that); }, ((duration + delay) * 1000) + 25); } // trigger page reflow so new elements can animate this.size() && this.get(0).clientLeft; this.css(cssValues); if (duration <= 0) setTimeout(function () { that.each(function () { wrappedCallback.call(this); }); }, 0); return this; }; var animate = function (properties, duration, ease, callback, delay) { if (isFunction(duration)) callback = duration, ease = undefined, duration = undefined; if (isFunction(ease)) callback = ease, ease = undefined; if (isPlainObject(duration)) ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration; if (duration) duration = (typeof duration == 'number' ? duration : (D.fx.speeds[duration] || D.fx.speeds._default)) / 1000; if (delay) delay = parseFloat(delay) / 1000; return this.anim(properties, duration, ease, callback, delay); }; var origShow = function () { return this.each(function () { this.style.display == 'none' && (this.style.display = ''); if (getComputedStyle(this, '').getPropertyValue('display') == 'none') this.style.display = defaultDisplay(this.nodeName); }); }; var origHide = function () { return this.css('display', 'none'); }; function anim(el, speed, opacity, scale, callback) { if (typeof speed == 'function' && !callback) callback = speed, speed = undefined; var props = { opacity: opacity }; if (scale) { props.scale = scale; el.css(D.fx.cssPrefix + 'transform-origin', '0 0'); } return el.animate(props, speed, null, callback); } function hideHelper(el, speed, scale, callback) { return anim(el, speed, 0, scale, function () { origHide.call(D(this)); callback && callback.call(this); }); } // Export var show = function (speed, callback) { origShow.call(this); if (speed === undefined) speed = 0; else this.css('opacity', 0); return anim(this, speed, 1, '1,1', callback); }; var hide = function (speed, callback) { if (speed === undefined) return origHide.call(this); else return hideHelper(this, speed, '0,0', callback); }; var fadeTo = function (speed, opacity, callback) { return anim(this, speed, opacity, null, callback); }; var fadeIn = function (speed, callback) { var target = this.css('opacity'); if (target > 0) this.css('opacity', 0); else target = 1; return origShow.call(this).fadeTo(speed, target, callback); }; var $ = D; var methods = { isPlainObject: isPlainObject, isArray: isArray, noop: noop }; var fnMethods = { find: find, closest: closest, css: css, addClass: addClass, hasClass: hasClass, removeClass: removeClass, attr: attr, removeAttr: removeAttr, append: append, remove: remove$1, empty: empty, html: html, width: width, height: height, scrollTop: scrollTop, scrollLeft: scrollLeft, offset: offset, offsetParent: offsetParent, position: position, on: on, off: off, show: show, hide: hide, anim: anim$1, animate: animate, fadeTo: fadeTo, fadeIn: fadeIn }; $.extend(methods); $.fn.extend(fnMethods); var DEFAULTS = { // Whether to enable modal dragging draggable: true, // Whether to enable modal resizing resizable: true, // Whether to enable image moving movable: true, // Whether to enable keyboard navigation keyboard: true, // Whether to show the title title: true, // Min width of modal modalWidth: 320, // Min height of modal modalHeight: 320, // Whether to change the modal size after image loaded fixedModalSize: false, // Whether to set maximized on init initMaximized: false, // Threshold of modal relative to browser window gapThreshold: 0.02, // Threshold of image ratio ratioThreshold: 0.1, // Min ratio of image when zoom out minRatio: 0.05, // Max ratio of image when zoom in maxRatio: 16, // Toolbar options in header headerToolbar: ['maximize', 'close'], // Toolbar options in footer footerToolbar: ['zoomIn', 'zoomOut', 'prev', 'fullscreen', 'next', 'actualSize', 'rotateRight'], // Custom icon for button icons: { minimize: "\n \n ", maximize: "\n \n ", close: "\n \n ", zoomIn: "\n \n ", zoomOut: "\n \n ", prev: "\n \n ", next: "\n \n ", fullscreen: "\n \n ", actualSize: "\n \n ", rotateLeft: "\n \n ", rotateRight: "\n \n " }, // Custom title for button i18n: { minimize: 'Minimize', maximize: 'Maximize', close: 'Close (q)', zoomIn: 'Zoom-in (+)', zoomOut: 'Zoom-out (-)', prev: 'Prev (←)', next: 'Next (→)', fullscreen: 'Fullscreen', actualSize: 'Actual-size (Ctrl+Alt+0)', rotateLeft: 'Rotate-left (Ctrl+,)', rotateRight: 'Rotate-right (Ctrl+.)' }, // Whether to enable multiple instances multiInstances: true, // Whether to enable modal transform animation initAnimation: true, // Modal transform animation duration animationDuration: 400, // Whether to disable modal translate to center after image changed fixedModalPos: false, // Modal css `z-index` zIndex: 1090, // Selector of drag handler dragHandle: null, // Callback events callbacks: { beforeOpen: $.noop, opened: $.noop, beforeClose: $.noop, closed: $.noop, beforeChange: $.noop, changed: $.noop }, // Start index of images index: 0, // Whether to load the image progressively progressiveLoading: true, // The DOM element which photoviewer will be attached to appendTo: 'body', // Custom Buttons customButtons: {}, // Whether to set modal css `position: fixed` positionFixed: true, // Init modal position `{top: 0, right: 0, bottom: 0, left: 0}` initModalPos: null }; var document = window.document; /** * Debounce function * * @param {function} fn - The function will be triggered * @param {number} delay - The debounce delay time * @return {function} */ function debounce(fn, delay) { var timer = null; return function () { var context = this; var args = arguments; clearTimeout(timer); timer = setTimeout(function () { fn.apply(context, args); }, delay); }; } /** * Preload a image * * @param {string} src - The image src * @param {function} success - The callback of success * @param {function} error - The callback of error */ function preloadImage(src, success, error) { var img = new Image(); img.onload = function () { success(img); }; img.onerror = function () { error(img); }; img.src = src; } /** * Request fullscreen * * @param {type} element */ function requestFullscreen(element) { if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); } } /** * Get the image name from its url * * @param {string} url - The image src * @return {string} */ function getImageNameFromUrl(url) { var reg = /^.*?\/*([^/?]*)\.[a-z]+(\?.+|$)/gi; var txt = url.replace(reg, '$1'); return txt; } /** * Set grab cursor when move image * * @param {object} imageData - The image data * @param {object} stageData - The stage data * @param {object} $stage - The stage element of domq * @param {boolean} isRotate - The image rotated flag */ function setGrabCursor(imageData, stageData, $stage, isRotated) { var imageWidth = !isRotated ? imageData.w : imageData.h; var imageHeight = !isRotated ? imageData.h : imageData.w; if (imageHeight > stageData.h || imageWidth > stageData.w) { $stage.addClass('is-grab'); } if (imageHeight <= stageData.h && imageWidth <= stageData.w) { $stage.removeClass('is-grab'); } } /** * Check whether browser support touch event * * @return {boolean} */ function supportTouch() { return !!('ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch); } /** * Check whether element is root node (`body` or `html`) * * @param {object} elem - The DOM element * @return {boolean} */ function isRootNode(elem) { return /^(?:body|html)$/i.test(elem.nodeName); } /** * Get sum value of CSS props * * @param {object} $elem - The domq element * @param {array} props - The array of CSS props * @return {number} */ function getCSSValueSum($elem, props) { return props.reduce(function (acc, cur) { return acc + parseFloat($elem.css(cur)); }, 0); } /** * Check whether element's CSS `box-sizing` is `border-box` * * @param {object} $elem - The domq element * @return {boolean} */ function isBorderBox($elem) { return $elem.css('box-sizing') === 'border-box'; } var $W = $(window); var $D = $(document); var CLICK_EVENT = 'click'; var RESIZE_EVENT = 'resize'; var KEYDOWN_EVENT = 'keydown'; var WHEEL_EVENT = 'wheel mousewheel DOMMouseScroll'; var TOUCH_START_EVENT = supportTouch() ? 'touchstart' : 'mousedown'; var TOUCH_MOVE_EVENT = supportTouch() ? 'touchmove' : 'mousemove'; var TOUCH_END_EVENT = supportTouch() ? 'touchend' : 'mouseup'; var NS = 'photoviewer'; var CLASS_NS = '.' + NS; var EVENT_NS = '.' + NS; var PUBLIC_VARS = { // Whether image is moving isMoving: false, // Whether modal is resizing isResizing: false, // Modal's `z-index` zIndex: 0 }; var draggable = { /** * Modal draggable * * @param {object} $modal - The modal element of domq * @param {object} dragHandle - The handle element when dragging * @param {object} dragCancel - The cancel element when dragging */ draggable: function draggable($modal, dragHandle, dragCancel) { var _this = this; var isDragging = false; var startX = 0; var startY = 0; var left = 0; var top = 0; var dragStart = function dragStart(e) { e = e || window.event; // Must be removed // e.preventDefault(); // Fix focus scroll issue on Chrome $modal[0].blur(); // Get clicked button var elemCancel = $(e.target).closest(dragCancel); // Stop modal moving when click buttons if (elemCancel.length) { return true; } if (_this.options.multiInstances) { $modal.css('z-index', ++PUBLIC_VARS['zIndex']); } isDragging = true; startX = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.clientX; startY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.clientY; // Get current position of the modal left = parseFloat($modal.css('left')); top = parseFloat($modal.css('top')); // Reset modal position with left and top $modal.css({ left: left, top: top, right: '', bottom: '' }); $D.on(TOUCH_MOVE_EVENT + EVENT_NS, dragMove).on(TOUCH_END_EVENT + EVENT_NS, dragEnd); }; var dragMove = function dragMove(e) { e = e || window.event; e.preventDefault(); if (isDragging && !PUBLIC_VARS['isMoving'] && !PUBLIC_VARS['isResizing'] && !_this.isMaximized) { var endX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.clientX; var endY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.clientY; var relativeX = endX - startX; var relativeY = endY - startY; $modal.css({ left: relativeX + left, top: relativeY + top }); } }; var dragEnd = function dragEnd() { $D.off(TOUCH_MOVE_EVENT + EVENT_NS, dragMove).off(TOUCH_END_EVENT + EVENT_NS, dragEnd); isDragging = false; // Focus must be executed after drag end $modal[0].focus(); }; $(dragHandle).on(TOUCH_START_EVENT + EVENT_NS, dragStart); } }; var ELEMS_WITH_GRABBING_CURSOR = "html, body, .".concat(NS, "-modal, .").concat(NS, "-stage, .").concat(NS, "-button, .").concat(NS, "-resizable-handle"); var movable = { /** * Image movable * * 1. No movable * 2. Vertical movable * 3. Horizontal movable * 4. Vertical & Horizontal movable * * @param {object} $stage - The stage element of domq * @param {object} $image - The image element of domq */ movable: function movable($stage, $image) { var _this = this; var isDragging = false; var startX = 0; var startY = 0; var left = 0; var top = 0; var widthDiff = 0; var heightDiff = 0; var δ = 0; var dragStart = function dragStart(e) { e = e || window.event; e.preventDefault(); var imageWidth = $image.width(); var imageHeight = $image.height(); var stageWidth = $stage.width(); var stageHeight = $stage.height(); startX = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.clientX; startY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.clientY; // δ is the difference between image width and height δ = !_this.isRotated ? 0 : (imageWidth - imageHeight) / 2; // Width or height difference can be use to limit image right or top position widthDiff = !_this.isRotated ? imageWidth - stageWidth : imageHeight - stageWidth; heightDiff = !_this.isRotated ? imageHeight - stageHeight : imageWidth - stageHeight; // Modal can be dragging if image is smaller to stage isDragging = widthDiff > 0 || heightDiff > 0 ? true : false; PUBLIC_VARS['isMoving'] = widthDiff > 0 || heightDiff > 0 ? true : false; // Reclac the element position when mousedown // Fix the issue of stage with a border left = $image.position().left - δ; top = $image.position().top + δ; // Add grabbing cursor if ($stage.hasClass('is-grab')) { $(ELEMS_WITH_GRABBING_CURSOR).addClass('is-grabbing'); } $D.on(TOUCH_MOVE_EVENT + EVENT_NS, dragMove).on(TOUCH_END_EVENT + EVENT_NS, dragEnd); }; var dragMove = function dragMove(e) { e = e || window.event; e.preventDefault(); if (isDragging) { var endX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.clientX; var endY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.clientY; var relativeX = endX - startX; var relativeY = endY - startY; var newLeft = relativeX + left; var newTop = relativeY + top; // Vertical limit if (heightDiff > 0) { if (relativeY + top > δ) { newTop = δ; } else if (relativeY + top < -heightDiff + δ) { newTop = -heightDiff + δ; } } else { newTop = top; } // Horizontal limit if (widthDiff > 0) { if (relativeX + left > -δ) { newLeft = -δ; } else if (relativeX + left < -widthDiff - δ) { newLeft = -widthDiff - δ; } } else { newLeft = left; } $image.css({ left: newLeft, top: newTop }); // Update image initial data $.extend(_this.imageData, { left: newLeft, top: newTop }); } }; var dragEnd = function dragEnd() { $D.off(TOUCH_MOVE_EVENT + EVENT_NS, dragMove).off(TOUCH_END_EVENT + EVENT_NS, dragEnd); isDragging = false; PUBLIC_VARS['isMoving'] = false; // Remove grabbing cursor $(ELEMS_WITH_GRABBING_CURSOR).removeClass('is-grabbing'); }; $stage.on(TOUCH_START_EVENT + EVENT_NS, dragStart); } }; var ELEMS_WITH_RESIZE_CURSOR = "html, body, .".concat(NS, "-modal, .").concat(NS, "-stage, .").concat(NS, "-button"); var resizable = { /** * Modal resizable * * 1. Can be resized in 8 directions * 2. Keep image in stage center * 3. Other restrictions for image * * @param {object} $modal - The modal element of domq * @param {object} $stage - The stage element of domq * @param {object} $image - The image element of domq * @param {number} minWidth - The modalWidth option * @param {number} minHeight - The modalHeight option */ resizable: function resizable($modal, $stage, $image, minWidth, minHeight) { var _this = this; var resizableHandleE = $("
")); var resizableHandleW = $("
")); var resizableHandleS = $("
")); var resizableHandleN = $("
")); var resizableHandleSE = $("
")); var resizableHandleSW = $("
")); var resizableHandleNE = $("
")); var resizableHandleNW = $("
")); var resizableHandles = { e: resizableHandleE, s: resizableHandleS, se: resizableHandleSE, n: resizableHandleN, w: resizableHandleW, nw: resizableHandleNW, ne: resizableHandleNE, sw: resizableHandleSW }; $modal.append(resizableHandleE, resizableHandleW, resizableHandleS, resizableHandleN, resizableHandleSE, resizableHandleSW, resizableHandleNE, resizableHandleNW); var isDragging = false; var startX = 0; var startY = 0; var modalData = { w: 0, h: 0, x: 0, y: 0 }; var stageData = { w: 0, h: 0, x: 0, y: 0 }; var imageData = { w: 0, h: 0, x: 0, y: 0 }; // δ is the difference between image width and height var δ = 0; var imgWidth = 0; var imgHeight = 0; var direction = ''; // Modal CSS options var getModalOpts = function getModalOpts(dir, offsetX, offsetY) { // Modal should not move when its width to the minwidth var modalLeft = -offsetX + modalData.w > minWidth ? offsetX + modalData.x : modalData.x + modalData.w - minWidth; var modalTop = -offsetY + modalData.h > minHeight ? offsetY + modalData.y : modalData.y + modalData.h - minHeight; var opts = { e: { width: Math.max(offsetX + modalData.w, minWidth) }, s: { height: Math.max(offsetY + modalData.h, minHeight) }, se: { width: Math.max(offsetX + modalData.w, minWidth), height: Math.max(offsetY + modalData.h, minHeight) }, w: { width: Math.max(-offsetX + modalData.w, minWidth), left: modalLeft }, n: { height: Math.max(-offsetY + modalData.h, minHeight), top: modalTop }, nw: { width: Math.max(-offsetX + modalData.w, minWidth), height: Math.max(-offsetY + modalData.h, minHeight), top: modalTop, left: modalLeft }, ne: { width: Math.max(offsetX + modalData.w, minWidth), height: Math.max(-offsetY + modalData.h, minHeight), top: modalTop }, sw: { width: Math.max(-offsetX + modalData.w, minWidth), height: Math.max(offsetY + modalData.h, minHeight), left: modalLeft } }; return opts[dir]; }; // Image CSS options var getImageOpts = function getImageOpts(dir, offsetX, offsetY) { // Image should not move when modal width to the min width // The minwidth is modal width, so we should clac the stage minwidth var widthDiff = offsetX + modalData.w > minWidth ? stageData.w - imgWidth + offsetX - δ : minWidth - (modalData.w - stageData.w) - imgWidth - δ; var heightDiff = offsetY + modalData.h > minHeight ? stageData.h - imgHeight + offsetY + δ : minHeight - (modalData.h - stageData.h) - imgHeight + δ; var widthDiff2 = -offsetX + modalData.w > minWidth ? stageData.w - imgWidth - offsetX - δ : minWidth - (modalData.w - stageData.w) - imgWidth - δ; var heightDiff2 = -offsetY + modalData.h > minHeight ? stageData.h - imgHeight - offsetY + δ : minHeight - (modalData.h - stageData.h) - imgHeight + δ; // Get image position in dragging var imgLeft = (widthDiff > 0 ? $image.position().left : $image.position().left < 0 ? $image.position().left : 0) - δ; var imgTop = (heightDiff > 0 ? $image.position().top : $image.position().top < 0 ? $image.position().top : 0) + δ; var imgLeft2 = (widthDiff2 > 0 ? $image.position().left : $image.position().left < 0 ? $image.position().left : 0) - δ; var imgTop2 = (heightDiff2 > 0 ? $image.position().top : $image.position().top < 0 ? $image.position().top : 0) + δ; var opts = { e: { left: widthDiff >= -δ ? (widthDiff - δ) / 2 : imgLeft > widthDiff ? imgLeft : widthDiff }, s: { top: heightDiff >= δ ? (heightDiff + δ) / 2 : imgTop > heightDiff ? imgTop : heightDiff }, se: { top: heightDiff >= δ ? (heightDiff + δ) / 2 : imgTop > heightDiff ? imgTop : heightDiff, left: widthDiff >= -δ ? (widthDiff - δ) / 2 : imgLeft > widthDiff ? imgLeft : widthDiff }, w: { left: widthDiff2 >= -δ ? (widthDiff2 - δ) / 2 : imgLeft2 > widthDiff2 ? imgLeft2 : widthDiff2 }, n: { top: heightDiff2 >= δ ? (heightDiff2 + δ) / 2 : imgTop2 > heightDiff2 ? imgTop2 : heightDiff2 }, nw: { top: heightDiff2 >= δ ? (heightDiff2 + δ) / 2 : imgTop2 > heightDiff2 ? imgTop2 : heightDiff2, left: widthDiff2 >= -δ ? (widthDiff2 - δ) / 2 : imgLeft2 > widthDiff2 ? imgLeft2 : widthDiff2 }, ne: { top: heightDiff2 >= δ ? (heightDiff2 + δ) / 2 : imgTop2 > heightDiff2 ? imgTop2 : heightDiff2, left: widthDiff >= -δ ? (widthDiff - δ) / 2 : imgLeft > widthDiff ? imgLeft : widthDiff }, sw: { top: heightDiff >= δ ? (heightDiff + δ) / 2 : imgTop > heightDiff ? imgTop : heightDiff, left: widthDiff2 >= -δ ? (widthDiff2 - δ) / 2 : imgLeft2 > widthDiff2 ? imgLeft2 : widthDiff2 } }; return opts[dir]; }; var dragStart = function dragStart(dir, e) { e = e || window.event; e.preventDefault(); isDragging = true; PUBLIC_VARS['isResizing'] = true; startX = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.clientX; startY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.clientY; // Reclac the modal data when mousedown modalData = { w: $modal.width() + (isBorderBox($modal) ? _this._modalEdgeValue.horizontal : 0), h: $modal.height() + (isBorderBox($modal) ? _this._modalEdgeValue.vertical : 0), x: $modal.position().left, y: $modal.position().top }; stageData = { w: $stage.width(), h: $stage.height(), x: $stage.position().left, y: $stage.position().top }; imageData = { w: $image.width(), h: $image.height(), x: $image.position().left, y: $image.position().top }; // δ is the difference between image width and height δ = !_this.isRotated ? 0 : (imageData.w - imageData.h) / 2; imgWidth = !_this.isRotated ? imageData.w : imageData.h; imgHeight = !_this.isRotated ? imageData.h : imageData.w; direction = dir; // Add resizable cursor $(ELEMS_WITH_RESIZE_CURSOR).css('cursor', dir + '-resize'); $D.on(TOUCH_MOVE_EVENT + EVENT_NS, dragMove).on(TOUCH_END_EVENT + EVENT_NS, dragEnd); }; var dragMove = function dragMove(e) { e = e || window.event; e.preventDefault(); if (isDragging && !_this.isMaximized) { var endX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.clientX; var endY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.clientY; var relativeX = endX - startX; var relativeY = endY - startY; var modalOpts = getModalOpts(direction, relativeX, relativeY); $modal.css(modalOpts); var imageOpts = getImageOpts(direction, relativeX, relativeY); $image.css(imageOpts); _this.isDoResize = true; } }; var dragEnd = function dragEnd() { $D.off(TOUCH_MOVE_EVENT + EVENT_NS, dragMove).off(TOUCH_END_EVENT + EVENT_NS, dragEnd); // Set grab cursor if (PUBLIC_VARS['isResizing']) { setGrabCursor({ w: imgWidth, h: imgHeight }, { w: $stage.width(), h: $stage.height() }, $stage); } isDragging = false; PUBLIC_VARS['isResizing'] = false; // Remove resizable cursor $(ELEMS_WITH_RESIZE_CURSOR).css('cursor', ''); // Update image initial data var scale = _this.getImageScale($stage.width(), $stage.height()); $.extend(_this.imageData, { initWidth: _this.img.width * scale, initHeight: _this.img.height * scale, initLeft: ($stage.width() - _this.img.width * scale) / 2, initTop: ($stage.height() - _this.img.height * scale) / 2 }); }; $.each(resizableHandles, function (dir, handle) { handle.on(TOUCH_START_EVENT + EVENT_NS, function (e) { dragStart(dir, e); }); }); } }; /** * PhotoViewer class */ var PhotoViewer = /*#__PURE__*/function () { function PhotoViewer(items, options, el) { _classCallCheck(this, PhotoViewer); this.options = $.extend(true, {}, DEFAULTS, options); if (options && $.isArray(options.footerToolbar)) { this.options.footerToolbar = options.footerToolbar; } if (options && $.isArray(options.headerToolbar)) { this.options.headerToolbar = options.headerToolbar; } // Store element of clicked this.$el = $(el); // As we have multiple instances, so every instance has the following variables. // Whether modal opened this.isOpened = false; // Whether modal maximized this.isMaximized = false; // Whether image rotated (`90*(2n+1)`) this.isRotated = false; // Image rotation degree this.rotationDegree = 0; // Whether modal do resize this.isDoResize = false; // Store image data in every instance this.imageData = {}; // Store modal data in every instance this.modalData = { width: null, height: null, left: null, top: null }; // Used for time comparison this._lastTimestamp = 0; this.init(items, this.options); } _createClass(PhotoViewer, [{ key: "init", value: function init(items, opts) { this.groupData = items; this.groupIndex = opts['index']; // Reset public z-index with option PUBLIC_VARS['zIndex'] = PUBLIC_VARS['zIndex'] === 0 ? opts['zIndex'] : PUBLIC_VARS['zIndex']; // Get image src var imgSrc = items[this.groupIndex]['src']; this.open(); this.loadImage(imgSrc); if (opts.draggable) { this.draggable(this.$photoviewer, this.dragHandle, CLASS_NS + '-button'); } if (opts.movable) { this.movable(this.$stage, this.$image); } if (opts.resizable) { this.resizable(this.$photoviewer, this.$stage, this.$image, opts.modalWidth, opts.modalHeight); } } }, { key: "_createBtns", value: function _createBtns(toolbar) { var _this = this; var btns = ['minimize', 'maximize', 'close', 'zoomIn', 'zoomOut', 'prev', 'next', 'fullscreen', 'actualSize', 'rotateLeft', 'rotateRight']; var btnsHTML = ''; $.each(toolbar, function (index, item) { var btnClass = "".concat(NS, "-button ").concat(NS, "-button-").concat(item); if (btns.indexOf(item) >= 0) { btnsHTML += ""); } else if (_this.options.customButtons[item]) { btnsHTML += ""); } }); return btnsHTML; } }, { key: "_createTitle", value: function _createTitle() { return this.options.title ? "
") : ''; } }, { key: "_createTemplate", value: function _createTemplate() { // PhotoViewer base HTML var photoviewerHTML = "
\n
\n
\n
\n ").concat(this._createBtns(this.options.headerToolbar), "\n
\n ").concat(this._createTitle(), "\n
\n
\n \"\"\n
\n
\n
\n ").concat(this._createBtns(this.options.footerToolbar), "\n
\n
\n
\n
"); return photoviewerHTML; } }, { key: "build", value: function build() { // Create PhotoViewer HTML string var photoviewerHTML = this._createTemplate(); // Make PhotoViewer HTML string to jQuery element var $photoviewer = $(photoviewerHTML); // Get all PhotoViewer elements this.$photoviewer = $photoviewer; this.$stage = $photoviewer.find(CLASS_NS + '-stage'); this.$title = $photoviewer.find(CLASS_NS + '-title'); this.$image = $photoviewer.find(CLASS_NS + '-image'); this.$close = $photoviewer.find(CLASS_NS + '-button-close'); this.$maximize = $photoviewer.find(CLASS_NS + '-button-maximize'); this.$minimize = $photoviewer.find(CLASS_NS + '-button-minimize'); this.$zoomIn = $photoviewer.find(CLASS_NS + '-button-zoomIn'); this.$zoomOut = $photoviewer.find(CLASS_NS + '-button-zoomOut'); this.$actualSize = $photoviewer.find(CLASS_NS + '-button-actualSize'); this.$fullscreen = $photoviewer.find(CLASS_NS + '-button-fullscreen'); this.$rotateLeft = $photoviewer.find(CLASS_NS + '-button-rotateLeft'); this.$rotateRight = $photoviewer.find(CLASS_NS + '-button-rotateRight'); this.$prev = $photoviewer.find(CLASS_NS + '-button-prev'); this.$next = $photoviewer.find(CLASS_NS + '-button-next'); // Add class before image loaded this.$stage.addClass('stage-ready'); this.$image.addClass('image-ready'); // Reset modal `z-index` of multiple instances this.$photoviewer.css('z-index', PUBLIC_VARS['zIndex']); if (this.options.positionFixed) { this.$photoviewer.css({ position: 'fixed' }); } // Set handle element of draggable if (!this.options.dragHandle || this.options.dragHandle === CLASS_NS + '-modal') { this.dragHandle = this.$photoviewer; } else { this.dragHandle = this.$photoviewer.find(this.options.dragHandle); } // Add PhotoViewer to DOM $(this.options.appendTo).eq(0).append(this.$photoviewer); // Store the edge value of stage this._stageEdgeValue = { horizontal: getCSSValueSum(this.$stage, ['left', 'right', 'border-left-width', 'border-right-width']), vertical: getCSSValueSum(this.$stage, ['top', 'bottom', 'border-top-width', 'border-bottom-width']) }; // Store the edge value of modal this._modalEdgeValue = { horizontal: getCSSValueSum(this.$photoviewer, ['padding-left', 'padding-right', 'border-left-width', 'border-right-width']), vertical: getCSSValueSum(this.$photoviewer, ['padding-top', 'padding-bottom', 'border-top-width', 'border-bottom-width']) }; this._addEvents(); this._addCustomButtonEvents(); } }, { key: "open", value: function open() { this._triggerHook('beforeOpen', this); if (!this.options.multiInstances) { $(CLASS_NS + '-modal').eq(0).remove(); } this.build(); this.setInitModalPos(); this._triggerHook('opened', this); } }, { key: "close", value: function close() { this._triggerHook('beforeClose', this); // Remove PhotoViewer instance this.$photoviewer.remove(); this.isOpened = false; this.isMaximized = false; this.isRotated = false; this.rotationDegree = 0; if (!$(CLASS_NS + '-modal').length) { // Reset `z-index` after close if (this.options.multiInstances) { PUBLIC_VARS['zIndex'] = this.options.zIndex; } // Off resize event $W.off(RESIZE_EVENT + EVENT_NS); } this._triggerHook('closed', this); } }, { key: "_getOffsetParentData", value: function _getOffsetParentData() { var offsetParent = $(this.options.appendTo)[0]; return { width: this.options.positionFixed || isRootNode(offsetParent) ? $W.width() : offsetParent.clientWidth, height: this.options.positionFixed || isRootNode(offsetParent) ? $W.height() : offsetParent.clientHeight, scrollLeft: this.options.positionFixed ? 0 : isRootNode(offsetParent) ? $D.scrollLeft() : offsetParent.scrollLeft, scrollTop: this.options.positionFixed ? 0 : isRootNode(offsetParent) ? $D.scrollTop() : offsetParent.scrollTop }; } }, { key: "setModalToCenter", value: function setModalToCenter() { var initLeft, initTop, initRight, initBottom; // Extra width/height for `content-box` var extraWidth = 0, extraHeight = 0; if (!isBorderBox(this.$photoviewer)) { extraWidth += this._modalEdgeValue.horizontal; extraHeight += this._modalEdgeValue.vertical; } if ($.isPlainObject(this.options.initModalPos)) { initLeft = this.options.initModalPos.left; initTop = this.options.initModalPos.top; initRight = this.options.initModalPos.right; initBottom = this.options.initModalPos.bottom; } else { var offsetParentData = this._getOffsetParentData(); initLeft = (offsetParentData.width - this.options.modalWidth - extraWidth) / 2 + offsetParentData.scrollLeft; initTop = (offsetParentData.height - this.options.modalHeight - extraHeight) / 2 + offsetParentData.scrollTop; } var modalInitCSS = { width: this.modalData.width || this.options.modalWidth, height: this.modalData.height || this.options.modalHeight, left: this.modalData.left || initLeft, top: this.modalData.top || initTop, right: this.modalData.right || initRight, bottom: this.modalData.bottom || initBottom }; this.$photoviewer.css(modalInitCSS); } }, { key: "setInitModalPos", value: function setInitModalPos() { if (this.options.initMaximized) { this.maximize(); this.isOpened = true; } else { this.setModalToCenter(); } // The focus must be set after opening this.$photoviewer[0].focus(); } }, { key: "setModalSize", value: function setModalSize(img) { var _this2 = this; var offsetParentData = this._getOffsetParentData(); // Modal size should calculate with stage css value var modalWidth = img.width + this._stageEdgeValue.horizontal; var modalHeight = img.height + this._stageEdgeValue.vertical; // Extra width/height for `content-box` var extraWidth = 0, extraHeight = 0; if (isBorderBox(this.$photoviewer)) { modalWidth += this._modalEdgeValue.horizontal; modalHeight += this._modalEdgeValue.vertical; } else { extraWidth += this._modalEdgeValue.horizontal; extraHeight += this._modalEdgeValue.vertical; } var gapThreshold = (this.options.gapThreshold > 0 ? this.options.gapThreshold : 0) + 1; // Modal scale relative to parent element var scale = Math.min(offsetParentData.width / ((modalWidth + extraWidth) * gapThreshold), offsetParentData.height / ((modalHeight + extraHeight) * gapThreshold), 1); var minWidth = Math.max(modalWidth * scale, this.options.modalWidth); var minHeight = Math.max(modalHeight * scale, this.options.modalHeight); minWidth = this.options.fixedModalSize ? this.options.modalWidth : Math.round(minWidth); minHeight = this.options.fixedModalSize ? this.options.modalHeight : Math.round(minHeight); var transLeft, transTop, transRight, transBottom; if ($.isPlainObject(this.options.initModalPos)) { transLeft = this.options.initModalPos.left; transTop = this.options.initModalPos.top; transRight = this.options.initModalPos.right; transBottom = this.options.initModalPos.bottom; } else { var _offsetParentData = this._getOffsetParentData(); transLeft = (_offsetParentData.width - minWidth - extraWidth) / 2 + _offsetParentData.scrollLeft; transTop = (_offsetParentData.height - minHeight - extraHeight) / 2 + _offsetParentData.scrollTop; } var modalTransCSS = { width: minWidth, height: minHeight, left: transLeft, top: transTop, right: transRight, bottom: transBottom }; // Add init animation for modal if (this.options.initAnimation) { this.$photoviewer.animate(modalTransCSS, this.options.animationDuration, 'ease-in-out', function () { _this2.setImageSize(img); }); } else { this.$photoviewer.css(modalTransCSS); this.setImageSize(img); } this.isOpened = true; } }, { key: "getImageScale", value: function getImageScale(stageWidth, stageHeight) { var scale = 1; if (!this.isRotated) { scale = Math.min(stageWidth / this.img.width, stageHeight / this.img.height, 1); } else { scale = Math.min(stageWidth / this.img.height, stageHeight / this.img.width, 1); } return scale; } }, { key: "setImageSize", value: function setImageSize(img) { var stageData = { w: this.$stage.width(), h: this.$stage.height() }; var scale = this.getImageScale(stageData.w, stageData.h); this.$image.css({ width: Math.round(img.width * scale), height: Math.round(img.height * scale), left: (stageData.w - Math.round(img.width * scale)) / 2, top: (stageData.h - Math.round(img.height * scale)) / 2 }); // Store image initial data $.extend(this.imageData, { initWidth: img.width * scale, initHeight: img.height * scale, initLeft: (stageData.w - img.width * scale) / 2, initTop: (stageData.h - img.height * scale) / 2, width: img.width * scale, height: img.height * scale, left: (stageData.w - img.width * scale) / 2, top: (stageData.h - img.height * scale) / 2 }); // Set grab cursor setGrabCursor({ w: this.$image.width(), h: this.$image.height() }, { w: this.$stage.width(), h: this.$stage.height() }, this.$stage, this.isRotated); // Just execute before image loaded if (!this.imageLoaded) { // Loader end this.$photoviewer.find(CLASS_NS + '-loader').remove(); // Remove class after image loaded this.$stage.removeClass('stage-ready'); this.$image.removeClass('image-ready'); // Add init animation for image if (this.options.initAnimation && !this.options.progressiveLoading) { this.$image.fadeIn(); } this.imageLoaded = true; } } }, { key: "loadImage", value: function loadImage(imgSrc, fn, err) { var _this3 = this; // Reset image this.$image.removeAttr('style').attr('src', ''); this.isRotated = false; this.rotationDegree = 0; this.imageLoaded = false; // Loader start this.$photoviewer.append("
")); // Add class before image loaded this.$stage.addClass('stage-ready'); this.$image.addClass('image-ready'); if (this.options.initAnimation && !this.options.progressiveLoading) { this.$image.hide(); } this.$image.attr('src', imgSrc); preloadImage(imgSrc, function (img) { // Store HTMLImageElement _this3.img = img; // Store original data _this3.imageData = { originalWidth: img.width, originalHeight: img.height }; if (_this3.isMaximized || _this3.isOpened && _this3.options.fixedModalPos) { _this3.setImageSize(img); } else { _this3.setModalSize(img); } // Callback of image loaded successfully if (fn) { fn.call(); } }, function () { // Loader end _this3.$photoviewer.find(CLASS_NS + '-loader').remove(); // Callback of image loading failed if (err) { err.call(); } }); if (this.options.title) { this.setImageTitle(imgSrc); } } }, { key: "setImageTitle", value: function setImageTitle(url) { var title = this.groupData[this.groupIndex].title || getImageNameFromUrl(url); this.$title.html(title); } }, { key: "jump", value: function jump(step) { this._triggerHook('beforeChange', [this, this.groupIndex]); // Make sure change image after the modal animation has finished var now = Date.now(); if (now - this._lastTimestamp >= this.options.animationDuration) { this.groupIndex = this.groupIndex + step; this.jumpTo(this.groupIndex); this._lastTimestamp = now; } } }, { key: "jumpTo", value: function jumpTo(index) { var _this4 = this; index = index % this.groupData.length; if (index >= 0) { index = index % this.groupData.length; } else if (index < 0) { index = (this.groupData.length + index) % this.groupData.length; } this.groupIndex = index; this.loadImage(this.groupData[index].src, function () { _this4._triggerHook('changed', [_this4, index]); }, function () { _this4._triggerHook('changed', [_this4, index]); }); } }, { key: "wheel", value: function wheel(e) { e.preventDefault(); var delta = 1; if (e.deltaY) { delta = e.deltaY > 0 ? 1 : -1; } else if (e.wheelDelta) { delta = -e.wheelDelta / 120; } else if (e.detail) { delta = e.detail > 0 ? 1 : -1; } // Ratio threshold var ratio = -delta * this.options.ratioThreshold; // Mouse point position relative to stage var pointer = { x: e.clientX - this.$stage.offset().left + $D.scrollLeft(), y: e.clientY - this.$stage.offset().top + $D.scrollTop() }; this.zoom(ratio, pointer); } }, { key: "zoom", value: function zoom(ratio, origin) { // Zoom out ratio & Zoom in ratio ratio = ratio < 0 ? 1 / (1 - ratio) : 1 + ratio; // Image ratio ratio = this.$image.width() / this.imageData.originalWidth * ratio; if (ratio > this.options.maxRatio || ratio < this.options.minRatio) { return; } this.zoomTo(ratio, origin); } }, { key: "zoomTo", value: function zoomTo(ratio, origin) { var $image = this.$image; var $stage = this.$stage; var imgData = { w: this.imageData.width, h: this.imageData.height, x: this.imageData.left, y: this.imageData.top }; // Image stage position // We will use it to calc the relative position of image var stageData = { w: $stage.width(), h: $stage.height(), x: $stage.offset().left, y: $stage.offset().top }; // Set default origin coordinates (center of stage) if (origin === void 0) { origin = { x: $stage.width() / 2, y: $stage.height() / 2 }; } var newWidth = this.imageData.originalWidth * ratio; var newHeight = this.imageData.originalHeight * ratio; // Think about it for a while var newLeft = origin.x - (origin.x - imgData.x) / imgData.w * newWidth; var newTop = origin.y - (origin.y - imgData.y) / imgData.h * newHeight; // δ is the difference between image new width and new height var δ = !this.isRotated ? 0 : (newWidth - newHeight) / 2; var imgNewWidth = !this.isRotated ? newWidth : newHeight; var imgNewHeight = !this.isRotated ? newHeight : newWidth; var offsetX = stageData.w - newWidth; var offsetY = stageData.h - newHeight; // Zoom out & Zoom in condition // It's important and it takes me a lot of time to get it // The conditions with image rotate 90 degree drive me crazy alomst! if (imgNewHeight <= stageData.h) { newTop = (stageData.h - newHeight) / 2; } else { newTop = newTop > δ ? δ : newTop > offsetY - δ ? newTop : offsetY - δ; } if (imgNewWidth <= stageData.w) { newLeft = (stageData.w - newWidth) / 2; } else { newLeft = newLeft > -δ ? -δ : newLeft > offsetX + δ ? newLeft : offsetX + δ; } // Whether the image scale get to the critical point if (Math.abs(this.imageData.initWidth - newWidth) < this.imageData.initWidth * 0.05) { this.setImageSize(this.img); } else { $image.css({ width: Math.round(newWidth), height: Math.round(newHeight), left: Math.round(newLeft), top: Math.round(newTop) }); // Set grab cursor setGrabCursor({ w: Math.round(imgNewWidth), h: Math.round(imgNewHeight) }, { w: stageData.w, h: stageData.h }, this.$stage); } // Update image initial data $.extend(this.imageData, { width: newWidth, height: newHeight, left: newLeft, top: newTop }); } }, { key: "rotate", value: function rotate(degree) { this.rotationDegree = this.rotationDegree + degree; if (this.rotationDegree / 90 % 2 === 0) { this.isRotated = false; } else { this.isRotated = true; } this.rotateTo(this.rotationDegree); } }, { key: "rotateTo", value: function rotateTo(degree) { this.$image.css({ transform: 'rotate(' + degree + 'deg)' }); this.setImageSize({ width: this.imageData.originalWidth, height: this.imageData.originalHeight }); // Remove grab cursor when rotate this.$stage.removeClass('is-grab'); } }, { key: "resize", value: function resize() { if (this.isOpened) { if (this.isMaximized) { this.setImageSize({ width: this.imageData.originalWidth, height: this.imageData.originalHeight }); } else { this.setModalSize({ width: this.imageData.originalWidth, height: this.imageData.originalHeight }); } } } }, { key: "maximize", value: function maximize() { this.$photoviewer.addClass(NS + '-maximized'); this.$photoviewer.css({ width: 'auto', height: 'auto', top: 0, right: 0, bottom: 0, left: 0 }); this.isMaximized = true; } }, { key: "exitMaximize", value: function exitMaximize() { this.$photoviewer.removeClass(NS + '-maximized'); this.setModalToCenter(); this.isMaximized = false; } }, { key: "toggleMaximize", value: function toggleMaximize() { if (!this.isMaximized) { var originalWidth = this.$photoviewer.width(); var originalHeight = this.$photoviewer.height(); if (isBorderBox(this.$photoviewer)) { originalWidth += this._modalEdgeValue.horizontal; originalHeight += this._modalEdgeValue.vertical; } // Store modal's size and position before maximized this.modalData = { width: originalWidth, height: originalHeight, left: parseFloat(this.$photoviewer.css('left')), top: parseFloat(this.$photoviewer.css('top')) }; this.maximize(); } else { this.exitMaximize(); } this.setImageSize({ width: this.imageData.originalWidth, height: this.imageData.originalHeight }); this.$photoviewer[0].focus(); } }, { key: "fullscreen", value: function fullscreen() { requestFullscreen(this.$photoviewer[0]); this.$photoviewer[0].focus(); } }, { key: "_keydown", value: function _keydown(e) { if (!this.options.keyboard) { return false; } var keyCode = e.keyCode || e.which || e.charCode; var ctrlKey = e.ctrlKey || e.metaKey; var altKey = e.altKey || e.metaKey; switch (keyCode) { // ← case 37: this.jump(-1); break; // → case 39: this.jump(1); break; // + case 187: this.zoom(this.options.ratioThreshold * 3, { x: this.$stage.width() / 2, y: this.$stage.height() / 2 }, e); break; // - case 189: this.zoom(-this.options.ratioThreshold * 3, { x: this.$stage.width() / 2, y: this.$stage.height() / 2 }, e); break; // + Firefox case 61: this.zoom(this.options.ratioThreshold * 3, { x: this.$stage.width() / 2, y: this.$stage.height() / 2 }, e); break; // - Firefox case 173: this.zoom(-this.options.ratioThreshold * 3, { x: this.$stage.width() / 2, y: this.$stage.height() / 2 }, e); break; // Ctrl + Alt + 0 case 48: if (ctrlKey && altKey) { this.zoomTo(1, { x: this.$stage.width() / 2, y: this.$stage.height() / 2 }, e); } break; // Ctrl + , case 188: if (ctrlKey) { this.rotate(-90); } break; // Ctrl + . case 190: if (ctrlKey) { this.rotate(90); } break; // Q case 81: this.close(); break; } } }, { key: "_addEvents", value: function _addEvents() { var _this5 = this; this.$close.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.close(); }); this.$stage.off(WHEEL_EVENT + EVENT_NS).on(WHEEL_EVENT + EVENT_NS, function (e) { _this5.wheel(e); }); this.$zoomIn.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.zoom(_this5.options.ratioThreshold * 3); }); this.$zoomOut.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.zoom(-_this5.options.ratioThreshold * 3); }); this.$actualSize.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.zoomTo(1); }); this.$prev.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.jump(-1); }); this.$fullscreen.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.fullscreen(); }); this.$next.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.jump(1); }); this.$rotateLeft.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.rotate(-90); }); this.$rotateRight.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.rotate(90); }); this.$maximize.off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function () { _this5.toggleMaximize(); }); this.$photoviewer.off(KEYDOWN_EVENT + EVENT_NS).on(KEYDOWN_EVENT + EVENT_NS, function (e) { _this5._keydown(e); }); $W.on(RESIZE_EVENT + EVENT_NS, debounce(function () { return _this5.resize(); }, 500)); } }, { key: "_addCustomButtonEvents", value: function _addCustomButtonEvents() { var _this6 = this; var _loop = function _loop(btnKey) { _this6.$photoviewer.find(CLASS_NS + '-button-' + btnKey).off(CLICK_EVENT + EVENT_NS).on(CLICK_EVENT + EVENT_NS, function (e) { _this6.options.customButtons[btnKey].click.apply(_this6, [_this6, e]); }); }; for (var btnKey in this.options.customButtons) { _loop(btnKey); } } }, { key: "_triggerHook", value: function _triggerHook(e, data) { if (this.options.callbacks[e]) { this.options.callbacks[e].apply(this, $.isArray(data) ? data : [data]); } } }]); return PhotoViewer; }(); /** * Add methods to PhotoViewer */ $.extend(PhotoViewer.prototype, draggable, movable, resizable); /** * Add PhotoViewer to globle */ window.PhotoViewer = PhotoViewer; return PhotoViewer; }));