Viewer.js mix

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

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

بۇ قوليازمىنى بىۋاسىتە قاچىلاشقا بولمايدۇ. بۇ باشقا قوليازمىلارنىڭ ئىشلىتىشى ئۈچۈن تەمىنلەنگەن ئامبار بولۇپ، ئىشلىتىش ئۈچۈن مېتا كۆرسەتمىسىگە قىستۇرىدىغان كود: // @require https://update.greatest.deepsurf.us/scripts/369647/606908/Viewerjs%20mix.js

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