Greasy Fork is available in English.

Viewer.js.Lib

A copy of Viewer.js, hosted on greasyfork.org

Dette script bør ikke installeres direkte. Det er et bibliotek, som andre scripts kan inkludere med metadirektivet // @require https://update.greasyfork.org/scripts/510239/1454424/ViewerjsLib.js

  1. /*!
  2. * Viewer.js v1.11.6
  3. * https://fengyuanchen.github.io/viewerjs
  4. *
  5. * Copyright 2015-present Chen Fengyuan
  6. * Released under the MIT license
  7. *
  8. * Date: 2023-09-17T03:16:38.052Z
  9. */
  10.  
  11. (function (global, factory) {
  12. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  13. typeof define === 'function' && define.amd ? define(factory) :
  14. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Viewer = factory());
  15. })(this, (function () { 'use strict';
  16.  
  17. function ownKeys(e, r) {
  18. var t = Object.keys(e);
  19. if (Object.getOwnPropertySymbols) {
  20. var o = Object.getOwnPropertySymbols(e);
  21. r && (o = o.filter(function (r) {
  22. return Object.getOwnPropertyDescriptor(e, r).enumerable;
  23. })), t.push.apply(t, o);
  24. }
  25. return t;
  26. }
  27. function _objectSpread2(e) {
  28. for (var r = 1; r < arguments.length; r++) {
  29. var t = null != arguments[r] ? arguments[r] : {};
  30. r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
  31. _defineProperty(e, r, t[r]);
  32. }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
  33. Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
  34. });
  35. }
  36. return e;
  37. }
  38. function _typeof(o) {
  39. "@babel/helpers - typeof";
  40.  
  41. return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
  42. return typeof o;
  43. } : function (o) {
  44. return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
  45. }, _typeof(o);
  46. }
  47. function _classCallCheck(instance, Constructor) {
  48. if (!(instance instanceof Constructor)) {
  49. throw new TypeError("Cannot call a class as a function");
  50. }
  51. }
  52. function _defineProperties(target, props) {
  53. for (var i = 0; i < props.length; i++) {
  54. var descriptor = props[i];
  55. descriptor.enumerable = descriptor.enumerable || false;
  56. descriptor.configurable = true;
  57. if ("value" in descriptor) descriptor.writable = true;
  58. Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
  59. }
  60. }
  61. function _createClass(Constructor, protoProps, staticProps) {
  62. if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  63. if (staticProps) _defineProperties(Constructor, staticProps);
  64. Object.defineProperty(Constructor, "prototype", {
  65. writable: false
  66. });
  67. return Constructor;
  68. }
  69. function _defineProperty(obj, key, value) {
  70. key = _toPropertyKey(key);
  71. if (key in obj) {
  72. Object.defineProperty(obj, key, {
  73. value: value,
  74. enumerable: true,
  75. configurable: true,
  76. writable: true
  77. });
  78. } else {
  79. obj[key] = value;
  80. }
  81. return obj;
  82. }
  83. function _toPrimitive(input, hint) {
  84. if (typeof input !== "object" || input === null) return input;
  85. var prim = input[Symbol.toPrimitive];
  86. if (prim !== undefined) {
  87. var res = prim.call(input, hint || "default");
  88. if (typeof res !== "object") return res;
  89. throw new TypeError("@@toPrimitive must return a primitive value.");
  90. }
  91. return (hint === "string" ? String : Number)(input);
  92. }
  93. function _toPropertyKey(arg) {
  94. var key = _toPrimitive(arg, "string");
  95. return typeof key === "symbol" ? key : String(key);
  96. }
  97.  
  98. var DEFAULTS = {
  99. /**
  100. * Enable a modal backdrop, specify `static` for a backdrop
  101. * which doesn't close the modal on click.
  102. * @type {boolean}
  103. */
  104. backdrop: true,
  105. /**
  106. * Show the button on the top-right of the viewer.
  107. * @type {boolean}
  108. */
  109. button: true,
  110. /**
  111. * Show the navbar.
  112. * @type {boolean | number}
  113. */
  114. navbar: true,
  115. /**
  116. * Specify the visibility and the content of the title.
  117. * @type {boolean | number | Function | Array}
  118. */
  119. title: true,
  120. /**
  121. * Show the toolbar.
  122. * @type {boolean | number | Object}
  123. */
  124. toolbar: true,
  125. /**
  126. * Custom class name(s) to add to the viewer's root element.
  127. * @type {string}
  128. */
  129. className: '',
  130. /**
  131. * Define where to put the viewer in modal mode.
  132. * @type {string | Element}
  133. */
  134. container: 'body',
  135. /**
  136. * Filter the images for viewing. Return true if the image is viewable.
  137. * @type {Function}
  138. */
  139. filter: null,
  140. /**
  141. * Enable to request fullscreen when play.
  142. * {@link https://developer.mozilla.org/en-US/docs/Web/API/FullscreenOptions}
  143. * @type {boolean|FullscreenOptions}
  144. */
  145. fullscreen: true,
  146. /**
  147. * Define the extra attributes to inherit from the original image.
  148. * @type {Array}
  149. */
  150. inheritedAttributes: ['crossOrigin', 'decoding', 'isMap', 'loading', 'referrerPolicy', 'sizes', 'srcset', 'useMap'],
  151. /**
  152. * Define the initial coverage of the viewing image.
  153. * @type {number}
  154. */
  155. initialCoverage: 0.9,
  156. /**
  157. * Define the initial index of the image for viewing.
  158. * @type {number}
  159. */
  160. initialViewIndex: 0,
  161. /**
  162. * Enable inline mode.
  163. * @type {boolean}
  164. */
  165. inline: false,
  166. /**
  167. * The amount of time to delay between automatically cycling an image when playing.
  168. * @type {number}
  169. */
  170. interval: 5000,
  171. /**
  172. * Enable keyboard support.
  173. * @type {boolean}
  174. */
  175. keyboard: true,
  176. /**
  177. * Focus the viewer when initialized.
  178. * @type {boolean}
  179. */
  180. focus: true,
  181. /**
  182. * Indicate if show a loading spinner when load image or not.
  183. * @type {boolean}
  184. */
  185. loading: true,
  186. /**
  187. * Indicate if enable loop viewing or not.
  188. * @type {boolean}
  189. */
  190. loop: true,
  191. /**
  192. * Min width of the viewer in inline mode.
  193. * @type {number}
  194. */
  195. minWidth: 200,
  196. /**
  197. * Min height of the viewer in inline mode.
  198. * @type {number}
  199. */
  200. minHeight: 100,
  201. /**
  202. * Enable to move the image.
  203. * @type {boolean}
  204. */
  205. movable: true,
  206. /**
  207. * Enable to rotate the image.
  208. * @type {boolean}
  209. */
  210. rotatable: true,
  211. /**
  212. * Enable to scale the image.
  213. * @type {boolean}
  214. */
  215. scalable: true,
  216. /**
  217. * Enable to zoom the image.
  218. * @type {boolean}
  219. */
  220. zoomable: true,
  221. /**
  222. * Enable to zoom the current image by dragging on the touch screen.
  223. * @type {boolean}
  224. */
  225. zoomOnTouch: true,
  226. /**
  227. * Enable to zoom the image by wheeling mouse.
  228. * @type {boolean}
  229. */
  230. zoomOnWheel: true,
  231. /**
  232. * Enable to slide to the next or previous image by swiping on the touch screen.
  233. * @type {boolean}
  234. */
  235. slideOnTouch: true,
  236. /**
  237. * Indicate if toggle the image size between its natural size
  238. * and initial size when double click on the image or not.
  239. * @type {boolean}
  240. */
  241. toggleOnDblclick: true,
  242. /**
  243. * Show the tooltip with image ratio (percentage) when zoom in or zoom out.
  244. * @type {boolean}
  245. */
  246. tooltip: true,
  247. /**
  248. * Enable CSS3 Transition for some special elements.
  249. * @type {boolean}
  250. */
  251. transition: true,
  252. /**
  253. * Define the CSS `z-index` value of viewer in modal mode.
  254. * @type {number}
  255. */
  256. zIndex: 2015,
  257. /**
  258. * Define the CSS `z-index` value of viewer in inline mode.
  259. * @type {number}
  260. */
  261. zIndexInline: 0,
  262. /**
  263. * Define the ratio when zoom the image by wheeling mouse.
  264. * @type {number}
  265. */
  266. zoomRatio: 0.1,
  267. /**
  268. * Define the min ratio of the image when zoom out.
  269. * @type {number}
  270. */
  271. minZoomRatio: 0.01,
  272. /**
  273. * Define the max ratio of the image when zoom in.
  274. * @type {number}
  275. */
  276. maxZoomRatio: 100,
  277. /**
  278. * Define where to get the original image URL for viewing.
  279. * @type {string | Function}
  280. */
  281. url: 'src',
  282. /**
  283. * Event shortcuts.
  284. * @type {Function}
  285. */
  286. ready: null,
  287. show: null,
  288. shown: null,
  289. hide: null,
  290. hidden: null,
  291. view: null,
  292. viewed: null,
  293. move: null,
  294. moved: null,
  295. rotate: null,
  296. rotated: null,
  297. scale: null,
  298. scaled: null,
  299. zoom: null,
  300. zoomed: null,
  301. play: null,
  302. stop: null
  303. };
  304.  
  305. var TEMPLATE = '<div class="viewer-container" tabindex="-1" touch-action="none">' + '<div class="viewer-canvas"></div>' + '<div class="viewer-footer">' + '<div class="viewer-title"></div>' + '<div class="viewer-toolbar"></div>' + '<div class="viewer-navbar">' + '<ul class="viewer-list" role="navigation"></ul>' + '</div>' + '</div>' + '<div class="viewer-tooltip" role="alert" aria-hidden="true"></div>' + '<div class="viewer-button" data-viewer-action="mix" role="button"></div>' + '<div class="viewer-player"></div>' + '</div>';
  306.  
  307. var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
  308. var WINDOW = IS_BROWSER ? window : {};
  309. var IS_TOUCH_DEVICE = IS_BROWSER && WINDOW.document.documentElement ? 'ontouchstart' in WINDOW.document.documentElement : false;
  310. var HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false;
  311. var NAMESPACE = 'viewer';
  312.  
  313. // Actions
  314. var ACTION_MOVE = 'move';
  315. var ACTION_SWITCH = 'switch';
  316. var ACTION_ZOOM = 'zoom';
  317.  
  318. // Classes
  319. var CLASS_ACTIVE = "".concat(NAMESPACE, "-active");
  320. var CLASS_CLOSE = "".concat(NAMESPACE, "-close");
  321. var CLASS_FADE = "".concat(NAMESPACE, "-fade");
  322. var CLASS_FIXED = "".concat(NAMESPACE, "-fixed");
  323. var CLASS_FULLSCREEN = "".concat(NAMESPACE, "-fullscreen");
  324. var CLASS_FULLSCREEN_EXIT = "".concat(NAMESPACE, "-fullscreen-exit");
  325. var CLASS_HIDE = "".concat(NAMESPACE, "-hide");
  326. var CLASS_HIDE_MD_DOWN = "".concat(NAMESPACE, "-hide-md-down");
  327. var CLASS_HIDE_SM_DOWN = "".concat(NAMESPACE, "-hide-sm-down");
  328. var CLASS_HIDE_XS_DOWN = "".concat(NAMESPACE, "-hide-xs-down");
  329. var CLASS_IN = "".concat(NAMESPACE, "-in");
  330. var CLASS_INVISIBLE = "".concat(NAMESPACE, "-invisible");
  331. var CLASS_LOADING = "".concat(NAMESPACE, "-loading");
  332. var CLASS_MOVE = "".concat(NAMESPACE, "-move");
  333. var CLASS_OPEN = "".concat(NAMESPACE, "-open");
  334. var CLASS_SHOW = "".concat(NAMESPACE, "-show");
  335. var CLASS_TRANSITION = "".concat(NAMESPACE, "-transition");
  336.  
  337. // Native events
  338. var EVENT_CLICK = 'click';
  339. var EVENT_DBLCLICK = 'dblclick';
  340. var EVENT_DRAG_START = 'dragstart';
  341. var EVENT_FOCUSIN = 'focusin';
  342. var EVENT_KEY_DOWN = 'keydown';
  343. var EVENT_LOAD = 'load';
  344. var EVENT_ERROR = 'error';
  345. var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';
  346. var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';
  347. var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';
  348. var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;
  349. var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;
  350. var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;
  351. var EVENT_RESIZE = 'resize';
  352. var EVENT_TRANSITION_END = 'transitionend';
  353. var EVENT_WHEEL = 'wheel';
  354.  
  355. // Custom events
  356. var EVENT_READY = 'ready';
  357. var EVENT_SHOW = 'show';
  358. var EVENT_SHOWN = 'shown';
  359. var EVENT_HIDE = 'hide';
  360. var EVENT_HIDDEN = 'hidden';
  361. var EVENT_VIEW = 'view';
  362. var EVENT_VIEWED = 'viewed';
  363. var EVENT_MOVE = 'move';
  364. var EVENT_MOVED = 'moved';
  365. var EVENT_ROTATE = 'rotate';
  366. var EVENT_ROTATED = 'rotated';
  367. var EVENT_SCALE = 'scale';
  368. var EVENT_SCALED = 'scaled';
  369. var EVENT_ZOOM = 'zoom';
  370. var EVENT_ZOOMED = 'zoomed';
  371. var EVENT_PLAY = 'play';
  372. var EVENT_STOP = 'stop';
  373.  
  374. // Data keys
  375. var DATA_ACTION = "".concat(NAMESPACE, "Action");
  376.  
  377. // RegExps
  378. var REGEXP_SPACES = /\s\s*/;
  379.  
  380. // Misc
  381. var BUTTONS = ['zoom-in', 'zoom-out', 'one-to-one', 'reset', 'prev', 'play', 'next', 'rotate-left', 'rotate-right', 'flip-horizontal', 'flip-vertical'];
  382.  
  383. /**
  384. * Check if the given value is a string.
  385. * @param {*} value - The value to check.
  386. * @returns {boolean} Returns `true` if the given value is a string, else `false`.
  387. */
  388. function isString(value) {
  389. return typeof value === 'string';
  390. }
  391.  
  392. /**
  393. * Check if the given value is not a number.
  394. */
  395. var isNaN = Number.isNaN || WINDOW.isNaN;
  396.  
  397. /**
  398. * Check if the given value is a number.
  399. * @param {*} value - The value to check.
  400. * @returns {boolean} Returns `true` if the given value is a number, else `false`.
  401. */
  402. function isNumber(value) {
  403. return typeof value === 'number' && !isNaN(value);
  404. }
  405.  
  406. /**
  407. * Check if the given value is undefined.
  408. * @param {*} value - The value to check.
  409. * @returns {boolean} Returns `true` if the given value is undefined, else `false`.
  410. */
  411. function isUndefined(value) {
  412. return typeof value === 'undefined';
  413. }
  414.  
  415. /**
  416. * Check if the given value is an object.
  417. * @param {*} value - The value to check.
  418. * @returns {boolean} Returns `true` if the given value is an object, else `false`.
  419. */
  420. function isObject(value) {
  421. return _typeof(value) === 'object' && value !== null;
  422. }
  423. var hasOwnProperty = Object.prototype.hasOwnProperty;
  424.  
  425. /**
  426. * Check if the given value is a plain object.
  427. * @param {*} value - The value to check.
  428. * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
  429. */
  430. function isPlainObject(value) {
  431. if (!isObject(value)) {
  432. return false;
  433. }
  434. try {
  435. var _constructor = value.constructor;
  436. var prototype = _constructor.prototype;
  437. return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');
  438. } catch (error) {
  439. return false;
  440. }
  441. }
  442.  
  443. /**
  444. * Check if the given value is a function.
  445. * @param {*} value - The value to check.
  446. * @returns {boolean} Returns `true` if the given value is a function, else `false`.
  447. */
  448. function isFunction(value) {
  449. return typeof value === 'function';
  450. }
  451.  
  452. /**
  453. * Iterate the given data.
  454. * @param {*} data - The data to iterate.
  455. * @param {Function} callback - The process function for each element.
  456. * @returns {*} The original data.
  457. */
  458. function forEach(data, callback) {
  459. if (data && isFunction(callback)) {
  460. if (Array.isArray(data) || isNumber(data.length) /* array-like */) {
  461. var length = data.length;
  462. var i;
  463. for (i = 0; i < length; i += 1) {
  464. if (callback.call(data, data[i], i, data) === false) {
  465. break;
  466. }
  467. }
  468. } else if (isObject(data)) {
  469. Object.keys(data).forEach(function (key) {
  470. callback.call(data, data[key], key, data);
  471. });
  472. }
  473. }
  474. return data;
  475. }
  476.  
  477. /**
  478. * Extend the given object.
  479. * @param {*} obj - The object to be extended.
  480. * @param {*} args - The rest objects which will be merged to the first object.
  481. * @returns {Object} The extended object.
  482. */
  483. var assign = Object.assign || function assign(obj) {
  484. for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  485. args[_key - 1] = arguments[_key];
  486. }
  487. if (isObject(obj) && args.length > 0) {
  488. args.forEach(function (arg) {
  489. if (isObject(arg)) {
  490. Object.keys(arg).forEach(function (key) {
  491. obj[key] = arg[key];
  492. });
  493. }
  494. });
  495. }
  496. return obj;
  497. };
  498. var REGEXP_SUFFIX = /^(?:width|height|left|top|marginLeft|marginTop)$/;
  499.  
  500. /**
  501. * Apply styles to the given element.
  502. * @param {Element} element - The target element.
  503. * @param {Object} styles - The styles for applying.
  504. */
  505. function setStyle(element, styles) {
  506. var style = element.style;
  507. forEach(styles, function (value, property) {
  508. if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
  509. value += 'px';
  510. }
  511. style[property] = value;
  512. });
  513. }
  514.  
  515. /**
  516. * Escape a string for using in HTML.
  517. * @param {String} value - The string to escape.
  518. * @returns {String} Returns the escaped string.
  519. */
  520. function escapeHTMLEntities(value) {
  521. return isString(value) ? value.replace(/&(?!amp;|quot;|#39;|lt;|gt;)/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;') : value;
  522. }
  523.  
  524. /**
  525. * Check if the given element has a special class.
  526. * @param {Element} element - The element to check.
  527. * @param {string} value - The class to search.
  528. * @returns {boolean} Returns `true` if the special class was found.
  529. */
  530. function hasClass(element, value) {
  531. if (!element || !value) {
  532. return false;
  533. }
  534. return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
  535. }
  536.  
  537. /**
  538. * Add classes to the given element.
  539. * @param {Element} element - The target element.
  540. * @param {string} value - The classes to be added.
  541. */
  542. function addClass(element, value) {
  543. if (!element || !value) {
  544. return;
  545. }
  546. if (isNumber(element.length)) {
  547. forEach(element, function (elem) {
  548. addClass(elem, value);
  549. });
  550. return;
  551. }
  552. if (element.classList) {
  553. element.classList.add(value);
  554. return;
  555. }
  556. var className = element.className.trim();
  557. if (!className) {
  558. element.className = value;
  559. } else if (className.indexOf(value) < 0) {
  560. element.className = "".concat(className, " ").concat(value);
  561. }
  562. }
  563.  
  564. /**
  565. * Remove classes from the given element.
  566. * @param {Element} element - The target element.
  567. * @param {string} value - The classes to be removed.
  568. */
  569. function removeClass(element, value) {
  570. if (!element || !value) {
  571. return;
  572. }
  573. if (isNumber(element.length)) {
  574. forEach(element, function (elem) {
  575. removeClass(elem, value);
  576. });
  577. return;
  578. }
  579. if (element.classList) {
  580. element.classList.remove(value);
  581. return;
  582. }
  583. if (element.className.indexOf(value) >= 0) {
  584. element.className = element.className.replace(value, '');
  585. }
  586. }
  587.  
  588. /**
  589. * Add or remove classes from the given element.
  590. * @param {Element} element - The target element.
  591. * @param {string} value - The classes to be toggled.
  592. * @param {boolean} added - Add only.
  593. */
  594. function toggleClass(element, value, added) {
  595. if (!value) {
  596. return;
  597. }
  598. if (isNumber(element.length)) {
  599. forEach(element, function (elem) {
  600. toggleClass(elem, value, added);
  601. });
  602. return;
  603. }
  604.  
  605. // IE10-11 doesn't support the second parameter of `classList.toggle`
  606. if (added) {
  607. addClass(element, value);
  608. } else {
  609. removeClass(element, value);
  610. }
  611. }
  612. var REGEXP_HYPHENATE = /([a-z\d])([A-Z])/g;
  613.  
  614. /**
  615. * Transform the given string from camelCase to kebab-case
  616. * @param {string} value - The value to transform.
  617. * @returns {string} The transformed value.
  618. */
  619. function hyphenate(value) {
  620. return value.replace(REGEXP_HYPHENATE, '$1-$2').toLowerCase();
  621. }
  622.  
  623. /**
  624. * Get data from the given element.
  625. * @param {Element} element - The target element.
  626. * @param {string} name - The data key to get.
  627. * @returns {string} The data value.
  628. */
  629. function getData(element, name) {
  630. if (isObject(element[name])) {
  631. return element[name];
  632. }
  633. if (element.dataset) {
  634. return element.dataset[name];
  635. }
  636. return element.getAttribute("data-".concat(hyphenate(name)));
  637. }
  638.  
  639. /**
  640. * Set data to the given element.
  641. * @param {Element} element - The target element.
  642. * @param {string} name - The data key to set.
  643. * @param {string} data - The data value.
  644. */
  645. function setData(element, name, data) {
  646. if (isObject(data)) {
  647. element[name] = data;
  648. } else if (element.dataset) {
  649. element.dataset[name] = data;
  650. } else {
  651. element.setAttribute("data-".concat(hyphenate(name)), data);
  652. }
  653. }
  654. var onceSupported = function () {
  655. var supported = false;
  656. if (IS_BROWSER) {
  657. var once = false;
  658. var listener = function listener() {};
  659. var options = Object.defineProperty({}, 'once', {
  660. get: function get() {
  661. supported = true;
  662. return once;
  663. },
  664. /**
  665. * This setter can fix a `TypeError` in strict mode
  666. * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
  667. * @param {boolean} value - The value to set
  668. */
  669. set: function set(value) {
  670. once = value;
  671. }
  672. });
  673. WINDOW.addEventListener('test', listener, options);
  674. WINDOW.removeEventListener('test', listener, options);
  675. }
  676. return supported;
  677. }();
  678.  
  679. /**
  680. * Remove event listener from the target element.
  681. * @param {Element} element - The event target.
  682. * @param {string} type - The event type(s).
  683. * @param {Function} listener - The event listener.
  684. * @param {Object} options - The event options.
  685. */
  686. function removeListener(element, type, listener) {
  687. var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  688. var handler = listener;
  689. type.trim().split(REGEXP_SPACES).forEach(function (event) {
  690. if (!onceSupported) {
  691. var listeners = element.listeners;
  692. if (listeners && listeners[event] && listeners[event][listener]) {
  693. handler = listeners[event][listener];
  694. delete listeners[event][listener];
  695. if (Object.keys(listeners[event]).length === 0) {
  696. delete listeners[event];
  697. }
  698. if (Object.keys(listeners).length === 0) {
  699. delete element.listeners;
  700. }
  701. }
  702. }
  703. element.removeEventListener(event, handler, options);
  704. });
  705. }
  706.  
  707. /**
  708. * Add event listener to the target element.
  709. * @param {Element} element - The event target.
  710. * @param {string} type - The event type(s).
  711. * @param {Function} listener - The event listener.
  712. * @param {Object} options - The event options.
  713. */
  714. function addListener(element, type, listener) {
  715. var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  716. var _handler = listener;
  717. type.trim().split(REGEXP_SPACES).forEach(function (event) {
  718. if (options.once && !onceSupported) {
  719. var _element$listeners = element.listeners,
  720. listeners = _element$listeners === void 0 ? {} : _element$listeners;
  721. _handler = function handler() {
  722. delete listeners[event][listener];
  723. element.removeEventListener(event, _handler, options);
  724. for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  725. args[_key2] = arguments[_key2];
  726. }
  727. listener.apply(element, args);
  728. };
  729. if (!listeners[event]) {
  730. listeners[event] = {};
  731. }
  732. if (listeners[event][listener]) {
  733. element.removeEventListener(event, listeners[event][listener], options);
  734. }
  735. listeners[event][listener] = _handler;
  736. element.listeners = listeners;
  737. }
  738. element.addEventListener(event, _handler, options);
  739. });
  740. }
  741.  
  742. /**
  743. * Dispatch event on the target element.
  744. * @param {Element} element - The event target.
  745. * @param {string} type - The event type(s).
  746. * @param {Object} data - The additional event data.
  747. * @param {Object} options - The additional event options.
  748. * @returns {boolean} Indicate if the event is default prevented or not.
  749. */
  750. function dispatchEvent(element, type, data, options) {
  751. var event;
  752.  
  753. // Event and CustomEvent on IE9-11 are global objects, not constructors
  754. if (isFunction(Event) && isFunction(CustomEvent)) {
  755. event = new CustomEvent(type, _objectSpread2({
  756. bubbles: true,
  757. cancelable: true,
  758. detail: data
  759. }, options));
  760. } else {
  761. event = document.createEvent('CustomEvent');
  762. event.initCustomEvent(type, true, true, data);
  763. }
  764. return element.dispatchEvent(event);
  765. }
  766.  
  767. /**
  768. * Get the offset base on the document.
  769. * @param {Element} element - The target element.
  770. * @returns {Object} The offset data.
  771. */
  772. function getOffset(element) {
  773. var box = element.getBoundingClientRect();
  774. return {
  775. left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
  776. top: box.top + (window.pageYOffset - document.documentElement.clientTop)
  777. };
  778. }
  779.  
  780. /**
  781. * Get transforms base on the given object.
  782. * @param {Object} obj - The target object.
  783. * @returns {string} A string contains transform values.
  784. */
  785. function getTransforms(_ref) {
  786. var rotate = _ref.rotate,
  787. scaleX = _ref.scaleX,
  788. scaleY = _ref.scaleY,
  789. translateX = _ref.translateX,
  790. translateY = _ref.translateY;
  791. var values = [];
  792. if (isNumber(translateX) && translateX !== 0) {
  793. values.push("translateX(".concat(translateX, "px)"));
  794. }
  795. if (isNumber(translateY) && translateY !== 0) {
  796. values.push("translateY(".concat(translateY, "px)"));
  797. }
  798.  
  799. // Rotate should come first before scale to match orientation transform
  800. if (isNumber(rotate) && rotate !== 0) {
  801. values.push("rotate(".concat(rotate, "deg)"));
  802. }
  803. if (isNumber(scaleX) && scaleX !== 1) {
  804. values.push("scaleX(".concat(scaleX, ")"));
  805. }
  806. if (isNumber(scaleY) && scaleY !== 1) {
  807. values.push("scaleY(".concat(scaleY, ")"));
  808. }
  809. var transform = values.length ? values.join(' ') : 'none';
  810. return {
  811. WebkitTransform: transform,
  812. msTransform: transform,
  813. transform: transform
  814. };
  815. }
  816.  
  817. /**
  818. * Get an image name from an image url.
  819. * @param {string} url - The target url.
  820. * @example
  821. * // picture.jpg
  822. * getImageNameFromURL('https://domain.com/path/to/picture.jpg?size=1280×960')
  823. * @returns {string} A string contains the image name.
  824. */
  825. function getImageNameFromURL(url) {
  826. return isString(url) ? decodeURIComponent(url.replace(/^.*\//, '').replace(/[?&#].*$/, '')) : '';
  827. }
  828. var IS_SAFARI = WINDOW.navigator && /Version\/\d+(\.\d+)+?\s+Safari/i.test(WINDOW.navigator.userAgent);
  829.  
  830. /**
  831. * Get an image's natural sizes.
  832. * @param {string} image - The target image.
  833. * @param {Object} options - The viewer options.
  834. * @param {Function} callback - The callback function.
  835. * @returns {HTMLImageElement} The new image.
  836. */
  837. function getImageNaturalSizes(image, options, callback) {
  838. var newImage = document.createElement('img');
  839.  
  840. // Modern browsers (except Safari)
  841. if (image.naturalWidth && !IS_SAFARI) {
  842. callback(image.naturalWidth, image.naturalHeight);
  843. return newImage;
  844. }
  845. var body = document.body || document.documentElement;
  846. newImage.onload = function () {
  847. callback(newImage.width, newImage.height);
  848. if (!IS_SAFARI) {
  849. body.removeChild(newImage);
  850. }
  851. };
  852. forEach(options.inheritedAttributes, function (name) {
  853. var value = image.getAttribute(name);
  854. if (value !== null) {
  855. newImage.setAttribute(name, value);
  856. }
  857. });
  858. newImage.src = image.src;
  859.  
  860. // iOS Safari will convert the image automatically
  861. // with its orientation once append it into DOM
  862. if (!IS_SAFARI) {
  863. newImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';
  864. body.appendChild(newImage);
  865. }
  866. return newImage;
  867. }
  868.  
  869. /**
  870. * Get the related class name of a responsive type number.
  871. * @param {string} type - The responsive type.
  872. * @returns {string} The related class name.
  873. */
  874. function getResponsiveClass(type) {
  875. switch (type) {
  876. case 2:
  877. return CLASS_HIDE_XS_DOWN;
  878. case 3:
  879. return CLASS_HIDE_SM_DOWN;
  880. case 4:
  881. return CLASS_HIDE_MD_DOWN;
  882. default:
  883. return '';
  884. }
  885. }
  886.  
  887. /**
  888. * Get the max ratio of a group of pointers.
  889. * @param {string} pointers - The target pointers.
  890. * @returns {number} The result ratio.
  891. */
  892. function getMaxZoomRatio(pointers) {
  893. var pointers2 = _objectSpread2({}, pointers);
  894. var ratios = [];
  895. forEach(pointers, function (pointer, pointerId) {
  896. delete pointers2[pointerId];
  897. forEach(pointers2, function (pointer2) {
  898. var x1 = Math.abs(pointer.startX - pointer2.startX);
  899. var y1 = Math.abs(pointer.startY - pointer2.startY);
  900. var x2 = Math.abs(pointer.endX - pointer2.endX);
  901. var y2 = Math.abs(pointer.endY - pointer2.endY);
  902. var z1 = Math.sqrt(x1 * x1 + y1 * y1);
  903. var z2 = Math.sqrt(x2 * x2 + y2 * y2);
  904. var ratio = (z2 - z1) / z1;
  905. ratios.push(ratio);
  906. });
  907. });
  908. ratios.sort(function (a, b) {
  909. return Math.abs(a) < Math.abs(b);
  910. });
  911. return ratios[0];
  912. }
  913.  
  914. /**
  915. * Get a pointer from an event object.
  916. * @param {Object} event - The target event object.
  917. * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
  918. * @returns {Object} The result pointer contains start and/or end point coordinates.
  919. */
  920. function getPointer(_ref2, endOnly) {
  921. var pageX = _ref2.pageX,
  922. pageY = _ref2.pageY;
  923. var end = {
  924. endX: pageX,
  925. endY: pageY
  926. };
  927. return endOnly ? end : _objectSpread2({
  928. timeStamp: Date.now(),
  929. startX: pageX,
  930. startY: pageY
  931. }, end);
  932. }
  933.  
  934. /**
  935. * Get the center point coordinate of a group of pointers.
  936. * @param {Object} pointers - The target pointers.
  937. * @returns {Object} The center point coordinate.
  938. */
  939. function getPointersCenter(pointers) {
  940. var pageX = 0;
  941. var pageY = 0;
  942. var count = 0;
  943. forEach(pointers, function (_ref3) {
  944. var startX = _ref3.startX,
  945. startY = _ref3.startY;
  946. pageX += startX;
  947. pageY += startY;
  948. count += 1;
  949. });
  950. pageX /= count;
  951. pageY /= count;
  952. return {
  953. pageX: pageX,
  954. pageY: pageY
  955. };
  956. }
  957.  
  958. var render = {
  959. render: function render() {
  960. this.initContainer();
  961. this.initViewer();
  962. this.initList();
  963. this.renderViewer();
  964. },
  965. initBody: function initBody() {
  966. var ownerDocument = this.element.ownerDocument;
  967. var body = ownerDocument.body || ownerDocument.documentElement;
  968. this.body = body;
  969. this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
  970. this.initialBodyPaddingRight = body.style.paddingRight;
  971. this.initialBodyComputedPaddingRight = window.getComputedStyle(body).paddingRight;
  972. },
  973. initContainer: function initContainer() {
  974. this.containerData = {
  975. width: window.innerWidth,
  976. height: window.innerHeight
  977. };
  978. },
  979. initViewer: function initViewer() {
  980. var options = this.options,
  981. parent = this.parent;
  982. var viewerData;
  983. if (options.inline) {
  984. viewerData = {
  985. width: Math.max(parent.offsetWidth, options.minWidth),
  986. height: Math.max(parent.offsetHeight, options.minHeight)
  987. };
  988. this.parentData = viewerData;
  989. }
  990. if (this.fulled || !viewerData) {
  991. viewerData = this.containerData;
  992. }
  993. this.viewerData = assign({}, viewerData);
  994. },
  995. renderViewer: function renderViewer() {
  996. if (this.options.inline && !this.fulled) {
  997. setStyle(this.viewer, this.viewerData);
  998. }
  999. },
  1000. initList: function initList() {
  1001. var _this = this;
  1002. var element = this.element,
  1003. options = this.options,
  1004. list = this.list;
  1005. var items = [];
  1006.  
  1007. // initList may be called in this.update, so should keep idempotent
  1008. list.innerHTML = '';
  1009. forEach(this.images, function (image, index) {
  1010. var src = image.src;
  1011. var alt = image.alt || getImageNameFromURL(src);
  1012. var url = _this.getImageURL(image);
  1013. if (src || url) {
  1014. var item = document.createElement('li');
  1015. var img = document.createElement('img');
  1016. forEach(options.inheritedAttributes, function (name) {
  1017. var value = image.getAttribute(name);
  1018. if (value !== null) {
  1019. img.setAttribute(name, value);
  1020. }
  1021. });
  1022. if (options.navbar) {
  1023. img.src = src || url;
  1024. }
  1025. img.alt = alt;
  1026. img.setAttribute('data-original-url', url || src);
  1027. item.setAttribute('data-index', index);
  1028. item.setAttribute('data-viewer-action', 'view');
  1029. item.setAttribute('role', 'button');
  1030. if (options.keyboard) {
  1031. item.setAttribute('tabindex', 0);
  1032. }
  1033. item.appendChild(img);
  1034. list.appendChild(item);
  1035. items.push(item);
  1036. }
  1037. });
  1038. this.items = items;
  1039. forEach(items, function (item) {
  1040. var image = item.firstElementChild;
  1041. var onLoad;
  1042. var onError;
  1043. setData(image, 'filled', true);
  1044. if (options.loading) {
  1045. addClass(item, CLASS_LOADING);
  1046. }
  1047. addListener(image, EVENT_LOAD, onLoad = function onLoad(event) {
  1048. removeListener(image, EVENT_ERROR, onError);
  1049. if (options.loading) {
  1050. removeClass(item, CLASS_LOADING);
  1051. }
  1052. _this.loadImage(event);
  1053. }, {
  1054. once: true
  1055. });
  1056. addListener(image, EVENT_ERROR, onError = function onError() {
  1057. removeListener(image, EVENT_LOAD, onLoad);
  1058. if (options.loading) {
  1059. removeClass(item, CLASS_LOADING);
  1060. }
  1061. }, {
  1062. once: true
  1063. });
  1064. });
  1065. if (options.transition) {
  1066. addListener(element, EVENT_VIEWED, function () {
  1067. addClass(list, CLASS_TRANSITION);
  1068. }, {
  1069. once: true
  1070. });
  1071. }
  1072. },
  1073. renderList: function renderList() {
  1074. var index = this.index;
  1075. var item = this.items[index];
  1076. if (!item) {
  1077. return;
  1078. }
  1079. var next = item.nextElementSibling;
  1080. var gutter = parseInt(window.getComputedStyle(next || item).marginLeft, 10);
  1081. var offsetWidth = item.offsetWidth;
  1082. var outerWidth = offsetWidth + gutter;
  1083.  
  1084. // Place the active item in the center of the screen
  1085. setStyle(this.list, assign({
  1086. width: outerWidth * this.length - gutter
  1087. }, getTransforms({
  1088. translateX: (this.viewerData.width - offsetWidth) / 2 - outerWidth * index
  1089. })));
  1090. },
  1091. resetList: function resetList() {
  1092. var list = this.list;
  1093. list.innerHTML = '';
  1094. removeClass(list, CLASS_TRANSITION);
  1095. setStyle(list, getTransforms({
  1096. translateX: 0
  1097. }));
  1098. },
  1099. initImage: function initImage(done) {
  1100. var _this2 = this;
  1101. var options = this.options,
  1102. image = this.image,
  1103. viewerData = this.viewerData;
  1104. var footerHeight = this.footer.offsetHeight;
  1105. var viewerWidth = viewerData.width;
  1106. var viewerHeight = Math.max(viewerData.height - footerHeight, footerHeight);
  1107. var oldImageData = this.imageData || {};
  1108. var sizingImage;
  1109. this.imageInitializing = {
  1110. abort: function abort() {
  1111. sizingImage.onload = null;
  1112. }
  1113. };
  1114. sizingImage = getImageNaturalSizes(image, options, function (naturalWidth, naturalHeight) {
  1115. var aspectRatio = naturalWidth / naturalHeight;
  1116. var initialCoverage = Math.max(0, Math.min(1, options.initialCoverage));
  1117. var width = viewerWidth;
  1118. var height = viewerHeight;
  1119. _this2.imageInitializing = false;
  1120. if (viewerHeight * aspectRatio > viewerWidth) {
  1121. height = viewerWidth / aspectRatio;
  1122. } else {
  1123. width = viewerHeight * aspectRatio;
  1124. }
  1125. initialCoverage = isNumber(initialCoverage) ? initialCoverage : 0.9;
  1126. width = Math.min(width * initialCoverage, naturalWidth);
  1127. height = Math.min(height * initialCoverage, naturalHeight);
  1128. var left = (viewerWidth - width) / 2;
  1129. var top = (viewerHeight - height) / 2;
  1130. var imageData = {
  1131. left: left,
  1132. top: top,
  1133. x: left,
  1134. y: top,
  1135. width: width,
  1136. height: height,
  1137. oldRatio: 1,
  1138. ratio: width / naturalWidth,
  1139. aspectRatio: aspectRatio,
  1140. naturalWidth: naturalWidth,
  1141. naturalHeight: naturalHeight
  1142. };
  1143. var initialImageData = assign({}, imageData);
  1144. if (options.rotatable) {
  1145. imageData.rotate = oldImageData.rotate || 0;
  1146. initialImageData.rotate = 0;
  1147. }
  1148. if (options.scalable) {
  1149. imageData.scaleX = oldImageData.scaleX || 1;
  1150. imageData.scaleY = oldImageData.scaleY || 1;
  1151. initialImageData.scaleX = 1;
  1152. initialImageData.scaleY = 1;
  1153. }
  1154. _this2.imageData = imageData;
  1155. _this2.initialImageData = initialImageData;
  1156. if (done) {
  1157. done();
  1158. }
  1159. });
  1160. },
  1161. renderImage: function renderImage(done) {
  1162. var _this3 = this;
  1163. var image = this.image,
  1164. imageData = this.imageData;
  1165. setStyle(image, assign({
  1166. width: imageData.width,
  1167. height: imageData.height,
  1168. // XXX: Not to use translateX/Y to avoid image shaking when zooming
  1169. marginLeft: imageData.x,
  1170. marginTop: imageData.y
  1171. }, getTransforms(imageData)));
  1172. if (done) {
  1173. if ((this.viewing || this.moving || this.rotating || this.scaling || this.zooming) && this.options.transition && hasClass(image, CLASS_TRANSITION)) {
  1174. var onTransitionEnd = function onTransitionEnd() {
  1175. _this3.imageRendering = false;
  1176. done();
  1177. };
  1178. this.imageRendering = {
  1179. abort: function abort() {
  1180. removeListener(image, EVENT_TRANSITION_END, onTransitionEnd);
  1181. }
  1182. };
  1183. addListener(image, EVENT_TRANSITION_END, onTransitionEnd, {
  1184. once: true
  1185. });
  1186. } else {
  1187. done();
  1188. }
  1189. }
  1190. },
  1191. resetImage: function resetImage() {
  1192. var image = this.image;
  1193. if (image) {
  1194. if (this.viewing) {
  1195. this.viewing.abort();
  1196. }
  1197. image.parentNode.removeChild(image);
  1198. this.image = null;
  1199. this.title.innerHTML = '';
  1200. }
  1201. }
  1202. };
  1203.  
  1204. var events = {
  1205. bind: function bind() {
  1206. var options = this.options,
  1207. viewer = this.viewer,
  1208. canvas = this.canvas;
  1209. var document = this.element.ownerDocument;
  1210. addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
  1211. addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
  1212. addListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
  1213. addListener(document, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
  1214. addListener(document, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
  1215. addListener(document, EVENT_KEY_DOWN, this.onKeyDown = this.keydown.bind(this));
  1216. addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));
  1217. if (options.zoomable && options.zoomOnWheel) {
  1218. addListener(viewer, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {
  1219. passive: false,
  1220. capture: true
  1221. });
  1222. }
  1223. if (options.toggleOnDblclick) {
  1224. addListener(canvas, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
  1225. }
  1226. },
  1227. unbind: function unbind() {
  1228. var options = this.options,
  1229. viewer = this.viewer,
  1230. canvas = this.canvas;
  1231. var document = this.element.ownerDocument;
  1232. removeListener(viewer, EVENT_CLICK, this.onClick);
  1233. removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
  1234. removeListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown);
  1235. removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove);
  1236. removeListener(document, EVENT_POINTER_UP, this.onPointerUp);
  1237. removeListener(document, EVENT_KEY_DOWN, this.onKeyDown);
  1238. removeListener(window, EVENT_RESIZE, this.onResize);
  1239. if (options.zoomable && options.zoomOnWheel) {
  1240. removeListener(viewer, EVENT_WHEEL, this.onWheel, {
  1241. passive: false,
  1242. capture: true
  1243. });
  1244. }
  1245. if (options.toggleOnDblclick) {
  1246. removeListener(canvas, EVENT_DBLCLICK, this.onDblclick);
  1247. }
  1248. }
  1249. };
  1250.  
  1251. var handlers = {
  1252. click: function click(event) {
  1253. var options = this.options,
  1254. imageData = this.imageData;
  1255. var target = event.target;
  1256. var action = getData(target, DATA_ACTION);
  1257. if (!action && target.localName === 'img' && target.parentElement.localName === 'li') {
  1258. target = target.parentElement;
  1259. action = getData(target, DATA_ACTION);
  1260. }
  1261.  
  1262. // Cancel the emulated click when the native click event was triggered.
  1263. if (IS_TOUCH_DEVICE && event.isTrusted && target === this.canvas) {
  1264. clearTimeout(this.clickCanvasTimeout);
  1265. }
  1266. switch (action) {
  1267. case 'mix':
  1268. if (this.played) {
  1269. this.stop();
  1270. } else if (options.inline) {
  1271. if (this.fulled) {
  1272. this.exit();
  1273. } else {
  1274. this.full();
  1275. }
  1276. } else {
  1277. this.hide();
  1278. }
  1279. break;
  1280. case 'hide':
  1281. if (!this.pointerMoved) {
  1282. this.hide();
  1283. }
  1284. break;
  1285. case 'view':
  1286. this.view(getData(target, 'index'));
  1287. break;
  1288. case 'zoom-in':
  1289. this.zoom(0.1, true);
  1290. break;
  1291. case 'zoom-out':
  1292. this.zoom(-0.1, true);
  1293. break;
  1294. case 'one-to-one':
  1295. this.toggle();
  1296. break;
  1297. case 'reset':
  1298. this.reset();
  1299. break;
  1300. case 'prev':
  1301. this.prev(options.loop);
  1302. break;
  1303. case 'play':
  1304. this.play(options.fullscreen);
  1305. break;
  1306. case 'next':
  1307. this.next(options.loop);
  1308. break;
  1309. case 'rotate-left':
  1310. this.rotate(-90);
  1311. break;
  1312. case 'rotate-right':
  1313. this.rotate(90);
  1314. break;
  1315. case 'flip-horizontal':
  1316. this.scaleX(-imageData.scaleX || -1);
  1317. break;
  1318. case 'flip-vertical':
  1319. this.scaleY(-imageData.scaleY || -1);
  1320. break;
  1321. default:
  1322. if (this.played) {
  1323. this.stop();
  1324. }
  1325. }
  1326. },
  1327. dblclick: function dblclick(event) {
  1328. event.preventDefault();
  1329. if (this.viewed && event.target === this.image) {
  1330. // Cancel the emulated double click when the native dblclick event was triggered.
  1331. if (IS_TOUCH_DEVICE && event.isTrusted) {
  1332. clearTimeout(this.doubleClickImageTimeout);
  1333. }
  1334.  
  1335. // XXX: No pageX/Y properties in custom event, fallback to the original event.
  1336. this.toggle(event.isTrusted ? event : event.detail && event.detail.originalEvent);
  1337. }
  1338. },
  1339. load: function load() {
  1340. var _this = this;
  1341. if (this.timeout) {
  1342. clearTimeout(this.timeout);
  1343. this.timeout = false;
  1344. }
  1345. var element = this.element,
  1346. options = this.options,
  1347. image = this.image,
  1348. index = this.index,
  1349. viewerData = this.viewerData;
  1350. removeClass(image, CLASS_INVISIBLE);
  1351. if (options.loading) {
  1352. removeClass(this.canvas, CLASS_LOADING);
  1353. }
  1354. image.style.cssText = 'height:0;' + "margin-left:".concat(viewerData.width / 2, "px;") + "margin-top:".concat(viewerData.height / 2, "px;") + 'max-width:none!important;' + 'position:relative;' + 'width:0;';
  1355. this.initImage(function () {
  1356. toggleClass(image, CLASS_MOVE, options.movable);
  1357. toggleClass(image, CLASS_TRANSITION, options.transition);
  1358. _this.renderImage(function () {
  1359. _this.viewed = true;
  1360. _this.viewing = false;
  1361. if (isFunction(options.viewed)) {
  1362. addListener(element, EVENT_VIEWED, options.viewed, {
  1363. once: true
  1364. });
  1365. }
  1366. dispatchEvent(element, EVENT_VIEWED, {
  1367. originalImage: _this.images[index],
  1368. index: index,
  1369. image: image
  1370. }, {
  1371. cancelable: false
  1372. });
  1373. });
  1374. });
  1375. },
  1376. loadImage: function loadImage(event) {
  1377. var image = event.target;
  1378. var parent = image.parentNode;
  1379. var parentWidth = parent.offsetWidth || 30;
  1380. var parentHeight = parent.offsetHeight || 50;
  1381. var filled = !!getData(image, 'filled');
  1382. getImageNaturalSizes(image, this.options, function (naturalWidth, naturalHeight) {
  1383. var aspectRatio = naturalWidth / naturalHeight;
  1384. var width = parentWidth;
  1385. var height = parentHeight;
  1386. if (parentHeight * aspectRatio > parentWidth) {
  1387. if (filled) {
  1388. width = parentHeight * aspectRatio;
  1389. } else {
  1390. height = parentWidth / aspectRatio;
  1391. }
  1392. } else if (filled) {
  1393. height = parentWidth / aspectRatio;
  1394. } else {
  1395. width = parentHeight * aspectRatio;
  1396. }
  1397. setStyle(image, assign({
  1398. width: width,
  1399. height: height
  1400. }, getTransforms({
  1401. translateX: (parentWidth - width) / 2,
  1402. translateY: (parentHeight - height) / 2
  1403. })));
  1404. });
  1405. },
  1406. keydown: function keydown(event) {
  1407. var options = this.options;
  1408. if (!options.keyboard) {
  1409. return;
  1410. }
  1411. var keyCode = event.keyCode || event.which || event.charCode;
  1412. switch (keyCode) {
  1413. // Enter
  1414. case 13:
  1415. if (this.viewer.contains(event.target)) {
  1416. this.click(event);
  1417. }
  1418. break;
  1419. }
  1420. if (!this.fulled) {
  1421. return;
  1422. }
  1423. switch (keyCode) {
  1424. // Escape
  1425. case 27:
  1426. if (this.played) {
  1427. this.stop();
  1428. } else if (options.inline) {
  1429. if (this.fulled) {
  1430. this.exit();
  1431. }
  1432. } else {
  1433. this.hide();
  1434. }
  1435. break;
  1436.  
  1437. // Space
  1438. case 32:
  1439. if (this.played) {
  1440. this.stop();
  1441. }
  1442. break;
  1443.  
  1444. // ArrowLeft
  1445. case 37:
  1446. if (this.played && this.playing) {
  1447. this.playing.prev();
  1448. } else {
  1449. this.prev(options.loop);
  1450. }
  1451. break;
  1452.  
  1453. // ArrowUp
  1454. case 38:
  1455. // Prevent scroll on Firefox
  1456. event.preventDefault();
  1457.  
  1458. // Zoom in
  1459. this.zoom(options.zoomRatio, true);
  1460. break;
  1461.  
  1462. // ArrowRight
  1463. case 39:
  1464. if (this.played && this.playing) {
  1465. this.playing.next();
  1466. } else {
  1467. this.next(options.loop);
  1468. }
  1469. break;
  1470.  
  1471. // ArrowDown
  1472. case 40:
  1473. // Prevent scroll on Firefox
  1474. event.preventDefault();
  1475.  
  1476. // Zoom out
  1477. this.zoom(-options.zoomRatio, true);
  1478. break;
  1479.  
  1480. // Ctrl + 0
  1481. case 48:
  1482. // Fall through
  1483.  
  1484. // Ctrl + 1
  1485. // eslint-disable-next-line no-fallthrough
  1486. case 49:
  1487. if (event.ctrlKey) {
  1488. event.preventDefault();
  1489. this.toggle();
  1490. }
  1491. break;
  1492. }
  1493. },
  1494. dragstart: function dragstart(event) {
  1495. if (event.target.localName === 'img') {
  1496. event.preventDefault();
  1497. }
  1498. },
  1499. pointerdown: function pointerdown(event) {
  1500. var options = this.options,
  1501. pointers = this.pointers;
  1502. var buttons = event.buttons,
  1503. button = event.button;
  1504. this.pointerMoved = false;
  1505. if (!this.viewed || this.showing || this.viewing || this.hiding
  1506.  
  1507. // Handle mouse event and pointer event and ignore touch event
  1508. || (event.type === 'mousedown' || event.type === 'pointerdown' && event.pointerType === 'mouse') && (
  1509. // No primary button (Usually the left button)
  1510. isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0
  1511.  
  1512. // Open context menu
  1513. || event.ctrlKey)) {
  1514. return;
  1515. }
  1516.  
  1517. // Prevent default behaviours as page zooming in touch devices.
  1518. event.preventDefault();
  1519. if (event.changedTouches) {
  1520. forEach(event.changedTouches, function (touch) {
  1521. pointers[touch.identifier] = getPointer(touch);
  1522. });
  1523. } else {
  1524. pointers[event.pointerId || 0] = getPointer(event);
  1525. }
  1526. var action = options.movable ? ACTION_MOVE : false;
  1527. if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) {
  1528. action = ACTION_ZOOM;
  1529. } else if (options.slideOnTouch && (event.pointerType === 'touch' || event.type === 'touchstart') && this.isSwitchable()) {
  1530. action = ACTION_SWITCH;
  1531. }
  1532. if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
  1533. removeClass(this.image, CLASS_TRANSITION);
  1534. }
  1535. this.action = action;
  1536. },
  1537. pointermove: function pointermove(event) {
  1538. var pointers = this.pointers,
  1539. action = this.action;
  1540. if (!this.viewed || !action) {
  1541. return;
  1542. }
  1543. event.preventDefault();
  1544. if (event.changedTouches) {
  1545. forEach(event.changedTouches, function (touch) {
  1546. assign(pointers[touch.identifier] || {}, getPointer(touch, true));
  1547. });
  1548. } else {
  1549. assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
  1550. }
  1551. this.change(event);
  1552. },
  1553. pointerup: function pointerup(event) {
  1554. var _this2 = this;
  1555. var options = this.options,
  1556. action = this.action,
  1557. pointers = this.pointers;
  1558. var pointer;
  1559. if (event.changedTouches) {
  1560. forEach(event.changedTouches, function (touch) {
  1561. pointer = pointers[touch.identifier];
  1562. delete pointers[touch.identifier];
  1563. });
  1564. } else {
  1565. pointer = pointers[event.pointerId || 0];
  1566. delete pointers[event.pointerId || 0];
  1567. }
  1568. if (!action) {
  1569. return;
  1570. }
  1571. event.preventDefault();
  1572. if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
  1573. addClass(this.image, CLASS_TRANSITION);
  1574. }
  1575. this.action = false;
  1576.  
  1577. // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
  1578. if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && pointer && Date.now() - pointer.timeStamp < 500) {
  1579. clearTimeout(this.clickCanvasTimeout);
  1580. clearTimeout(this.doubleClickImageTimeout);
  1581. if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
  1582. if (this.imageClicked) {
  1583. this.imageClicked = false;
  1584.  
  1585. // This timeout will be cleared later when a native dblclick event is triggering
  1586. this.doubleClickImageTimeout = setTimeout(function () {
  1587. dispatchEvent(_this2.image, EVENT_DBLCLICK, {
  1588. originalEvent: event
  1589. });
  1590. }, 50);
  1591. } else {
  1592. this.imageClicked = true;
  1593.  
  1594. // The default timing of a double click in Windows is 500 ms
  1595. this.doubleClickImageTimeout = setTimeout(function () {
  1596. _this2.imageClicked = false;
  1597. }, 500);
  1598. }
  1599. } else {
  1600. this.imageClicked = false;
  1601. if (options.backdrop && options.backdrop !== 'static' && event.target === this.canvas) {
  1602. // This timeout will be cleared later when a native click event is triggering
  1603. this.clickCanvasTimeout = setTimeout(function () {
  1604. dispatchEvent(_this2.canvas, EVENT_CLICK, {
  1605. originalEvent: event
  1606. });
  1607. }, 50);
  1608. }
  1609. }
  1610. }
  1611. },
  1612. resize: function resize() {
  1613. var _this3 = this;
  1614. if (!this.isShown || this.hiding) {
  1615. return;
  1616. }
  1617. if (this.fulled) {
  1618. this.close();
  1619. this.initBody();
  1620. this.open();
  1621. }
  1622. this.initContainer();
  1623. this.initViewer();
  1624. this.renderViewer();
  1625. this.renderList();
  1626. if (this.viewed) {
  1627. this.initImage(function () {
  1628. _this3.renderImage();
  1629. });
  1630. }
  1631. if (this.played) {
  1632. if (this.options.fullscreen && this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
  1633. this.stop();
  1634. return;
  1635. }
  1636. forEach(this.player.getElementsByTagName('img'), function (image) {
  1637. addListener(image, EVENT_LOAD, _this3.loadImage.bind(_this3), {
  1638. once: true
  1639. });
  1640. dispatchEvent(image, EVENT_LOAD);
  1641. });
  1642. }
  1643. },
  1644. wheel: function wheel(event) {
  1645. var _this4 = this;
  1646. if (!this.viewed) {
  1647. return;
  1648. }
  1649. event.preventDefault();
  1650.  
  1651. // Limit wheel speed to prevent zoom too fast
  1652. if (this.wheeling) {
  1653. return;
  1654. }
  1655. this.wheeling = true;
  1656. setTimeout(function () {
  1657. _this4.wheeling = false;
  1658. }, 50);
  1659. var ratio = Number(this.options.zoomRatio) || 0.1;
  1660. var delta = 1;
  1661. if (event.deltaY) {
  1662. delta = event.deltaY > 0 ? 1 : -1;
  1663. } else if (event.wheelDelta) {
  1664. delta = -event.wheelDelta / 120;
  1665. } else if (event.detail) {
  1666. delta = event.detail > 0 ? 1 : -1;
  1667. }
  1668. this.zoom(-delta * ratio, true, null, event);
  1669. }
  1670. };
  1671.  
  1672. var methods = {
  1673. /** Show the viewer (only available in modal mode)
  1674. * @param {boolean} [immediate=false] - Indicates if show the viewer immediately or not.
  1675. * @returns {Viewer} this
  1676. */
  1677. show: function show() {
  1678. var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1679. var element = this.element,
  1680. options = this.options;
  1681. if (options.inline || this.showing || this.isShown || this.showing) {
  1682. return this;
  1683. }
  1684. if (!this.ready) {
  1685. this.build();
  1686. if (this.ready) {
  1687. this.show(immediate);
  1688. }
  1689. return this;
  1690. }
  1691. if (isFunction(options.show)) {
  1692. addListener(element, EVENT_SHOW, options.show, {
  1693. once: true
  1694. });
  1695. }
  1696. if (dispatchEvent(element, EVENT_SHOW) === false || !this.ready) {
  1697. return this;
  1698. }
  1699. if (this.hiding) {
  1700. this.transitioning.abort();
  1701. }
  1702. this.showing = true;
  1703. this.open();
  1704. var viewer = this.viewer;
  1705. removeClass(viewer, CLASS_HIDE);
  1706. viewer.setAttribute('role', 'dialog');
  1707. viewer.setAttribute('aria-labelledby', this.title.id);
  1708. viewer.setAttribute('aria-modal', true);
  1709. viewer.removeAttribute('aria-hidden');
  1710. if (options.transition && !immediate) {
  1711. var shown = this.shown.bind(this);
  1712. this.transitioning = {
  1713. abort: function abort() {
  1714. removeListener(viewer, EVENT_TRANSITION_END, shown);
  1715. removeClass(viewer, CLASS_IN);
  1716. }
  1717. };
  1718. addClass(viewer, CLASS_TRANSITION);
  1719.  
  1720. // Force reflow to enable CSS3 transition
  1721. viewer.initialOffsetWidth = viewer.offsetWidth;
  1722. addListener(viewer, EVENT_TRANSITION_END, shown, {
  1723. once: true
  1724. });
  1725. addClass(viewer, CLASS_IN);
  1726. } else {
  1727. addClass(viewer, CLASS_IN);
  1728. this.shown();
  1729. }
  1730. return this;
  1731. },
  1732. /**
  1733. * Hide the viewer (only available in modal mode)
  1734. * @param {boolean} [immediate=false] - Indicates if hide the viewer immediately or not.
  1735. * @returns {Viewer} this
  1736. */
  1737. hide: function hide() {
  1738. var _this = this;
  1739. var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1740. var element = this.element,
  1741. options = this.options;
  1742. if (options.inline || this.hiding || !(this.isShown || this.showing)) {
  1743. return this;
  1744. }
  1745. if (isFunction(options.hide)) {
  1746. addListener(element, EVENT_HIDE, options.hide, {
  1747. once: true
  1748. });
  1749. }
  1750. if (dispatchEvent(element, EVENT_HIDE) === false) {
  1751. return this;
  1752. }
  1753. if (this.showing) {
  1754. this.transitioning.abort();
  1755. }
  1756. this.hiding = true;
  1757. if (this.played) {
  1758. this.stop();
  1759. } else if (this.viewing) {
  1760. this.viewing.abort();
  1761. }
  1762. var viewer = this.viewer,
  1763. image = this.image;
  1764. var hideImmediately = function hideImmediately() {
  1765. removeClass(viewer, CLASS_IN);
  1766. _this.hidden();
  1767. };
  1768. if (options.transition && !immediate) {
  1769. var onViewerTransitionEnd = function onViewerTransitionEnd(event) {
  1770. // Ignore all propagating `transitionend` events (#275).
  1771. if (event && event.target === viewer) {
  1772. removeListener(viewer, EVENT_TRANSITION_END, onViewerTransitionEnd);
  1773. _this.hidden();
  1774. }
  1775. };
  1776. var onImageTransitionEnd = function onImageTransitionEnd() {
  1777. // In case of show the viewer by `viewer.show(true)` previously (#407).
  1778. if (hasClass(viewer, CLASS_TRANSITION)) {
  1779. addListener(viewer, EVENT_TRANSITION_END, onViewerTransitionEnd);
  1780. removeClass(viewer, CLASS_IN);
  1781. } else {
  1782. hideImmediately();
  1783. }
  1784. };
  1785. this.transitioning = {
  1786. abort: function abort() {
  1787. if (_this.viewed && hasClass(image, CLASS_TRANSITION)) {
  1788. removeListener(image, EVENT_TRANSITION_END, onImageTransitionEnd);
  1789. } else if (hasClass(viewer, CLASS_TRANSITION)) {
  1790. removeListener(viewer, EVENT_TRANSITION_END, onViewerTransitionEnd);
  1791. }
  1792. }
  1793. };
  1794.  
  1795. // In case of hiding the viewer when holding on the image (#255),
  1796. // note that the `CLASS_TRANSITION` class will be removed on pointer down.
  1797. if (this.viewed && hasClass(image, CLASS_TRANSITION)) {
  1798. addListener(image, EVENT_TRANSITION_END, onImageTransitionEnd, {
  1799. once: true
  1800. });
  1801. this.zoomTo(0, false, null, null, true);
  1802. } else {
  1803. onImageTransitionEnd();
  1804. }
  1805. } else {
  1806. hideImmediately();
  1807. }
  1808. return this;
  1809. },
  1810. /**
  1811. * View one of the images with image's index
  1812. * @param {number} index - The index of the image to view.
  1813. * @returns {Viewer} this
  1814. */
  1815. view: function view() {
  1816. var _this2 = this;
  1817. var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.initialViewIndex;
  1818. index = Number(index) || 0;
  1819. if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) {
  1820. return this;
  1821. }
  1822. if (!this.isShown) {
  1823. this.index = index;
  1824. return this.show();
  1825. }
  1826. if (this.viewing) {
  1827. this.viewing.abort();
  1828. }
  1829. var element = this.element,
  1830. options = this.options,
  1831. title = this.title,
  1832. canvas = this.canvas;
  1833. var item = this.items[index];
  1834. var img = item.querySelector('img');
  1835. var url = getData(img, 'originalUrl');
  1836. var alt = img.getAttribute('alt');
  1837. var image = document.createElement('img');
  1838. forEach(options.inheritedAttributes, function (name) {
  1839. var value = img.getAttribute(name);
  1840. if (value !== null) {
  1841. image.setAttribute(name, value);
  1842. }
  1843. });
  1844. image.src = url;
  1845. image.alt = alt;
  1846. if (isFunction(options.view)) {
  1847. addListener(element, EVENT_VIEW, options.view, {
  1848. once: true
  1849. });
  1850. }
  1851. if (dispatchEvent(element, EVENT_VIEW, {
  1852. originalImage: this.images[index],
  1853. index: index,
  1854. image: image
  1855. }) === false || !this.isShown || this.hiding || this.played) {
  1856. return this;
  1857. }
  1858. var activeItem = this.items[this.index];
  1859. if (activeItem) {
  1860. removeClass(activeItem, CLASS_ACTIVE);
  1861. activeItem.removeAttribute('aria-selected');
  1862. }
  1863. addClass(item, CLASS_ACTIVE);
  1864. item.setAttribute('aria-selected', true);
  1865. if (options.focus) {
  1866. item.focus();
  1867. }
  1868. this.image = image;
  1869. this.viewed = false;
  1870. this.index = index;
  1871. this.imageData = {};
  1872. addClass(image, CLASS_INVISIBLE);
  1873. if (options.loading) {
  1874. addClass(canvas, CLASS_LOADING);
  1875. }
  1876. canvas.innerHTML = '';
  1877. canvas.appendChild(image);
  1878.  
  1879. // Center current item
  1880. this.renderList();
  1881.  
  1882. // Clear title
  1883. title.innerHTML = '';
  1884.  
  1885. // Generate title after viewed
  1886. var onViewed = function onViewed() {
  1887. var imageData = _this2.imageData;
  1888. var render = Array.isArray(options.title) ? options.title[1] : options.title;
  1889. title.innerHTML = escapeHTMLEntities(isFunction(render) ? render.call(_this2, image, imageData) : "".concat(alt, " (").concat(imageData.naturalWidth, " \xD7 ").concat(imageData.naturalHeight, ")"));
  1890. };
  1891. var onLoad;
  1892. var onError;
  1893. addListener(element, EVENT_VIEWED, onViewed, {
  1894. once: true
  1895. });
  1896. this.viewing = {
  1897. abort: function abort() {
  1898. removeListener(element, EVENT_VIEWED, onViewed);
  1899. if (image.complete) {
  1900. if (_this2.imageRendering) {
  1901. _this2.imageRendering.abort();
  1902. } else if (_this2.imageInitializing) {
  1903. _this2.imageInitializing.abort();
  1904. }
  1905. } else {
  1906. // Cancel download to save bandwidth.
  1907. image.src = '';
  1908. removeListener(image, EVENT_LOAD, onLoad);
  1909. if (_this2.timeout) {
  1910. clearTimeout(_this2.timeout);
  1911. }
  1912. }
  1913. }
  1914. };
  1915. if (image.complete) {
  1916. this.load();
  1917. } else {
  1918. addListener(image, EVENT_LOAD, onLoad = function onLoad() {
  1919. removeListener(image, EVENT_ERROR, onError);
  1920. _this2.load();
  1921. }, {
  1922. once: true
  1923. });
  1924. addListener(image, EVENT_ERROR, onError = function onError() {
  1925. removeListener(image, EVENT_LOAD, onLoad);
  1926. if (_this2.timeout) {
  1927. clearTimeout(_this2.timeout);
  1928. _this2.timeout = false;
  1929. }
  1930. removeClass(image, CLASS_INVISIBLE);
  1931. if (options.loading) {
  1932. removeClass(_this2.canvas, CLASS_LOADING);
  1933. }
  1934. }, {
  1935. once: true
  1936. });
  1937. if (this.timeout) {
  1938. clearTimeout(this.timeout);
  1939. }
  1940.  
  1941. // Make the image visible if it fails to load within 1s
  1942. this.timeout = setTimeout(function () {
  1943. removeClass(image, CLASS_INVISIBLE);
  1944. _this2.timeout = false;
  1945. }, 1000);
  1946. }
  1947. return this;
  1948. },
  1949. /**
  1950. * View the previous image
  1951. * @param {boolean} [loop=false] - Indicate if view the last one
  1952. * when it is the first one at present.
  1953. * @returns {Viewer} this
  1954. */
  1955. prev: function prev() {
  1956. var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1957. var index = this.index - 1;
  1958. if (index < 0) {
  1959. index = loop ? this.length - 1 : 0;
  1960. }
  1961. this.view(index);
  1962. return this;
  1963. },
  1964. /**
  1965. * View the next image
  1966. * @param {boolean} [loop=false] - Indicate if view the first one
  1967. * when it is the last one at present.
  1968. * @returns {Viewer} this
  1969. */
  1970. next: function next() {
  1971. var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1972. var maxIndex = this.length - 1;
  1973. var index = this.index + 1;
  1974. if (index > maxIndex) {
  1975. index = loop ? 0 : maxIndex;
  1976. }
  1977. this.view(index);
  1978. return this;
  1979. },
  1980. /**
  1981. * Move the image with relative offsets.
  1982. * @param {number} x - The moving distance in the horizontal direction.
  1983. * @param {number} [y=x] The moving distance in the vertical direction.
  1984. * @returns {Viewer} this
  1985. */
  1986. move: function move(x) {
  1987. var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
  1988. var imageData = this.imageData;
  1989. this.moveTo(isUndefined(x) ? x : imageData.x + Number(x), isUndefined(y) ? y : imageData.y + Number(y));
  1990. return this;
  1991. },
  1992. /**
  1993. * Move the image to an absolute point.
  1994. * @param {number} x - The new position in the horizontal direction.
  1995. * @param {number} [y=x] - The new position in the vertical direction.
  1996. * @param {Event} [_originalEvent=null] - The original event if any.
  1997. * @returns {Viewer} this
  1998. */
  1999. moveTo: function moveTo(x) {
  2000. var _this3 = this;
  2001. var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
  2002. var _originalEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  2003. var element = this.element,
  2004. options = this.options,
  2005. imageData = this.imageData;
  2006. x = Number(x);
  2007. y = Number(y);
  2008. if (this.viewed && !this.played && options.movable) {
  2009. var oldX = imageData.x;
  2010. var oldY = imageData.y;
  2011. var changed = false;
  2012. if (isNumber(x)) {
  2013. changed = true;
  2014. } else {
  2015. x = oldX;
  2016. }
  2017. if (isNumber(y)) {
  2018. changed = true;
  2019. } else {
  2020. y = oldY;
  2021. }
  2022. if (changed) {
  2023. if (isFunction(options.move)) {
  2024. addListener(element, EVENT_MOVE, options.move, {
  2025. once: true
  2026. });
  2027. }
  2028. if (dispatchEvent(element, EVENT_MOVE, {
  2029. x: x,
  2030. y: y,
  2031. oldX: oldX,
  2032. oldY: oldY,
  2033. originalEvent: _originalEvent
  2034. }) === false) {
  2035. return this;
  2036. }
  2037. imageData.x = x;
  2038. imageData.y = y;
  2039. imageData.left = x;
  2040. imageData.top = y;
  2041. this.moving = true;
  2042. this.renderImage(function () {
  2043. _this3.moving = false;
  2044. if (isFunction(options.moved)) {
  2045. addListener(element, EVENT_MOVED, options.moved, {
  2046. once: true
  2047. });
  2048. }
  2049. dispatchEvent(element, EVENT_MOVED, {
  2050. x: x,
  2051. y: y,
  2052. oldX: oldX,
  2053. oldY: oldY,
  2054. originalEvent: _originalEvent
  2055. }, {
  2056. cancelable: false
  2057. });
  2058. });
  2059. }
  2060. }
  2061. return this;
  2062. },
  2063. /**
  2064. * Rotate the image with a relative degree.
  2065. * @param {number} degree - The rotate degree.
  2066. * @returns {Viewer} this
  2067. */
  2068. rotate: function rotate(degree) {
  2069. this.rotateTo((this.imageData.rotate || 0) + Number(degree));
  2070. return this;
  2071. },
  2072. /**
  2073. * Rotate the image to an absolute degree.
  2074. * @param {number} degree - The rotate degree.
  2075. * @returns {Viewer} this
  2076. */
  2077. rotateTo: function rotateTo(degree) {
  2078. var _this4 = this;
  2079. var element = this.element,
  2080. options = this.options,
  2081. imageData = this.imageData;
  2082. degree = Number(degree);
  2083. if (isNumber(degree) && this.viewed && !this.played && options.rotatable) {
  2084. var oldDegree = imageData.rotate;
  2085. if (isFunction(options.rotate)) {
  2086. addListener(element, EVENT_ROTATE, options.rotate, {
  2087. once: true
  2088. });
  2089. }
  2090. if (dispatchEvent(element, EVENT_ROTATE, {
  2091. degree: degree,
  2092. oldDegree: oldDegree
  2093. }) === false) {
  2094. return this;
  2095. }
  2096. imageData.rotate = degree;
  2097. this.rotating = true;
  2098. this.renderImage(function () {
  2099. _this4.rotating = false;
  2100. if (isFunction(options.rotated)) {
  2101. addListener(element, EVENT_ROTATED, options.rotated, {
  2102. once: true
  2103. });
  2104. }
  2105. dispatchEvent(element, EVENT_ROTATED, {
  2106. degree: degree,
  2107. oldDegree: oldDegree
  2108. }, {
  2109. cancelable: false
  2110. });
  2111. });
  2112. }
  2113. return this;
  2114. },
  2115. /**
  2116. * Scale the image on the x-axis.
  2117. * @param {number} scaleX - The scale ratio on the x-axis.
  2118. * @returns {Viewer} this
  2119. */
  2120. scaleX: function scaleX(_scaleX) {
  2121. this.scale(_scaleX, this.imageData.scaleY);
  2122. return this;
  2123. },
  2124. /**
  2125. * Scale the image on the y-axis.
  2126. * @param {number} scaleY - The scale ratio on the y-axis.
  2127. * @returns {Viewer} this
  2128. */
  2129. scaleY: function scaleY(_scaleY) {
  2130. this.scale(this.imageData.scaleX, _scaleY);
  2131. return this;
  2132. },
  2133. /**
  2134. * Scale the image.
  2135. * @param {number} scaleX - The scale ratio on the x-axis.
  2136. * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
  2137. * @returns {Viewer} this
  2138. */
  2139. scale: function scale(scaleX) {
  2140. var _this5 = this;
  2141. var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;
  2142. var element = this.element,
  2143. options = this.options,
  2144. imageData = this.imageData;
  2145. scaleX = Number(scaleX);
  2146. scaleY = Number(scaleY);
  2147. if (this.viewed && !this.played && options.scalable) {
  2148. var oldScaleX = imageData.scaleX;
  2149. var oldScaleY = imageData.scaleY;
  2150. var changed = false;
  2151. if (isNumber(scaleX)) {
  2152. changed = true;
  2153. } else {
  2154. scaleX = oldScaleX;
  2155. }
  2156. if (isNumber(scaleY)) {
  2157. changed = true;
  2158. } else {
  2159. scaleY = oldScaleY;
  2160. }
  2161. if (changed) {
  2162. if (isFunction(options.scale)) {
  2163. addListener(element, EVENT_SCALE, options.scale, {
  2164. once: true
  2165. });
  2166. }
  2167. if (dispatchEvent(element, EVENT_SCALE, {
  2168. scaleX: scaleX,
  2169. scaleY: scaleY,
  2170. oldScaleX: oldScaleX,
  2171. oldScaleY: oldScaleY
  2172. }) === false) {
  2173. return this;
  2174. }
  2175. imageData.scaleX = scaleX;
  2176. imageData.scaleY = scaleY;
  2177. this.scaling = true;
  2178. this.renderImage(function () {
  2179. _this5.scaling = false;
  2180. if (isFunction(options.scaled)) {
  2181. addListener(element, EVENT_SCALED, options.scaled, {
  2182. once: true
  2183. });
  2184. }
  2185. dispatchEvent(element, EVENT_SCALED, {
  2186. scaleX: scaleX,
  2187. scaleY: scaleY,
  2188. oldScaleX: oldScaleX,
  2189. oldScaleY: oldScaleY
  2190. }, {
  2191. cancelable: false
  2192. });
  2193. });
  2194. }
  2195. }
  2196. return this;
  2197. },
  2198. /**
  2199. * Zoom the image with a relative ratio.
  2200. * @param {number} ratio - The target ratio.
  2201. * @param {boolean} [showTooltip=false] - Indicates whether to show the tooltip.
  2202. * @param {Object} [pivot] - The pivot point coordinate for zooming.
  2203. * @param {Event} [_originalEvent=null] - The original event if any.
  2204. * @returns {Viewer} this
  2205. */
  2206. zoom: function zoom(ratio) {
  2207. var showTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  2208. var pivot = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  2209. var _originalEvent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
  2210. var imageData = this.imageData;
  2211. ratio = Number(ratio);
  2212. if (ratio < 0) {
  2213. ratio = 1 / (1 - ratio);
  2214. } else {
  2215. ratio = 1 + ratio;
  2216. }
  2217. this.zoomTo(imageData.width * ratio / imageData.naturalWidth, showTooltip, pivot, _originalEvent);
  2218. return this;
  2219. },
  2220. /**
  2221. * Zoom the image to an absolute ratio.
  2222. * @param {number} ratio - The target ratio.
  2223. * @param {boolean} [showTooltip] - Indicates whether to show the tooltip.
  2224. * @param {Object} [pivot] - The pivot point coordinate for zooming.
  2225. * @param {Event} [_originalEvent=null] - The original event if any.
  2226. * @param {Event} [_zoomable=false] - Indicates if the current zoom is available or not.
  2227. * @returns {Viewer} this
  2228. */
  2229. zoomTo: function zoomTo(ratio) {
  2230. var _this6 = this;
  2231. var showTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  2232. var pivot = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  2233. var _originalEvent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
  2234. var _zoomable = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
  2235. var element = this.element,
  2236. options = this.options,
  2237. pointers = this.pointers,
  2238. imageData = this.imageData;
  2239. var x = imageData.x,
  2240. y = imageData.y,
  2241. width = imageData.width,
  2242. height = imageData.height,
  2243. naturalWidth = imageData.naturalWidth,
  2244. naturalHeight = imageData.naturalHeight;
  2245. ratio = Math.max(0, ratio);
  2246. if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
  2247. if (!_zoomable) {
  2248. var minZoomRatio = Math.max(0.01, options.minZoomRatio);
  2249. var maxZoomRatio = Math.min(100, options.maxZoomRatio);
  2250. ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
  2251. }
  2252. if (_originalEvent) {
  2253. switch (_originalEvent.type) {
  2254. case 'wheel':
  2255. if (options.zoomRatio >= 0.055 && ratio > 0.95 && ratio < 1.05) {
  2256. ratio = 1;
  2257. }
  2258. break;
  2259. case 'pointermove':
  2260. case 'touchmove':
  2261. case 'mousemove':
  2262. if (ratio > 0.99 && ratio < 1.01) {
  2263. ratio = 1;
  2264. }
  2265. break;
  2266. }
  2267. }
  2268. var newWidth = naturalWidth * ratio;
  2269. var newHeight = naturalHeight * ratio;
  2270. var offsetWidth = newWidth - width;
  2271. var offsetHeight = newHeight - height;
  2272. var oldRatio = imageData.ratio;
  2273. if (isFunction(options.zoom)) {
  2274. addListener(element, EVENT_ZOOM, options.zoom, {
  2275. once: true
  2276. });
  2277. }
  2278. if (dispatchEvent(element, EVENT_ZOOM, {
  2279. ratio: ratio,
  2280. oldRatio: oldRatio,
  2281. originalEvent: _originalEvent
  2282. }) === false) {
  2283. return this;
  2284. }
  2285. this.zooming = true;
  2286. if (_originalEvent) {
  2287. var offset = getOffset(this.viewer);
  2288. var center = pointers && Object.keys(pointers).length > 0 ? getPointersCenter(pointers) : {
  2289. pageX: _originalEvent.pageX,
  2290. pageY: _originalEvent.pageY
  2291. };
  2292.  
  2293. // Zoom from the triggering point of the event
  2294. imageData.x -= offsetWidth * ((center.pageX - offset.left - x) / width);
  2295. imageData.y -= offsetHeight * ((center.pageY - offset.top - y) / height);
  2296. } else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {
  2297. imageData.x -= offsetWidth * ((pivot.x - x) / width);
  2298. imageData.y -= offsetHeight * ((pivot.y - y) / height);
  2299. } else {
  2300. // Zoom from the center of the image
  2301. imageData.x -= offsetWidth / 2;
  2302. imageData.y -= offsetHeight / 2;
  2303. }
  2304. imageData.left = imageData.x;
  2305. imageData.top = imageData.y;
  2306. imageData.width = newWidth;
  2307. imageData.height = newHeight;
  2308. imageData.oldRatio = oldRatio;
  2309. imageData.ratio = ratio;
  2310. this.renderImage(function () {
  2311. _this6.zooming = false;
  2312. if (isFunction(options.zoomed)) {
  2313. addListener(element, EVENT_ZOOMED, options.zoomed, {
  2314. once: true
  2315. });
  2316. }
  2317. dispatchEvent(element, EVENT_ZOOMED, {
  2318. ratio: ratio,
  2319. oldRatio: oldRatio,
  2320. originalEvent: _originalEvent
  2321. }, {
  2322. cancelable: false
  2323. });
  2324. });
  2325. if (showTooltip) {
  2326. this.tooltip();
  2327. }
  2328. }
  2329. return this;
  2330. },
  2331. /**
  2332. * Play the images
  2333. * @param {boolean|FullscreenOptions} [fullscreen=false] - Indicate if request fullscreen or not.
  2334. * @returns {Viewer} this
  2335. */
  2336. play: function play() {
  2337. var _this7 = this;
  2338. var fullscreen = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  2339. if (!this.isShown || this.played) {
  2340. return this;
  2341. }
  2342. var element = this.element,
  2343. options = this.options;
  2344. if (isFunction(options.play)) {
  2345. addListener(element, EVENT_PLAY, options.play, {
  2346. once: true
  2347. });
  2348. }
  2349. if (dispatchEvent(element, EVENT_PLAY) === false) {
  2350. return this;
  2351. }
  2352. var player = this.player;
  2353. var onLoad = this.loadImage.bind(this);
  2354. var list = [];
  2355. var total = 0;
  2356. var index = 0;
  2357. this.played = true;
  2358. this.onLoadWhenPlay = onLoad;
  2359. if (fullscreen) {
  2360. this.requestFullscreen(fullscreen);
  2361. }
  2362. addClass(player, CLASS_SHOW);
  2363. forEach(this.items, function (item, i) {
  2364. var img = item.querySelector('img');
  2365. var image = document.createElement('img');
  2366. image.src = getData(img, 'originalUrl');
  2367. image.alt = img.getAttribute('alt');
  2368. image.referrerPolicy = img.referrerPolicy;
  2369. total += 1;
  2370. addClass(image, CLASS_FADE);
  2371. toggleClass(image, CLASS_TRANSITION, options.transition);
  2372. if (hasClass(item, CLASS_ACTIVE)) {
  2373. addClass(image, CLASS_IN);
  2374. index = i;
  2375. }
  2376. list.push(image);
  2377. addListener(image, EVENT_LOAD, onLoad, {
  2378. once: true
  2379. });
  2380. player.appendChild(image);
  2381. });
  2382. if (isNumber(options.interval) && options.interval > 0) {
  2383. var prev = function prev() {
  2384. clearTimeout(_this7.playing.timeout);
  2385. removeClass(list[index], CLASS_IN);
  2386. index -= 1;
  2387. index = index >= 0 ? index : total - 1;
  2388. addClass(list[index], CLASS_IN);
  2389. _this7.playing.timeout = setTimeout(prev, options.interval);
  2390. };
  2391. var next = function next() {
  2392. clearTimeout(_this7.playing.timeout);
  2393. removeClass(list[index], CLASS_IN);
  2394. index += 1;
  2395. index = index < total ? index : 0;
  2396. addClass(list[index], CLASS_IN);
  2397. _this7.playing.timeout = setTimeout(next, options.interval);
  2398. };
  2399. if (total > 1) {
  2400. this.playing = {
  2401. prev: prev,
  2402. next: next,
  2403. timeout: setTimeout(next, options.interval)
  2404. };
  2405. }
  2406. }
  2407. return this;
  2408. },
  2409. // Stop play
  2410. stop: function stop() {
  2411. var _this8 = this;
  2412. if (!this.played) {
  2413. return this;
  2414. }
  2415. var element = this.element,
  2416. options = this.options;
  2417. if (isFunction(options.stop)) {
  2418. addListener(element, EVENT_STOP, options.stop, {
  2419. once: true
  2420. });
  2421. }
  2422. if (dispatchEvent(element, EVENT_STOP) === false) {
  2423. return this;
  2424. }
  2425. var player = this.player;
  2426. clearTimeout(this.playing.timeout);
  2427. this.playing = false;
  2428. this.played = false;
  2429. forEach(player.getElementsByTagName('img'), function (image) {
  2430. removeListener(image, EVENT_LOAD, _this8.onLoadWhenPlay);
  2431. });
  2432. removeClass(player, CLASS_SHOW);
  2433. player.innerHTML = '';
  2434. this.exitFullscreen();
  2435. return this;
  2436. },
  2437. // Enter modal mode (only available in inline mode)
  2438. full: function full() {
  2439. var _this9 = this;
  2440. var options = this.options,
  2441. viewer = this.viewer,
  2442. image = this.image,
  2443. list = this.list;
  2444. if (!this.isShown || this.played || this.fulled || !options.inline) {
  2445. return this;
  2446. }
  2447. this.fulled = true;
  2448. this.open();
  2449. addClass(this.button, CLASS_FULLSCREEN_EXIT);
  2450. if (options.transition) {
  2451. removeClass(list, CLASS_TRANSITION);
  2452. if (this.viewed) {
  2453. removeClass(image, CLASS_TRANSITION);
  2454. }
  2455. }
  2456. addClass(viewer, CLASS_FIXED);
  2457. viewer.setAttribute('role', 'dialog');
  2458. viewer.setAttribute('aria-labelledby', this.title.id);
  2459. viewer.setAttribute('aria-modal', true);
  2460. viewer.removeAttribute('style');
  2461. setStyle(viewer, {
  2462. zIndex: options.zIndex
  2463. });
  2464. if (options.focus) {
  2465. this.enforceFocus();
  2466. }
  2467. this.initContainer();
  2468. this.viewerData = assign({}, this.containerData);
  2469. this.renderList();
  2470. if (this.viewed) {
  2471. this.initImage(function () {
  2472. _this9.renderImage(function () {
  2473. if (options.transition) {
  2474. setTimeout(function () {
  2475. addClass(image, CLASS_TRANSITION);
  2476. addClass(list, CLASS_TRANSITION);
  2477. }, 0);
  2478. }
  2479. });
  2480. });
  2481. }
  2482. return this;
  2483. },
  2484. // Exit modal mode (only available in inline mode)
  2485. exit: function exit() {
  2486. var _this10 = this;
  2487. var options = this.options,
  2488. viewer = this.viewer,
  2489. image = this.image,
  2490. list = this.list;
  2491. if (!this.isShown || this.played || !this.fulled || !options.inline) {
  2492. return this;
  2493. }
  2494. this.fulled = false;
  2495. this.close();
  2496. removeClass(this.button, CLASS_FULLSCREEN_EXIT);
  2497. if (options.transition) {
  2498. removeClass(list, CLASS_TRANSITION);
  2499. if (this.viewed) {
  2500. removeClass(image, CLASS_TRANSITION);
  2501. }
  2502. }
  2503. if (options.focus) {
  2504. this.clearEnforceFocus();
  2505. }
  2506. viewer.removeAttribute('role');
  2507. viewer.removeAttribute('aria-labelledby');
  2508. viewer.removeAttribute('aria-modal');
  2509. removeClass(viewer, CLASS_FIXED);
  2510. setStyle(viewer, {
  2511. zIndex: options.zIndexInline
  2512. });
  2513. this.viewerData = assign({}, this.parentData);
  2514. this.renderViewer();
  2515. this.renderList();
  2516. if (this.viewed) {
  2517. this.initImage(function () {
  2518. _this10.renderImage(function () {
  2519. if (options.transition) {
  2520. setTimeout(function () {
  2521. addClass(image, CLASS_TRANSITION);
  2522. addClass(list, CLASS_TRANSITION);
  2523. }, 0);
  2524. }
  2525. });
  2526. });
  2527. }
  2528. return this;
  2529. },
  2530. // Show the current ratio of the image with percentage
  2531. tooltip: function tooltip() {
  2532. var _this11 = this;
  2533. var options = this.options,
  2534. tooltipBox = this.tooltipBox,
  2535. imageData = this.imageData;
  2536. if (!this.viewed || this.played || !options.tooltip) {
  2537. return this;
  2538. }
  2539. tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
  2540. if (!this.tooltipping) {
  2541. if (options.transition) {
  2542. if (this.fading) {
  2543. dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
  2544. }
  2545. addClass(tooltipBox, CLASS_SHOW);
  2546. addClass(tooltipBox, CLASS_FADE);
  2547. addClass(tooltipBox, CLASS_TRANSITION);
  2548. tooltipBox.removeAttribute('aria-hidden');
  2549.  
  2550. // Force reflow to enable CSS3 transition
  2551. tooltipBox.initialOffsetWidth = tooltipBox.offsetWidth;
  2552. addClass(tooltipBox, CLASS_IN);
  2553. } else {
  2554. addClass(tooltipBox, CLASS_SHOW);
  2555. tooltipBox.removeAttribute('aria-hidden');
  2556. }
  2557. } else {
  2558. clearTimeout(this.tooltipping);
  2559. }
  2560. this.tooltipping = setTimeout(function () {
  2561. if (options.transition) {
  2562. addListener(tooltipBox, EVENT_TRANSITION_END, function () {
  2563. removeClass(tooltipBox, CLASS_SHOW);
  2564. removeClass(tooltipBox, CLASS_FADE);
  2565. removeClass(tooltipBox, CLASS_TRANSITION);
  2566. tooltipBox.setAttribute('aria-hidden', true);
  2567. _this11.fading = false;
  2568. }, {
  2569. once: true
  2570. });
  2571. removeClass(tooltipBox, CLASS_IN);
  2572. _this11.fading = true;
  2573. } else {
  2574. removeClass(tooltipBox, CLASS_SHOW);
  2575. tooltipBox.setAttribute('aria-hidden', true);
  2576. }
  2577. _this11.tooltipping = false;
  2578. }, 1000);
  2579. return this;
  2580. },
  2581. /**
  2582. * Toggle the image size between its current size and natural size
  2583. * @param {Event} [_originalEvent=null] - The original event if any.
  2584. * @returns {Viewer} this
  2585. */
  2586. toggle: function toggle() {
  2587. var _originalEvent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
  2588. if (this.imageData.ratio === 1) {
  2589. this.zoomTo(this.imageData.oldRatio, true, null, _originalEvent);
  2590. } else {
  2591. this.zoomTo(1, true, null, _originalEvent);
  2592. }
  2593. return this;
  2594. },
  2595. // Reset the image to its initial state
  2596. reset: function reset() {
  2597. if (this.viewed && !this.played) {
  2598. this.imageData = assign({}, this.initialImageData);
  2599. this.renderImage();
  2600. }
  2601. return this;
  2602. },
  2603. // Update viewer when images changed
  2604. update: function update() {
  2605. var _this12 = this;
  2606. var element = this.element,
  2607. options = this.options,
  2608. isImg = this.isImg;
  2609.  
  2610. // Destroy viewer if the target image was deleted
  2611. if (isImg && !element.parentNode) {
  2612. return this.destroy();
  2613. }
  2614. var images = [];
  2615. forEach(isImg ? [element] : element.querySelectorAll('img'), function (image) {
  2616. if (isFunction(options.filter)) {
  2617. if (options.filter.call(_this12, image)) {
  2618. images.push(image);
  2619. }
  2620. } else if (_this12.getImageURL(image)) {
  2621. images.push(image);
  2622. }
  2623. });
  2624. if (!images.length) {
  2625. return this;
  2626. }
  2627. this.images = images;
  2628. this.length = images.length;
  2629. if (this.ready) {
  2630. var changedIndexes = [];
  2631. forEach(this.items, function (item, i) {
  2632. var img = item.querySelector('img');
  2633. var image = images[i];
  2634. if (image && img) {
  2635. if (image.src !== img.src
  2636.  
  2637. // Title changed (#408)
  2638. || image.alt !== img.alt) {
  2639. changedIndexes.push(i);
  2640. }
  2641. } else {
  2642. changedIndexes.push(i);
  2643. }
  2644. });
  2645. setStyle(this.list, {
  2646. width: 'auto'
  2647. });
  2648. this.initList();
  2649. if (this.isShown) {
  2650. if (this.length) {
  2651. if (this.viewed) {
  2652. var changedIndex = changedIndexes.indexOf(this.index);
  2653. if (changedIndex >= 0) {
  2654. this.viewed = false;
  2655. this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
  2656. } else {
  2657. var activeItem = this.items[this.index];
  2658.  
  2659. // Reactivate the current viewing item after reset the list.
  2660. addClass(activeItem, CLASS_ACTIVE);
  2661. activeItem.setAttribute('aria-selected', true);
  2662. }
  2663. }
  2664. } else {
  2665. this.image = null;
  2666. this.viewed = false;
  2667. this.index = 0;
  2668. this.imageData = {};
  2669. this.canvas.innerHTML = '';
  2670. this.title.innerHTML = '';
  2671. }
  2672. }
  2673. } else {
  2674. this.build();
  2675. }
  2676. return this;
  2677. },
  2678. // Destroy the viewer
  2679. destroy: function destroy() {
  2680. var element = this.element,
  2681. options = this.options;
  2682. if (!element[NAMESPACE]) {
  2683. return this;
  2684. }
  2685. this.destroyed = true;
  2686. if (this.ready) {
  2687. if (this.played) {
  2688. this.stop();
  2689. }
  2690. if (options.inline) {
  2691. if (this.fulled) {
  2692. this.exit();
  2693. }
  2694. this.unbind();
  2695. } else if (this.isShown) {
  2696. if (this.viewing) {
  2697. if (this.imageRendering) {
  2698. this.imageRendering.abort();
  2699. } else if (this.imageInitializing) {
  2700. this.imageInitializing.abort();
  2701. }
  2702. }
  2703. if (this.hiding) {
  2704. this.transitioning.abort();
  2705. }
  2706. this.hidden();
  2707. } else if (this.showing) {
  2708. this.transitioning.abort();
  2709. this.hidden();
  2710. }
  2711. this.ready = false;
  2712. this.viewer.parentNode.removeChild(this.viewer);
  2713. } else if (options.inline) {
  2714. if (this.delaying) {
  2715. this.delaying.abort();
  2716. } else if (this.initializing) {
  2717. this.initializing.abort();
  2718. }
  2719. }
  2720. if (!options.inline) {
  2721. removeListener(element, EVENT_CLICK, this.onStart);
  2722. }
  2723. element[NAMESPACE] = undefined;
  2724. return this;
  2725. }
  2726. };
  2727.  
  2728. var others = {
  2729. getImageURL: function getImageURL(image) {
  2730. var url = this.options.url;
  2731. if (isString(url)) {
  2732. url = image.getAttribute(url);
  2733. } else if (isFunction(url)) {
  2734. url = url.call(this, image);
  2735. } else {
  2736. url = '';
  2737. }
  2738. return url;
  2739. },
  2740. enforceFocus: function enforceFocus() {
  2741. var _this = this;
  2742. this.clearEnforceFocus();
  2743. addListener(document, EVENT_FOCUSIN, this.onFocusin = function (event) {
  2744. var viewer = _this.viewer;
  2745. var target = event.target;
  2746. if (target === document || target === viewer || viewer.contains(target)) {
  2747. return;
  2748. }
  2749. while (target) {
  2750. // Avoid conflicts with other modals (#474, #540)
  2751. if (target.getAttribute('tabindex') !== null || target.getAttribute('aria-modal') === 'true') {
  2752. return;
  2753. }
  2754. target = target.parentElement;
  2755. }
  2756. viewer.focus();
  2757. });
  2758. },
  2759. clearEnforceFocus: function clearEnforceFocus() {
  2760. if (this.onFocusin) {
  2761. removeListener(document, EVENT_FOCUSIN, this.onFocusin);
  2762. this.onFocusin = null;
  2763. }
  2764. },
  2765. open: function open() {
  2766. var body = this.body;
  2767. addClass(body, CLASS_OPEN);
  2768. if (this.scrollbarWidth > 0) {
  2769. body.style.paddingRight = "".concat(this.scrollbarWidth + (parseFloat(this.initialBodyComputedPaddingRight) || 0), "px");
  2770. }
  2771. },
  2772. close: function close() {
  2773. var body = this.body;
  2774. removeClass(body, CLASS_OPEN);
  2775. if (this.scrollbarWidth > 0) {
  2776. body.style.paddingRight = this.initialBodyPaddingRight;
  2777. }
  2778. },
  2779. shown: function shown() {
  2780. var element = this.element,
  2781. options = this.options,
  2782. viewer = this.viewer;
  2783. this.fulled = true;
  2784. this.isShown = true;
  2785. this.render();
  2786. this.bind();
  2787. this.showing = false;
  2788. if (options.focus) {
  2789. viewer.focus();
  2790. this.enforceFocus();
  2791. }
  2792. if (isFunction(options.shown)) {
  2793. addListener(element, EVENT_SHOWN, options.shown, {
  2794. once: true
  2795. });
  2796. }
  2797. if (dispatchEvent(element, EVENT_SHOWN) === false) {
  2798. return;
  2799. }
  2800. if (this.ready && this.isShown && !this.hiding) {
  2801. this.view(this.index);
  2802. }
  2803. },
  2804. hidden: function hidden() {
  2805. var element = this.element,
  2806. options = this.options,
  2807. viewer = this.viewer;
  2808. if (options.fucus) {
  2809. this.clearEnforceFocus();
  2810. }
  2811. this.close();
  2812. this.unbind();
  2813. addClass(viewer, CLASS_HIDE);
  2814. viewer.removeAttribute('role');
  2815. viewer.removeAttribute('aria-labelledby');
  2816. viewer.removeAttribute('aria-modal');
  2817. viewer.setAttribute('aria-hidden', true);
  2818. this.resetList();
  2819. this.resetImage();
  2820. this.fulled = false;
  2821. this.viewed = false;
  2822. this.isShown = false;
  2823. this.hiding = false;
  2824. if (!this.destroyed) {
  2825. if (isFunction(options.hidden)) {
  2826. addListener(element, EVENT_HIDDEN, options.hidden, {
  2827. once: true
  2828. });
  2829. }
  2830. dispatchEvent(element, EVENT_HIDDEN, null, {
  2831. cancelable: false
  2832. });
  2833. }
  2834. },
  2835. requestFullscreen: function requestFullscreen(options) {
  2836. var document = this.element.ownerDocument;
  2837. if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
  2838. var documentElement = document.documentElement;
  2839.  
  2840. // Element.requestFullscreen()
  2841. if (documentElement.requestFullscreen) {
  2842. // Avoid TypeError when convert `options` to dictionary
  2843. if (isPlainObject(options)) {
  2844. documentElement.requestFullscreen(options);
  2845. } else {
  2846. documentElement.requestFullscreen();
  2847. }
  2848. } else if (documentElement.webkitRequestFullscreen) {
  2849. documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  2850. } else if (documentElement.mozRequestFullScreen) {
  2851. documentElement.mozRequestFullScreen();
  2852. } else if (documentElement.msRequestFullscreen) {
  2853. documentElement.msRequestFullscreen();
  2854. }
  2855. }
  2856. },
  2857. exitFullscreen: function exitFullscreen() {
  2858. var document = this.element.ownerDocument;
  2859. if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
  2860. // Document.exitFullscreen()
  2861. if (document.exitFullscreen) {
  2862. document.exitFullscreen();
  2863. } else if (document.webkitExitFullscreen) {
  2864. document.webkitExitFullscreen();
  2865. } else if (document.mozCancelFullScreen) {
  2866. document.mozCancelFullScreen();
  2867. } else if (document.msExitFullscreen) {
  2868. document.msExitFullscreen();
  2869. }
  2870. }
  2871. },
  2872. change: function change(event) {
  2873. var options = this.options,
  2874. pointers = this.pointers;
  2875. var pointer = pointers[Object.keys(pointers)[0]];
  2876.  
  2877. // In the case of the `pointers` object is empty (#421)
  2878. if (!pointer) {
  2879. return;
  2880. }
  2881. var offsetX = pointer.endX - pointer.startX;
  2882. var offsetY = pointer.endY - pointer.startY;
  2883. switch (this.action) {
  2884. // Move the current image
  2885. case ACTION_MOVE:
  2886. if (offsetX !== 0 || offsetY !== 0) {
  2887. this.pointerMoved = true;
  2888. this.move(offsetX, offsetY, event);
  2889. }
  2890. break;
  2891.  
  2892. // Zoom the current image
  2893. case ACTION_ZOOM:
  2894. this.zoom(getMaxZoomRatio(pointers), false, null, event);
  2895. break;
  2896. case ACTION_SWITCH:
  2897. {
  2898. this.action = 'switched';
  2899. var absoluteOffsetX = Math.abs(offsetX);
  2900. if (absoluteOffsetX > 1 && absoluteOffsetX > Math.abs(offsetY)) {
  2901. // Empty `pointers` as `touchend` event will not be fired after swiped in iOS browsers.
  2902. this.pointers = {};
  2903. if (offsetX > 1) {
  2904. this.prev(options.loop);
  2905. } else if (offsetX < -1) {
  2906. this.next(options.loop);
  2907. }
  2908. }
  2909. break;
  2910. }
  2911. }
  2912.  
  2913. // Override
  2914. forEach(pointers, function (p) {
  2915. p.startX = p.endX;
  2916. p.startY = p.endY;
  2917. });
  2918. },
  2919. isSwitchable: function isSwitchable() {
  2920. var imageData = this.imageData,
  2921. viewerData = this.viewerData;
  2922. return this.length > 1 && imageData.x >= 0 && imageData.y >= 0 && imageData.width <= viewerData.width && imageData.height <= viewerData.height;
  2923. }
  2924. };
  2925.  
  2926. var AnotherViewer = WINDOW.Viewer;
  2927. var getUniqueID = function (id) {
  2928. return function () {
  2929. id += 1;
  2930. return id;
  2931. };
  2932. }(-1);
  2933. var Viewer = /*#__PURE__*/function () {
  2934. /**
  2935. * Create a new Viewer.
  2936. * @param {Element} element - The target element for viewing.
  2937. * @param {Object} [options={}] - The configuration options.
  2938. */
  2939. function Viewer(element) {
  2940. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  2941. _classCallCheck(this, Viewer);
  2942. if (!element || element.nodeType !== 1) {
  2943. throw new Error('The first argument is required and must be an element.');
  2944. }
  2945. this.element = element;
  2946. this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
  2947. this.action = false;
  2948. this.fading = false;
  2949. this.fulled = false;
  2950. this.hiding = false;
  2951. this.imageClicked = false;
  2952. this.imageData = {};
  2953. this.index = this.options.initialViewIndex;
  2954. this.isImg = false;
  2955. this.isShown = false;
  2956. this.length = 0;
  2957. this.moving = false;
  2958. this.played = false;
  2959. this.playing = false;
  2960. this.pointers = {};
  2961. this.ready = false;
  2962. this.rotating = false;
  2963. this.scaling = false;
  2964. this.showing = false;
  2965. this.timeout = false;
  2966. this.tooltipping = false;
  2967. this.viewed = false;
  2968. this.viewing = false;
  2969. this.wheeling = false;
  2970. this.zooming = false;
  2971. this.pointerMoved = false;
  2972. this.id = getUniqueID();
  2973. this.init();
  2974. }
  2975. _createClass(Viewer, [{
  2976. key: "init",
  2977. value: function init() {
  2978. var _this = this;
  2979. var element = this.element,
  2980. options = this.options;
  2981. if (element[NAMESPACE]) {
  2982. return;
  2983. }
  2984. element[NAMESPACE] = this;
  2985.  
  2986. // The `focus` option requires the `keyboard` option set to `true`.
  2987. if (options.focus && !options.keyboard) {
  2988. options.focus = false;
  2989. }
  2990. var isImg = element.localName === 'img';
  2991. var images = [];
  2992. forEach(isImg ? [element] : element.querySelectorAll('img'), function (image) {
  2993. if (isFunction(options.filter)) {
  2994. if (options.filter.call(_this, image)) {
  2995. images.push(image);
  2996. }
  2997. } else if (_this.getImageURL(image)) {
  2998. images.push(image);
  2999. }
  3000. });
  3001. this.isImg = isImg;
  3002. this.length = images.length;
  3003. this.images = images;
  3004. this.initBody();
  3005.  
  3006. // Override `transition` option if it is not supported
  3007. if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
  3008. options.transition = false;
  3009. }
  3010. if (options.inline) {
  3011. var count = 0;
  3012. var progress = function progress() {
  3013. count += 1;
  3014. if (count === _this.length) {
  3015. var timeout;
  3016. _this.initializing = false;
  3017. _this.delaying = {
  3018. abort: function abort() {
  3019. clearTimeout(timeout);
  3020. }
  3021. };
  3022.  
  3023. // build asynchronously to keep `this.viewer` is accessible in `ready` event handler.
  3024. timeout = setTimeout(function () {
  3025. _this.delaying = false;
  3026. _this.build();
  3027. }, 0);
  3028. }
  3029. };
  3030. this.initializing = {
  3031. abort: function abort() {
  3032. forEach(images, function (image) {
  3033. if (!image.complete) {
  3034. removeListener(image, EVENT_LOAD, progress);
  3035. removeListener(image, EVENT_ERROR, progress);
  3036. }
  3037. });
  3038. }
  3039. };
  3040. forEach(images, function (image) {
  3041. if (image.complete) {
  3042. progress();
  3043. } else {
  3044. var onLoad;
  3045. var onError;
  3046. addListener(image, EVENT_LOAD, onLoad = function onLoad() {
  3047. removeListener(image, EVENT_ERROR, onError);
  3048. progress();
  3049. }, {
  3050. once: true
  3051. });
  3052. addListener(image, EVENT_ERROR, onError = function onError() {
  3053. removeListener(image, EVENT_LOAD, onLoad);
  3054. progress();
  3055. }, {
  3056. once: true
  3057. });
  3058. }
  3059. });
  3060. } else {
  3061. addListener(element, EVENT_CLICK, this.onStart = function (_ref) {
  3062. var target = _ref.target;
  3063. if (target.localName === 'img' && (!isFunction(options.filter) || options.filter.call(_this, target))) {
  3064. _this.view(_this.images.indexOf(target));
  3065. }
  3066. });
  3067. }
  3068. }
  3069. }, {
  3070. key: "build",
  3071. value: function build() {
  3072. if (this.ready) {
  3073. return;
  3074. }
  3075. var element = this.element,
  3076. options = this.options;
  3077. var parent = element.parentNode;
  3078. var template = document.createElement('div');
  3079. template.innerHTML = TEMPLATE;
  3080. var viewer = template.querySelector(".".concat(NAMESPACE, "-container"));
  3081. var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
  3082. var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
  3083. var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
  3084. var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
  3085. var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
  3086. this.parent = parent;
  3087. this.viewer = viewer;
  3088. this.title = title;
  3089. this.toolbar = toolbar;
  3090. this.navbar = navbar;
  3091. this.button = button;
  3092. this.canvas = canvas;
  3093. this.footer = viewer.querySelector(".".concat(NAMESPACE, "-footer"));
  3094. this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
  3095. this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
  3096. this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
  3097. viewer.id = "".concat(NAMESPACE).concat(this.id);
  3098. title.id = "".concat(NAMESPACE, "Title").concat(this.id);
  3099. addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
  3100. addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
  3101. toggleClass(button, CLASS_HIDE, !options.button);
  3102. if (options.keyboard) {
  3103. button.setAttribute('tabindex', 0);
  3104. }
  3105. if (options.backdrop) {
  3106. addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
  3107. if (!options.inline && options.backdrop !== 'static') {
  3108. setData(canvas, DATA_ACTION, 'hide');
  3109. }
  3110. }
  3111. if (isString(options.className) && options.className) {
  3112. // In case there are multiple class names
  3113. options.className.split(REGEXP_SPACES).forEach(function (className) {
  3114. addClass(viewer, className);
  3115. });
  3116. }
  3117. if (options.toolbar) {
  3118. var list = document.createElement('ul');
  3119. var custom = isPlainObject(options.toolbar);
  3120. var zoomButtons = BUTTONS.slice(0, 3);
  3121. var rotateButtons = BUTTONS.slice(7, 9);
  3122. var scaleButtons = BUTTONS.slice(9);
  3123. if (!custom) {
  3124. addClass(toolbar, getResponsiveClass(options.toolbar));
  3125. }
  3126. forEach(custom ? options.toolbar : BUTTONS, function (value, index) {
  3127. var deep = custom && isPlainObject(value);
  3128. var name = custom ? hyphenate(index) : value;
  3129. var show = deep && !isUndefined(value.show) ? value.show : value;
  3130. if (!show || !options.zoomable && zoomButtons.indexOf(name) !== -1 || !options.rotatable && rotateButtons.indexOf(name) !== -1 || !options.scalable && scaleButtons.indexOf(name) !== -1) {
  3131. return;
  3132. }
  3133. var size = deep && !isUndefined(value.size) ? value.size : value;
  3134. var click = deep && !isUndefined(value.click) ? value.click : value;
  3135. var item = document.createElement('li');
  3136. if (options.keyboard) {
  3137. item.setAttribute('tabindex', 0);
  3138. }
  3139. item.setAttribute('role', 'button');
  3140. addClass(item, "".concat(NAMESPACE, "-").concat(name));
  3141. if (!isFunction(click)) {
  3142. setData(item, DATA_ACTION, name);
  3143. }
  3144. if (isNumber(show)) {
  3145. addClass(item, getResponsiveClass(show));
  3146. }
  3147. if (['small', 'large'].indexOf(size) !== -1) {
  3148. addClass(item, "".concat(NAMESPACE, "-").concat(size));
  3149. } else if (name === 'play') {
  3150. addClass(item, "".concat(NAMESPACE, "-large"));
  3151. }
  3152. if (isFunction(click)) {
  3153. addListener(item, EVENT_CLICK, click);
  3154. }
  3155. list.appendChild(item);
  3156. });
  3157. toolbar.appendChild(list);
  3158. } else {
  3159. addClass(toolbar, CLASS_HIDE);
  3160. }
  3161. if (!options.rotatable) {
  3162. var rotates = toolbar.querySelectorAll('li[class*="rotate"]');
  3163. addClass(rotates, CLASS_INVISIBLE);
  3164. forEach(rotates, function (rotate) {
  3165. toolbar.appendChild(rotate);
  3166. });
  3167. }
  3168. if (options.inline) {
  3169. addClass(button, CLASS_FULLSCREEN);
  3170. setStyle(viewer, {
  3171. zIndex: options.zIndexInline
  3172. });
  3173. if (window.getComputedStyle(parent).position === 'static') {
  3174. setStyle(parent, {
  3175. position: 'relative'
  3176. });
  3177. }
  3178. parent.insertBefore(viewer, element.nextSibling);
  3179. } else {
  3180. addClass(button, CLASS_CLOSE);
  3181. addClass(viewer, CLASS_FIXED);
  3182. addClass(viewer, CLASS_FADE);
  3183. addClass(viewer, CLASS_HIDE);
  3184. setStyle(viewer, {
  3185. zIndex: options.zIndex
  3186. });
  3187. var container = options.container;
  3188. if (isString(container)) {
  3189. container = element.ownerDocument.querySelector(container);
  3190. }
  3191. if (!container) {
  3192. container = this.body;
  3193. }
  3194. container.appendChild(viewer);
  3195. }
  3196. if (options.inline) {
  3197. this.render();
  3198. this.bind();
  3199. this.isShown = true;
  3200. }
  3201. this.ready = true;
  3202. if (isFunction(options.ready)) {
  3203. addListener(element, EVENT_READY, options.ready, {
  3204. once: true
  3205. });
  3206. }
  3207. if (dispatchEvent(element, EVENT_READY) === false) {
  3208. this.ready = false;
  3209. return;
  3210. }
  3211. if (this.ready && options.inline) {
  3212. this.view(this.index);
  3213. }
  3214. }
  3215.  
  3216. /**
  3217. * Get the no conflict viewer class.
  3218. * @returns {Viewer} The viewer class.
  3219. */
  3220. }], [{
  3221. key: "noConflict",
  3222. value: function noConflict() {
  3223. window.Viewer = AnotherViewer;
  3224. return Viewer;
  3225. }
  3226.  
  3227. /**
  3228. * Change the default options.
  3229. * @param {Object} options - The new default options.
  3230. */
  3231. }, {
  3232. key: "setDefaults",
  3233. value: function setDefaults(options) {
  3234. assign(DEFAULTS, isPlainObject(options) && options);
  3235. }
  3236. }]);
  3237. return Viewer;
  3238. }();
  3239. assign(Viewer.prototype, render, events, handlers, methods, others);
  3240.  
  3241. return Viewer;
  3242.  
  3243. }));