Greasy Fork is available in English.

Viewer.js mix

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

2018-06-21 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

بۇ قوليازمىنى بىۋاسىتە قاچىلاشقا بولمايدۇ. بۇ باشقا قوليازمىلارنىڭ ئىشلىتىشى ئۈچۈن تەمىنلەنگەن ئامبار بولۇپ، ئىشلىتىش ئۈچۈن مېتا كۆرسەتمىسىگە قىستۇرىدىغان كود: // @require https://update.greatest.deepsurf.us/scripts/369647/607134/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. if (document.fullscreenElement || document.msFullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || false) {
  1110. // 全屏模式时不执行
  1111. return false;
  1112. }
  1113. var rotate = _ref.rotate,
  1114. scaleX = _ref.scaleX,
  1115. scaleY = _ref.scaleY,
  1116. translateX = _ref.translateX,
  1117. translateY = _ref.translateY;
  1118.  
  1119. var values = [];
  1120.  
  1121. if (isNumber(translateX) && translateX !== 0) {
  1122. values.push('translateX(' + translateX + 'px)');
  1123. }
  1124.  
  1125. if (isNumber(translateY) && translateY !== 0) {
  1126. values.push('translateY(' + translateY + 'px)');
  1127. }
  1128.  
  1129. // Rotate should come first before scale to match orientation transform
  1130. if (isNumber(rotate) && rotate !== 0) {
  1131. values.push('rotate(' + rotate + 'deg)');
  1132. }
  1133.  
  1134. if (isNumber(scaleX) && scaleX !== 1) {
  1135. values.push('scaleX(' + scaleX + ')');
  1136. }
  1137.  
  1138. if (isNumber(scaleY) && scaleY !== 1) {
  1139. values.push('scaleY(' + scaleY + ')');
  1140. }
  1141.  
  1142. var transform = values.length ? values.join(' ') : 'none';
  1143.  
  1144. return {
  1145. WebkitTransform: transform,
  1146. msTransform: transform,
  1147. transform: transform
  1148. };
  1149. }
  1150.  
  1151. /**
  1152. * Get an image name from an image url.
  1153. * @param {string} url - The target url.
  1154. * @example
  1155. * // picture.jpg
  1156. * getImageNameFromURL('http://domain.com/path/to/picture.jpg?size=1280×960')
  1157. * @returns {string} A string contains the image name.
  1158. */
  1159. function getImageNameFromURL(url) {
  1160. return isString(url) ? url.replace(/^.*\//, '').replace(/[?&#].*$/, '') : '';
  1161. }
  1162.  
  1163. var IS_SAFARI = WINDOW.navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(WINDOW.navigator.userAgent);
  1164.  
  1165. /**
  1166. * Get an image's natural sizes.
  1167. * @param {string} image - The target image.
  1168. * @param {Function} callback - The callback function.
  1169. * @returns {HTMLImageElement} The new image.
  1170. */
  1171. function getImageNaturalSizes(image, callback) {
  1172. var newImage = document.createElement('img');
  1173.  
  1174. // Modern browsers (except Safari)
  1175. if (image.naturalWidth && !IS_SAFARI) {
  1176. callback(image.naturalWidth, image.naturalHeight);
  1177. return newImage;
  1178. }
  1179.  
  1180. var body = document.body || document.documentElement;
  1181.  
  1182. newImage.onload = function () {
  1183. callback(newImage.width, newImage.height);
  1184.  
  1185. if (!IS_SAFARI) {
  1186. body.removeChild(newImage);
  1187. }
  1188. };
  1189.  
  1190. newImage.src = image.src;
  1191.  
  1192. // iOS Safari will convert the image automatically
  1193. // with its orientation once append it into DOM
  1194. if (!IS_SAFARI) {
  1195. 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;';
  1196. body.appendChild(newImage);
  1197. }
  1198.  
  1199. return newImage;
  1200. }
  1201.  
  1202. /**
  1203. * Get the related class name of a responsive type number.
  1204. * @param {string} type - The responsive type.
  1205. * @returns {string} The related class name.
  1206. */
  1207. function getResponsiveClass(type) {
  1208. switch (type) {
  1209. case 2:
  1210. return CLASS_HIDE_XS_DOWN;
  1211.  
  1212. case 3:
  1213. return CLASS_HIDE_SM_DOWN;
  1214.  
  1215. case 4:
  1216. return CLASS_HIDE_MD_DOWN;
  1217.  
  1218. default:
  1219. return '';
  1220. }
  1221. }
  1222.  
  1223. /**
  1224. * Get the max ratio of a group of pointers.
  1225. * @param {string} pointers - The target pointers.
  1226. * @returns {number} The result ratio.
  1227. */
  1228. function getMaxZoomRatio(pointers) {
  1229. var pointers2 = assign({}, pointers);
  1230. var ratios = [];
  1231.  
  1232. forEach(pointers, function (pointer, pointerId) {
  1233. delete pointers2[pointerId];
  1234.  
  1235. forEach(pointers2, function (pointer2) {
  1236. var x1 = Math.abs(pointer.startX - pointer2.startX);
  1237. var y1 = Math.abs(pointer.startY - pointer2.startY);
  1238. var x2 = Math.abs(pointer.endX - pointer2.endX);
  1239. var y2 = Math.abs(pointer.endY - pointer2.endY);
  1240. var z1 = Math.sqrt(x1 * x1 + y1 * y1);
  1241. var z2 = Math.sqrt(x2 * x2 + y2 * y2);
  1242. var ratio = (z2 - z1) / z1;
  1243.  
  1244. ratios.push(ratio);
  1245. });
  1246. });
  1247.  
  1248. ratios.sort(function (a, b) {
  1249. return Math.abs(a) < Math.abs(b);
  1250. });
  1251.  
  1252. return ratios[0];
  1253. }
  1254.  
  1255. /**
  1256. * Get a pointer from an event object.
  1257. * @param {Object} event - The target event object.
  1258. * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
  1259. * @returns {Object} The result pointer contains start and/or end point coordinates.
  1260. */
  1261. function getPointer(_ref2, endOnly) {
  1262. var pageX = _ref2.pageX,
  1263. pageY = _ref2.pageY;
  1264.  
  1265. var end = {
  1266. endX: pageX,
  1267. endY: pageY
  1268. };
  1269.  
  1270. return endOnly ? end : assign({
  1271. startX: pageX,
  1272. startY: pageY
  1273. }, end);
  1274. }
  1275.  
  1276. /**
  1277. * Get the center point coordinate of a group of pointers.
  1278. * @param {Object} pointers - The target pointers.
  1279. * @returns {Object} The center point coordinate.
  1280. */
  1281. function getPointersCenter(pointers) {
  1282. var pageX = 0;
  1283. var pageY = 0;
  1284. var count = 0;
  1285.  
  1286. forEach(pointers, function (_ref3) {
  1287. var startX = _ref3.startX,
  1288. startY = _ref3.startY;
  1289.  
  1290. pageX += startX;
  1291. pageY += startY;
  1292. count += 1;
  1293. });
  1294.  
  1295. pageX /= count;
  1296. pageY /= count;
  1297.  
  1298. return {
  1299. pageX: pageX,
  1300. pageY: pageY
  1301. };
  1302. }
  1303.  
  1304. var render = {
  1305. render: function render() {
  1306. this.initContainer();
  1307. this.initViewer();
  1308. this.initList();
  1309. this.renderViewer();
  1310. },
  1311. initContainer: function initContainer() {
  1312. this.containerData = {
  1313. width: window.innerWidth,
  1314. height: window.innerHeight
  1315. };
  1316. },
  1317. initViewer: function initViewer() {
  1318. var options = this.options,
  1319. parent = this.parent;
  1320.  
  1321. var viewerData = void 0;
  1322.  
  1323. if (options.inline) {
  1324. viewerData = {
  1325. width: Math.max(parent.offsetWidth, options.minWidth),
  1326. height: Math.max(parent.offsetHeight, options.minHeight)
  1327. };
  1328.  
  1329. this.parentData = viewerData;
  1330. }
  1331.  
  1332. if (this.fulled || !viewerData) {
  1333. viewerData = this.containerData;
  1334. }
  1335.  
  1336. this.viewerData = assign({}, viewerData);
  1337. },
  1338. renderViewer: function renderViewer() {
  1339. if (this.options.inline && !this.fulled) {
  1340. setStyle(this.viewer, this.viewerData);
  1341. }
  1342. },
  1343. initList: function initList() {
  1344. var _this = this;
  1345.  
  1346. var element = this.element,
  1347. options = this.options,
  1348. list = this.list;
  1349.  
  1350. var items = [];
  1351.  
  1352. forEach(this.images, function (image, i) {
  1353. var src = image.src;
  1354.  
  1355. var alt = image.alt || getImageNameFromURL(src);
  1356. var url = options.url;
  1357.  
  1358.  
  1359. if (isString(url)) {
  1360. url = image.getAttribute(url);
  1361. } else if (isFunction(url)) {
  1362. url = url.call(_this, image);
  1363. }
  1364.  
  1365. if (src || url) {
  1366. items.push('<li>' + '<img' + (' src="' + (src || url) + '"') + ' role="button"' + ' data-viewer-action="view"' + (' data-index="' + i + '"') + (' data-original-url="' + (url || src) + '"') + (' alt="' + alt + '"') + '>' + '</li>');
  1367. }
  1368. });
  1369.  
  1370. list.innerHTML = items.join('');
  1371. this.items = list.getElementsByTagName('li');
  1372. forEach(this.items, function (item) {
  1373. var image = item.firstElementChild;
  1374.  
  1375. setData(image, 'filled', true);
  1376.  
  1377. if (options.loading) {
  1378. addClass(item, CLASS_LOADING);
  1379. }
  1380.  
  1381. addListener(image, EVENT_LOAD, function (event) {
  1382. if (options.loading) {
  1383. removeClass(item, CLASS_LOADING);
  1384. }
  1385.  
  1386. _this.loadImage(event);
  1387. }, {
  1388. once: true
  1389. });
  1390. });
  1391.  
  1392. if (options.transition) {
  1393. addListener(element, EVENT_VIEWED, function () {
  1394. addClass(list, CLASS_TRANSITION);
  1395. }, {
  1396. once: true
  1397. });
  1398. }
  1399. },
  1400. renderList: function renderList(index) {
  1401. var i = index || this.index;
  1402. var width = this.items[i].offsetWidth || 30;
  1403. var outerWidth = width + 1; // 1 pixel of `margin-left` width
  1404.  
  1405. // Place the active item in the center of the screen
  1406. setStyle(this.list, assign({
  1407. width: outerWidth * this.length
  1408. }, getTransforms({
  1409. translateX: (this.viewerData.width - width) / 2 - outerWidth * i
  1410. })));
  1411. },
  1412. resetList: function resetList() {
  1413. var list = this.list;
  1414.  
  1415.  
  1416. list.innerHTML = '';
  1417. removeClass(list, CLASS_TRANSITION);
  1418. setStyle(list, getTransforms({
  1419. translateX: 0
  1420. }));
  1421. },
  1422. initImage: function initImage(done) {
  1423. var _this2 = this;
  1424.  
  1425. var options = this.options,
  1426. image = this.image,
  1427. viewerData = this.viewerData;
  1428.  
  1429. var footerHeight = this.footer.offsetHeight;
  1430. var viewerWidth = viewerData.width;
  1431. var viewerHeight = Math.max(viewerData.height - footerHeight, footerHeight);
  1432. var oldImageData = this.imageData || {};
  1433. var sizingImage = void 0;
  1434.  
  1435. this.imageInitializing = {
  1436. abort: function abort() {
  1437. sizingImage.onload = null;
  1438. }
  1439. };
  1440.  
  1441. sizingImage = getImageNaturalSizes(image, function (naturalWidth, naturalHeight) {
  1442. var aspectRatio = naturalWidth / naturalHeight;
  1443. var width = viewerWidth;
  1444. var height = viewerHeight;
  1445.  
  1446. _this2.imageInitializing = false;
  1447.  
  1448. if (viewerHeight * aspectRatio > viewerWidth) {
  1449. height = viewerWidth / aspectRatio;
  1450. } else {
  1451. width = viewerHeight * aspectRatio;
  1452. }
  1453.  
  1454. width = Math.min(width * 0.9, naturalWidth);
  1455. height = Math.min(height * 0.9, naturalHeight);
  1456.  
  1457. var imageData = {
  1458. naturalWidth: naturalWidth,
  1459. naturalHeight: naturalHeight,
  1460. aspectRatio: aspectRatio,
  1461. ratio: width / naturalWidth,
  1462. width: width,
  1463. height: height,
  1464. left: (viewerWidth - width) / 2,
  1465. top: (viewerHeight - height) / 2
  1466. };
  1467. var initialImageData = assign({}, imageData);
  1468.  
  1469. if (options.rotatable) {
  1470. imageData.rotate = oldImageData.rotate || 0;
  1471. initialImageData.rotate = 0;
  1472. }
  1473.  
  1474. if (options.scalable) {
  1475. imageData.scaleX = oldImageData.scaleX || 1;
  1476. imageData.scaleY = oldImageData.scaleY || 1;
  1477. initialImageData.scaleX = 1;
  1478. initialImageData.scaleY = 1;
  1479. }
  1480.  
  1481. _this2.imageData = imageData;
  1482. _this2.initialImageData = initialImageData;
  1483.  
  1484. if (done) {
  1485. done();
  1486. }
  1487. });
  1488. },
  1489. renderImage: function renderImage(done) {
  1490. var _this3 = this;
  1491.  
  1492. var image = this.image,
  1493. imageData = this.imageData;
  1494.  
  1495.  
  1496. setStyle(image, assign({
  1497. width: imageData.width,
  1498. height: imageData.height,
  1499. marginLeft: imageData.left,
  1500. marginTop: imageData.top
  1501. }, getTransforms(imageData)));
  1502.  
  1503. if (done) {
  1504. if ((this.viewing || this.zooming) && this.options.transition) {
  1505. var onTransitionEnd = function onTransitionEnd() {
  1506. _this3.imageRendering = false;
  1507. done();
  1508. };
  1509.  
  1510. this.imageRendering = {
  1511. abort: function abort() {
  1512. removeListener(image, EVENT_TRANSITION_END, onTransitionEnd);
  1513. }
  1514. };
  1515.  
  1516. addListener(image, EVENT_TRANSITION_END, onTransitionEnd, {
  1517. once: true
  1518. });
  1519. } else {
  1520. done();
  1521. }
  1522. }
  1523. },
  1524. resetImage: function resetImage() {
  1525. // this.image only defined after viewed
  1526. if (this.viewing || this.viewed) {
  1527. var image = this.image;
  1528.  
  1529.  
  1530. if (this.viewing) {
  1531. this.viewing.abort();
  1532. }
  1533.  
  1534. image.parentNode.removeChild(image);
  1535. this.image = null;
  1536. }
  1537. }
  1538. };
  1539.  
  1540. var events = {
  1541. bind: function bind() {
  1542. var element = this.element,
  1543. viewer = this.viewer;
  1544.  
  1545.  
  1546. addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
  1547. addListener(viewer, EVENT_WHEEL, this.onWheel = this.wheel.bind(this));
  1548. addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
  1549. addListener(this.canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
  1550. addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
  1551. addListener(element.ownerDocument, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
  1552. addListener(element.ownerDocument, EVENT_KEY_DOWN, this.onKeyDown = this.keydown.bind(this));
  1553. addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));
  1554. },
  1555. unbind: function unbind() {
  1556. var element = this.element,
  1557. viewer = this.viewer;
  1558.  
  1559.  
  1560. removeListener(viewer, EVENT_CLICK, this.onClick);
  1561. removeListener(viewer, EVENT_WHEEL, this.onWheel);
  1562. removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
  1563. removeListener(this.canvas, EVENT_POINTER_DOWN, this.onPointerDown);
  1564. removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onPointerMove);
  1565. removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onPointerUp);
  1566. removeListener(element.ownerDocument, EVENT_KEY_DOWN, this.onKeyDown);
  1567. removeListener(window, EVENT_RESIZE, this.onResize);
  1568. }
  1569. };
  1570.  
  1571. var handlers = {
  1572. click: function click(_ref) {
  1573. var target = _ref.target;
  1574. var options = this.options,
  1575. imageData = this.imageData;
  1576.  
  1577. var action = getData(target, DATA_ACTION);
  1578.  
  1579. switch (action) {
  1580. case 'mix':
  1581. if (this.played) {
  1582. this.stop();
  1583. } else if (options.inline) {
  1584. if (this.fulled) {
  1585. this.exit();
  1586. } else {
  1587. this.full();
  1588. }
  1589. } else {
  1590. this.hide();
  1591. }
  1592.  
  1593. break;
  1594.  
  1595. case 'hide':
  1596. this.hide();
  1597. break;
  1598.  
  1599. case 'view':
  1600. this.view(getData(target, 'index'));
  1601. break;
  1602.  
  1603. case 'zoom-in':
  1604. this.zoom(0.1, true);
  1605. break;
  1606.  
  1607. case 'zoom-out':
  1608. this.zoom(-0.1, true);
  1609. break;
  1610.  
  1611. case 'one-to-one':
  1612. this.toggle();
  1613. break;
  1614.  
  1615. case 'reset':
  1616. this.reset();
  1617. break;
  1618.  
  1619. case 'prev':
  1620. this.prev(options.loop);
  1621. break;
  1622.  
  1623. case 'play':
  1624. this.play(options.fullscreen);
  1625. break;
  1626.  
  1627. case 'next':
  1628. this.next(options.loop);
  1629. break;
  1630.  
  1631. case 'rotate-left':
  1632. this.rotate(-90);
  1633. break;
  1634.  
  1635. case 'rotate-right':
  1636. this.rotate(90);
  1637. break;
  1638.  
  1639. case 'flip-horizontal':
  1640. this.scaleX(-imageData.scaleX || -1);
  1641. break;
  1642.  
  1643. case 'flip-vertical':
  1644. this.scaleY(-imageData.scaleY || -1);
  1645. break;
  1646.  
  1647. default:
  1648. if (this.played) {
  1649. this.stop();
  1650. }
  1651. }
  1652. },
  1653. load: function load() {
  1654. var _this = this;
  1655.  
  1656. if (this.timeout) {
  1657. clearTimeout(this.timeout);
  1658. this.timeout = false;
  1659. }
  1660.  
  1661. var element = this.element,
  1662. options = this.options,
  1663. image = this.image,
  1664. index = this.index,
  1665. viewerData = this.viewerData;
  1666.  
  1667.  
  1668. removeClass(image, CLASS_INVISIBLE);
  1669.  
  1670. if (options.loading) {
  1671. removeClass(this.canvas, CLASS_LOADING);
  1672. }
  1673.  
  1674. 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;';
  1675.  
  1676. this.initImage(function () {
  1677. toggleClass(image, CLASS_MOVE, options.movable);
  1678. toggleClass(image, CLASS_TRANSITION, options.transition);
  1679.  
  1680. _this.renderImage(function () {
  1681. _this.viewed = true;
  1682. _this.viewing = false;
  1683.  
  1684. if (isFunction(options.viewed)) {
  1685. addListener(element, EVENT_VIEWED, options.viewed, {
  1686. once: true
  1687. });
  1688. }
  1689.  
  1690. dispatchEvent(element, EVENT_VIEWED, {
  1691. originalImage: _this.images[index],
  1692. index: index,
  1693. image: image
  1694. });
  1695. });
  1696. });
  1697. },
  1698. loadImage: function loadImage(e) {
  1699. var image = e.target;
  1700. var parent = image.parentNode;
  1701. var parentWidth = parent.offsetWidth || 30;
  1702. var parentHeight = parent.offsetHeight || 50;
  1703. var filled = !!getData(image, 'filled');
  1704.  
  1705. getImageNaturalSizes(image, function (naturalWidth, naturalHeight) {
  1706. var aspectRatio = naturalWidth / naturalHeight;
  1707. var width = parentWidth;
  1708. var height = parentHeight;
  1709.  
  1710. if (parentHeight * aspectRatio > parentWidth) {
  1711. if (filled) {
  1712. width = parentHeight * aspectRatio;
  1713. } else {
  1714. height = parentWidth / aspectRatio;
  1715. }
  1716. } else if (filled) {
  1717. height = parentWidth / aspectRatio;
  1718. } else {
  1719. width = parentHeight * aspectRatio;
  1720. }
  1721.  
  1722. setStyle(image, assign({
  1723. width: width,
  1724. height: height
  1725. }, getTransforms({
  1726. translateX: (parentWidth - width) / 2,
  1727. translateY: (parentHeight - height) / 2
  1728. })));
  1729. });
  1730. },
  1731. keydown: function keydown(e) {
  1732. var options = this.options;
  1733.  
  1734.  
  1735. if (!this.fulled || !options.keyboard) {
  1736. return;
  1737. }
  1738.  
  1739. switch (e.keyCode || e.which || e.charCode) {
  1740. // Escape
  1741. case 27:
  1742. if (this.played) {
  1743. this.stop();
  1744. } else if (options.inline) {
  1745. if (this.fulled) {
  1746. this.exit();
  1747. }
  1748. } else {
  1749. this.hide();
  1750. }
  1751.  
  1752. break;
  1753.  
  1754. // Space
  1755. case 32:
  1756. if (this.played) {
  1757. this.stop();
  1758. }
  1759.  
  1760. break;
  1761.  
  1762. // ArrowLeft
  1763. case 37:
  1764. this.prev(options.loop);
  1765. break;
  1766.  
  1767. // ArrowUp
  1768. case 38:
  1769. // Prevent scroll on Firefox
  1770. e.preventDefault();
  1771.  
  1772. // Zoom in
  1773. this.zoom(options.zoomRatio, true);
  1774. break;
  1775.  
  1776. // ArrowRight
  1777. case 39:
  1778. this.next(options.loop);
  1779. break;
  1780.  
  1781. // ArrowDown
  1782. case 40:
  1783. // Prevent scroll on Firefox
  1784. e.preventDefault();
  1785.  
  1786. // Zoom out
  1787. this.zoom(-options.zoomRatio, true);
  1788. break;
  1789.  
  1790. // Ctrl + 0
  1791. case 48:
  1792. // Fall through
  1793.  
  1794. // Ctrl + 1
  1795. // eslint-disable-next-line no-fallthrough
  1796. case 49:
  1797. if (e.ctrlKey) {
  1798. e.preventDefault();
  1799. this.toggle();
  1800. }
  1801.  
  1802. break;
  1803.  
  1804. default:
  1805. }
  1806. },
  1807. dragstart: function dragstart(e) {
  1808. if (e.target.tagName.toLowerCase() === 'img') {
  1809. e.preventDefault();
  1810. }
  1811. },
  1812. pointerdown: function pointerdown(e) {
  1813. var options = this.options,
  1814. pointers = this.pointers;
  1815.  
  1816.  
  1817. if (!this.viewed || this.showing || this.viewing || this.hiding) {
  1818. return;
  1819. }
  1820.  
  1821. // This line is required for preventing page zooming in iOS browsers
  1822. e.preventDefault();
  1823.  
  1824. if (e.changedTouches) {
  1825. forEach(e.changedTouches, function (touch) {
  1826. pointers[touch.identifier] = getPointer(touch);
  1827. });
  1828. } else {
  1829. pointers[e.pointerId || 0] = getPointer(e);
  1830. }
  1831.  
  1832. var action = options.movable ? ACTION_MOVE : false;
  1833.  
  1834. if (Object.keys(pointers).length > 1) {
  1835. action = ACTION_ZOOM;
  1836. } else if ((e.pointerType === 'touch' || e.type === 'touchstart') && this.isSwitchable()) {
  1837. action = ACTION_SWITCH;
  1838. }
  1839.  
  1840. if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
  1841. removeClass(this.image, CLASS_TRANSITION);
  1842. }
  1843.  
  1844. this.action = action;
  1845. },
  1846. pointermove: function pointermove(e) {
  1847. var pointers = this.pointers,
  1848. action = this.action;
  1849.  
  1850.  
  1851. if (!this.viewed || !action) {
  1852. return;
  1853. }
  1854.  
  1855. e.preventDefault();
  1856.  
  1857. if (e.changedTouches) {
  1858. forEach(e.changedTouches, function (touch) {
  1859. assign(pointers[touch.identifier], getPointer(touch, true));
  1860. });
  1861. } else {
  1862. assign(pointers[e.pointerId || 0], getPointer(e, true));
  1863. }
  1864.  
  1865. this.change(e);
  1866. },
  1867. pointerup: function pointerup(e) {
  1868. var action = this.action,
  1869. pointers = this.pointers;
  1870.  
  1871.  
  1872. if (e.changedTouches) {
  1873. forEach(e.changedTouches, function (touch) {
  1874. delete pointers[touch.identifier];
  1875. });
  1876. } else {
  1877. delete pointers[e.pointerId || 0];
  1878. }
  1879.  
  1880. if (!action) {
  1881. return;
  1882. }
  1883.  
  1884. e.preventDefault();
  1885.  
  1886. if (this.options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
  1887. addClass(this.image, CLASS_TRANSITION);
  1888. }
  1889.  
  1890. this.action = false;
  1891. },
  1892. resize: function resize() {
  1893. var _this2 = this;
  1894.  
  1895. if (!this.isShown || this.hiding) {
  1896. return;
  1897. }
  1898.  
  1899. this.initContainer();
  1900. this.initViewer();
  1901. this.renderViewer();
  1902. this.renderList();
  1903.  
  1904. if (this.viewed) { // 当窗口 resize 时,如果是非全屏则重置图片大小。全屏时不重置
  1905. if (!(document.fullscreenElement || document.msFullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || false)) {
  1906. this.initImage(function () {
  1907. _this2.renderImage();
  1908. });
  1909. }
  1910. }
  1911.  
  1912. if (this.played) {
  1913. if (this.options.fullscreen && this.fulled && !document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
  1914. this.stop();
  1915. return;
  1916. }
  1917.  
  1918. forEach(this.player.getElementsByTagName('img'), function (image) {
  1919. addListener(image, EVENT_LOAD, _this2.loadImage.bind(_this2), {
  1920. once: true
  1921. });
  1922. dispatchEvent(image, EVENT_LOAD);
  1923. });
  1924. }
  1925. },
  1926. wheel: function wheel(e) {
  1927. var _this3 = this;
  1928.  
  1929. if (!this.viewed) {
  1930. return;
  1931. }
  1932.  
  1933. e.preventDefault();
  1934.  
  1935. // Limit wheel speed to prevent zoom too fast
  1936. if (this.wheeling) {
  1937. return;
  1938. }
  1939.  
  1940. this.wheeling = true;
  1941.  
  1942. setTimeout(function () {
  1943. _this3.wheeling = false;
  1944. }, 50);
  1945.  
  1946. var ratio = Number(this.options.zoomRatio) || 0.1;
  1947. var delta = 1;
  1948.  
  1949. if (e.deltaY) {
  1950. delta = e.deltaY > 0 ? 1 : -1;
  1951. } else if (e.wheelDelta) {
  1952. delta = -e.wheelDelta / 120;
  1953. } else if (e.detail) {
  1954. delta = e.detail > 0 ? 1 : -1;
  1955. }
  1956.  
  1957. this.zoom(-delta * ratio, true, e);
  1958. }
  1959. };
  1960.  
  1961. var methods = {
  1962. /** Show the viewer (only available in modal mode)
  1963. * @param {boolean} [immediate=false] - Indicates if show the viewer immediately or not.
  1964. * @returns {Viewer} this
  1965. */
  1966. show: function show() {
  1967. var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1968. var element = this.element,
  1969. options = this.options;
  1970.  
  1971.  
  1972. if (options.inline || this.showing || this.isShown || this.showing) {
  1973. return this;
  1974. }
  1975.  
  1976. if (!this.ready) {
  1977. this.build();
  1978.  
  1979. if (this.ready) {
  1980. this.show(immediate);
  1981. }
  1982.  
  1983. return this;
  1984. }
  1985.  
  1986. if (isFunction(options.show)) {
  1987. addListener(element, EVENT_SHOW, options.show, {
  1988. once: true
  1989. });
  1990. }
  1991.  
  1992. if (dispatchEvent(element, EVENT_SHOW) === false || !this.ready) {
  1993. return this;
  1994. }
  1995.  
  1996. if (this.hiding) {
  1997. this.transitioning.abort();
  1998. }
  1999.  
  2000. this.showing = true;
  2001. this.open();
  2002.  
  2003. var viewer = this.viewer;
  2004.  
  2005.  
  2006. removeClass(viewer, CLASS_HIDE);
  2007.  
  2008. if (options.transition && !immediate) {
  2009. var shown = this.shown.bind(this);
  2010.  
  2011. this.transitioning = {
  2012. abort: function abort() {
  2013. removeListener(viewer, EVENT_TRANSITION_END, shown);
  2014. removeClass(viewer, CLASS_IN);
  2015. }
  2016. };
  2017.  
  2018. addClass(viewer, CLASS_TRANSITION);
  2019.  
  2020. // Force reflow to enable CSS3 transition
  2021. // eslint-disable-next-line
  2022. viewer.offsetWidth;
  2023. addListener(viewer, EVENT_TRANSITION_END, shown, {
  2024. once: true
  2025. });
  2026. addClass(viewer, CLASS_IN);
  2027. } else {
  2028. addClass(viewer, CLASS_IN);
  2029. this.shown();
  2030. }
  2031.  
  2032. return this;
  2033. },
  2034.  
  2035.  
  2036. /**
  2037. * Hide the viewer (only available in modal mode)
  2038. * @param {boolean} [immediate=false] - Indicates if hide the viewer immediately or not.
  2039. * @returns {Viewer} this
  2040. */
  2041. hide: function hide() {
  2042. var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  2043. var element = this.element,
  2044. options = this.options;
  2045.  
  2046.  
  2047. if (options.inline || this.hiding || !(this.isShown || this.showing)) {
  2048. return this;
  2049. }
  2050.  
  2051. if (isFunction(options.hide)) {
  2052. addListener(element, EVENT_HIDE, options.hide, {
  2053. once: true
  2054. });
  2055. }
  2056.  
  2057. if (dispatchEvent(element, EVENT_HIDE) === false) {
  2058. return this;
  2059. }
  2060.  
  2061. if (this.showing) {
  2062. this.transitioning.abort();
  2063. }
  2064.  
  2065. this.hiding = true;
  2066.  
  2067. if (this.played) {
  2068. this.stop();
  2069. } else if (this.viewing) {
  2070. this.viewing.abort();
  2071. }
  2072.  
  2073. var viewer = this.viewer;
  2074.  
  2075.  
  2076. if (options.transition && !immediate) {
  2077. var hidden = this.hidden.bind(this);
  2078. var hide = function hide() {
  2079. addListener(viewer, EVENT_TRANSITION_END, hidden, {
  2080. once: true
  2081. });
  2082. removeClass(viewer, CLASS_IN);
  2083. };
  2084.  
  2085. this.transitioning = {
  2086. abort: function abort() {
  2087. if (this.viewed) {
  2088. removeListener(this.image, EVENT_TRANSITION_END, hide);
  2089. } else {
  2090. removeListener(viewer, EVENT_TRANSITION_END, hidden);
  2091. }
  2092. }
  2093. };
  2094.  
  2095. if (this.viewed) {
  2096. addListener(this.image, EVENT_TRANSITION_END, hide, {
  2097. once: true
  2098. });
  2099. this.zoomTo(0, false, false, true);
  2100. } else {
  2101. hide();
  2102. }
  2103. } else {
  2104. removeClass(viewer, CLASS_IN);
  2105. this.hidden();
  2106. }
  2107.  
  2108. return this;
  2109. },
  2110.  
  2111.  
  2112. /**
  2113. * View one of the images with image's index
  2114. * @param {number} index - The index of the image to view.
  2115. * @returns {Viewer} this
  2116. */
  2117. view: function view() {
  2118. var _this = this;
  2119.  
  2120. var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  2121.  
  2122. index = Number(index) || 0;
  2123.  
  2124. if (!this.isShown) {
  2125. this.index = index;
  2126. return this.show();
  2127. }
  2128.  
  2129. if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) {
  2130. return this;
  2131. }
  2132.  
  2133. if (this.viewing) {
  2134. this.viewing.abort();
  2135. }
  2136.  
  2137. var element = this.element,
  2138. options = this.options,
  2139. title = this.title,
  2140. canvas = this.canvas;
  2141.  
  2142. var item = this.items[index];
  2143. var img = item.querySelector('img');
  2144. var url = getData(img, 'originalUrl');
  2145. var alt = img.getAttribute('alt');
  2146. var image = document.createElement('img');
  2147.  
  2148. image.src = url;
  2149. image.alt = alt;
  2150.  
  2151. if (isFunction(options.view)) {
  2152. addListener(element, EVENT_VIEW, options.view, {
  2153. once: true
  2154. });
  2155. }
  2156.  
  2157. if (dispatchEvent(element, EVENT_VIEW, {
  2158. originalImage: this.images[index],
  2159. index: index,
  2160. image: image
  2161. }) === false || !this.isShown || this.hiding || this.played) {
  2162. return this;
  2163. }
  2164.  
  2165. this.image = image;
  2166. removeClass(this.items[this.index], CLASS_ACTIVE);
  2167. addClass(item, CLASS_ACTIVE);
  2168. this.viewed = false;
  2169. this.index = index;
  2170. this.imageData = {};
  2171. addClass(image, CLASS_INVISIBLE);
  2172.  
  2173. if (options.loading) {
  2174. addClass(canvas, CLASS_LOADING);
  2175. }
  2176.  
  2177. canvas.innerHTML = '';
  2178. canvas.appendChild(image);
  2179.  
  2180. // Center current item
  2181. this.renderList();
  2182.  
  2183. // Clear title
  2184. title.innerHTML = '';
  2185.  
  2186. // Generate title after viewed
  2187. var onViewed = function onViewed() {
  2188. var imageData = _this.imageData;
  2189.  
  2190.  
  2191. title.textContent = alt + ' (' + imageData.naturalWidth + ' \xD7 ' + imageData.naturalHeight + ')';
  2192. };
  2193. var onLoad = void 0;
  2194.  
  2195. addListener(element, EVENT_VIEWED, onViewed, {
  2196. once: true
  2197. });
  2198.  
  2199. this.viewing = {
  2200. abort: function abort() {
  2201. removeListener(element, EVENT_VIEWED, onViewed);
  2202.  
  2203. if (image.complete) {
  2204. if (this.imageRendering) {
  2205. this.imageRendering.abort();
  2206. } else if (this.imageInitializing) {
  2207. this.imageInitializing.abort();
  2208. }
  2209. } else {
  2210. removeListener(image, EVENT_LOAD, onLoad);
  2211.  
  2212. if (this.timeout) {
  2213. clearTimeout(this.timeout);
  2214. }
  2215. }
  2216. }
  2217. };
  2218.  
  2219. if (image.complete) {
  2220. this.load();
  2221. } else {
  2222. addListener(image, EVENT_LOAD, onLoad = this.load.bind(this), {
  2223. once: true
  2224. });
  2225.  
  2226. if (this.timeout) {
  2227. clearTimeout(this.timeout);
  2228. }
  2229.  
  2230. // Make the image visible if it fails to load within 1s
  2231. this.timeout = setTimeout(function () {
  2232. removeClass(image, CLASS_INVISIBLE);
  2233. _this.timeout = false;
  2234. }, 1000);
  2235. }
  2236.  
  2237. return this;
  2238. },
  2239.  
  2240.  
  2241. /**
  2242. * View the previous image
  2243. * @param {boolean} [loop=false] - Indicate if view the last one
  2244. * when it is the first one at present.
  2245. * @returns {Viewer} this
  2246. */
  2247. prev: function prev() {
  2248. var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  2249.  
  2250. var index = this.index - 1;
  2251.  
  2252. if (index < 0) {
  2253. index = loop ? this.length - 1 : 0;
  2254. }
  2255.  
  2256. this.view(index);
  2257. return this;
  2258. },
  2259.  
  2260.  
  2261. /**
  2262. * View the next image
  2263. * @param {boolean} [loop=false] - Indicate if view the first one
  2264. * when it is the last one at present.
  2265. * @returns {Viewer} this
  2266. */
  2267. next: function next() {
  2268. var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  2269.  
  2270. var maxIndex = this.length - 1;
  2271. var index = this.index + 1;
  2272.  
  2273. if (index > maxIndex) {
  2274. index = loop ? 0 : maxIndex;
  2275. }
  2276.  
  2277. this.view(index);
  2278. return this;
  2279. },
  2280.  
  2281.  
  2282. /**
  2283. * Move the image with relative offsets.
  2284. * @param {number} offsetX - The relative offset distance on the x-axis.
  2285. * @param {number} offsetY - The relative offset distance on the y-axis.
  2286. * @returns {Viewer} this
  2287. */
  2288. move: function move(offsetX, offsetY) {
  2289. var imageData = this.imageData;
  2290.  
  2291.  
  2292. this.moveTo(isUndefined(offsetX) ? offsetX : imageData.left + Number(offsetX), isUndefined(offsetY) ? offsetY : imageData.top + Number(offsetY));
  2293.  
  2294. return this;
  2295. },
  2296.  
  2297.  
  2298. /**
  2299. * Move the image to an absolute point.
  2300. * @param {number} x - The x-axis coordinate.
  2301. * @param {number} [y=x] - The y-axis coordinate.
  2302. * @returns {Viewer} this
  2303. */
  2304. moveTo: function moveTo(x) {
  2305. var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
  2306. var imageData = this.imageData;
  2307.  
  2308.  
  2309. x = Number(x);
  2310. y = Number(y);
  2311.  
  2312. if (this.viewed && !this.played && this.options.movable) {
  2313. var changed = false;
  2314.  
  2315. if (isNumber(x)) {
  2316. imageData.left = x;
  2317. changed = true;
  2318. }
  2319.  
  2320. if (isNumber(y)) {
  2321. imageData.top = y;
  2322. changed = true;
  2323. }
  2324.  
  2325. if (changed) {
  2326. this.renderImage();
  2327. }
  2328. }
  2329.  
  2330. return this;
  2331. },
  2332.  
  2333.  
  2334. /**
  2335. * Zoom the image with a relative ratio.
  2336. * @param {number} ratio - The target ratio.
  2337. * @param {boolean} [hasTooltip=false] - Indicates if it has a tooltip or not.
  2338. * @param {Event} [_originalEvent=null] - The original event if any.
  2339. * @returns {Viewer} this
  2340. */
  2341. zoom: function zoom(ratio) {
  2342. var hasTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  2343.  
  2344. var _originalEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  2345.  
  2346. var imageData = this.imageData;
  2347.  
  2348.  
  2349. ratio = Number(ratio);
  2350.  
  2351. if (ratio < 0) {
  2352. ratio = 1 / (1 - ratio);
  2353. } else {
  2354. ratio = 1 + ratio;
  2355. }
  2356.  
  2357. this.zoomTo(imageData.width * ratio / imageData.naturalWidth, hasTooltip, _originalEvent);
  2358.  
  2359. return this;
  2360. },
  2361.  
  2362.  
  2363. /**
  2364. * Zoom the image to an absolute ratio.
  2365. * @param {number} ratio - The target ratio.
  2366. * @param {boolean} [hasTooltip=false] - Indicates if it has a tooltip or not.
  2367. * @param {Event} [_originalEvent=null] - The original event if any.
  2368. * @param {Event} [_zoomable=false] - Indicates if the current zoom is available or not.
  2369. * @returns {Viewer} this
  2370. */
  2371. zoomTo: function zoomTo(ratio) {
  2372. var hasTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  2373.  
  2374. var _this2 = this;
  2375.  
  2376. var _originalEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  2377.  
  2378. var _zoomable = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
  2379.  
  2380. var element = this.element,
  2381. options = this.options,
  2382. pointers = this.pointers,
  2383. imageData = this.imageData;
  2384.  
  2385.  
  2386. ratio = Math.max(0, ratio);
  2387.  
  2388. if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
  2389. if (!_zoomable) {
  2390. var minZoomRatio = Math.max(0.01, options.minZoomRatio);
  2391. var maxZoomRatio = Math.min(100, options.maxZoomRatio);
  2392.  
  2393. ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
  2394. }
  2395.  
  2396. if (_originalEvent && ratio > 0.95 && ratio < 1.05) {
  2397. ratio = 1;
  2398. }
  2399.  
  2400. var newWidth = imageData.naturalWidth * ratio;
  2401. var newHeight = imageData.naturalHeight * ratio;
  2402. var oldRatio = imageData.width / imageData.naturalWidth;
  2403.  
  2404. if (isFunction(options.zoom)) {
  2405. addListener(element, EVENT_ZOOM, options.zoom, {
  2406. once: true
  2407. });
  2408. }
  2409.  
  2410. if (dispatchEvent(element, EVENT_ZOOM, {
  2411. ratio: ratio,
  2412. oldRatio: oldRatio,
  2413. originalEvent: _originalEvent
  2414. }) === false) {
  2415. return this;
  2416. }
  2417.  
  2418. this.zooming = true;
  2419.  
  2420. if (_originalEvent) {
  2421. var offset = getOffset(this.viewer);
  2422. var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {
  2423. pageX: _originalEvent.pageX,
  2424. pageY: _originalEvent.pageY
  2425. };
  2426.  
  2427. // Zoom from the triggering point of the event
  2428. imageData.left -= (newWidth - imageData.width) * ((center.pageX - offset.left - imageData.left) / imageData.width);
  2429. imageData.top -= (newHeight - imageData.height) * ((center.pageY - offset.top - imageData.top) / imageData.height);
  2430. } else {
  2431. // Zoom from the center of the image
  2432. imageData.left -= (newWidth - imageData.width) / 2;
  2433. imageData.top -= (newHeight - imageData.height) / 2;
  2434. }
  2435.  
  2436. imageData.width = newWidth;
  2437. imageData.height = newHeight;
  2438. imageData.ratio = ratio;
  2439. this.renderImage(function () {
  2440. _this2.zooming = false;
  2441.  
  2442. if (isFunction(options.zoomed)) {
  2443. addListener(element, EVENT_ZOOMED, options.zoomed, {
  2444. once: true
  2445. });
  2446. }
  2447.  
  2448. dispatchEvent(element, EVENT_ZOOMED, {
  2449. ratio: ratio,
  2450. oldRatio: oldRatio,
  2451. originalEvent: _originalEvent
  2452. });
  2453. });
  2454.  
  2455. if (hasTooltip) {
  2456. this.tooltip();
  2457. }
  2458. }
  2459.  
  2460. return this;
  2461. },
  2462.  
  2463.  
  2464. /**
  2465. * Rotate the image with a relative degree.
  2466. * @param {number} degree - The rotate degree.
  2467. * @returns {Viewer} this
  2468. */
  2469. rotate: function rotate(degree) {
  2470. this.rotateTo((this.imageData.rotate || 0) + Number(degree));
  2471.  
  2472. return this;
  2473. },
  2474.  
  2475.  
  2476. /**
  2477. * Rotate the image to an absolute degree.
  2478. * @param {number} degree - The rotate degree.
  2479. * @returns {Viewer} this
  2480. */
  2481. rotateTo: function rotateTo(degree) {
  2482. var imageData = this.imageData;
  2483.  
  2484.  
  2485. degree = Number(degree);
  2486.  
  2487. if (isNumber(degree) && this.viewed && !this.played && this.options.rotatable) {
  2488. imageData.rotate = degree;
  2489. this.renderImage();
  2490. }
  2491.  
  2492. return this;
  2493. },
  2494.  
  2495.  
  2496. /**
  2497. * Scale the image on the x-axis.
  2498. * @param {number} scaleX - The scale ratio on the x-axis.
  2499. * @returns {Viewer} this
  2500. */
  2501. scaleX: function scaleX(_scaleX) {
  2502. this.scale(_scaleX, this.imageData.scaleY);
  2503.  
  2504. return this;
  2505. },
  2506.  
  2507.  
  2508. /**
  2509. * Scale the image on the y-axis.
  2510. * @param {number} scaleY - The scale ratio on the y-axis.
  2511. * @returns {Viewer} this
  2512. */
  2513. scaleY: function scaleY(_scaleY) {
  2514. this.scale(this.imageData.scaleX, _scaleY);
  2515.  
  2516. return this;
  2517. },
  2518.  
  2519.  
  2520. /**
  2521. * Scale the image.
  2522. * @param {number} scaleX - The scale ratio on the x-axis.
  2523. * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
  2524. * @returns {Viewer} this
  2525. */
  2526. scale: function scale(scaleX) {
  2527. var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;
  2528. var imageData = this.imageData;
  2529.  
  2530.  
  2531. scaleX = Number(scaleX);
  2532. scaleY = Number(scaleY);
  2533.  
  2534. if (this.viewed && !this.played && this.options.scalable) {
  2535. var changed = false;
  2536.  
  2537. if (isNumber(scaleX)) {
  2538. imageData.scaleX = scaleX;
  2539. changed = true;
  2540. }
  2541.  
  2542. if (isNumber(scaleY)) {
  2543. imageData.scaleY = scaleY;
  2544. changed = true;
  2545. }
  2546.  
  2547. if (changed) {
  2548. this.renderImage();
  2549. }
  2550. }
  2551.  
  2552. return this;
  2553. },
  2554.  
  2555.  
  2556. /**
  2557. * Play the images
  2558. * @param {boolean} [fullscreen=false] - Indicate if request fullscreen or not.
  2559. * @returns {Viewer} this
  2560. */
  2561. play: function play() {
  2562. var _this3 = this;
  2563.  
  2564. var fullscreen = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  2565.  
  2566. if (!this.isShown || this.played) {
  2567. return this;
  2568. }
  2569.  
  2570. var options = this.options,
  2571. player = this.player;
  2572.  
  2573. var onLoad = this.loadImage.bind(this);
  2574. var list = [];
  2575. var total = 0;
  2576. var index = 0;
  2577.  
  2578. this.played = true;
  2579. this.onLoadWhenPlay = onLoad;
  2580.  
  2581. if (fullscreen) {
  2582. this.requestFullscreen();
  2583. }
  2584.  
  2585. addClass(player, CLASS_SHOW);
  2586. forEach(this.items, function (item, i) {
  2587. var img = item.querySelector('img');
  2588. var image = document.createElement('img');
  2589.  
  2590. image.src = getData(img, 'originalUrl');
  2591. image.alt = img.getAttribute('alt');
  2592. total += 1;
  2593. addClass(image, CLASS_FADE);
  2594. toggleClass(image, CLASS_TRANSITION, options.transition);
  2595.  
  2596. if (hasClass(item, CLASS_ACTIVE)) {
  2597. addClass(image, CLASS_IN);
  2598. index = i;
  2599. }
  2600.  
  2601. list.push(image);
  2602. addListener(image, EVENT_LOAD, onLoad, {
  2603. once: true
  2604. });
  2605. player.appendChild(image);
  2606. });
  2607.  
  2608. if (isNumber(options.interval) && options.interval > 0) {
  2609. var play = function play() {
  2610. _this3.playing = setTimeout(function () {
  2611. removeClass(list[index], CLASS_IN);
  2612. index += 1;
  2613. index = index < total ? index : 0;
  2614. addClass(list[index], CLASS_IN);
  2615. play();
  2616. }, options.interval);
  2617. };
  2618.  
  2619. if (total > 1) {
  2620. play();
  2621. }
  2622. }
  2623.  
  2624. return this;
  2625. },
  2626.  
  2627.  
  2628. // Stop play
  2629. stop: function stop() {
  2630. var _this4 = this;
  2631.  
  2632. if (!this.played) {
  2633. return this;
  2634. }
  2635.  
  2636. var player = this.player;
  2637.  
  2638.  
  2639. this.played = false;
  2640. clearTimeout(this.playing);
  2641. forEach(player.getElementsByTagName('img'), function (image) {
  2642. removeListener(image, EVENT_LOAD, _this4.onLoadWhenPlay);
  2643. });
  2644. removeClass(player, CLASS_SHOW);
  2645. player.innerHTML = '';
  2646. this.exitFullscreen();
  2647.  
  2648. return this;
  2649. },
  2650.  
  2651.  
  2652. // Enter modal mode (only available in inline mode)
  2653. full: function full() {
  2654. var _this5 = this;
  2655.  
  2656. var options = this.options,
  2657. viewer = this.viewer,
  2658. image = this.image,
  2659. list = this.list;
  2660.  
  2661.  
  2662. if (!this.isShown || this.played || this.fulled || !options.inline) {
  2663. return this;
  2664. }
  2665.  
  2666. this.fulled = true;
  2667. this.open();
  2668. addClass(this.button, CLASS_FULLSCREEN_EXIT);
  2669.  
  2670. if (options.transition) {
  2671. removeClass(list, CLASS_TRANSITION);
  2672.  
  2673. if (this.viewed) {
  2674. removeClass(image, CLASS_TRANSITION);
  2675. }
  2676. }
  2677.  
  2678. addClass(viewer, CLASS_FIXED);
  2679. viewer.setAttribute('style', '');
  2680. setStyle(viewer, {
  2681. zIndex: options.zIndex
  2682. });
  2683.  
  2684. this.initContainer();
  2685. this.viewerData = assign({}, this.containerData);
  2686. this.renderList();
  2687.  
  2688. if (this.viewed) {
  2689. this.initImage(function () {
  2690. _this5.renderImage(function () {
  2691. if (options.transition) {
  2692. setTimeout(function () {
  2693. addClass(image, CLASS_TRANSITION);
  2694. addClass(list, CLASS_TRANSITION);
  2695. }, 0);
  2696. }
  2697. });
  2698. });
  2699. }
  2700.  
  2701. return this;
  2702. },
  2703.  
  2704.  
  2705. // Exit modal mode (only available in inline mode)
  2706. exit: function exit() {
  2707. var _this6 = this;
  2708.  
  2709. var options = this.options,
  2710. viewer = this.viewer,
  2711. image = this.image,
  2712. list = this.list;
  2713.  
  2714.  
  2715. if (!this.isShown || this.played || !this.fulled || !options.inline) {
  2716. return this;
  2717. }
  2718.  
  2719. this.fulled = false;
  2720. this.close();
  2721. removeClass(this.button, CLASS_FULLSCREEN_EXIT);
  2722.  
  2723. if (options.transition) {
  2724. removeClass(list, CLASS_TRANSITION);
  2725.  
  2726. if (this.viewed) {
  2727. removeClass(image, CLASS_TRANSITION);
  2728. }
  2729. }
  2730.  
  2731. removeClass(viewer, CLASS_FIXED);
  2732. setStyle(viewer, {
  2733. zIndex: options.zIndexInline
  2734. });
  2735.  
  2736. this.viewerData = assign({}, this.parentData);
  2737. this.renderViewer();
  2738. this.renderList();
  2739.  
  2740. if (this.viewed) {
  2741. this.initImage(function () {
  2742. _this6.renderImage(function () {
  2743. if (options.transition) {
  2744. setTimeout(function () {
  2745. addClass(image, CLASS_TRANSITION);
  2746. addClass(list, CLASS_TRANSITION);
  2747. }, 0);
  2748. }
  2749. });
  2750. });
  2751. }
  2752.  
  2753. return this;
  2754. },
  2755.  
  2756.  
  2757. // Show the current ratio of the image with percentage
  2758. tooltip: function tooltip() {
  2759. var _this7 = this;
  2760.  
  2761. var options = this.options,
  2762. tooltipBox = this.tooltipBox,
  2763. imageData = this.imageData;
  2764.  
  2765.  
  2766. if (!this.viewed || this.played || !options.tooltip) {
  2767. return this;
  2768. }
  2769.  
  2770. tooltipBox.textContent = Math.round(imageData.ratio * 100) + '%';
  2771.  
  2772. if (!this.tooltipping) {
  2773. if (options.transition) {
  2774. if (this.fading) {
  2775. dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
  2776. }
  2777.  
  2778. addClass(tooltipBox, CLASS_SHOW);
  2779. addClass(tooltipBox, CLASS_FADE);
  2780. addClass(tooltipBox, CLASS_TRANSITION);
  2781.  
  2782. // Force reflow to enable CSS3 transition
  2783. // eslint-disable-next-line
  2784. tooltipBox.offsetWidth;
  2785. addClass(tooltipBox, CLASS_IN);
  2786. } else {
  2787. addClass(tooltipBox, CLASS_SHOW);
  2788. }
  2789. } else {
  2790. clearTimeout(this.tooltipping);
  2791. }
  2792.  
  2793. this.tooltipping = setTimeout(function () {
  2794. if (options.transition) {
  2795. addListener(tooltipBox, EVENT_TRANSITION_END, function () {
  2796. removeClass(tooltipBox, CLASS_SHOW);
  2797. removeClass(tooltipBox, CLASS_FADE);
  2798. removeClass(tooltipBox, CLASS_TRANSITION);
  2799. _this7.fading = false;
  2800. }, {
  2801. once: true
  2802. });
  2803.  
  2804. removeClass(tooltipBox, CLASS_IN);
  2805. _this7.fading = true;
  2806. } else {
  2807. removeClass(tooltipBox, CLASS_SHOW);
  2808. }
  2809.  
  2810. _this7.tooltipping = false;
  2811. }, 1000);
  2812.  
  2813. return this;
  2814. },
  2815.  
  2816.  
  2817. // Toggle the image size between its natural size and initial size
  2818. toggle: function toggle() {
  2819. if (this.imageData.ratio === 1) {
  2820. this.zoomTo(this.initialImageData.ratio, true);
  2821. } else {
  2822. this.zoomTo(1, true);
  2823. }
  2824.  
  2825. return this;
  2826. },
  2827.  
  2828.  
  2829. // Reset the image to its initial state
  2830. reset: function reset() {
  2831. if (this.viewed && !this.played) {
  2832. this.imageData = assign({}, this.initialImageData);
  2833. this.renderImage();
  2834. }
  2835.  
  2836. return this;
  2837. },
  2838.  
  2839.  
  2840. // Update viewer when images changed
  2841. update: function update() {
  2842. var element = this.element,
  2843. options = this.options,
  2844. isImg = this.isImg;
  2845.  
  2846. // Destroy viewer if the target image was deleted
  2847.  
  2848. if (isImg && !element.parentNode) {
  2849. return this.destroy();
  2850. }
  2851.  
  2852. var images = [];
  2853.  
  2854. forEach(isImg ? [element] : element.querySelectorAll('img'), function (image) {
  2855. if (options.filter) {
  2856. if (options.filter(image)) {
  2857. images.push(image);
  2858. }
  2859. } else {
  2860. images.push(image);
  2861. }
  2862. });
  2863.  
  2864. if (!images.length) {
  2865. return this;
  2866. }
  2867.  
  2868. this.images = images;
  2869. this.length = images.length;
  2870.  
  2871. if (this.ready) {
  2872. var indexes = [];
  2873.  
  2874. forEach(this.items, function (item, i) {
  2875. var img = item.querySelector('img');
  2876. var image = images[i];
  2877.  
  2878. if (image) {
  2879. if (image.src !== img.src) {
  2880. indexes.push(i);
  2881. }
  2882. } else {
  2883. indexes.push(i);
  2884. }
  2885. });
  2886.  
  2887. setStyle(this.list, {
  2888. width: 'auto'
  2889. });
  2890.  
  2891. this.initList();
  2892.  
  2893. if (this.isShown) {
  2894. if (this.length) {
  2895. if (this.viewed) {
  2896. var index = indexes.indexOf(this.index);
  2897.  
  2898. if (index >= 0) {
  2899. this.viewed = false;
  2900. this.view(Math.max(this.index - (index + 1), 0));
  2901. } else {
  2902. addClass(this.items[this.index], CLASS_ACTIVE);
  2903. }
  2904. }
  2905. } else {
  2906. this.image = null;
  2907. this.viewed = false;
  2908. this.index = 0;
  2909. this.imageData = {};
  2910. this.canvas.innerHTML = '';
  2911. this.title.innerHTML = '';
  2912. }
  2913. }
  2914. } else {
  2915. this.build();
  2916. }
  2917.  
  2918. return this;
  2919. },
  2920.  
  2921.  
  2922. // Destroy the viewer
  2923. destroy: function destroy() {
  2924. var element = this.element,
  2925. options = this.options;
  2926.  
  2927.  
  2928. if (!getData(element, NAMESPACE)) {
  2929. return this;
  2930. }
  2931.  
  2932. this.destroyed = true;
  2933.  
  2934. if (this.ready) {
  2935. if (this.played) {
  2936. this.stop();
  2937. }
  2938.  
  2939. if (options.inline) {
  2940. if (this.fulled) {
  2941. this.exit();
  2942. }
  2943.  
  2944. this.unbind();
  2945. } else if (this.isShown) {
  2946. if (this.viewing) {
  2947. if (this.imageRendering) {
  2948. this.imageRendering.abort();
  2949. } else if (this.imageInitializing) {
  2950. this.imageInitializing.abort();
  2951. }
  2952. }
  2953.  
  2954. if (this.hiding) {
  2955. this.transitioning.abort();
  2956. }
  2957.  
  2958. this.hidden();
  2959. } else if (this.showing) {
  2960. this.transitioning.abort();
  2961. this.hidden();
  2962. }
  2963.  
  2964. this.ready = false;
  2965. this.viewer.parentNode.removeChild(this.viewer);
  2966. } else if (options.inline) {
  2967. if (this.delaying) {
  2968. this.delaying.abort();
  2969. } else if (this.initializing) {
  2970. this.initializing.abort();
  2971. }
  2972. }
  2973.  
  2974. if (!options.inline) {
  2975. removeListener(element, EVENT_CLICK, this.onStart);
  2976. }
  2977.  
  2978. removeData(element, NAMESPACE);
  2979. return this;
  2980. }
  2981. };
  2982.  
  2983. var others = {
  2984. open: function open() {
  2985. var body = this.body;
  2986.  
  2987.  
  2988. addClass(body, CLASS_OPEN);
  2989.  
  2990. body.style.paddingRight = this.scrollbarWidth + (parseFloat(this.initialBodyPaddingRight) || 0) + 'px';
  2991. },
  2992. close: function close() {
  2993. var body = this.body;
  2994.  
  2995.  
  2996. removeClass(body, CLASS_OPEN);
  2997. body.style.paddingRight = this.initialBodyPaddingRight;
  2998. },
  2999. shown: function shown() {
  3000. var element = this.element,
  3001. options = this.options;
  3002.  
  3003.  
  3004. this.fulled = true;
  3005. this.isShown = true;
  3006. this.render();
  3007. this.bind();
  3008. this.showing = false;
  3009.  
  3010. if (isFunction(options.shown)) {
  3011. addListener(element, EVENT_SHOWN, options.shown, {
  3012. once: true
  3013. });
  3014. }
  3015.  
  3016. if (dispatchEvent(element, EVENT_SHOWN) === false) {
  3017. return;
  3018. }
  3019.  
  3020. if (this.ready && this.isShown && !this.hiding) {
  3021. this.view(this.index);
  3022. }
  3023. },
  3024. hidden: function hidden() {
  3025. var element = this.element,
  3026. options = this.options;
  3027.  
  3028.  
  3029. this.fulled = false;
  3030. this.viewed = false;
  3031. this.isShown = false;
  3032. this.close();
  3033. this.unbind();
  3034. addClass(this.viewer, CLASS_HIDE);
  3035. this.resetList();
  3036. this.resetImage();
  3037. this.hiding = false;
  3038.  
  3039. if (!this.destroyed) {
  3040. if (isFunction(options.hidden)) {
  3041. addListener(element, EVENT_HIDDEN, options.hidden, {
  3042. once: true
  3043. });
  3044. }
  3045.  
  3046. dispatchEvent(element, EVENT_HIDDEN);
  3047. }
  3048. },
  3049. requestFullscreen: function requestFullscreen() {
  3050. var document = this.element.ownerDocument;
  3051.  
  3052. if (this.fulled && !document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
  3053. var documentElement = document.documentElement;
  3054.  
  3055.  
  3056. if (documentElement.requestFullscreen) {
  3057. documentElement.requestFullscreen();
  3058. } else if (documentElement.msRequestFullscreen) {
  3059. documentElement.msRequestFullscreen();
  3060. } else if (documentElement.mozRequestFullScreen) {
  3061. documentElement.mozRequestFullScreen();
  3062. } else if (documentElement.webkitRequestFullscreen) {
  3063. documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  3064. }
  3065. }
  3066. },
  3067. exitFullscreen: function exitFullscreen() {
  3068. if (this.fulled) {
  3069. var document = this.element.ownerDocument;
  3070.  
  3071. if (document.exitFullscreen) {
  3072. document.exitFullscreen();
  3073. } else if (document.msExitFullscreen) {
  3074. document.msExitFullscreen();
  3075. } else if (document.mozCancelFullScreen) {
  3076. document.mozCancelFullScreen();
  3077. } else if (document.webkitExitFullscreen) {
  3078. document.webkitExitFullscreen();
  3079. }
  3080. }
  3081. },
  3082. change: function change(e) {
  3083. var options = this.options,
  3084. pointers = this.pointers;
  3085.  
  3086. var pointer = pointers[Object.keys(pointers)[0]];
  3087. var offsetX = pointer.endX - pointer.startX;
  3088. var offsetY = pointer.endY - pointer.startY;
  3089.  
  3090. switch (this.action) {
  3091. // Move the current image
  3092. case ACTION_MOVE:
  3093. this.move(offsetX, offsetY);
  3094. break;
  3095.  
  3096. // Zoom the current image
  3097. case ACTION_ZOOM:
  3098. this.zoom(getMaxZoomRatio(pointers), false, e);
  3099. break;
  3100.  
  3101. case ACTION_SWITCH:
  3102. {
  3103. this.action = 'switched';
  3104.  
  3105. var absoluteOffsetX = Math.abs(offsetX);
  3106.  
  3107. if (absoluteOffsetX > 1 && absoluteOffsetX > Math.abs(offsetY)) {
  3108. // Empty `pointers` as `touchend` event will not be fired after swiped in iOS browsers.
  3109. this.pointers = {};
  3110.  
  3111. if (offsetX > 1) {
  3112. this.prev(options.loop);
  3113. } else if (offsetX < -1) {
  3114. this.next(options.loop);
  3115. }
  3116. }
  3117.  
  3118. break;
  3119. }
  3120.  
  3121. default:
  3122. }
  3123.  
  3124. // Override
  3125. forEach(pointers, function (p) {
  3126. p.startX = p.endX;
  3127. p.startY = p.endY;
  3128. });
  3129. },
  3130. isSwitchable: function isSwitchable() {
  3131. var imageData = this.imageData,
  3132. viewerData = this.viewerData;
  3133.  
  3134.  
  3135. return this.length > 1 && imageData.left >= 0 && imageData.top >= 0 && imageData.width <= viewerData.width && imageData.height <= viewerData.height;
  3136. }
  3137. };
  3138.  
  3139. var AnotherViewer = WINDOW.Viewer;
  3140.  
  3141. var Viewer = function () {
  3142. /**
  3143. * Create a new Viewer.
  3144. * @param {Element} element - The target element for viewing.
  3145. * @param {Object} [options={}] - The configuration options.
  3146. */
  3147. function Viewer(element) {
  3148. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  3149. classCallCheck(this, Viewer);
  3150.  
  3151. if (!element || element.nodeType !== 1) {
  3152. throw new Error('The first argument is required and must be an element.');
  3153. }
  3154.  
  3155. this.element = element;
  3156. this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
  3157. this.action = false;
  3158. this.fading = false;
  3159. this.fulled = false;
  3160. this.hiding = false;
  3161. this.imageData = {};
  3162. this.index = 0;
  3163. this.isImg = false;
  3164. this.isShown = false;
  3165. this.length = 0;
  3166. this.played = false;
  3167. this.playing = false;
  3168. this.pointers = {};
  3169. this.ready = false;
  3170. this.showing = false;
  3171. this.timeout = false;
  3172. this.tooltipping = false;
  3173. this.viewed = false;
  3174. this.viewing = false;
  3175. this.wheeling = false;
  3176. this.zooming = false;
  3177. this.init();
  3178. }
  3179.  
  3180. createClass(Viewer, [{
  3181. key: 'init',
  3182. value: function init() {
  3183. var _this = this;
  3184.  
  3185. var element = this.element,
  3186. options = this.options;
  3187.  
  3188.  
  3189. if (getData(element, NAMESPACE)) {
  3190. return;
  3191. }
  3192.  
  3193. setData(element, NAMESPACE, this);
  3194.  
  3195. var isImg = element.tagName.toLowerCase() === 'img';
  3196. var images = [];
  3197.  
  3198. forEach(isImg ? [element] : element.querySelectorAll('img'), function (image) {
  3199. if (isFunction(options.filter)) {
  3200. if (options.filter.call(_this, image)) {
  3201. images.push(image);
  3202. }
  3203. } else {
  3204. images.push(image);
  3205. }
  3206. });
  3207.  
  3208. if (!images.length) {
  3209. return;
  3210. }
  3211.  
  3212. this.isImg = isImg;
  3213. this.length = images.length;
  3214. this.images = images;
  3215.  
  3216. var ownerDocument = element.ownerDocument;
  3217.  
  3218. var body = ownerDocument.body || ownerDocument.documentElement;
  3219.  
  3220. this.body = body;
  3221. this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
  3222. this.initialBodyPaddingRight = window.getComputedStyle(body).paddingRight;
  3223.  
  3224. // Override `transition` option if it is not supported
  3225. if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
  3226. options.transition = false;
  3227. }
  3228.  
  3229. if (options.inline) {
  3230. var count = 0;
  3231. var progress = function progress() {
  3232. count += 1;
  3233.  
  3234. if (count === _this.length) {
  3235. var timeout = void 0;
  3236.  
  3237. _this.initializing = false;
  3238. _this.delaying = {
  3239. abort: function abort() {
  3240. clearTimeout(timeout);
  3241. }
  3242. };
  3243.  
  3244. // build asynchronously to keep `this.viewer` is accessible in `ready` event handler.
  3245. timeout = setTimeout(function () {
  3246. _this.delaying = false;
  3247. _this.build();
  3248. }, 0);
  3249. }
  3250. };
  3251.  
  3252. this.initializing = {
  3253. abort: function abort() {
  3254. forEach(images, function (image) {
  3255. if (!image.complete) {
  3256. removeListener(image, EVENT_LOAD, progress);
  3257. }
  3258. });
  3259. }
  3260. };
  3261.  
  3262. forEach(images, function (image) {
  3263. if (image.complete) {
  3264. progress();
  3265. } else {
  3266. addListener(image, EVENT_LOAD, progress, {
  3267. once: true
  3268. });
  3269. }
  3270. });
  3271. } else {
  3272. addListener(element, EVENT_CLICK, this.onStart = function (_ref) {
  3273. var target = _ref.target;
  3274.  
  3275. if (target.tagName.toLowerCase() === 'img') {
  3276. _this.view(_this.images.indexOf(target));
  3277. }
  3278. });
  3279. }
  3280. }
  3281. }, {
  3282. key: 'build',
  3283. value: function build() {
  3284. if (this.ready) {
  3285. return;
  3286. }
  3287.  
  3288. var element = this.element,
  3289. options = this.options;
  3290.  
  3291. var parent = element.parentNode;
  3292. var template = document.createElement('div');
  3293.  
  3294. template.innerHTML = TEMPLATE;
  3295.  
  3296. var viewer = template.querySelector('.' + NAMESPACE + '-container');
  3297. var title = viewer.querySelector('.' + NAMESPACE + '-title');
  3298. var toolbar = viewer.querySelector('.' + NAMESPACE + '-toolbar');
  3299. var navbar = viewer.querySelector('.' + NAMESPACE + '-navbar');
  3300. var button = viewer.querySelector('.' + NAMESPACE + '-button');
  3301. var canvas = viewer.querySelector('.' + NAMESPACE + '-canvas');
  3302.  
  3303. this.parent = parent;
  3304. this.viewer = viewer;
  3305. this.title = title;
  3306. this.toolbar = toolbar;
  3307. this.navbar = navbar;
  3308. this.button = button;
  3309. this.canvas = canvas;
  3310. this.footer = viewer.querySelector('.' + NAMESPACE + '-footer');
  3311. this.tooltipBox = viewer.querySelector('.' + NAMESPACE + '-tooltip');
  3312. this.player = viewer.querySelector('.' + NAMESPACE + '-player');
  3313. this.list = viewer.querySelector('.' + NAMESPACE + '-list');
  3314.  
  3315. addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(options.title));
  3316. addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
  3317. toggleClass(button, CLASS_HIDE, !options.button);
  3318.  
  3319. if (options.backdrop) {
  3320. addClass(viewer, NAMESPACE + '-backdrop');
  3321.  
  3322. if (!options.inline && options.backdrop === true) {
  3323. setData(canvas, DATA_ACTION, 'hide');
  3324. }
  3325. }
  3326.  
  3327. if (options.toolbar) {
  3328. var list = document.createElement('ul');
  3329. var custom = isPlainObject(options.toolbar);
  3330. var zoomButtons = BUTTONS.slice(0, 3);
  3331. var rotateButtons = BUTTONS.slice(7, 9);
  3332. var scaleButtons = BUTTONS.slice(9);
  3333.  
  3334. if (!custom) {
  3335. addClass(toolbar, getResponsiveClass(options.toolbar));
  3336. }
  3337.  
  3338. forEach(custom ? options.toolbar : BUTTONS, function (value, index) {
  3339. var deep = custom && isPlainObject(value);
  3340. var name = custom ? hyphenate(index) : value;
  3341. var show = deep && !isUndefined(value.show) ? value.show : value;
  3342.  
  3343. if (!show || !options.zoomable && zoomButtons.indexOf(name) !== -1 || !options.rotatable && rotateButtons.indexOf(name) !== -1 || !options.scalable && scaleButtons.indexOf(name) !== -1) {
  3344. return;
  3345. }
  3346.  
  3347. var size = deep && !isUndefined(value.size) ? value.size : value;
  3348. var click = deep && !isUndefined(value.click) ? value.click : value;
  3349. var item = document.createElement('li');
  3350.  
  3351. item.setAttribute('role', 'button');
  3352. addClass(item, NAMESPACE + '-' + name);
  3353.  
  3354. if (!isFunction(click)) {
  3355. setData(item, DATA_ACTION, name);
  3356. }
  3357.  
  3358. if (isNumber(show)) {
  3359. addClass(item, getResponsiveClass(show));
  3360. }
  3361.  
  3362. if (['small', 'large'].indexOf(size) !== -1) {
  3363. addClass(item, NAMESPACE + '-' + size);
  3364. } else if (name === 'play') {
  3365. addClass(item, NAMESPACE + '-large');
  3366. }
  3367.  
  3368. if (isFunction(click)) {
  3369. addListener(item, EVENT_CLICK, click);
  3370. }
  3371.  
  3372. list.appendChild(item);
  3373. });
  3374.  
  3375. toolbar.appendChild(list);
  3376. } else {
  3377. addClass(toolbar, CLASS_HIDE);
  3378. }
  3379.  
  3380. if (!options.rotatable) {
  3381. var rotates = toolbar.querySelectorAll('li[class*="rotate"]');
  3382.  
  3383. addClass(rotates, CLASS_INVISIBLE);
  3384. forEach(rotates, function (rotate) {
  3385. toolbar.appendChild(rotate);
  3386. });
  3387. }
  3388.  
  3389. if (options.inline) {
  3390. addClass(button, CLASS_FULLSCREEN);
  3391. setStyle(viewer, {
  3392. zIndex: options.zIndexInline
  3393. });
  3394.  
  3395. if (window.getComputedStyle(parent).position === 'static') {
  3396. setStyle(parent, {
  3397. position: 'relative'
  3398. });
  3399. }
  3400.  
  3401. parent.insertBefore(viewer, element.nextSibling);
  3402. } else {
  3403. addClass(button, CLASS_CLOSE);
  3404. addClass(viewer, CLASS_FIXED);
  3405. addClass(viewer, CLASS_FADE);
  3406. addClass(viewer, CLASS_HIDE);
  3407.  
  3408. setStyle(viewer, {
  3409. zIndex: options.zIndex
  3410. });
  3411.  
  3412. var container = options.container;
  3413.  
  3414.  
  3415. if (isString(container)) {
  3416. container = element.ownerDocument.querySelector(container);
  3417. }
  3418.  
  3419. if (!container) {
  3420. container = this.body;
  3421. }
  3422.  
  3423. container.appendChild(viewer);
  3424. }
  3425.  
  3426. if (options.inline) {
  3427. this.render();
  3428. this.bind();
  3429. this.isShown = true;
  3430. }
  3431.  
  3432. this.ready = true;
  3433.  
  3434. if (isFunction(options.ready)) {
  3435. addListener(element, EVENT_READY, options.ready, {
  3436. once: true
  3437. });
  3438. }
  3439.  
  3440. if (dispatchEvent(element, EVENT_READY) === false) {
  3441. this.ready = false;
  3442. return;
  3443. }
  3444.  
  3445. if (this.ready && options.inline) {
  3446. this.view();
  3447. }
  3448. }
  3449.  
  3450. /**
  3451. * Get the no conflict viewer class.
  3452. * @returns {Viewer} The viewer class.
  3453. */
  3454.  
  3455. }], [{
  3456. key: 'noConflict',
  3457. value: function noConflict() {
  3458. window.Viewer = AnotherViewer;
  3459. return Viewer;
  3460. }
  3461.  
  3462. /**
  3463. * Change the default options.
  3464. * @param {Object} options - The new default options.
  3465. */
  3466.  
  3467. }, {
  3468. key: 'setDefaults',
  3469. value: function setDefaults(options) {
  3470. assign(DEFAULTS, isPlainObject(options) && options);
  3471. }
  3472. }]);
  3473. return Viewer;
  3474. }();
  3475.  
  3476. assign(Viewer.prototype, render, events, handlers, methods, others);
  3477.  
  3478. return Viewer;
  3479.  
  3480. })));