Viewer.js mix

本文件是我将 Viewer.js 的 css 和 JavaScript 后合并到了一个文件中,以便调用。感谢原作者~

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/369647/607137/Viewerjs%20mix.js

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址