Viewer

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

目前為 2024-01-03 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/449471/1305483/Viewer.js

  1. /*!
  2. * Viewer.js v1.11.6
  3. * https://fengyuanchen.github.io/viewerjs
  4. *
  5. * Copyright 2015-present Chen Fengyuan
  6. * Released under the MIT license
  7. *
  8. * Date: 2023-09-17T03:16:35.830Z
  9. */
  10. (function () {
  11. let cssResource = `
  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/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAAUCAYAAABWOyJDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAQPSURBVHic7Zs/iFxVFMa/0U2UaJGksUgnIVhYxVhpjDbZCBmLdAYECxsRFBTUamcXUiSNncgKQbSxsxH8gzAP3FU2jY0kKKJNiiiIghFlccnP4p3nPCdv3p9778vsLOcHB2bfveeb7955c3jvvNkBIMdxnD64a94GHMfZu3iBcRynN7zAOI7TG15gHCeeNUkr8zaxG2lbYDYsdgMbktBsP03jdQwljSXdtBhLOmtjowC9Mg9L+knSlcD8TNKpSA9lBpK2JF2VdDSR5n5J64m0qli399hNFMUlpshQii5jbXTbHGviB0nLNeNDSd9VO4A2UdB2fp+x0eCnaXxWXGA2X0au/3HgN9P4LFCjIANOJdrLr0zzZ+BEpNYDwKbpnQMeAw4m8HjQtM6Z9qa917zPQwFr3M5KgA6J5rTJCdFZJj9/lyvGhsDvwFNVuV2MhhjrK6b9bFiE+j1r87eBl4HDwCF7/U/k+ofAX5b/EXBv5JoLMuILzf3Ap6Z3EzgdqHMCuF7hcQf4HDgeoHnccncqdK/TvSDWffFXI/exICY/xZyqc6XLWF1UFZna4gJ7q8BsRvgd2/xXpo6P+D9dfT7PpECtA3cnWPM0GXGFZh/wgWltA+cDNC7X+AP4GzjZQe+k5dRxuYPeiuXU7e1qwLpDz7dFjXKRaSwuMLvAlG8zZlG+YmiK1HoFqT7wP2z+4Q45TfEGcMt01xLoNZEBTwRqD4BLpnMLeC1A41UmVxsXgXeBayV/Wx20rpTyrpnWRft7p6O/FdqzGrDukPNtkaMoMo3FBdBSQMOnYBCReyf05s126fU9ytfX98+mY54Kxnp7S9K3kj6U9KYdG0h6UdLbkh7poFXMfUnSOyVvL0h6VtIXHbS6nOP+s/Zm9mvyXW1uuC9ohZ72E9uDmXWLJOB1GxsH+DxPftsB8B6wlGDN02TAkxG6+4D3TWsbeC5CS8CDFce+AW500LhhOW2020TRjK3b21HEmgti9m0RonxbdMZeVzV+/4tF3cBpP7E9mKHNL5q8h5g0eYsCMQz0epq8gQrwMXAgcs0FGXGFRcB9wCemF9PkbYqM/Bas7fxLwNeJPdTdpo4itQti8lPMqTpXuozVRVXPpbHI3KkNTB1NfkL81j2mvhDp91HgV9MKuRIqrykj3WPq4rHyL+axj8/qGPmTqi6F9YDlHOvJU6oYcTsh/TYSzWmTE6JT19CtLTJt32D6CmHe0eQn1O8z5AXgT4sx4Vcu0/EQecMydB8z0hUWkTd2t4CrwNEePqMBcAR4mrBbwyXLPWJa8zrXmmLEhNBmfpkuY2102xxrih+pb+ieAb6vGhuA97UcJ5KR8gZ77K+99xxeYBzH6Q3/Z0fHcXrDC4zjOL3hBcZxnN74F+zlvXFWXF9PAAAAAElFTkSuQmCC');
  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.6
  473. * https://fengyuanchen.github.io/viewerjs
  474. *
  475. * Copyright 2015-present Chen Fengyuan
  476. * Released under the MIT license
  477. *
  478. * Date: 2023-09-17T03:16:38.052Z
  479. */
  480.  
  481. (function (global, factory) {
  482. typeof exports === "object" && typeof module !== "undefined"
  483. ? (module.exports = factory())
  484. : typeof define === "function" && define.amd
  485. ? define(factory)
  486. : ((global =
  487. typeof globalThis !== "undefined" ? globalThis : global || self),
  488. (global.Viewer = factory()));
  489. })(this, function () {
  490. "use strict";
  491.  
  492. function ownKeys(e, r) {
  493. var t = Object.keys(e);
  494. if (Object.getOwnPropertySymbols) {
  495. var o = Object.getOwnPropertySymbols(e);
  496. r &&
  497. (o = o.filter(function (r) {
  498. return Object.getOwnPropertyDescriptor(e, r).enumerable;
  499. })),
  500. t.push.apply(t, o);
  501. }
  502. return t;
  503. }
  504. function _objectSpread2(e) {
  505. for (var r = 1; r < arguments.length; r++) {
  506. var t = null != arguments[r] ? arguments[r] : {};
  507. r % 2
  508. ? ownKeys(Object(t), !0).forEach(function (r) {
  509. _defineProperty(e, r, t[r]);
  510. })
  511. : Object.getOwnPropertyDescriptors
  512. ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t))
  513. : ownKeys(Object(t)).forEach(function (r) {
  514. Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
  515. });
  516. }
  517. return e;
  518. }
  519. function _typeof(o) {
  520. "@babel/helpers - typeof";
  521.  
  522. return (
  523. (_typeof =
  524. "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
  525. ? function (o) {
  526. return typeof o;
  527. }
  528. : function (o) {
  529. return o &&
  530. "function" == typeof Symbol &&
  531. o.constructor === Symbol &&
  532. o !== Symbol.prototype
  533. ? "symbol"
  534. : typeof o;
  535. }),
  536. _typeof(o)
  537. );
  538. }
  539. function _classCallCheck(instance, Constructor) {
  540. if (!(instance instanceof Constructor)) {
  541. throw new TypeError("Cannot call a class as a function");
  542. }
  543. }
  544. function _defineProperties(target, props) {
  545. for (var i = 0; i < props.length; i++) {
  546. var descriptor = props[i];
  547. descriptor.enumerable = descriptor.enumerable || false;
  548. descriptor.configurable = true;
  549. if ("value" in descriptor) descriptor.writable = true;
  550. Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
  551. }
  552. }
  553. function _createClass(Constructor, protoProps, staticProps) {
  554. if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  555. if (staticProps) _defineProperties(Constructor, staticProps);
  556. Object.defineProperty(Constructor, "prototype", {
  557. writable: false,
  558. });
  559. return Constructor;
  560. }
  561. function _defineProperty(obj, key, value) {
  562. key = _toPropertyKey(key);
  563. if (key in obj) {
  564. Object.defineProperty(obj, key, {
  565. value: value,
  566. enumerable: true,
  567. configurable: true,
  568. writable: true,
  569. });
  570. } else {
  571. obj[key] = value;
  572. }
  573. return obj;
  574. }
  575. function _toPrimitive(input, hint) {
  576. if (typeof input !== "object" || input === null) return input;
  577. var prim = input[Symbol.toPrimitive];
  578. if (prim !== undefined) {
  579. var res = prim.call(input, hint || "default");
  580. if (typeof res !== "object") return res;
  581. throw new TypeError("@@toPrimitive must return a primitive value.");
  582. }
  583. return (hint === "string" ? String : Number)(input);
  584. }
  585. function _toPropertyKey(arg) {
  586. var key = _toPrimitive(arg, "string");
  587. return typeof key === "symbol" ? key : String(key);
  588. }
  589.  
  590. var DEFAULTS = {
  591. /**
  592. * Enable a modal backdrop, specify `static` for a backdrop
  593. * which doesn't close the modal on click.
  594. * @type {boolean}
  595. */
  596. backdrop: true,
  597. /**
  598. * Show the button on the top-right of the viewer.
  599. * @type {boolean}
  600. */
  601. button: true,
  602. /**
  603. * Show the navbar.
  604. * @type {boolean | number}
  605. */
  606. navbar: true,
  607. /**
  608. * Specify the visibility and the content of the title.
  609. * @type {boolean | number | Function | Array}
  610. */
  611. title: true,
  612. /**
  613. * Show the toolbar.
  614. * @type {boolean | number | Object}
  615. */
  616. toolbar: true,
  617. /**
  618. * Custom class name(s) to add to the viewer's root element.
  619. * @type {string}
  620. */
  621. className: "",
  622. /**
  623. * Define where to put the viewer in modal mode.
  624. * @type {string | Element}
  625. */
  626. container: "body",
  627. /**
  628. * Filter the images for viewing. Return true if the image is viewable.
  629. * @type {Function}
  630. */
  631. filter: null,
  632. /**
  633. * Enable to request fullscreen when play.
  634. * {@link https://developer.mozilla.org/en-US/docs/Web/API/FullscreenOptions}
  635. * @type {boolean|FullscreenOptions}
  636. */
  637. fullscreen: true,
  638. /**
  639. * Define the extra attributes to inherit from the original image.
  640. * @type {Array}
  641. */
  642. inheritedAttributes: [
  643. "crossOrigin",
  644. "decoding",
  645. "isMap",
  646. "loading",
  647. "referrerPolicy",
  648. "sizes",
  649. "srcset",
  650. "useMap",
  651. ],
  652. /**
  653. * Define the initial coverage of the viewing image.
  654. * @type {number}
  655. */
  656. initialCoverage: 0.9,
  657. /**
  658. * Define the initial index of the image for viewing.
  659. * @type {number}
  660. */
  661. initialViewIndex: 0,
  662. /**
  663. * Enable inline mode.
  664. * @type {boolean}
  665. */
  666. inline: false,
  667. /**
  668. * The amount of time to delay between automatically cycling an image when playing.
  669. * @type {number}
  670. */
  671. interval: 5000,
  672. /**
  673. * Enable keyboard support.
  674. * @type {boolean}
  675. */
  676. keyboard: true,
  677. /**
  678. * Focus the viewer when initialized.
  679. * @type {boolean}
  680. */
  681. focus: true,
  682. /**
  683. * Indicate if show a loading spinner when load image or not.
  684. * @type {boolean}
  685. */
  686. loading: true,
  687. /**
  688. * Indicate if enable loop viewing or not.
  689. * @type {boolean}
  690. */
  691. loop: true,
  692. /**
  693. * Min width of the viewer in inline mode.
  694. * @type {number}
  695. */
  696. minWidth: 200,
  697. /**
  698. * Min height of the viewer in inline mode.
  699. * @type {number}
  700. */
  701. minHeight: 100,
  702. /**
  703. * Enable to move the image.
  704. * @type {boolean}
  705. */
  706. movable: true,
  707. /**
  708. * Enable to rotate the image.
  709. * @type {boolean}
  710. */
  711. rotatable: true,
  712. /**
  713. * Enable to scale the image.
  714. * @type {boolean}
  715. */
  716. scalable: true,
  717. /**
  718. * Enable to zoom the image.
  719. * @type {boolean}
  720. */
  721. zoomable: true,
  722. /**
  723. * Enable to zoom the current image by dragging on the touch screen.
  724. * @type {boolean}
  725. */
  726. zoomOnTouch: true,
  727. /**
  728. * Enable to zoom the image by wheeling mouse.
  729. * @type {boolean}
  730. */
  731. zoomOnWheel: true,
  732. /**
  733. * Enable to slide to the next or previous image by swiping on the touch screen.
  734. * @type {boolean}
  735. */
  736. slideOnTouch: true,
  737. /**
  738. * Indicate if toggle the image size between its natural size
  739. * and initial size when double click on the image or not.
  740. * @type {boolean}
  741. */
  742. toggleOnDblclick: true,
  743. /**
  744. * Show the tooltip with image ratio (percentage) when zoom in or zoom out.
  745. * @type {boolean}
  746. */
  747. tooltip: true,
  748. /**
  749. * Enable CSS3 Transition for some special elements.
  750. * @type {boolean}
  751. */
  752. transition: true,
  753. /**
  754. * Define the CSS `z-index` value of viewer in modal mode.
  755. * @type {number}
  756. */
  757. zIndex: 2015,
  758. /**
  759. * Define the CSS `z-index` value of viewer in inline mode.
  760. * @type {number}
  761. */
  762. zIndexInline: 0,
  763. /**
  764. * Define the ratio when zoom the image by wheeling mouse.
  765. * @type {number}
  766. */
  767. zoomRatio: 0.1,
  768. /**
  769. * Define the min ratio of the image when zoom out.
  770. * @type {number}
  771. */
  772. minZoomRatio: 0.01,
  773. /**
  774. * Define the max ratio of the image when zoom in.
  775. * @type {number}
  776. */
  777. maxZoomRatio: 100,
  778. /**
  779. * Define where to get the original image URL for viewing.
  780. * @type {string | Function}
  781. */
  782. url: "src",
  783. /**
  784. * Event shortcuts.
  785. * @type {Function}
  786. */
  787. ready: null,
  788. show: null,
  789. shown: null,
  790. hide: null,
  791. hidden: null,
  792. view: null,
  793. viewed: null,
  794. move: null,
  795. moved: null,
  796. rotate: null,
  797. rotated: null,
  798. scale: null,
  799. scaled: null,
  800. zoom: null,
  801. zoomed: null,
  802. play: null,
  803. stop: null,
  804. };
  805.  
  806. var TEMPLATE =
  807. '<div class="viewer-container" tabindex="-1" touch-action="none">' +
  808. '<div class="viewer-canvas"></div>' +
  809. '<div class="viewer-footer">' +
  810. '<div class="viewer-title"></div>' +
  811. '<div class="viewer-toolbar"></div>' +
  812. '<div class="viewer-navbar">' +
  813. '<ul class="viewer-list" role="navigation"></ul>' +
  814. "</div>" +
  815. "</div>" +
  816. '<div class="viewer-tooltip" role="alert" aria-hidden="true"></div>' +
  817. '<div class="viewer-button" data-viewer-action="mix" role="button"></div>' +
  818. '<div class="viewer-player"></div>' +
  819. "</div>";
  820.  
  821. var IS_BROWSER =
  822. typeof window !== "undefined" && typeof window.document !== "undefined";
  823. var WINDOW = IS_BROWSER ? window : {};
  824. var IS_TOUCH_DEVICE =
  825. IS_BROWSER && WINDOW.document.documentElement
  826. ? "ontouchstart" in WINDOW.document.documentElement
  827. : false;
  828. var HAS_POINTER_EVENT = IS_BROWSER ? "PointerEvent" in WINDOW : false;
  829. var NAMESPACE = "viewer";
  830.  
  831. // Actions
  832. var ACTION_MOVE = "move";
  833. var ACTION_SWITCH = "switch";
  834. var ACTION_ZOOM = "zoom";
  835.  
  836. // Classes
  837. var CLASS_ACTIVE = "".concat(NAMESPACE, "-active");
  838. var CLASS_CLOSE = "".concat(NAMESPACE, "-close");
  839. var CLASS_FADE = "".concat(NAMESPACE, "-fade");
  840. var CLASS_FIXED = "".concat(NAMESPACE, "-fixed");
  841. var CLASS_FULLSCREEN = "".concat(NAMESPACE, "-fullscreen");
  842. var CLASS_FULLSCREEN_EXIT = "".concat(NAMESPACE, "-fullscreen-exit");
  843. var CLASS_HIDE = "".concat(NAMESPACE, "-hide");
  844. var CLASS_HIDE_MD_DOWN = "".concat(NAMESPACE, "-hide-md-down");
  845. var CLASS_HIDE_SM_DOWN = "".concat(NAMESPACE, "-hide-sm-down");
  846. var CLASS_HIDE_XS_DOWN = "".concat(NAMESPACE, "-hide-xs-down");
  847. var CLASS_IN = "".concat(NAMESPACE, "-in");
  848. var CLASS_INVISIBLE = "".concat(NAMESPACE, "-invisible");
  849. var CLASS_LOADING = "".concat(NAMESPACE, "-loading");
  850. var CLASS_MOVE = "".concat(NAMESPACE, "-move");
  851. var CLASS_OPEN = "".concat(NAMESPACE, "-open");
  852. var CLASS_SHOW = "".concat(NAMESPACE, "-show");
  853. var CLASS_TRANSITION = "".concat(NAMESPACE, "-transition");
  854.  
  855. // Native events
  856. var EVENT_CLICK = "click";
  857. var EVENT_DBLCLICK = "dblclick";
  858. var EVENT_DRAG_START = "dragstart";
  859. var EVENT_FOCUSIN = "focusin";
  860. var EVENT_KEY_DOWN = "keydown";
  861. var EVENT_LOAD = "load";
  862. var EVENT_ERROR = "error";
  863. var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? "touchend touchcancel" : "mouseup";
  864. var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? "touchmove" : "mousemove";
  865. var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? "touchstart" : "mousedown";
  866. var EVENT_POINTER_DOWN = HAS_POINTER_EVENT
  867. ? "pointerdown"
  868. : EVENT_TOUCH_START;
  869. var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? "pointermove" : EVENT_TOUCH_MOVE;
  870. var EVENT_POINTER_UP = HAS_POINTER_EVENT
  871. ? "pointerup pointercancel"
  872. : EVENT_TOUCH_END;
  873. var EVENT_RESIZE = "resize";
  874. var EVENT_TRANSITION_END = "transitionend";
  875. var EVENT_WHEEL = "wheel";
  876.  
  877. // Custom events
  878. var EVENT_READY = "ready";
  879. var EVENT_SHOW = "show";
  880. var EVENT_SHOWN = "shown";
  881. var EVENT_HIDE = "hide";
  882. var EVENT_HIDDEN = "hidden";
  883. var EVENT_VIEW = "view";
  884. var EVENT_VIEWED = "viewed";
  885. var EVENT_MOVE = "move";
  886. var EVENT_MOVED = "moved";
  887. var EVENT_ROTATE = "rotate";
  888. var EVENT_ROTATED = "rotated";
  889. var EVENT_SCALE = "scale";
  890. var EVENT_SCALED = "scaled";
  891. var EVENT_ZOOM = "zoom";
  892. var EVENT_ZOOMED = "zoomed";
  893. var EVENT_PLAY = "play";
  894. var EVENT_STOP = "stop";
  895.  
  896. // Data keys
  897. var DATA_ACTION = "".concat(NAMESPACE, "Action");
  898.  
  899. // RegExps
  900. var REGEXP_SPACES = /\s\s*/;
  901.  
  902. // Misc
  903. var BUTTONS = [
  904. "zoom-in",
  905. "zoom-out",
  906. "one-to-one",
  907. "reset",
  908. "prev",
  909. "play",
  910. "next",
  911. "rotate-left",
  912. "rotate-right",
  913. "flip-horizontal",
  914. "flip-vertical",
  915. ];
  916.  
  917. /**
  918. * Check if the given value is a string.
  919. * @param {*} value - The value to check.
  920. * @returns {boolean} Returns `true` if the given value is a string, else `false`.
  921. */
  922. function isString(value) {
  923. return typeof value === "string";
  924. }
  925.  
  926. /**
  927. * Check if the given value is not a number.
  928. */
  929. var isNaN = Number.isNaN || WINDOW.isNaN;
  930.  
  931. /**
  932. * Check if the given value is a number.
  933. * @param {*} value - The value to check.
  934. * @returns {boolean} Returns `true` if the given value is a number, else `false`.
  935. */
  936. function isNumber(value) {
  937. return typeof value === "number" && !isNaN(value);
  938. }
  939.  
  940. /**
  941. * Check if the given value is undefined.
  942. * @param {*} value - The value to check.
  943. * @returns {boolean} Returns `true` if the given value is undefined, else `false`.
  944. */
  945. function isUndefined(value) {
  946. return typeof value === "undefined";
  947. }
  948.  
  949. /**
  950. * Check if the given value is an object.
  951. * @param {*} value - The value to check.
  952. * @returns {boolean} Returns `true` if the given value is an object, else `false`.
  953. */
  954. function isObject(value) {
  955. return _typeof(value) === "object" && value !== null;
  956. }
  957. var hasOwnProperty = Object.prototype.hasOwnProperty;
  958.  
  959. /**
  960. * Check if the given value is a plain object.
  961. * @param {*} value - The value to check.
  962. * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
  963. */
  964. function isPlainObject(value) {
  965. if (!isObject(value)) {
  966. return false;
  967. }
  968. try {
  969. var _constructor = value.constructor;
  970. var prototype = _constructor.prototype;
  971. return (
  972. _constructor &&
  973. prototype &&
  974. hasOwnProperty.call(prototype, "isPrototypeOf")
  975. );
  976. } catch (error) {
  977. return false;
  978. }
  979. }
  980.  
  981. /**
  982. * Check if the given value is a function.
  983. * @param {*} value - The value to check.
  984. * @returns {boolean} Returns `true` if the given value is a function, else `false`.
  985. */
  986. function isFunction(value) {
  987. return typeof value === "function";
  988. }
  989.  
  990. /**
  991. * Iterate the given data.
  992. * @param {*} data - The data to iterate.
  993. * @param {Function} callback - The process function for each element.
  994. * @returns {*} The original data.
  995. */
  996. function forEach(data, callback) {
  997. if (data && isFunction(callback)) {
  998. if (Array.isArray(data) || isNumber(data.length) /* array-like */) {
  999. var length = data.length;
  1000. var i;
  1001. for (i = 0; i < length; i += 1) {
  1002. if (callback.call(data, data[i], i, data) === false) {
  1003. break;
  1004. }
  1005. }
  1006. } else if (isObject(data)) {
  1007. Object.keys(data).forEach(function (key) {
  1008. callback.call(data, data[key], key, data);
  1009. });
  1010. }
  1011. }
  1012. return data;
  1013. }
  1014.  
  1015. /**
  1016. * Extend the given object.
  1017. * @param {*} obj - The object to be extended.
  1018. * @param {*} args - The rest objects which will be merged to the first object.
  1019. * @returns {Object} The extended object.
  1020. */
  1021. var assign =
  1022. Object.assign ||
  1023. function assign(obj) {
  1024. for (
  1025. var _len = arguments.length,
  1026. args = new Array(_len > 1 ? _len - 1 : 0),
  1027. _key = 1;
  1028. _key < _len;
  1029. _key++
  1030. ) {
  1031. args[_key - 1] = arguments[_key];
  1032. }
  1033. if (isObject(obj) && args.length > 0) {
  1034. args.forEach(function (arg) {
  1035. if (isObject(arg)) {
  1036. Object.keys(arg).forEach(function (key) {
  1037. obj[key] = arg[key];
  1038. });
  1039. }
  1040. });
  1041. }
  1042. return obj;
  1043. };
  1044. var REGEXP_SUFFIX = /^(?:width|height|left|top|marginLeft|marginTop)$/;
  1045.  
  1046. /**
  1047. * Apply styles to the given element.
  1048. * @param {Element} element - The target element.
  1049. * @param {Object} styles - The styles for applying.
  1050. */
  1051. function setStyle(element, styles) {
  1052. var style = element.style;
  1053. forEach(styles, function (value, property) {
  1054. if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
  1055. value += "px";
  1056. }
  1057. style[property] = value;
  1058. });
  1059. }
  1060.  
  1061. /**
  1062. * Escape a string for using in HTML.
  1063. * @param {String} value - The string to escape.
  1064. * @returns {String} Returns the escaped string.
  1065. */
  1066. function escapeHTMLEntities(value) {
  1067. return isString(value)
  1068. ? value
  1069. .replace(/&(?!amp;|quot;|#39;|lt;|gt;)/g, "&amp;")
  1070. .replace(/"/g, "&quot;")
  1071. .replace(/'/g, "&#39;")
  1072. .replace(/</g, "&lt;")
  1073. .replace(/>/g, "&gt;")
  1074. : value;
  1075. }
  1076.  
  1077. /**
  1078. * Check if the given element has a special class.
  1079. * @param {Element} element - The element to check.
  1080. * @param {string} value - The class to search.
  1081. * @returns {boolean} Returns `true` if the special class was found.
  1082. */
  1083. function hasClass(element, value) {
  1084. if (!element || !value) {
  1085. return false;
  1086. }
  1087. return element.classList
  1088. ? element.classList.contains(value)
  1089. : element.className.indexOf(value) > -1;
  1090. }
  1091.  
  1092. /**
  1093. * Add classes to the given element.
  1094. * @param {Element} element - The target element.
  1095. * @param {string} value - The classes to be added.
  1096. */
  1097. function addClass(element, value) {
  1098. if (!element || !value) {
  1099. return;
  1100. }
  1101. if (isNumber(element.length)) {
  1102. forEach(element, function (elem) {
  1103. addClass(elem, value);
  1104. });
  1105. return;
  1106. }
  1107. if (element.classList) {
  1108. element.classList.add(value);
  1109. return;
  1110. }
  1111. var className = element.className.trim();
  1112. if (!className) {
  1113. element.className = value;
  1114. } else if (className.indexOf(value) < 0) {
  1115. element.className = "".concat(className, " ").concat(value);
  1116. }
  1117. }
  1118.  
  1119. /**
  1120. * Remove classes from the given element.
  1121. * @param {Element} element - The target element.
  1122. * @param {string} value - The classes to be removed.
  1123. */
  1124. function removeClass(element, value) {
  1125. if (!element || !value) {
  1126. return;
  1127. }
  1128. if (isNumber(element.length)) {
  1129. forEach(element, function (elem) {
  1130. removeClass(elem, value);
  1131. });
  1132. return;
  1133. }
  1134. if (element.classList) {
  1135. element.classList.remove(value);
  1136. return;
  1137. }
  1138. if (element.className.indexOf(value) >= 0) {
  1139. element.className = element.className.replace(value, "");
  1140. }
  1141. }
  1142.  
  1143. /**
  1144. * Add or remove classes from the given element.
  1145. * @param {Element} element - The target element.
  1146. * @param {string} value - The classes to be toggled.
  1147. * @param {boolean} added - Add only.
  1148. */
  1149. function toggleClass(element, value, added) {
  1150. if (!value) {
  1151. return;
  1152. }
  1153. if (isNumber(element.length)) {
  1154. forEach(element, function (elem) {
  1155. toggleClass(elem, value, added);
  1156. });
  1157. return;
  1158. }
  1159.  
  1160. // IE10-11 doesn't support the second parameter of `classList.toggle`
  1161. if (added) {
  1162. addClass(element, value);
  1163. } else {
  1164. removeClass(element, value);
  1165. }
  1166. }
  1167. var REGEXP_HYPHENATE = /([a-z\d])([A-Z])/g;
  1168.  
  1169. /**
  1170. * Transform the given string from camelCase to kebab-case
  1171. * @param {string} value - The value to transform.
  1172. * @returns {string} The transformed value.
  1173. */
  1174. function hyphenate(value) {
  1175. return value.replace(REGEXP_HYPHENATE, "$1-$2").toLowerCase();
  1176. }
  1177.  
  1178. /**
  1179. * Get data from the given element.
  1180. * @param {Element} element - The target element.
  1181. * @param {string} name - The data key to get.
  1182. * @returns {string} The data value.
  1183. */
  1184. function getData(element, name) {
  1185. if (isObject(element[name])) {
  1186. return element[name];
  1187. }
  1188. if (element.dataset) {
  1189. return element.dataset[name];
  1190. }
  1191. return element.getAttribute("data-".concat(hyphenate(name)));
  1192. }
  1193.  
  1194. /**
  1195. * Set data to the given element.
  1196. * @param {Element} element - The target element.
  1197. * @param {string} name - The data key to set.
  1198. * @param {string} data - The data value.
  1199. */
  1200. function setData(element, name, data) {
  1201. if (isObject(data)) {
  1202. element[name] = data;
  1203. } else if (element.dataset) {
  1204. element.dataset[name] = data;
  1205. } else {
  1206. element.setAttribute("data-".concat(hyphenate(name)), data);
  1207. }
  1208. }
  1209. var onceSupported = (function () {
  1210. var supported = false;
  1211. if (IS_BROWSER) {
  1212. var once = false;
  1213. var listener = function listener() {};
  1214. var options = Object.defineProperty({}, "once", {
  1215. get: function get() {
  1216. supported = true;
  1217. return once;
  1218. },
  1219. /**
  1220. * This setter can fix a `TypeError` in strict mode
  1221. * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
  1222. * @param {boolean} value - The value to set
  1223. */
  1224. set: function set(value) {
  1225. once = value;
  1226. },
  1227. });
  1228. WINDOW.addEventListener("test", listener, options);
  1229. WINDOW.removeEventListener("test", listener, options);
  1230. }
  1231. return supported;
  1232. })();
  1233.  
  1234. /**
  1235. * Remove event listener from the target element.
  1236. * @param {Element} element - The event target.
  1237. * @param {string} type - The event type(s).
  1238. * @param {Function} listener - The event listener.
  1239. * @param {Object} options - The event options.
  1240. */
  1241. function removeListener(element, type, listener) {
  1242. var options =
  1243. arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  1244. var handler = listener;
  1245. type
  1246. .trim()
  1247. .split(REGEXP_SPACES)
  1248. .forEach(function (event) {
  1249. if (!onceSupported) {
  1250. var listeners = element.listeners;
  1251. if (listeners && listeners[event] && listeners[event][listener]) {
  1252. handler = listeners[event][listener];
  1253. delete listeners[event][listener];
  1254. if (Object.keys(listeners[event]).length === 0) {
  1255. delete listeners[event];
  1256. }
  1257. if (Object.keys(listeners).length === 0) {
  1258. delete element.listeners;
  1259. }
  1260. }
  1261. }
  1262. element.removeEventListener(event, handler, options);
  1263. });
  1264. }
  1265.  
  1266. /**
  1267. * Add event listener to the target element.
  1268. * @param {Element} element - The event target.
  1269. * @param {string} type - The event type(s).
  1270. * @param {Function} listener - The event listener.
  1271. * @param {Object} options - The event options.
  1272. */
  1273. function addListener(element, type, listener) {
  1274. var options =
  1275. arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  1276. var _handler = listener;
  1277. type
  1278. .trim()
  1279. .split(REGEXP_SPACES)
  1280. .forEach(function (event) {
  1281. if (options.once && !onceSupported) {
  1282. var _element$listeners = element.listeners,
  1283. listeners = _element$listeners === void 0 ? {} : _element$listeners;
  1284. _handler = function handler() {
  1285. delete listeners[event][listener];
  1286. element.removeEventListener(event, _handler, options);
  1287. for (
  1288. var _len2 = arguments.length, args = new Array(_len2), _key2 = 0;
  1289. _key2 < _len2;
  1290. _key2++
  1291. ) {
  1292. args[_key2] = arguments[_key2];
  1293. }
  1294. listener.apply(element, args);
  1295. };
  1296. if (!listeners[event]) {
  1297. listeners[event] = {};
  1298. }
  1299. if (listeners[event][listener]) {
  1300. element.removeEventListener(
  1301. event,
  1302. listeners[event][listener],
  1303. options
  1304. );
  1305. }
  1306. listeners[event][listener] = _handler;
  1307. element.listeners = listeners;
  1308. }
  1309. element.addEventListener(event, _handler, options);
  1310. });
  1311. }
  1312.  
  1313. /**
  1314. * Dispatch event on the target element.
  1315. * @param {Element} element - The event target.
  1316. * @param {string} type - The event type(s).
  1317. * @param {Object} data - The additional event data.
  1318. * @param {Object} options - The additional event options.
  1319. * @returns {boolean} Indicate if the event is default prevented or not.
  1320. */
  1321. function dispatchEvent(element, type, data, options) {
  1322. var event;
  1323.  
  1324. // Event and CustomEvent on IE9-11 are global objects, not constructors
  1325. if (isFunction(Event) && isFunction(CustomEvent)) {
  1326. event = new CustomEvent(
  1327. type,
  1328. _objectSpread2(
  1329. {
  1330. bubbles: true,
  1331. cancelable: true,
  1332. detail: data,
  1333. },
  1334. options
  1335. )
  1336. );
  1337. } else {
  1338. event = document.createEvent("CustomEvent");
  1339. event.initCustomEvent(type, true, true, data);
  1340. }
  1341. return element.dispatchEvent(event);
  1342. }
  1343.  
  1344. /**
  1345. * Get the offset base on the document.
  1346. * @param {Element} element - The target element.
  1347. * @returns {Object} The offset data.
  1348. */
  1349. function getOffset(element) {
  1350. var box = element.getBoundingClientRect();
  1351. return {
  1352. left:
  1353. box.left + (window.pageXOffset - document.documentElement.clientLeft),
  1354. top: box.top + (window.pageYOffset - document.documentElement.clientTop),
  1355. };
  1356. }
  1357.  
  1358. /**
  1359. * Get transforms base on the given object.
  1360. * @param {Object} obj - The target object.
  1361. * @returns {string} A string contains transform values.
  1362. */
  1363. function getTransforms(_ref) {
  1364. var rotate = _ref.rotate,
  1365. scaleX = _ref.scaleX,
  1366. scaleY = _ref.scaleY,
  1367. translateX = _ref.translateX,
  1368. translateY = _ref.translateY;
  1369. var values = [];
  1370. if (isNumber(translateX) && translateX !== 0) {
  1371. values.push("translateX(".concat(translateX, "px)"));
  1372. }
  1373. if (isNumber(translateY) && translateY !== 0) {
  1374. values.push("translateY(".concat(translateY, "px)"));
  1375. }
  1376.  
  1377. // Rotate should come first before scale to match orientation transform
  1378. if (isNumber(rotate) && rotate !== 0) {
  1379. values.push("rotate(".concat(rotate, "deg)"));
  1380. }
  1381. if (isNumber(scaleX) && scaleX !== 1) {
  1382. values.push("scaleX(".concat(scaleX, ")"));
  1383. }
  1384. if (isNumber(scaleY) && scaleY !== 1) {
  1385. values.push("scaleY(".concat(scaleY, ")"));
  1386. }
  1387. var transform = values.length ? values.join(" ") : "none";
  1388. return {
  1389. WebkitTransform: transform,
  1390. msTransform: transform,
  1391. transform: transform,
  1392. };
  1393. }
  1394.  
  1395. /**
  1396. * Get an image name from an image url.
  1397. * @param {string} url - The target url.
  1398. * @example
  1399. * // picture.jpg
  1400. * getImageNameFromURL('https://domain.com/path/to/picture.jpg?size=1280×960')
  1401. * @returns {string} A string contains the image name.
  1402. */
  1403. function getImageNameFromURL(url) {
  1404. return isString(url)
  1405. ? decodeURIComponent(url.replace(/^.*\//, "").replace(/[?&#].*$/, ""))
  1406. : "";
  1407. }
  1408. var IS_SAFARI =
  1409. WINDOW.navigator &&
  1410. /Version\/\d+(\.\d+)+?\s+Safari/i.test(WINDOW.navigator.userAgent);
  1411.  
  1412. /**
  1413. * Get an image's natural sizes.
  1414. * @param {string} image - The target image.
  1415. * @param {Object} options - The viewer options.
  1416. * @param {Function} callback - The callback function.
  1417. * @returns {HTMLImageElement} The new image.
  1418. */
  1419. function getImageNaturalSizes(image, options, callback) {
  1420. var newImage = document.createElement("img");
  1421.  
  1422. // Modern browsers (except Safari)
  1423. if (image.naturalWidth && !IS_SAFARI) {
  1424. callback(image.naturalWidth, image.naturalHeight);
  1425. return newImage;
  1426. }
  1427. var body = document.body || document.documentElement;
  1428. newImage.onload = function () {
  1429. callback(newImage.width, newImage.height);
  1430. if (!IS_SAFARI) {
  1431. body.removeChild(newImage);
  1432. }
  1433. };
  1434. forEach(options.inheritedAttributes, function (name) {
  1435. var value = image.getAttribute(name);
  1436. if (value !== null) {
  1437. newImage.setAttribute(name, value);
  1438. }
  1439. });
  1440. newImage.src = image.src;
  1441.  
  1442. // iOS Safari will convert the image automatically
  1443. // with its orientation once append it into DOM
  1444. if (!IS_SAFARI) {
  1445. newImage.style.cssText =
  1446. "left:0;" +
  1447. "max-height:none!important;" +
  1448. "max-width:none!important;" +
  1449. "min-height:0!important;" +
  1450. "min-width:0!important;" +
  1451. "opacity:0;" +
  1452. "position:absolute;" +
  1453. "top:0;" +
  1454. "z-index:-1;";
  1455. body.appendChild(newImage);
  1456. }
  1457. return newImage;
  1458. }
  1459.  
  1460. /**
  1461. * Get the related class name of a responsive type number.
  1462. * @param {string} type - The responsive type.
  1463. * @returns {string} The related class name.
  1464. */
  1465. function getResponsiveClass(type) {
  1466. switch (type) {
  1467. case 2:
  1468. return CLASS_HIDE_XS_DOWN;
  1469. case 3:
  1470. return CLASS_HIDE_SM_DOWN;
  1471. case 4:
  1472. return CLASS_HIDE_MD_DOWN;
  1473. default:
  1474. return "";
  1475. }
  1476. }
  1477.  
  1478. /**
  1479. * Get the max ratio of a group of pointers.
  1480. * @param {string} pointers - The target pointers.
  1481. * @returns {number} The result ratio.
  1482. */
  1483. function getMaxZoomRatio(pointers) {
  1484. var pointers2 = _objectSpread2({}, pointers);
  1485. var ratios = [];
  1486. forEach(pointers, function (pointer, pointerId) {
  1487. delete pointers2[pointerId];
  1488. forEach(pointers2, function (pointer2) {
  1489. var x1 = Math.abs(pointer.startX - pointer2.startX);
  1490. var y1 = Math.abs(pointer.startY - pointer2.startY);
  1491. var x2 = Math.abs(pointer.endX - pointer2.endX);
  1492. var y2 = Math.abs(pointer.endY - pointer2.endY);
  1493. var z1 = Math.sqrt(x1 * x1 + y1 * y1);
  1494. var z2 = Math.sqrt(x2 * x2 + y2 * y2);
  1495. var ratio = (z2 - z1) / z1;
  1496. ratios.push(ratio);
  1497. });
  1498. });
  1499. ratios.sort(function (a, b) {
  1500. return Math.abs(a) < Math.abs(b);
  1501. });
  1502. return ratios[0];
  1503. }
  1504.  
  1505. /**
  1506. * Get a pointer from an event object.
  1507. * @param {Object} event - The target event object.
  1508. * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
  1509. * @returns {Object} The result pointer contains start and/or end point coordinates.
  1510. */
  1511. function getPointer(_ref2, endOnly) {
  1512. var pageX = _ref2.pageX,
  1513. pageY = _ref2.pageY;
  1514. var end = {
  1515. endX: pageX,
  1516. endY: pageY,
  1517. };
  1518. return endOnly
  1519. ? end
  1520. : _objectSpread2(
  1521. {
  1522. timeStamp: Date.now(),
  1523. startX: pageX,
  1524. startY: pageY,
  1525. },
  1526. end
  1527. );
  1528. }
  1529.  
  1530. /**
  1531. * Get the center point coordinate of a group of pointers.
  1532. * @param {Object} pointers - The target pointers.
  1533. * @returns {Object} The center point coordinate.
  1534. */
  1535. function getPointersCenter(pointers) {
  1536. var pageX = 0;
  1537. var pageY = 0;
  1538. var count = 0;
  1539. forEach(pointers, function (_ref3) {
  1540. var startX = _ref3.startX,
  1541. startY = _ref3.startY;
  1542. pageX += startX;
  1543. pageY += startY;
  1544. count += 1;
  1545. });
  1546. pageX /= count;
  1547. pageY /= count;
  1548. return {
  1549. pageX: pageX,
  1550. pageY: pageY,
  1551. };
  1552. }
  1553.  
  1554. var render = {
  1555. render: function render() {
  1556. this.initContainer();
  1557. this.initViewer();
  1558. this.initList();
  1559. this.renderViewer();
  1560. },
  1561. initBody: function initBody() {
  1562. var ownerDocument = this.element.ownerDocument;
  1563. var body = ownerDocument.body || ownerDocument.documentElement;
  1564. this.body = body;
  1565. this.scrollbarWidth =
  1566. window.innerWidth - ownerDocument.documentElement.clientWidth;
  1567. this.initialBodyPaddingRight = body.style.paddingRight;
  1568. this.initialBodyComputedPaddingRight =
  1569. window.getComputedStyle(body).paddingRight;
  1570. },
  1571. initContainer: function initContainer() {
  1572. this.containerData = {
  1573. width: window.innerWidth,
  1574. height: window.innerHeight,
  1575. };
  1576. },
  1577. initViewer: function initViewer() {
  1578. var options = this.options,
  1579. parent = this.parent;
  1580. var viewerData;
  1581. if (options.inline) {
  1582. viewerData = {
  1583. width: Math.max(parent.offsetWidth, options.minWidth),
  1584. height: Math.max(parent.offsetHeight, options.minHeight),
  1585. };
  1586. this.parentData = viewerData;
  1587. }
  1588. if (this.fulled || !viewerData) {
  1589. viewerData = this.containerData;
  1590. }
  1591. this.viewerData = assign({}, viewerData);
  1592. },
  1593. renderViewer: function renderViewer() {
  1594. if (this.options.inline && !this.fulled) {
  1595. setStyle(this.viewer, this.viewerData);
  1596. }
  1597. },
  1598. initList: function initList() {
  1599. var _this = this;
  1600. var element = this.element,
  1601. options = this.options,
  1602. list = this.list;
  1603. var items = [];
  1604.  
  1605. // initList may be called in this.update, so should keep idempotent
  1606. list.innerHTML = "";
  1607. forEach(this.images, function (image, index) {
  1608. var src = image.src;
  1609. var alt = image.alt || getImageNameFromURL(src);
  1610. var url = _this.getImageURL(image);
  1611. if (src || url) {
  1612. var item = document.createElement("li");
  1613. var img = document.createElement("img");
  1614. forEach(options.inheritedAttributes, function (name) {
  1615. var value = image.getAttribute(name);
  1616. if (value !== null) {
  1617. img.setAttribute(name, value);
  1618. }
  1619. });
  1620. if (options.navbar) {
  1621. img.src = src || url;
  1622. }
  1623. img.alt = alt;
  1624. img.setAttribute("data-original-url", url || src);
  1625. item.setAttribute("data-index", index);
  1626. item.setAttribute("data-viewer-action", "view");
  1627. item.setAttribute("role", "button");
  1628. if (options.keyboard) {
  1629. item.setAttribute("tabindex", 0);
  1630. }
  1631. item.appendChild(img);
  1632. list.appendChild(item);
  1633. items.push(item);
  1634. }
  1635. });
  1636. this.items = items;
  1637. forEach(items, function (item) {
  1638. var image = item.firstElementChild;
  1639. var onLoad;
  1640. var onError;
  1641. setData(image, "filled", true);
  1642. if (options.loading) {
  1643. addClass(item, CLASS_LOADING);
  1644. }
  1645. addListener(
  1646. image,
  1647. EVENT_LOAD,
  1648. (onLoad = function onLoad(event) {
  1649. removeListener(image, EVENT_ERROR, onError);
  1650. if (options.loading) {
  1651. removeClass(item, CLASS_LOADING);
  1652. }
  1653. _this.loadImage(event);
  1654. }),
  1655. {
  1656. once: true,
  1657. }
  1658. );
  1659. addListener(
  1660. image,
  1661. EVENT_ERROR,
  1662. (onError = function onError() {
  1663. removeListener(image, EVENT_LOAD, onLoad);
  1664. if (options.loading) {
  1665. removeClass(item, CLASS_LOADING);
  1666. }
  1667. }),
  1668. {
  1669. once: true,
  1670. }
  1671. );
  1672. });
  1673. if (options.transition) {
  1674. addListener(
  1675. element,
  1676. EVENT_VIEWED,
  1677. function () {
  1678. addClass(list, CLASS_TRANSITION);
  1679. },
  1680. {
  1681. once: true,
  1682. }
  1683. );
  1684. }
  1685. },
  1686. renderList: function renderList() {
  1687. var index = this.index;
  1688. var item = this.items[index];
  1689. if (!item) {
  1690. return;
  1691. }
  1692. var next = item.nextElementSibling;
  1693. var gutter = parseInt(
  1694. window.getComputedStyle(next || item).marginLeft,
  1695. 10
  1696. );
  1697. var offsetWidth = item.offsetWidth;
  1698. var outerWidth = offsetWidth + gutter;
  1699.  
  1700. // Place the active item in the center of the screen
  1701. setStyle(
  1702. this.list,
  1703. assign(
  1704. {
  1705. width: outerWidth * this.length - gutter,
  1706. },
  1707. getTransforms({
  1708. translateX:
  1709. (this.viewerData.width - offsetWidth) / 2 - outerWidth * index,
  1710. })
  1711. )
  1712. );
  1713. },
  1714. resetList: function resetList() {
  1715. var list = this.list;
  1716. list.innerHTML = "";
  1717. removeClass(list, CLASS_TRANSITION);
  1718. setStyle(
  1719. list,
  1720. getTransforms({
  1721. translateX: 0,
  1722. })
  1723. );
  1724. },
  1725. initImage: function initImage(done) {
  1726. var _this2 = this;
  1727. var options = this.options,
  1728. image = this.image,
  1729. viewerData = this.viewerData;
  1730. var footerHeight = this.footer.offsetHeight;
  1731. var viewerWidth = viewerData.width;
  1732. var viewerHeight = Math.max(
  1733. viewerData.height - footerHeight,
  1734. footerHeight
  1735. );
  1736. var oldImageData = this.imageData || {};
  1737. var sizingImage;
  1738. this.imageInitializing = {
  1739. abort: function abort() {
  1740. sizingImage.onload = null;
  1741. },
  1742. };
  1743. sizingImage = getImageNaturalSizes(
  1744. image,
  1745. options,
  1746. function (naturalWidth, naturalHeight) {
  1747. var aspectRatio = naturalWidth / naturalHeight;
  1748. var initialCoverage = Math.max(
  1749. 0,
  1750. Math.min(1, options.initialCoverage)
  1751. );
  1752. var width = viewerWidth;
  1753. var height = viewerHeight;
  1754. _this2.imageInitializing = false;
  1755. if (viewerHeight * aspectRatio > viewerWidth) {
  1756. height = viewerWidth / aspectRatio;
  1757. } else {
  1758. width = viewerHeight * aspectRatio;
  1759. }
  1760. initialCoverage = isNumber(initialCoverage) ? initialCoverage : 0.9;
  1761. width = Math.min(width * initialCoverage, naturalWidth);
  1762. height = Math.min(height * initialCoverage, naturalHeight);
  1763. var left = (viewerWidth - width) / 2;
  1764. var top = (viewerHeight - height) / 2;
  1765. var imageData = {
  1766. left: left,
  1767. top: top,
  1768. x: left,
  1769. y: top,
  1770. width: width,
  1771. height: height,
  1772. oldRatio: 1,
  1773. ratio: width / naturalWidth,
  1774. aspectRatio: aspectRatio,
  1775. naturalWidth: naturalWidth,
  1776. naturalHeight: naturalHeight,
  1777. };
  1778. var initialImageData = assign({}, imageData);
  1779. if (options.rotatable) {
  1780. imageData.rotate = oldImageData.rotate || 0;
  1781. initialImageData.rotate = 0;
  1782. }
  1783. if (options.scalable) {
  1784. imageData.scaleX = oldImageData.scaleX || 1;
  1785. imageData.scaleY = oldImageData.scaleY || 1;
  1786. initialImageData.scaleX = 1;
  1787. initialImageData.scaleY = 1;
  1788. }
  1789. _this2.imageData = imageData;
  1790. _this2.initialImageData = initialImageData;
  1791. if (done) {
  1792. done();
  1793. }
  1794. }
  1795. );
  1796. },
  1797. renderImage: function renderImage(done) {
  1798. var _this3 = this;
  1799. var image = this.image,
  1800. imageData = this.imageData;
  1801. setStyle(
  1802. image,
  1803. assign(
  1804. {
  1805. width: imageData.width,
  1806. height: imageData.height,
  1807. // XXX: Not to use translateX/Y to avoid image shaking when zooming
  1808. marginLeft: imageData.x,
  1809. marginTop: imageData.y,
  1810. },
  1811. getTransforms(imageData)
  1812. )
  1813. );
  1814. if (done) {
  1815. if (
  1816. (this.viewing ||
  1817. this.moving ||
  1818. this.rotating ||
  1819. this.scaling ||
  1820. this.zooming) &&
  1821. this.options.transition &&
  1822. hasClass(image, CLASS_TRANSITION)
  1823. ) {
  1824. var onTransitionEnd = function onTransitionEnd() {
  1825. _this3.imageRendering = false;
  1826. done();
  1827. };
  1828. this.imageRendering = {
  1829. abort: function abort() {
  1830. removeListener(image, EVENT_TRANSITION_END, onTransitionEnd);
  1831. },
  1832. };
  1833. addListener(image, EVENT_TRANSITION_END, onTransitionEnd, {
  1834. once: true,
  1835. });
  1836. } else {
  1837. done();
  1838. }
  1839. }
  1840. },
  1841. resetImage: function resetImage() {
  1842. var image = this.image;
  1843. if (image) {
  1844. if (this.viewing) {
  1845. this.viewing.abort();
  1846. }
  1847. image.parentNode.removeChild(image);
  1848. this.image = null;
  1849. this.title.innerHTML = "";
  1850. }
  1851. },
  1852. };
  1853.  
  1854. var events = {
  1855. bind: function bind() {
  1856. var options = this.options,
  1857. viewer = this.viewer,
  1858. canvas = this.canvas;
  1859. var document = this.element.ownerDocument;
  1860. addListener(viewer, EVENT_CLICK, (this.onClick = this.click.bind(this)));
  1861. addListener(
  1862. viewer,
  1863. EVENT_DRAG_START,
  1864. (this.onDragStart = this.dragstart.bind(this))
  1865. );
  1866. addListener(
  1867. canvas,
  1868. EVENT_POINTER_DOWN,
  1869. (this.onPointerDown = this.pointerdown.bind(this))
  1870. );
  1871. addListener(
  1872. document,
  1873. EVENT_POINTER_MOVE,
  1874. (this.onPointerMove = this.pointermove.bind(this))
  1875. );
  1876. addListener(
  1877. document,
  1878. EVENT_POINTER_UP,
  1879. (this.onPointerUp = this.pointerup.bind(this))
  1880. );
  1881. addListener(
  1882. document,
  1883. EVENT_KEY_DOWN,
  1884. (this.onKeyDown = this.keydown.bind(this))
  1885. );
  1886. addListener(
  1887. window,
  1888. EVENT_RESIZE,
  1889. (this.onResize = this.resize.bind(this))
  1890. );
  1891. if (options.zoomable && options.zoomOnWheel) {
  1892. addListener(
  1893. viewer,
  1894. EVENT_WHEEL,
  1895. (this.onWheel = this.wheel.bind(this)),
  1896. {
  1897. passive: false,
  1898. capture: true,
  1899. }
  1900. );
  1901. }
  1902. if (options.toggleOnDblclick) {
  1903. addListener(
  1904. canvas,
  1905. EVENT_DBLCLICK,
  1906. (this.onDblclick = this.dblclick.bind(this))
  1907. );
  1908. }
  1909. },
  1910. unbind: function unbind() {
  1911. var options = this.options,
  1912. viewer = this.viewer,
  1913. canvas = this.canvas;
  1914. var document = this.element.ownerDocument;
  1915. removeListener(viewer, EVENT_CLICK, this.onClick);
  1916. removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
  1917. removeListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown);
  1918. removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove);
  1919. removeListener(document, EVENT_POINTER_UP, this.onPointerUp);
  1920. removeListener(document, EVENT_KEY_DOWN, this.onKeyDown);
  1921. removeListener(window, EVENT_RESIZE, this.onResize);
  1922. if (options.zoomable && options.zoomOnWheel) {
  1923. removeListener(viewer, EVENT_WHEEL, this.onWheel, {
  1924. passive: false,
  1925. capture: true,
  1926. });
  1927. }
  1928. if (options.toggleOnDblclick) {
  1929. removeListener(canvas, EVENT_DBLCLICK, this.onDblclick);
  1930. }
  1931. },
  1932. };
  1933.  
  1934. var handlers = {
  1935. click: function click(event) {
  1936. var options = this.options,
  1937. imageData = this.imageData;
  1938. var target = event.target;
  1939. var action = getData(target, DATA_ACTION);
  1940. if (
  1941. !action &&
  1942. target.localName === "img" &&
  1943. target.parentElement.localName === "li"
  1944. ) {
  1945. target = target.parentElement;
  1946. action = getData(target, DATA_ACTION);
  1947. }
  1948.  
  1949. // Cancel the emulated click when the native click event was triggered.
  1950. if (IS_TOUCH_DEVICE && event.isTrusted && target === this.canvas) {
  1951. clearTimeout(this.clickCanvasTimeout);
  1952. }
  1953. switch (action) {
  1954. case "mix":
  1955. if (this.played) {
  1956. this.stop();
  1957. } else if (options.inline) {
  1958. if (this.fulled) {
  1959. this.exit();
  1960. } else {
  1961. this.full();
  1962. }
  1963. } else {
  1964. this.hide();
  1965. }
  1966. break;
  1967. case "hide":
  1968. if (!this.pointerMoved) {
  1969. this.hide();
  1970. }
  1971. break;
  1972. case "view":
  1973. this.view(getData(target, "index"));
  1974. break;
  1975. case "zoom-in":
  1976. this.zoom(0.1, true);
  1977. break;
  1978. case "zoom-out":
  1979. this.zoom(-0.1, true);
  1980. break;
  1981. case "one-to-one":
  1982. this.toggle();
  1983. break;
  1984. case "reset":
  1985. this.reset();
  1986. break;
  1987. case "prev":
  1988. this.prev(options.loop);
  1989. break;
  1990. case "play":
  1991. this.play(options.fullscreen);
  1992. break;
  1993. case "next":
  1994. this.next(options.loop);
  1995. break;
  1996. case "rotate-left":
  1997. this.rotate(-90);
  1998. break;
  1999. case "rotate-right":
  2000. this.rotate(90);
  2001. break;
  2002. case "flip-horizontal":
  2003. this.scaleX(-imageData.scaleX || -1);
  2004. break;
  2005. case "flip-vertical":
  2006. this.scaleY(-imageData.scaleY || -1);
  2007. break;
  2008. default:
  2009. if (this.played) {
  2010. this.stop();
  2011. }
  2012. }
  2013. },
  2014. dblclick: function dblclick(event) {
  2015. event.preventDefault();
  2016. if (this.viewed && event.target === this.image) {
  2017. // Cancel the emulated double click when the native dblclick event was triggered.
  2018. if (IS_TOUCH_DEVICE && event.isTrusted) {
  2019. clearTimeout(this.doubleClickImageTimeout);
  2020. }
  2021.  
  2022. // XXX: No pageX/Y properties in custom event, fallback to the original event.
  2023. this.toggle(
  2024. event.isTrusted ? event : event.detail && event.detail.originalEvent
  2025. );
  2026. }
  2027. },
  2028. load: function load() {
  2029. var _this = this;
  2030. if (this.timeout) {
  2031. clearTimeout(this.timeout);
  2032. this.timeout = false;
  2033. }
  2034. var element = this.element,
  2035. options = this.options,
  2036. image = this.image,
  2037. index = this.index,
  2038. viewerData = this.viewerData;
  2039. removeClass(image, CLASS_INVISIBLE);
  2040. if (options.loading) {
  2041. removeClass(this.canvas, CLASS_LOADING);
  2042. }
  2043. image.style.cssText =
  2044. "height:0;" +
  2045. "margin-left:".concat(viewerData.width / 2, "px;") +
  2046. "margin-top:".concat(viewerData.height / 2, "px;") +
  2047. "max-width:none!important;" +
  2048. "position:relative;" +
  2049. "width:0;";
  2050. this.initImage(function () {
  2051. toggleClass(image, CLASS_MOVE, options.movable);
  2052. toggleClass(image, CLASS_TRANSITION, options.transition);
  2053. _this.renderImage(function () {
  2054. _this.viewed = true;
  2055. _this.viewing = false;
  2056. if (isFunction(options.viewed)) {
  2057. addListener(element, EVENT_VIEWED, options.viewed, {
  2058. once: true,
  2059. });
  2060. }
  2061. dispatchEvent(
  2062. element,
  2063. EVENT_VIEWED,
  2064. {
  2065. originalImage: _this.images[index],
  2066. index: index,
  2067. image: image,
  2068. },
  2069. {
  2070. cancelable: false,
  2071. }
  2072. );
  2073. });
  2074. });
  2075. },
  2076. loadImage: function loadImage(event) {
  2077. var image = event.target;
  2078. var parent = image.parentNode;
  2079. var parentWidth = parent.offsetWidth || 30;
  2080. var parentHeight = parent.offsetHeight || 50;
  2081. var filled = !!getData(image, "filled");
  2082. getImageNaturalSizes(
  2083. image,
  2084. this.options,
  2085. function (naturalWidth, naturalHeight) {
  2086. var aspectRatio = naturalWidth / naturalHeight;
  2087. var width = parentWidth;
  2088. var height = parentHeight;
  2089. if (parentHeight * aspectRatio > parentWidth) {
  2090. if (filled) {
  2091. width = parentHeight * aspectRatio;
  2092. } else {
  2093. height = parentWidth / aspectRatio;
  2094. }
  2095. } else if (filled) {
  2096. height = parentWidth / aspectRatio;
  2097. } else {
  2098. width = parentHeight * aspectRatio;
  2099. }
  2100. setStyle(
  2101. image,
  2102. assign(
  2103. {
  2104. width: width,
  2105. height: height,
  2106. },
  2107. getTransforms({
  2108. translateX: (parentWidth - width) / 2,
  2109. translateY: (parentHeight - height) / 2,
  2110. })
  2111. )
  2112. );
  2113. }
  2114. );
  2115. },
  2116. keydown: function keydown(event) {
  2117. var options = this.options;
  2118. if (!options.keyboard) {
  2119. return;
  2120. }
  2121. var keyCode = event.keyCode || event.which || event.charCode;
  2122. switch (keyCode) {
  2123. // Enter
  2124. case 13:
  2125. if (this.viewer.contains(event.target)) {
  2126. this.click(event);
  2127. }
  2128. break;
  2129. }
  2130. if (!this.fulled) {
  2131. return;
  2132. }
  2133. switch (keyCode) {
  2134. // Escape
  2135. case 27:
  2136. if (this.played) {
  2137. this.stop();
  2138. } else if (options.inline) {
  2139. if (this.fulled) {
  2140. this.exit();
  2141. }
  2142. } else {
  2143. this.hide();
  2144. }
  2145. break;
  2146.  
  2147. // Space
  2148. case 32:
  2149. if (this.played) {
  2150. this.stop();
  2151. }
  2152. break;
  2153.  
  2154. // ArrowLeft
  2155. case 37:
  2156. if (this.played && this.playing) {
  2157. this.playing.prev();
  2158. } else {
  2159. this.prev(options.loop);
  2160. }
  2161. break;
  2162.  
  2163. // ArrowUp
  2164. case 38:
  2165. // Prevent scroll on Firefox
  2166. event.preventDefault();
  2167.  
  2168. // Zoom in
  2169. this.zoom(options.zoomRatio, true);
  2170. break;
  2171.  
  2172. // ArrowRight
  2173. case 39:
  2174. if (this.played && this.playing) {
  2175. this.playing.next();
  2176. } else {
  2177. this.next(options.loop);
  2178. }
  2179. break;
  2180.  
  2181. // ArrowDown
  2182. case 40:
  2183. // Prevent scroll on Firefox
  2184. event.preventDefault();
  2185.  
  2186. // Zoom out
  2187. this.zoom(-options.zoomRatio, true);
  2188. break;
  2189.  
  2190. // Ctrl + 0
  2191. case 48:
  2192. // Fall through
  2193.  
  2194. // Ctrl + 1
  2195. // eslint-disable-next-line no-fallthrough
  2196. case 49:
  2197. if (event.ctrlKey) {
  2198. event.preventDefault();
  2199. this.toggle();
  2200. }
  2201. break;
  2202. }
  2203. },
  2204. dragstart: function dragstart(event) {
  2205. if (event.target.localName === "img") {
  2206. event.preventDefault();
  2207. }
  2208. },
  2209. pointerdown: function pointerdown(event) {
  2210. var options = this.options,
  2211. pointers = this.pointers;
  2212. var buttons = event.buttons,
  2213. button = event.button;
  2214. this.pointerMoved = false;
  2215. if (
  2216. !this.viewed ||
  2217. this.showing ||
  2218. this.viewing ||
  2219. this.hiding ||
  2220. // Handle mouse event and pointer event and ignore touch event
  2221. ((event.type === "mousedown" ||
  2222. (event.type === "pointerdown" && event.pointerType === "mouse")) &&
  2223. // No primary button (Usually the left button)
  2224. ((isNumber(buttons) && buttons !== 1) ||
  2225. (isNumber(button) && button !== 0) ||
  2226. // Open context menu
  2227. event.ctrlKey))
  2228. ) {
  2229. return;
  2230. }
  2231.  
  2232. // Prevent default behaviours as page zooming in touch devices.
  2233. event.preventDefault();
  2234. if (event.changedTouches) {
  2235. forEach(event.changedTouches, function (touch) {
  2236. pointers[touch.identifier] = getPointer(touch);
  2237. });
  2238. } else {
  2239. pointers[event.pointerId || 0] = getPointer(event);
  2240. }
  2241. var action = options.movable ? ACTION_MOVE : false;
  2242. if (
  2243. options.zoomOnTouch &&
  2244. options.zoomable &&
  2245. Object.keys(pointers).length > 1
  2246. ) {
  2247. action = ACTION_ZOOM;
  2248. } else if (
  2249. options.slideOnTouch &&
  2250. (event.pointerType === "touch" || event.type === "touchstart") &&
  2251. this.isSwitchable()
  2252. ) {
  2253. action = ACTION_SWITCH;
  2254. }
  2255. if (
  2256. options.transition &&
  2257. (action === ACTION_MOVE || action === ACTION_ZOOM)
  2258. ) {
  2259. removeClass(this.image, CLASS_TRANSITION);
  2260. }
  2261. this.action = action;
  2262. },
  2263. pointermove: function pointermove(event) {
  2264. var pointers = this.pointers,
  2265. action = this.action;
  2266. if (!this.viewed || !action) {
  2267. return;
  2268. }
  2269. event.preventDefault();
  2270. if (event.changedTouches) {
  2271. forEach(event.changedTouches, function (touch) {
  2272. assign(pointers[touch.identifier] || {}, getPointer(touch, true));
  2273. });
  2274. } else {
  2275. assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
  2276. }
  2277. this.change(event);
  2278. },
  2279. pointerup: function pointerup(event) {
  2280. var _this2 = this;
  2281. var options = this.options,
  2282. action = this.action,
  2283. pointers = this.pointers;
  2284. var pointer;
  2285. if (event.changedTouches) {
  2286. forEach(event.changedTouches, function (touch) {
  2287. pointer = pointers[touch.identifier];
  2288. delete pointers[touch.identifier];
  2289. });
  2290. } else {
  2291. pointer = pointers[event.pointerId || 0];
  2292. delete pointers[event.pointerId || 0];
  2293. }
  2294. if (!action) {
  2295. return;
  2296. }
  2297. event.preventDefault();
  2298. if (
  2299. options.transition &&
  2300. (action === ACTION_MOVE || action === ACTION_ZOOM)
  2301. ) {
  2302. addClass(this.image, CLASS_TRANSITION);
  2303. }
  2304. this.action = false;
  2305.  
  2306. // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
  2307. if (
  2308. IS_TOUCH_DEVICE &&
  2309. action !== ACTION_ZOOM &&
  2310. pointer &&
  2311. Date.now() - pointer.timeStamp < 500
  2312. ) {
  2313. clearTimeout(this.clickCanvasTimeout);
  2314. clearTimeout(this.doubleClickImageTimeout);
  2315. if (
  2316. options.toggleOnDblclick &&
  2317. this.viewed &&
  2318. event.target === this.image
  2319. ) {
  2320. if (this.imageClicked) {
  2321. this.imageClicked = false;
  2322.  
  2323. // This timeout will be cleared later when a native dblclick event is triggering
  2324. this.doubleClickImageTimeout = setTimeout(function () {
  2325. dispatchEvent(_this2.image, EVENT_DBLCLICK, {
  2326. originalEvent: event,
  2327. });
  2328. }, 50);
  2329. } else {
  2330. this.imageClicked = true;
  2331.  
  2332. // The default timing of a double click in Windows is 500 ms
  2333. this.doubleClickImageTimeout = setTimeout(function () {
  2334. _this2.imageClicked = false;
  2335. }, 500);
  2336. }
  2337. } else {
  2338. this.imageClicked = false;
  2339. if (
  2340. options.backdrop &&
  2341. options.backdrop !== "static" &&
  2342. event.target === this.canvas
  2343. ) {
  2344. // This timeout will be cleared later when a native click event is triggering
  2345. this.clickCanvasTimeout = setTimeout(function () {
  2346. dispatchEvent(_this2.canvas, EVENT_CLICK, {
  2347. originalEvent: event,
  2348. });
  2349. }, 50);
  2350. }
  2351. }
  2352. }
  2353. },
  2354. resize: function resize() {
  2355. var _this3 = this;
  2356. if (!this.isShown || this.hiding) {
  2357. return;
  2358. }
  2359. if (this.fulled) {
  2360. this.close();
  2361. this.initBody();
  2362. this.open();
  2363. }
  2364. this.initContainer();
  2365. this.initViewer();
  2366. this.renderViewer();
  2367. this.renderList();
  2368. if (this.viewed) {
  2369. this.initImage(function () {
  2370. _this3.renderImage();
  2371. });
  2372. }
  2373. if (this.played) {
  2374. if (
  2375. this.options.fullscreen &&
  2376. this.fulled &&
  2377. !(
  2378. document.fullscreenElement ||
  2379. document.webkitFullscreenElement ||
  2380. document.mozFullScreenElement ||
  2381. document.msFullscreenElement
  2382. )
  2383. ) {
  2384. this.stop();
  2385. return;
  2386. }
  2387. forEach(this.player.getElementsByTagName("img"), function (image) {
  2388. addListener(image, EVENT_LOAD, _this3.loadImage.bind(_this3), {
  2389. once: true,
  2390. });
  2391. dispatchEvent(image, EVENT_LOAD);
  2392. });
  2393. }
  2394. },
  2395. wheel: function wheel(event) {
  2396. var _this4 = this;
  2397. if (!this.viewed) {
  2398. return;
  2399. }
  2400. event.preventDefault();
  2401.  
  2402. // Limit wheel speed to prevent zoom too fast
  2403. if (this.wheeling) {
  2404. return;
  2405. }
  2406. this.wheeling = true;
  2407. setTimeout(function () {
  2408. _this4.wheeling = false;
  2409. }, 50);
  2410. var ratio = Number(this.options.zoomRatio) || 0.1;
  2411. var delta = 1;
  2412. if (event.deltaY) {
  2413. delta = event.deltaY > 0 ? 1 : -1;
  2414. } else if (event.wheelDelta) {
  2415. delta = -event.wheelDelta / 120;
  2416. } else if (event.detail) {
  2417. delta = event.detail > 0 ? 1 : -1;
  2418. }
  2419. this.zoom(-delta * ratio, true, null, event);
  2420. },
  2421. };
  2422.  
  2423. var methods = {
  2424. /** Show the viewer (only available in modal mode)
  2425. * @param {boolean} [immediate=false] - Indicates if show the viewer immediately or not.
  2426. * @returns {Viewer} this
  2427. */
  2428. show: function show() {
  2429. var immediate =
  2430. arguments.length > 0 && arguments[0] !== undefined
  2431. ? arguments[0]
  2432. : false;
  2433. var element = this.element,
  2434. options = this.options;
  2435. if (options.inline || this.showing || this.isShown || this.showing) {
  2436. return this;
  2437. }
  2438. if (!this.ready) {
  2439. this.build();
  2440. if (this.ready) {
  2441. this.show(immediate);
  2442. }
  2443. return this;
  2444. }
  2445. if (isFunction(options.show)) {
  2446. addListener(element, EVENT_SHOW, options.show, {
  2447. once: true,
  2448. });
  2449. }
  2450. if (dispatchEvent(element, EVENT_SHOW) === false || !this.ready) {
  2451. return this;
  2452. }
  2453. if (this.hiding) {
  2454. this.transitioning.abort();
  2455. }
  2456. this.showing = true;
  2457. this.open();
  2458. var viewer = this.viewer;
  2459. removeClass(viewer, CLASS_HIDE);
  2460. viewer.setAttribute("role", "dialog");
  2461. viewer.setAttribute("aria-labelledby", this.title.id);
  2462. viewer.setAttribute("aria-modal", true);
  2463. viewer.removeAttribute("aria-hidden");
  2464. if (options.transition && !immediate) {
  2465. var shown = this.shown.bind(this);
  2466. this.transitioning = {
  2467. abort: function abort() {
  2468. removeListener(viewer, EVENT_TRANSITION_END, shown);
  2469. removeClass(viewer, CLASS_IN);
  2470. },
  2471. };
  2472. addClass(viewer, CLASS_TRANSITION);
  2473.  
  2474. // Force reflow to enable CSS3 transition
  2475. viewer.initialOffsetWidth = viewer.offsetWidth;
  2476. addListener(viewer, EVENT_TRANSITION_END, shown, {
  2477. once: true,
  2478. });
  2479. addClass(viewer, CLASS_IN);
  2480. } else {
  2481. addClass(viewer, CLASS_IN);
  2482. this.shown();
  2483. }
  2484. return this;
  2485. },
  2486. /**
  2487. * Hide the viewer (only available in modal mode)
  2488. * @param {boolean} [immediate=false] - Indicates if hide the viewer immediately or not.
  2489. * @returns {Viewer} this
  2490. */
  2491. hide: function hide() {
  2492. var _this = this;
  2493. var immediate =
  2494. arguments.length > 0 && arguments[0] !== undefined
  2495. ? arguments[0]
  2496. : false;
  2497. var element = this.element,
  2498. options = this.options;
  2499. if (options.inline || this.hiding || !(this.isShown || this.showing)) {
  2500. return this;
  2501. }
  2502. if (isFunction(options.hide)) {
  2503. addListener(element, EVENT_HIDE, options.hide, {
  2504. once: true,
  2505. });
  2506. }
  2507. if (dispatchEvent(element, EVENT_HIDE) === false) {
  2508. return this;
  2509. }
  2510. if (this.showing) {
  2511. this.transitioning.abort();
  2512. }
  2513. this.hiding = true;
  2514. if (this.played) {
  2515. this.stop();
  2516. } else if (this.viewing) {
  2517. this.viewing.abort();
  2518. }
  2519. var viewer = this.viewer,
  2520. image = this.image;
  2521. var hideImmediately = function hideImmediately() {
  2522. removeClass(viewer, CLASS_IN);
  2523. _this.hidden();
  2524. };
  2525. if (options.transition && !immediate) {
  2526. var onViewerTransitionEnd = function onViewerTransitionEnd(event) {
  2527. // Ignore all propagating `transitionend` events (#275).
  2528. if (event && event.target === viewer) {
  2529. removeListener(viewer, EVENT_TRANSITION_END, onViewerTransitionEnd);
  2530. _this.hidden();
  2531. }
  2532. };
  2533. var onImageTransitionEnd = function onImageTransitionEnd() {
  2534. // In case of show the viewer by `viewer.show(true)` previously (#407).
  2535. if (hasClass(viewer, CLASS_TRANSITION)) {
  2536. addListener(viewer, EVENT_TRANSITION_END, onViewerTransitionEnd);
  2537. removeClass(viewer, CLASS_IN);
  2538. } else {
  2539. hideImmediately();
  2540. }
  2541. };
  2542. this.transitioning = {
  2543. abort: function abort() {
  2544. if (_this.viewed && hasClass(image, CLASS_TRANSITION)) {
  2545. removeListener(image, EVENT_TRANSITION_END, onImageTransitionEnd);
  2546. } else if (hasClass(viewer, CLASS_TRANSITION)) {
  2547. removeListener(
  2548. viewer,
  2549. EVENT_TRANSITION_END,
  2550. onViewerTransitionEnd
  2551. );
  2552. }
  2553. },
  2554. };
  2555.  
  2556. // In case of hiding the viewer when holding on the image (#255),
  2557. // note that the `CLASS_TRANSITION` class will be removed on pointer down.
  2558. if (this.viewed && hasClass(image, CLASS_TRANSITION)) {
  2559. addListener(image, EVENT_TRANSITION_END, onImageTransitionEnd, {
  2560. once: true,
  2561. });
  2562. this.zoomTo(0, false, null, null, true);
  2563. } else {
  2564. onImageTransitionEnd();
  2565. }
  2566. } else {
  2567. hideImmediately();
  2568. }
  2569. return this;
  2570. },
  2571. /**
  2572. * View one of the images with image's index
  2573. * @param {number} index - The index of the image to view.
  2574. * @returns {Viewer} this
  2575. */
  2576. view: function view() {
  2577. var _this2 = this;
  2578. var index =
  2579. arguments.length > 0 && arguments[0] !== undefined
  2580. ? arguments[0]
  2581. : this.options.initialViewIndex;
  2582. index = Number(index) || 0;
  2583. if (
  2584. this.hiding ||
  2585. this.played ||
  2586. index < 0 ||
  2587. index >= this.length ||
  2588. (this.viewed && index === this.index)
  2589. ) {
  2590. return this;
  2591. }
  2592. if (!this.isShown) {
  2593. this.index = index;
  2594. return this.show();
  2595. }
  2596. if (this.viewing) {
  2597. this.viewing.abort();
  2598. }
  2599. var element = this.element,
  2600. options = this.options,
  2601. title = this.title,
  2602. canvas = this.canvas;
  2603. var item = this.items[index];
  2604. var img = item.querySelector("img");
  2605. var url = getData(img, "originalUrl");
  2606. var alt = img.getAttribute("alt");
  2607. var image = document.createElement("img");
  2608. forEach(options.inheritedAttributes, function (name) {
  2609. var value = img.getAttribute(name);
  2610. if (value !== null) {
  2611. image.setAttribute(name, value);
  2612. }
  2613. });
  2614. image.src = url;
  2615. image.alt = alt;
  2616. if (isFunction(options.view)) {
  2617. addListener(element, EVENT_VIEW, options.view, {
  2618. once: true,
  2619. });
  2620. }
  2621. if (
  2622. dispatchEvent(element, EVENT_VIEW, {
  2623. originalImage: this.images[index],
  2624. index: index,
  2625. image: image,
  2626. }) === false ||
  2627. !this.isShown ||
  2628. this.hiding ||
  2629. this.played
  2630. ) {
  2631. return this;
  2632. }
  2633. var activeItem = this.items[this.index];
  2634. if (activeItem) {
  2635. removeClass(activeItem, CLASS_ACTIVE);
  2636. activeItem.removeAttribute("aria-selected");
  2637. }
  2638. addClass(item, CLASS_ACTIVE);
  2639. item.setAttribute("aria-selected", true);
  2640. if (options.focus) {
  2641. item.focus();
  2642. }
  2643. this.image = image;
  2644. this.viewed = false;
  2645. this.index = index;
  2646. this.imageData = {};
  2647. addClass(image, CLASS_INVISIBLE);
  2648. if (options.loading) {
  2649. addClass(canvas, CLASS_LOADING);
  2650. }
  2651. canvas.innerHTML = "";
  2652. canvas.appendChild(image);
  2653.  
  2654. // Center current item
  2655. this.renderList();
  2656.  
  2657. // Clear title
  2658. title.innerHTML = "";
  2659.  
  2660. // Generate title after viewed
  2661. var onViewed = function onViewed() {
  2662. var imageData = _this2.imageData;
  2663. var render = Array.isArray(options.title)
  2664. ? options.title[1]
  2665. : options.title;
  2666. title.innerHTML = escapeHTMLEntities(
  2667. isFunction(render)
  2668. ? render.call(_this2, image, imageData)
  2669. : ""
  2670. .concat(alt, " (")
  2671. .concat(imageData.naturalWidth, " \xD7 ")
  2672. .concat(imageData.naturalHeight, ")")
  2673. );
  2674. };
  2675. var onLoad;
  2676. var onError;
  2677. addListener(element, EVENT_VIEWED, onViewed, {
  2678. once: true,
  2679. });
  2680. this.viewing = {
  2681. abort: function abort() {
  2682. removeListener(element, EVENT_VIEWED, onViewed);
  2683. if (image.complete) {
  2684. if (_this2.imageRendering) {
  2685. _this2.imageRendering.abort();
  2686. } else if (_this2.imageInitializing) {
  2687. _this2.imageInitializing.abort();
  2688. }
  2689. } else {
  2690. // Cancel download to save bandwidth.
  2691. image.src = "";
  2692. removeListener(image, EVENT_LOAD, onLoad);
  2693. if (_this2.timeout) {
  2694. clearTimeout(_this2.timeout);
  2695. }
  2696. }
  2697. },
  2698. };
  2699. if (image.complete) {
  2700. this.load();
  2701. } else {
  2702. addListener(
  2703. image,
  2704. EVENT_LOAD,
  2705. (onLoad = function onLoad() {
  2706. removeListener(image, EVENT_ERROR, onError);
  2707. _this2.load();
  2708. }),
  2709. {
  2710. once: true,
  2711. }
  2712. );
  2713. addListener(
  2714. image,
  2715. EVENT_ERROR,
  2716. (onError = function onError() {
  2717. removeListener(image, EVENT_LOAD, onLoad);
  2718. if (_this2.timeout) {
  2719. clearTimeout(_this2.timeout);
  2720. _this2.timeout = false;
  2721. }
  2722. removeClass(image, CLASS_INVISIBLE);
  2723. if (options.loading) {
  2724. removeClass(_this2.canvas, CLASS_LOADING);
  2725. }
  2726. }),
  2727. {
  2728. once: true,
  2729. }
  2730. );
  2731. if (this.timeout) {
  2732. clearTimeout(this.timeout);
  2733. }
  2734.  
  2735. // Make the image visible if it fails to load within 1s
  2736. this.timeout = setTimeout(function () {
  2737. removeClass(image, CLASS_INVISIBLE);
  2738. _this2.timeout = false;
  2739. }, 1000);
  2740. }
  2741. return this;
  2742. },
  2743. /**
  2744. * View the previous image
  2745. * @param {boolean} [loop=false] - Indicate if view the last one
  2746. * when it is the first one at present.
  2747. * @returns {Viewer} this
  2748. */
  2749. prev: function prev() {
  2750. var loop =
  2751. arguments.length > 0 && arguments[0] !== undefined
  2752. ? arguments[0]
  2753. : false;
  2754. var index = this.index - 1;
  2755. if (index < 0) {
  2756. index = loop ? this.length - 1 : 0;
  2757. }
  2758. this.view(index);
  2759. return this;
  2760. },
  2761. /**
  2762. * View the next image
  2763. * @param {boolean} [loop=false] - Indicate if view the first one
  2764. * when it is the last one at present.
  2765. * @returns {Viewer} this
  2766. */
  2767. next: function next() {
  2768. var loop =
  2769. arguments.length > 0 && arguments[0] !== undefined
  2770. ? arguments[0]
  2771. : false;
  2772. var maxIndex = this.length - 1;
  2773. var index = this.index + 1;
  2774. if (index > maxIndex) {
  2775. index = loop ? 0 : maxIndex;
  2776. }
  2777. this.view(index);
  2778. return this;
  2779. },
  2780. /**
  2781. * Move the image with relative offsets.
  2782. * @param {number} x - The moving distance in the horizontal direction.
  2783. * @param {number} [y=x] The moving distance in the vertical direction.
  2784. * @returns {Viewer} this
  2785. */
  2786. move: function move(x) {
  2787. var y =
  2788. arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
  2789. var imageData = this.imageData;
  2790. this.moveTo(
  2791. isUndefined(x) ? x : imageData.x + Number(x),
  2792. isUndefined(y) ? y : imageData.y + Number(y)
  2793. );
  2794. return this;
  2795. },
  2796. /**
  2797. * Move the image to an absolute point.
  2798. * @param {number} x - The new position in the horizontal direction.
  2799. * @param {number} [y=x] - The new position in the vertical direction.
  2800. * @param {Event} [_originalEvent=null] - The original event if any.
  2801. * @returns {Viewer} this
  2802. */
  2803. moveTo: function moveTo(x) {
  2804. var _this3 = this;
  2805. var y =
  2806. arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
  2807. var _originalEvent =
  2808. arguments.length > 2 && arguments[2] !== undefined
  2809. ? arguments[2]
  2810. : null;
  2811. var element = this.element,
  2812. options = this.options,
  2813. imageData = this.imageData;
  2814. x = Number(x);
  2815. y = Number(y);
  2816. if (this.viewed && !this.played && options.movable) {
  2817. var oldX = imageData.x;
  2818. var oldY = imageData.y;
  2819. var changed = false;
  2820. if (isNumber(x)) {
  2821. changed = true;
  2822. } else {
  2823. x = oldX;
  2824. }
  2825. if (isNumber(y)) {
  2826. changed = true;
  2827. } else {
  2828. y = oldY;
  2829. }
  2830. if (changed) {
  2831. if (isFunction(options.move)) {
  2832. addListener(element, EVENT_MOVE, options.move, {
  2833. once: true,
  2834. });
  2835. }
  2836. if (
  2837. dispatchEvent(element, EVENT_MOVE, {
  2838. x: x,
  2839. y: y,
  2840. oldX: oldX,
  2841. oldY: oldY,
  2842. originalEvent: _originalEvent,
  2843. }) === false
  2844. ) {
  2845. return this;
  2846. }
  2847. imageData.x = x;
  2848. imageData.y = y;
  2849. imageData.left = x;
  2850. imageData.top = y;
  2851. this.moving = true;
  2852. this.renderImage(function () {
  2853. _this3.moving = false;
  2854. if (isFunction(options.moved)) {
  2855. addListener(element, EVENT_MOVED, options.moved, {
  2856. once: true,
  2857. });
  2858. }
  2859. dispatchEvent(
  2860. element,
  2861. EVENT_MOVED,
  2862. {
  2863. x: x,
  2864. y: y,
  2865. oldX: oldX,
  2866. oldY: oldY,
  2867. originalEvent: _originalEvent,
  2868. },
  2869. {
  2870. cancelable: false,
  2871. }
  2872. );
  2873. });
  2874. }
  2875. }
  2876. return this;
  2877. },
  2878. /**
  2879. * Rotate the image with a relative degree.
  2880. * @param {number} degree - The rotate degree.
  2881. * @returns {Viewer} this
  2882. */
  2883. rotate: function rotate(degree) {
  2884. this.rotateTo((this.imageData.rotate || 0) + Number(degree));
  2885. return this;
  2886. },
  2887. /**
  2888. * Rotate the image to an absolute degree.
  2889. * @param {number} degree - The rotate degree.
  2890. * @returns {Viewer} this
  2891. */
  2892. rotateTo: function rotateTo(degree) {
  2893. var _this4 = this;
  2894. var element = this.element,
  2895. options = this.options,
  2896. imageData = this.imageData;
  2897. degree = Number(degree);
  2898. if (
  2899. isNumber(degree) &&
  2900. this.viewed &&
  2901. !this.played &&
  2902. options.rotatable
  2903. ) {
  2904. var oldDegree = imageData.rotate;
  2905. if (isFunction(options.rotate)) {
  2906. addListener(element, EVENT_ROTATE, options.rotate, {
  2907. once: true,
  2908. });
  2909. }
  2910. if (
  2911. dispatchEvent(element, EVENT_ROTATE, {
  2912. degree: degree,
  2913. oldDegree: oldDegree,
  2914. }) === false
  2915. ) {
  2916. return this;
  2917. }
  2918. imageData.rotate = degree;
  2919. this.rotating = true;
  2920. this.renderImage(function () {
  2921. _this4.rotating = false;
  2922. if (isFunction(options.rotated)) {
  2923. addListener(element, EVENT_ROTATED, options.rotated, {
  2924. once: true,
  2925. });
  2926. }
  2927. dispatchEvent(
  2928. element,
  2929. EVENT_ROTATED,
  2930. {
  2931. degree: degree,
  2932. oldDegree: oldDegree,
  2933. },
  2934. {
  2935. cancelable: false,
  2936. }
  2937. );
  2938. });
  2939. }
  2940. return this;
  2941. },
  2942. /**
  2943. * Scale the image on the x-axis.
  2944. * @param {number} scaleX - The scale ratio on the x-axis.
  2945. * @returns {Viewer} this
  2946. */
  2947. scaleX: function scaleX(_scaleX) {
  2948. this.scale(_scaleX, this.imageData.scaleY);
  2949. return this;
  2950. },
  2951. /**
  2952. * Scale the image on the y-axis.
  2953. * @param {number} scaleY - The scale ratio on the y-axis.
  2954. * @returns {Viewer} this
  2955. */
  2956. scaleY: function scaleY(_scaleY) {
  2957. this.scale(this.imageData.scaleX, _scaleY);
  2958. return this;
  2959. },
  2960. /**
  2961. * Scale the image.
  2962. * @param {number} scaleX - The scale ratio on the x-axis.
  2963. * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
  2964. * @returns {Viewer} this
  2965. */
  2966. scale: function scale(scaleX) {
  2967. var _this5 = this;
  2968. var scaleY =
  2969. arguments.length > 1 && arguments[1] !== undefined
  2970. ? arguments[1]
  2971. : scaleX;
  2972. var element = this.element,
  2973. options = this.options,
  2974. imageData = this.imageData;
  2975. scaleX = Number(scaleX);
  2976. scaleY = Number(scaleY);
  2977. if (this.viewed && !this.played && options.scalable) {
  2978. var oldScaleX = imageData.scaleX;
  2979. var oldScaleY = imageData.scaleY;
  2980. var changed = false;
  2981. if (isNumber(scaleX)) {
  2982. changed = true;
  2983. } else {
  2984. scaleX = oldScaleX;
  2985. }
  2986. if (isNumber(scaleY)) {
  2987. changed = true;
  2988. } else {
  2989. scaleY = oldScaleY;
  2990. }
  2991. if (changed) {
  2992. if (isFunction(options.scale)) {
  2993. addListener(element, EVENT_SCALE, options.scale, {
  2994. once: true,
  2995. });
  2996. }
  2997. if (
  2998. dispatchEvent(element, EVENT_SCALE, {
  2999. scaleX: scaleX,
  3000. scaleY: scaleY,
  3001. oldScaleX: oldScaleX,
  3002. oldScaleY: oldScaleY,
  3003. }) === false
  3004. ) {
  3005. return this;
  3006. }
  3007. imageData.scaleX = scaleX;
  3008. imageData.scaleY = scaleY;
  3009. this.scaling = true;
  3010. this.renderImage(function () {
  3011. _this5.scaling = false;
  3012. if (isFunction(options.scaled)) {
  3013. addListener(element, EVENT_SCALED, options.scaled, {
  3014. once: true,
  3015. });
  3016. }
  3017. dispatchEvent(
  3018. element,
  3019. EVENT_SCALED,
  3020. {
  3021. scaleX: scaleX,
  3022. scaleY: scaleY,
  3023. oldScaleX: oldScaleX,
  3024. oldScaleY: oldScaleY,
  3025. },
  3026. {
  3027. cancelable: false,
  3028. }
  3029. );
  3030. });
  3031. }
  3032. }
  3033. return this;
  3034. },
  3035. /**
  3036. * Zoom the image with a relative ratio.
  3037. * @param {number} ratio - The target ratio.
  3038. * @param {boolean} [showTooltip=false] - Indicates whether to show the tooltip.
  3039. * @param {Object} [pivot] - The pivot point coordinate for zooming.
  3040. * @param {Event} [_originalEvent=null] - The original event if any.
  3041. * @returns {Viewer} this
  3042. */
  3043. zoom: function zoom(ratio) {
  3044. var showTooltip =
  3045. arguments.length > 1 && arguments[1] !== undefined
  3046. ? arguments[1]
  3047. : false;
  3048. var pivot =
  3049. arguments.length > 2 && arguments[2] !== undefined
  3050. ? arguments[2]
  3051. : null;
  3052. var _originalEvent =
  3053. arguments.length > 3 && arguments[3] !== undefined
  3054. ? arguments[3]
  3055. : null;
  3056. var imageData = this.imageData;
  3057. ratio = Number(ratio);
  3058. if (ratio < 0) {
  3059. ratio = 1 / (1 - ratio);
  3060. } else {
  3061. ratio = 1 + ratio;
  3062. }
  3063. this.zoomTo(
  3064. (imageData.width * ratio) / imageData.naturalWidth,
  3065. showTooltip,
  3066. pivot,
  3067. _originalEvent
  3068. );
  3069. return this;
  3070. },
  3071. /**
  3072. * Zoom the image to an absolute ratio.
  3073. * @param {number} ratio - The target ratio.
  3074. * @param {boolean} [showTooltip] - Indicates whether to show the tooltip.
  3075. * @param {Object} [pivot] - The pivot point coordinate for zooming.
  3076. * @param {Event} [_originalEvent=null] - The original event if any.
  3077. * @param {Event} [_zoomable=false] - Indicates if the current zoom is available or not.
  3078. * @returns {Viewer} this
  3079. */
  3080. zoomTo: function zoomTo(ratio) {
  3081. var _this6 = this;
  3082. var showTooltip =
  3083. arguments.length > 1 && arguments[1] !== undefined
  3084. ? arguments[1]
  3085. : false;
  3086. var pivot =
  3087. arguments.length > 2 && arguments[2] !== undefined
  3088. ? arguments[2]
  3089. : null;
  3090. var _originalEvent =
  3091. arguments.length > 3 && arguments[3] !== undefined
  3092. ? arguments[3]
  3093. : null;
  3094. var _zoomable =
  3095. arguments.length > 4 && arguments[4] !== undefined
  3096. ? arguments[4]
  3097. : false;
  3098. var element = this.element,
  3099. options = this.options,
  3100. pointers = this.pointers,
  3101. imageData = this.imageData;
  3102. var x = imageData.x,
  3103. y = imageData.y,
  3104. width = imageData.width,
  3105. height = imageData.height,
  3106. naturalWidth = imageData.naturalWidth,
  3107. naturalHeight = imageData.naturalHeight;
  3108. ratio = Math.max(0, ratio);
  3109. if (
  3110. isNumber(ratio) &&
  3111. this.viewed &&
  3112. !this.played &&
  3113. (_zoomable || options.zoomable)
  3114. ) {
  3115. if (!_zoomable) {
  3116. var minZoomRatio = Math.max(0.01, options.minZoomRatio);
  3117. var maxZoomRatio = Math.min(100, options.maxZoomRatio);
  3118. ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
  3119. }
  3120. if (_originalEvent) {
  3121. switch (_originalEvent.type) {
  3122. case "wheel":
  3123. if (options.zoomRatio >= 0.055 && ratio > 0.95 && ratio < 1.05) {
  3124. ratio = 1;
  3125. }
  3126. break;
  3127. case "pointermove":
  3128. case "touchmove":
  3129. case "mousemove":
  3130. if (ratio > 0.99 && ratio < 1.01) {
  3131. ratio = 1;
  3132. }
  3133. break;
  3134. }
  3135. }
  3136. var newWidth = naturalWidth * ratio;
  3137. var newHeight = naturalHeight * ratio;
  3138. var offsetWidth = newWidth - width;
  3139. var offsetHeight = newHeight - height;
  3140. var oldRatio = imageData.ratio;
  3141. if (isFunction(options.zoom)) {
  3142. addListener(element, EVENT_ZOOM, options.zoom, {
  3143. once: true,
  3144. });
  3145. }
  3146. if (
  3147. dispatchEvent(element, EVENT_ZOOM, {
  3148. ratio: ratio,
  3149. oldRatio: oldRatio,
  3150. originalEvent: _originalEvent,
  3151. }) === false
  3152. ) {
  3153. return this;
  3154. }
  3155. this.zooming = true;
  3156. if (_originalEvent) {
  3157. var offset = getOffset(this.viewer);
  3158. var center =
  3159. pointers && Object.keys(pointers).length > 0
  3160. ? getPointersCenter(pointers)
  3161. : {
  3162. pageX: _originalEvent.pageX,
  3163. pageY: _originalEvent.pageY,
  3164. };
  3165.  
  3166. // Zoom from the triggering point of the event
  3167. imageData.x -=
  3168. offsetWidth * ((center.pageX - offset.left - x) / width);
  3169. imageData.y -=
  3170. offsetHeight * ((center.pageY - offset.top - y) / height);
  3171. } else if (
  3172. isPlainObject(pivot) &&
  3173. isNumber(pivot.x) &&
  3174. isNumber(pivot.y)
  3175. ) {
  3176. imageData.x -= offsetWidth * ((pivot.x - x) / width);
  3177. imageData.y -= offsetHeight * ((pivot.y - y) / height);
  3178. } else {
  3179. // Zoom from the center of the image
  3180. imageData.x -= offsetWidth / 2;
  3181. imageData.y -= offsetHeight / 2;
  3182. }
  3183. imageData.left = imageData.x;
  3184. imageData.top = imageData.y;
  3185. imageData.width = newWidth;
  3186. imageData.height = newHeight;
  3187. imageData.oldRatio = oldRatio;
  3188. imageData.ratio = ratio;
  3189. this.renderImage(function () {
  3190. _this6.zooming = false;
  3191. if (isFunction(options.zoomed)) {
  3192. addListener(element, EVENT_ZOOMED, options.zoomed, {
  3193. once: true,
  3194. });
  3195. }
  3196. dispatchEvent(
  3197. element,
  3198. EVENT_ZOOMED,
  3199. {
  3200. ratio: ratio,
  3201. oldRatio: oldRatio,
  3202. originalEvent: _originalEvent,
  3203. },
  3204. {
  3205. cancelable: false,
  3206. }
  3207. );
  3208. });
  3209. if (showTooltip) {
  3210. this.tooltip();
  3211. }
  3212. }
  3213. return this;
  3214. },
  3215. /**
  3216. * Play the images
  3217. * @param {boolean|FullscreenOptions} [fullscreen=false] - Indicate if request fullscreen or not.
  3218. * @returns {Viewer} this
  3219. */
  3220. play: function play() {
  3221. var _this7 = this;
  3222. var fullscreen =
  3223. arguments.length > 0 && arguments[0] !== undefined
  3224. ? arguments[0]
  3225. : false;
  3226. if (!this.isShown || this.played) {
  3227. return this;
  3228. }
  3229. var element = this.element,
  3230. options = this.options;
  3231. if (isFunction(options.play)) {
  3232. addListener(element, EVENT_PLAY, options.play, {
  3233. once: true,
  3234. });
  3235. }
  3236. if (dispatchEvent(element, EVENT_PLAY) === false) {
  3237. return this;
  3238. }
  3239. var player = this.player;
  3240. var onLoad = this.loadImage.bind(this);
  3241. var list = [];
  3242. var total = 0;
  3243. var index = 0;
  3244. this.played = true;
  3245. this.onLoadWhenPlay = onLoad;
  3246. if (fullscreen) {
  3247. this.requestFullscreen(fullscreen);
  3248. }
  3249. addClass(player, CLASS_SHOW);
  3250. forEach(this.items, function (item, i) {
  3251. var img = item.querySelector("img");
  3252. var image = document.createElement("img");
  3253. image.src = getData(img, "originalUrl");
  3254. image.alt = img.getAttribute("alt");
  3255. image.referrerPolicy = img.referrerPolicy;
  3256. total += 1;
  3257. addClass(image, CLASS_FADE);
  3258. toggleClass(image, CLASS_TRANSITION, options.transition);
  3259. if (hasClass(item, CLASS_ACTIVE)) {
  3260. addClass(image, CLASS_IN);
  3261. index = i;
  3262. }
  3263. list.push(image);
  3264. addListener(image, EVENT_LOAD, onLoad, {
  3265. once: true,
  3266. });
  3267. player.appendChild(image);
  3268. });
  3269. if (isNumber(options.interval) && options.interval > 0) {
  3270. var prev = function prev() {
  3271. clearTimeout(_this7.playing.timeout);
  3272. removeClass(list[index], CLASS_IN);
  3273. index -= 1;
  3274. index = index >= 0 ? index : total - 1;
  3275. addClass(list[index], CLASS_IN);
  3276. _this7.playing.timeout = setTimeout(prev, options.interval);
  3277. };
  3278. var next = function next() {
  3279. clearTimeout(_this7.playing.timeout);
  3280. removeClass(list[index], CLASS_IN);
  3281. index += 1;
  3282. index = index < total ? index : 0;
  3283. addClass(list[index], CLASS_IN);
  3284. _this7.playing.timeout = setTimeout(next, options.interval);
  3285. };
  3286. if (total > 1) {
  3287. this.playing = {
  3288. prev: prev,
  3289. next: next,
  3290. timeout: setTimeout(next, options.interval),
  3291. };
  3292. }
  3293. }
  3294. return this;
  3295. },
  3296. // Stop play
  3297. stop: function stop() {
  3298. var _this8 = this;
  3299. if (!this.played) {
  3300. return this;
  3301. }
  3302. var element = this.element,
  3303. options = this.options;
  3304. if (isFunction(options.stop)) {
  3305. addListener(element, EVENT_STOP, options.stop, {
  3306. once: true,
  3307. });
  3308. }
  3309. if (dispatchEvent(element, EVENT_STOP) === false) {
  3310. return this;
  3311. }
  3312. var player = this.player;
  3313. clearTimeout(this.playing.timeout);
  3314. this.playing = false;
  3315. this.played = false;
  3316. forEach(player.getElementsByTagName("img"), function (image) {
  3317. removeListener(image, EVENT_LOAD, _this8.onLoadWhenPlay);
  3318. });
  3319. removeClass(player, CLASS_SHOW);
  3320. player.innerHTML = "";
  3321. this.exitFullscreen();
  3322. return this;
  3323. },
  3324. // Enter modal mode (only available in inline mode)
  3325. full: function full() {
  3326. var _this9 = this;
  3327. var options = this.options,
  3328. viewer = this.viewer,
  3329. image = this.image,
  3330. list = this.list;
  3331. if (!this.isShown || this.played || this.fulled || !options.inline) {
  3332. return this;
  3333. }
  3334. this.fulled = true;
  3335. this.open();
  3336. addClass(this.button, CLASS_FULLSCREEN_EXIT);
  3337. if (options.transition) {
  3338. removeClass(list, CLASS_TRANSITION);
  3339. if (this.viewed) {
  3340. removeClass(image, CLASS_TRANSITION);
  3341. }
  3342. }
  3343. addClass(viewer, CLASS_FIXED);
  3344. viewer.setAttribute("role", "dialog");
  3345. viewer.setAttribute("aria-labelledby", this.title.id);
  3346. viewer.setAttribute("aria-modal", true);
  3347. viewer.removeAttribute("style");
  3348. setStyle(viewer, {
  3349. zIndex: options.zIndex,
  3350. });
  3351. if (options.focus) {
  3352. this.enforceFocus();
  3353. }
  3354. this.initContainer();
  3355. this.viewerData = assign({}, this.containerData);
  3356. this.renderList();
  3357. if (this.viewed) {
  3358. this.initImage(function () {
  3359. _this9.renderImage(function () {
  3360. if (options.transition) {
  3361. setTimeout(function () {
  3362. addClass(image, CLASS_TRANSITION);
  3363. addClass(list, CLASS_TRANSITION);
  3364. }, 0);
  3365. }
  3366. });
  3367. });
  3368. }
  3369. return this;
  3370. },
  3371. // Exit modal mode (only available in inline mode)
  3372. exit: function exit() {
  3373. var _this10 = this;
  3374. var options = this.options,
  3375. viewer = this.viewer,
  3376. image = this.image,
  3377. list = this.list;
  3378. if (!this.isShown || this.played || !this.fulled || !options.inline) {
  3379. return this;
  3380. }
  3381. this.fulled = false;
  3382. this.close();
  3383. removeClass(this.button, CLASS_FULLSCREEN_EXIT);
  3384. if (options.transition) {
  3385. removeClass(list, CLASS_TRANSITION);
  3386. if (this.viewed) {
  3387. removeClass(image, CLASS_TRANSITION);
  3388. }
  3389. }
  3390. if (options.focus) {
  3391. this.clearEnforceFocus();
  3392. }
  3393. viewer.removeAttribute("role");
  3394. viewer.removeAttribute("aria-labelledby");
  3395. viewer.removeAttribute("aria-modal");
  3396. removeClass(viewer, CLASS_FIXED);
  3397. setStyle(viewer, {
  3398. zIndex: options.zIndexInline,
  3399. });
  3400. this.viewerData = assign({}, this.parentData);
  3401. this.renderViewer();
  3402. this.renderList();
  3403. if (this.viewed) {
  3404. this.initImage(function () {
  3405. _this10.renderImage(function () {
  3406. if (options.transition) {
  3407. setTimeout(function () {
  3408. addClass(image, CLASS_TRANSITION);
  3409. addClass(list, CLASS_TRANSITION);
  3410. }, 0);
  3411. }
  3412. });
  3413. });
  3414. }
  3415. return this;
  3416. },
  3417. // Show the current ratio of the image with percentage
  3418. tooltip: function tooltip() {
  3419. var _this11 = this;
  3420. var options = this.options,
  3421. tooltipBox = this.tooltipBox,
  3422. imageData = this.imageData;
  3423. if (!this.viewed || this.played || !options.tooltip) {
  3424. return this;
  3425. }
  3426. tooltipBox.textContent = "".concat(
  3427. Math.round(imageData.ratio * 100),
  3428. "%"
  3429. );
  3430. if (!this.tooltipping) {
  3431. if (options.transition) {
  3432. if (this.fading) {
  3433. dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
  3434. }
  3435. addClass(tooltipBox, CLASS_SHOW);
  3436. addClass(tooltipBox, CLASS_FADE);
  3437. addClass(tooltipBox, CLASS_TRANSITION);
  3438. tooltipBox.removeAttribute("aria-hidden");
  3439.  
  3440. // Force reflow to enable CSS3 transition
  3441. tooltipBox.initialOffsetWidth = tooltipBox.offsetWidth;
  3442. addClass(tooltipBox, CLASS_IN);
  3443. } else {
  3444. addClass(tooltipBox, CLASS_SHOW);
  3445. tooltipBox.removeAttribute("aria-hidden");
  3446. }
  3447. } else {
  3448. clearTimeout(this.tooltipping);
  3449. }
  3450. this.tooltipping = setTimeout(function () {
  3451. if (options.transition) {
  3452. addListener(
  3453. tooltipBox,
  3454. EVENT_TRANSITION_END,
  3455. function () {
  3456. removeClass(tooltipBox, CLASS_SHOW);
  3457. removeClass(tooltipBox, CLASS_FADE);
  3458. removeClass(tooltipBox, CLASS_TRANSITION);
  3459. tooltipBox.setAttribute("aria-hidden", true);
  3460. _this11.fading = false;
  3461. },
  3462. {
  3463. once: true,
  3464. }
  3465. );
  3466. removeClass(tooltipBox, CLASS_IN);
  3467. _this11.fading = true;
  3468. } else {
  3469. removeClass(tooltipBox, CLASS_SHOW);
  3470. tooltipBox.setAttribute("aria-hidden", true);
  3471. }
  3472. _this11.tooltipping = false;
  3473. }, 1000);
  3474. return this;
  3475. },
  3476. /**
  3477. * Toggle the image size between its current size and natural size
  3478. * @param {Event} [_originalEvent=null] - The original event if any.
  3479. * @returns {Viewer} this
  3480. */
  3481. toggle: function toggle() {
  3482. var _originalEvent =
  3483. arguments.length > 0 && arguments[0] !== undefined
  3484. ? arguments[0]
  3485. : null;
  3486. if (this.imageData.ratio === 1) {
  3487. this.zoomTo(this.imageData.oldRatio, true, null, _originalEvent);
  3488. } else {
  3489. this.zoomTo(1, true, null, _originalEvent);
  3490. }
  3491. return this;
  3492. },
  3493. // Reset the image to its initial state
  3494. reset: function reset() {
  3495. if (this.viewed && !this.played) {
  3496. this.imageData = assign({}, this.initialImageData);
  3497. this.renderImage();
  3498. }
  3499. return this;
  3500. },
  3501. // Update viewer when images changed
  3502. update: function update() {
  3503. var _this12 = this;
  3504. var element = this.element,
  3505. options = this.options,
  3506. isImg = this.isImg;
  3507.  
  3508. // Destroy viewer if the target image was deleted
  3509. if (isImg && !element.parentNode) {
  3510. return this.destroy();
  3511. }
  3512. var images = [];
  3513. forEach(
  3514. isImg ? [element] : element.querySelectorAll("img"),
  3515. function (image) {
  3516. if (isFunction(options.filter)) {
  3517. if (options.filter.call(_this12, image)) {
  3518. images.push(image);
  3519. }
  3520. } else if (_this12.getImageURL(image)) {
  3521. images.push(image);
  3522. }
  3523. }
  3524. );
  3525. if (!images.length) {
  3526. return this;
  3527. }
  3528. this.images = images;
  3529. this.length = images.length;
  3530. if (this.ready) {
  3531. var changedIndexes = [];
  3532. forEach(this.items, function (item, i) {
  3533. var img = item.querySelector("img");
  3534. var image = images[i];
  3535. if (image && img) {
  3536. if (
  3537. image.src !== img.src ||
  3538. // Title changed (#408)
  3539. image.alt !== img.alt
  3540. ) {
  3541. changedIndexes.push(i);
  3542. }
  3543. } else {
  3544. changedIndexes.push(i);
  3545. }
  3546. });
  3547. setStyle(this.list, {
  3548. width: "auto",
  3549. });
  3550. this.initList();
  3551. if (this.isShown) {
  3552. if (this.length) {
  3553. if (this.viewed) {
  3554. var changedIndex = changedIndexes.indexOf(this.index);
  3555. if (changedIndex >= 0) {
  3556. this.viewed = false;
  3557. this.view(
  3558. Math.max(
  3559. Math.min(this.index - changedIndex, this.length - 1),
  3560. 0
  3561. )
  3562. );
  3563. } else {
  3564. var activeItem = this.items[this.index];
  3565.  
  3566. // Reactivate the current viewing item after reset the list.
  3567. addClass(activeItem, CLASS_ACTIVE);
  3568. activeItem.setAttribute("aria-selected", true);
  3569. }
  3570. }
  3571. } else {
  3572. this.image = null;
  3573. this.viewed = false;
  3574. this.index = 0;
  3575. this.imageData = {};
  3576. this.canvas.innerHTML = "";
  3577. this.title.innerHTML = "";
  3578. }
  3579. }
  3580. } else {
  3581. this.build();
  3582. }
  3583. return this;
  3584. },
  3585. // Destroy the viewer
  3586. destroy: function destroy() {
  3587. var element = this.element,
  3588. options = this.options;
  3589. if (!element[NAMESPACE]) {
  3590. return this;
  3591. }
  3592. this.destroyed = true;
  3593. if (this.ready) {
  3594. if (this.played) {
  3595. this.stop();
  3596. }
  3597. if (options.inline) {
  3598. if (this.fulled) {
  3599. this.exit();
  3600. }
  3601. this.unbind();
  3602. } else if (this.isShown) {
  3603. if (this.viewing) {
  3604. if (this.imageRendering) {
  3605. this.imageRendering.abort();
  3606. } else if (this.imageInitializing) {
  3607. this.imageInitializing.abort();
  3608. }
  3609. }
  3610. if (this.hiding) {
  3611. this.transitioning.abort();
  3612. }
  3613. this.hidden();
  3614. } else if (this.showing) {
  3615. this.transitioning.abort();
  3616. this.hidden();
  3617. }
  3618. this.ready = false;
  3619. this.viewer.parentNode.removeChild(this.viewer);
  3620. } else if (options.inline) {
  3621. if (this.delaying) {
  3622. this.delaying.abort();
  3623. } else if (this.initializing) {
  3624. this.initializing.abort();
  3625. }
  3626. }
  3627. if (!options.inline) {
  3628. removeListener(element, EVENT_CLICK, this.onStart);
  3629. }
  3630. element[NAMESPACE] = undefined;
  3631. return this;
  3632. },
  3633. };
  3634.  
  3635. var others = {
  3636. getImageURL: function getImageURL(image) {
  3637. var url = this.options.url;
  3638. if (isString(url)) {
  3639. url = image.getAttribute(url);
  3640. } else if (isFunction(url)) {
  3641. url = url.call(this, image);
  3642. } else {
  3643. url = "";
  3644. }
  3645. return url;
  3646. },
  3647. enforceFocus: function enforceFocus() {
  3648. var _this = this;
  3649. this.clearEnforceFocus();
  3650. addListener(
  3651. document,
  3652. EVENT_FOCUSIN,
  3653. (this.onFocusin = function (event) {
  3654. var viewer = _this.viewer;
  3655. var target = event.target;
  3656. if (
  3657. target === document ||
  3658. target === viewer ||
  3659. viewer.contains(target)
  3660. ) {
  3661. return;
  3662. }
  3663. while (target) {
  3664. // Avoid conflicts with other modals (#474, #540)
  3665. if (
  3666. target.getAttribute("tabindex") !== null ||
  3667. target.getAttribute("aria-modal") === "true"
  3668. ) {
  3669. return;
  3670. }
  3671. target = target.parentElement;
  3672. }
  3673. viewer.focus();
  3674. })
  3675. );
  3676. },
  3677. clearEnforceFocus: function clearEnforceFocus() {
  3678. if (this.onFocusin) {
  3679. removeListener(document, EVENT_FOCUSIN, this.onFocusin);
  3680. this.onFocusin = null;
  3681. }
  3682. },
  3683. open: function open() {
  3684. var body = this.body;
  3685. addClass(body, CLASS_OPEN);
  3686. if (this.scrollbarWidth > 0) {
  3687. body.style.paddingRight = "".concat(
  3688. this.scrollbarWidth +
  3689. (parseFloat(this.initialBodyComputedPaddingRight) || 0),
  3690. "px"
  3691. );
  3692. }
  3693. },
  3694. close: function close() {
  3695. var body = this.body;
  3696. removeClass(body, CLASS_OPEN);
  3697. if (this.scrollbarWidth > 0) {
  3698. body.style.paddingRight = this.initialBodyPaddingRight;
  3699. }
  3700. },
  3701. shown: function shown() {
  3702. var element = this.element,
  3703. options = this.options,
  3704. viewer = this.viewer;
  3705. this.fulled = true;
  3706. this.isShown = true;
  3707. this.render();
  3708. this.bind();
  3709. this.showing = false;
  3710. if (options.focus) {
  3711. viewer.focus();
  3712. this.enforceFocus();
  3713. }
  3714. if (isFunction(options.shown)) {
  3715. addListener(element, EVENT_SHOWN, options.shown, {
  3716. once: true,
  3717. });
  3718. }
  3719. if (dispatchEvent(element, EVENT_SHOWN) === false) {
  3720. return;
  3721. }
  3722. if (this.ready && this.isShown && !this.hiding) {
  3723. this.view(this.index);
  3724. }
  3725. },
  3726. hidden: function hidden() {
  3727. var element = this.element,
  3728. options = this.options,
  3729. viewer = this.viewer;
  3730. if (options.fucus) {
  3731. this.clearEnforceFocus();
  3732. }
  3733. this.close();
  3734. this.unbind();
  3735. addClass(viewer, CLASS_HIDE);
  3736. viewer.removeAttribute("role");
  3737. viewer.removeAttribute("aria-labelledby");
  3738. viewer.removeAttribute("aria-modal");
  3739. viewer.setAttribute("aria-hidden", true);
  3740. this.resetList();
  3741. this.resetImage();
  3742. this.fulled = false;
  3743. this.viewed = false;
  3744. this.isShown = false;
  3745. this.hiding = false;
  3746. if (!this.destroyed) {
  3747. if (isFunction(options.hidden)) {
  3748. addListener(element, EVENT_HIDDEN, options.hidden, {
  3749. once: true,
  3750. });
  3751. }
  3752. dispatchEvent(element, EVENT_HIDDEN, null, {
  3753. cancelable: false,
  3754. });
  3755. }
  3756. },
  3757. requestFullscreen: function requestFullscreen(options) {
  3758. var document = this.element.ownerDocument;
  3759. if (
  3760. this.fulled &&
  3761. !(
  3762. document.fullscreenElement ||
  3763. document.webkitFullscreenElement ||
  3764. document.mozFullScreenElement ||
  3765. document.msFullscreenElement
  3766. )
  3767. ) {
  3768. var documentElement = document.documentElement;
  3769.  
  3770. // Element.requestFullscreen()
  3771. if (documentElement.requestFullscreen) {
  3772. // Avoid TypeError when convert `options` to dictionary
  3773. if (isPlainObject(options)) {
  3774. documentElement.requestFullscreen(options);
  3775. } else {
  3776. documentElement.requestFullscreen();
  3777. }
  3778. } else if (documentElement.webkitRequestFullscreen) {
  3779. documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  3780. } else if (documentElement.mozRequestFullScreen) {
  3781. documentElement.mozRequestFullScreen();
  3782. } else if (documentElement.msRequestFullscreen) {
  3783. documentElement.msRequestFullscreen();
  3784. }
  3785. }
  3786. },
  3787. exitFullscreen: function exitFullscreen() {
  3788. var document = this.element.ownerDocument;
  3789. if (
  3790. this.fulled &&
  3791. (document.fullscreenElement ||
  3792. document.webkitFullscreenElement ||
  3793. document.mozFullScreenElement ||
  3794. document.msFullscreenElement)
  3795. ) {
  3796. // Document.exitFullscreen()
  3797. if (document.exitFullscreen) {
  3798. document.exitFullscreen();
  3799. } else if (document.webkitExitFullscreen) {
  3800. document.webkitExitFullscreen();
  3801. } else if (document.mozCancelFullScreen) {
  3802. document.mozCancelFullScreen();
  3803. } else if (document.msExitFullscreen) {
  3804. document.msExitFullscreen();
  3805. }
  3806. }
  3807. },
  3808. change: function change(event) {
  3809. var options = this.options,
  3810. pointers = this.pointers;
  3811. var pointer = pointers[Object.keys(pointers)[0]];
  3812.  
  3813. // In the case of the `pointers` object is empty (#421)
  3814. if (!pointer) {
  3815. return;
  3816. }
  3817. var offsetX = pointer.endX - pointer.startX;
  3818. var offsetY = pointer.endY - pointer.startY;
  3819. switch (this.action) {
  3820. // Move the current image
  3821. case ACTION_MOVE:
  3822. if (offsetX !== 0 || offsetY !== 0) {
  3823. this.pointerMoved = true;
  3824. this.move(offsetX, offsetY, event);
  3825. }
  3826. break;
  3827.  
  3828. // Zoom the current image
  3829. case ACTION_ZOOM:
  3830. this.zoom(getMaxZoomRatio(pointers), false, null, event);
  3831. break;
  3832. case ACTION_SWITCH: {
  3833. this.action = "switched";
  3834. var absoluteOffsetX = Math.abs(offsetX);
  3835. if (absoluteOffsetX > 1 && absoluteOffsetX > Math.abs(offsetY)) {
  3836. // Empty `pointers` as `touchend` event will not be fired after swiped in iOS browsers.
  3837. this.pointers = {};
  3838. if (offsetX > 1) {
  3839. this.prev(options.loop);
  3840. } else if (offsetX < -1) {
  3841. this.next(options.loop);
  3842. }
  3843. }
  3844. break;
  3845. }
  3846. }
  3847.  
  3848. // Override
  3849. forEach(pointers, function (p) {
  3850. p.startX = p.endX;
  3851. p.startY = p.endY;
  3852. });
  3853. },
  3854. isSwitchable: function isSwitchable() {
  3855. var imageData = this.imageData,
  3856. viewerData = this.viewerData;
  3857. return (
  3858. this.length > 1 &&
  3859. imageData.x >= 0 &&
  3860. imageData.y >= 0 &&
  3861. imageData.width <= viewerData.width &&
  3862. imageData.height <= viewerData.height
  3863. );
  3864. },
  3865. };
  3866.  
  3867. var AnotherViewer = WINDOW.Viewer;
  3868. var getUniqueID = (function (id) {
  3869. return function () {
  3870. id += 1;
  3871. return id;
  3872. };
  3873. })(-1);
  3874. var Viewer = /*#__PURE__*/ (function () {
  3875. /**
  3876. * Create a new Viewer.
  3877. * @param {Element} element - The target element for viewing.
  3878. * @param {Object} [options={}] - The configuration options.
  3879. */
  3880. function Viewer(element) {
  3881. var options =
  3882. arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  3883. _classCallCheck(this, Viewer);
  3884. if (!element || element.nodeType !== 1) {
  3885. throw new Error(
  3886. "The first argument is required and must be an element."
  3887. );
  3888. }
  3889. this.element = element;
  3890. this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
  3891. this.action = false;
  3892. this.fading = false;
  3893. this.fulled = false;
  3894. this.hiding = false;
  3895. this.imageClicked = false;
  3896. this.imageData = {};
  3897. this.index = this.options.initialViewIndex;
  3898. this.isImg = false;
  3899. this.isShown = false;
  3900. this.length = 0;
  3901. this.moving = false;
  3902. this.played = false;
  3903. this.playing = false;
  3904. this.pointers = {};
  3905. this.ready = false;
  3906. this.rotating = false;
  3907. this.scaling = false;
  3908. this.showing = false;
  3909. this.timeout = false;
  3910. this.tooltipping = false;
  3911. this.viewed = false;
  3912. this.viewing = false;
  3913. this.wheeling = false;
  3914. this.zooming = false;
  3915. this.pointerMoved = false;
  3916. this.id = getUniqueID();
  3917. this.init();
  3918. }
  3919. _createClass(
  3920. Viewer,
  3921. [
  3922. {
  3923. key: "init",
  3924. value: function init() {
  3925. var _this = this;
  3926. var element = this.element,
  3927. options = this.options;
  3928. if (element[NAMESPACE]) {
  3929. return;
  3930. }
  3931. element[NAMESPACE] = this;
  3932.  
  3933. // The `focus` option requires the `keyboard` option set to `true`.
  3934. if (options.focus && !options.keyboard) {
  3935. options.focus = false;
  3936. }
  3937. var isImg = element.localName === "img";
  3938. var images = [];
  3939. forEach(
  3940. isImg ? [element] : element.querySelectorAll("img"),
  3941. function (image) {
  3942. if (isFunction(options.filter)) {
  3943. if (options.filter.call(_this, image)) {
  3944. images.push(image);
  3945. }
  3946. } else if (_this.getImageURL(image)) {
  3947. images.push(image);
  3948. }
  3949. }
  3950. );
  3951. this.isImg = isImg;
  3952. this.length = images.length;
  3953. this.images = images;
  3954. this.initBody();
  3955.  
  3956. // Override `transition` option if it is not supported
  3957. if (
  3958. isUndefined(document.createElement(NAMESPACE).style.transition)
  3959. ) {
  3960. options.transition = false;
  3961. }
  3962. if (options.inline) {
  3963. var count = 0;
  3964. var progress = function progress() {
  3965. count += 1;
  3966. if (count === _this.length) {
  3967. var timeout;
  3968. _this.initializing = false;
  3969. _this.delaying = {
  3970. abort: function abort() {
  3971. clearTimeout(timeout);
  3972. },
  3973. };
  3974.  
  3975. // build asynchronously to keep `this.viewer` is accessible in `ready` event handler.
  3976. timeout = setTimeout(function () {
  3977. _this.delaying = false;
  3978. _this.build();
  3979. }, 0);
  3980. }
  3981. };
  3982. this.initializing = {
  3983. abort: function abort() {
  3984. forEach(images, function (image) {
  3985. if (!image.complete) {
  3986. removeListener(image, EVENT_LOAD, progress);
  3987. removeListener(image, EVENT_ERROR, progress);
  3988. }
  3989. });
  3990. },
  3991. };
  3992. forEach(images, function (image) {
  3993. if (image.complete) {
  3994. progress();
  3995. } else {
  3996. var onLoad;
  3997. var onError;
  3998. addListener(
  3999. image,
  4000. EVENT_LOAD,
  4001. (onLoad = function onLoad() {
  4002. removeListener(image, EVENT_ERROR, onError);
  4003. progress();
  4004. }),
  4005. {
  4006. once: true,
  4007. }
  4008. );
  4009. addListener(
  4010. image,
  4011. EVENT_ERROR,
  4012. (onError = function onError() {
  4013. removeListener(image, EVENT_LOAD, onLoad);
  4014. progress();
  4015. }),
  4016. {
  4017. once: true,
  4018. }
  4019. );
  4020. }
  4021. });
  4022. } else {
  4023. addListener(
  4024. element,
  4025. EVENT_CLICK,
  4026. (this.onStart = function (_ref) {
  4027. var target = _ref.target;
  4028. if (
  4029. target.localName === "img" &&
  4030. (!isFunction(options.filter) ||
  4031. options.filter.call(_this, target))
  4032. ) {
  4033. _this.view(_this.images.indexOf(target));
  4034. }
  4035. })
  4036. );
  4037. }
  4038. },
  4039. },
  4040. {
  4041. key: "build",
  4042. value: function build() {
  4043. if (this.ready) {
  4044. return;
  4045. }
  4046. var element = this.element,
  4047. options = this.options;
  4048. var parent = element.parentNode;
  4049. var template = document.createElement("div");
  4050. template.innerHTML = TEMPLATE;
  4051. var viewer = template.querySelector(
  4052. ".".concat(NAMESPACE, "-container")
  4053. );
  4054. var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
  4055. var toolbar = viewer.querySelector(
  4056. ".".concat(NAMESPACE, "-toolbar")
  4057. );
  4058. var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
  4059. var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
  4060. var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
  4061. this.parent = parent;
  4062. this.viewer = viewer;
  4063. this.title = title;
  4064. this.toolbar = toolbar;
  4065. this.navbar = navbar;
  4066. this.button = button;
  4067. this.canvas = canvas;
  4068. this.footer = viewer.querySelector(
  4069. ".".concat(NAMESPACE, "-footer")
  4070. );
  4071. this.tooltipBox = viewer.querySelector(
  4072. ".".concat(NAMESPACE, "-tooltip")
  4073. );
  4074. this.player = viewer.querySelector(
  4075. ".".concat(NAMESPACE, "-player")
  4076. );
  4077. this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
  4078. viewer.id = "".concat(NAMESPACE).concat(this.id);
  4079. title.id = "".concat(NAMESPACE, "Title").concat(this.id);
  4080. addClass(
  4081. title,
  4082. !options.title
  4083. ? CLASS_HIDE
  4084. : getResponsiveClass(
  4085. Array.isArray(options.title)
  4086. ? options.title[0]
  4087. : options.title
  4088. )
  4089. );
  4090. addClass(
  4091. navbar,
  4092. !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar)
  4093. );
  4094. toggleClass(button, CLASS_HIDE, !options.button);
  4095. if (options.keyboard) {
  4096. button.setAttribute("tabindex", 0);
  4097. }
  4098. if (options.backdrop) {
  4099. addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
  4100. if (!options.inline && options.backdrop !== "static") {
  4101. setData(canvas, DATA_ACTION, "hide");
  4102. }
  4103. }
  4104. if (isString(options.className) && options.className) {
  4105. // In case there are multiple class names
  4106. options.className
  4107. .split(REGEXP_SPACES)
  4108. .forEach(function (className) {
  4109. addClass(viewer, className);
  4110. });
  4111. }
  4112. if (options.toolbar) {
  4113. var list = document.createElement("ul");
  4114. var custom = isPlainObject(options.toolbar);
  4115. var zoomButtons = BUTTONS.slice(0, 3);
  4116. var rotateButtons = BUTTONS.slice(7, 9);
  4117. var scaleButtons = BUTTONS.slice(9);
  4118. if (!custom) {
  4119. addClass(toolbar, getResponsiveClass(options.toolbar));
  4120. }
  4121. forEach(
  4122. custom ? options.toolbar : BUTTONS,
  4123. function (value, index) {
  4124. var deep = custom && isPlainObject(value);
  4125. var name = custom ? hyphenate(index) : value;
  4126. var show =
  4127. deep && !isUndefined(value.show) ? value.show : value;
  4128. if (
  4129. !show ||
  4130. (!options.zoomable && zoomButtons.indexOf(name) !== -1) ||
  4131. (!options.rotatable &&
  4132. rotateButtons.indexOf(name) !== -1) ||
  4133. (!options.scalable && scaleButtons.indexOf(name) !== -1)
  4134. ) {
  4135. return;
  4136. }
  4137. var size =
  4138. deep && !isUndefined(value.size) ? value.size : value;
  4139. var click =
  4140. deep && !isUndefined(value.click) ? value.click : value;
  4141. var item = document.createElement("li");
  4142. if (options.keyboard) {
  4143. item.setAttribute("tabindex", 0);
  4144. }
  4145. item.setAttribute("role", "button");
  4146. addClass(item, "".concat(NAMESPACE, "-").concat(name));
  4147. if (!isFunction(click)) {
  4148. setData(item, DATA_ACTION, name);
  4149. }
  4150. if (isNumber(show)) {
  4151. addClass(item, getResponsiveClass(show));
  4152. }
  4153. if (["small", "large"].indexOf(size) !== -1) {
  4154. addClass(item, "".concat(NAMESPACE, "-").concat(size));
  4155. } else if (name === "play") {
  4156. addClass(item, "".concat(NAMESPACE, "-large"));
  4157. }
  4158. if (isFunction(click)) {
  4159. addListener(item, EVENT_CLICK, click);
  4160. }
  4161. list.appendChild(item);
  4162. }
  4163. );
  4164. toolbar.appendChild(list);
  4165. } else {
  4166. addClass(toolbar, CLASS_HIDE);
  4167. }
  4168. if (!options.rotatable) {
  4169. var rotates = toolbar.querySelectorAll('li[class*="rotate"]');
  4170. addClass(rotates, CLASS_INVISIBLE);
  4171. forEach(rotates, function (rotate) {
  4172. toolbar.appendChild(rotate);
  4173. });
  4174. }
  4175. if (options.inline) {
  4176. addClass(button, CLASS_FULLSCREEN);
  4177. setStyle(viewer, {
  4178. zIndex: options.zIndexInline,
  4179. });
  4180. if (window.getComputedStyle(parent).position === "static") {
  4181. setStyle(parent, {
  4182. position: "relative",
  4183. });
  4184. }
  4185. parent.insertBefore(viewer, element.nextSibling);
  4186. } else {
  4187. addClass(button, CLASS_CLOSE);
  4188. addClass(viewer, CLASS_FIXED);
  4189. addClass(viewer, CLASS_FADE);
  4190. addClass(viewer, CLASS_HIDE);
  4191. setStyle(viewer, {
  4192. zIndex: options.zIndex,
  4193. });
  4194. var container = options.container;
  4195. if (isString(container)) {
  4196. container = element.ownerDocument.querySelector(container);
  4197. }
  4198. if (!container) {
  4199. container = this.body;
  4200. }
  4201. container.appendChild(viewer);
  4202. }
  4203. if (options.inline) {
  4204. this.render();
  4205. this.bind();
  4206. this.isShown = true;
  4207. }
  4208. this.ready = true;
  4209. if (isFunction(options.ready)) {
  4210. addListener(element, EVENT_READY, options.ready, {
  4211. once: true,
  4212. });
  4213. }
  4214. if (dispatchEvent(element, EVENT_READY) === false) {
  4215. this.ready = false;
  4216. return;
  4217. }
  4218. if (this.ready && options.inline) {
  4219. this.view(this.index);
  4220. }
  4221. },
  4222.  
  4223. /**
  4224. * Get the no conflict viewer class.
  4225. * @returns {Viewer} The viewer class.
  4226. */
  4227. },
  4228. ],
  4229. [
  4230. {
  4231. key: "noConflict",
  4232. value: function noConflict() {
  4233. window.Viewer = AnotherViewer;
  4234. return Viewer;
  4235. },
  4236.  
  4237. /**
  4238. * Change the default options.
  4239. * @param {Object} options - The new default options.
  4240. */
  4241. },
  4242. {
  4243. key: "setDefaults",
  4244. value: function setDefaults(options) {
  4245. assign(DEFAULTS, isPlainObject(options) && options);
  4246. },
  4247. },
  4248. ]
  4249. );
  4250. return Viewer;
  4251. })();
  4252. assign(Viewer.prototype, render, events, handlers, methods, others);
  4253.  
  4254. return Viewer;
  4255. });