Viewer

强大的图片查看器插件Viewer.js

لا ينبغي أن لا يتم تثبيت هذا السكريت مباشرة. هو مكتبة لسكبتات لتشمل مع التوجيه الفوقية // @require https://update.greatest.deepsurf.us/scripts/449471/1503381/Viewer.js

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