viewerjs 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * Viewer.js v1.12.0
2
+ * Viewer.js v1.13.0
3
3
  * https://fengyuanchen.github.io/viewerjs
4
4
  *
5
5
  * Copyright 2015-present Chen Fengyuan
6
6
  * Released under the MIT license
7
7
  *
8
- * Date: 2026-08-22T13:00:39.652Z
8
+ * Date: 2026-09-06T10:39:44.170Z
9
9
  */
10
10
 
11
11
  'use strict';
@@ -91,9 +91,14 @@ var DEFAULTS = {
91
91
  button: true,
92
92
  /**
93
93
  * Show the navbar.
94
- * @type {boolean | number}
94
+ * @type {boolean | number | string | Object}
95
95
  */
96
96
  navbar: true,
97
+ /**
98
+ * Show the navigation buttons.
99
+ * @type {boolean | number | Object}
100
+ */
101
+ navigation: false,
97
102
  /**
98
103
  * Specify the visibility and the content of the title.
99
104
  * @type {boolean | number | Function | Array}
@@ -145,6 +150,11 @@ var DEFAULTS = {
145
150
  * @type {boolean}
146
151
  */
147
152
  inline: false,
153
+ /**
154
+ * Enable to automatically play the images when playing.
155
+ * @type {boolean}
156
+ */
157
+ autoplay: true,
148
158
  /**
149
159
  * The amount of time to delay between automatically cycling an image when playing.
150
160
  * @type {number}
@@ -170,6 +180,11 @@ var DEFAULTS = {
170
180
  * @type {boolean}
171
181
  */
172
182
  loop: true,
183
+ /**
184
+ * Enable to preload the next or previous image before viewing it.
185
+ * @type {boolean}
186
+ */
187
+ preload: true,
173
188
  /**
174
189
  * Min width of the viewer in inline mode.
175
190
  * @type {number}
@@ -185,11 +200,26 @@ var DEFAULTS = {
185
200
  * @type {boolean}
186
201
  */
187
202
  movable: true,
203
+ /**
204
+ * Show a magnifier when hovering over the image in fullscreen mode.
205
+ * @type {boolean | Object}
206
+ */
207
+ magnifier: false,
188
208
  /**
189
209
  * Enable to rotate the image.
190
210
  * @type {boolean}
191
211
  */
192
212
  rotatable: true,
213
+ /**
214
+ * Enable to rotate the current image by gesture.
215
+ * @type {boolean}
216
+ */
217
+ rotateOnGesture: true,
218
+ /**
219
+ * Enable to rotate the current image by dragging on the touch screen.
220
+ * @type {boolean}
221
+ */
222
+ rotateOnTouch: true,
193
223
  /**
194
224
  * Enable to scale the image.
195
225
  * @type {boolean}
@@ -205,6 +235,11 @@ var DEFAULTS = {
205
235
  * @type {boolean}
206
236
  */
207
237
  zoomOnTouch: true,
238
+ /**
239
+ * Enable to zoom the current image by gesture.
240
+ * @type {boolean}
241
+ */
242
+ zoomOnGesture: true,
208
243
  /**
209
244
  * Enable to zoom the image by wheeling mouse.
210
245
  * @type {boolean}
@@ -228,7 +263,7 @@ var DEFAULTS = {
228
263
  tooltip: true,
229
264
  /**
230
265
  * Enable CSS3 Transition for some special elements.
231
- * @type {boolean}
266
+ * @type {boolean | Object}
232
267
  */
233
268
  transition: true,
234
269
  /**
@@ -248,12 +283,12 @@ var DEFAULTS = {
248
283
  zoomRatio: 0.1,
249
284
  /**
250
285
  * Define the min ratio of the image when zoom out.
251
- * @type {number}
286
+ * @type {number | Function}
252
287
  */
253
288
  minZoomRatio: 0.01,
254
289
  /**
255
290
  * Define the max ratio of the image when zoom in.
256
- * @type {number}
291
+ * @type {number | Function}
257
292
  */
258
293
  maxZoomRatio: 100,
259
294
  /**
@@ -284,7 +319,7 @@ var DEFAULTS = {
284
319
  stop: null
285
320
  };
286
321
 
287
- var TEMPLATE = '<div class="viewer-container" tabindex="-1" touch-action="none">' + '<div class="viewer-canvas"></div>' + '<div class="viewer-footer">' + '<div class="viewer-title"></div>' + '<div class="viewer-toolbar"></div>' + '<div class="viewer-navbar">' + '<ul class="viewer-list" role="navigation"></ul>' + '</div>' + '</div>' + '<div class="viewer-tooltip" role="alert" aria-hidden="true"></div>' + '<div class="viewer-button" data-viewer-action="mix" role="button"></div>' + '<div class="viewer-player"></div>' + '</div>';
322
+ var TEMPLATE = '<div class="viewer-container" tabindex="-1" touch-action="none">' + '<div class="viewer-canvas"></div>' + '<div class="viewer-magnifier" aria-hidden="true">' + '<img class="viewer-magnifier-image" alt="">' + '</div>' + '<div class="viewer-navigation">' + '<div class="viewer-prev" data-viewer-action="prev" role="button" aria-label="Previous"></div>' + '<div class="viewer-next" data-viewer-action="next" role="button" aria-label="Next"></div>' + '</div>' + '<div class="viewer-footer">' + '<div class="viewer-title"></div>' + '<div class="viewer-toolbar"></div>' + '<div class="viewer-navbar">' + '<ul class="viewer-list" role="navigation"></ul>' + '</div>' + '</div>' + '<div class="viewer-tooltip" role="alert" aria-hidden="true"></div>' + '<div class="viewer-button" data-viewer-action="mix" role="button"></div>' + '<div class="viewer-player"></div>' + '</div>';
288
323
 
289
324
  var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
290
325
  var WINDOW = IS_BROWSER ? window : {};
@@ -294,7 +329,9 @@ var NAMESPACE = 'viewer';
294
329
 
295
330
  // Actions
296
331
  var ACTION_MOVE = 'move';
332
+ var ACTION_ROTATE = 'rotate';
297
333
  var ACTION_SWITCH = 'switch';
334
+ var ACTION_TRANSFORM = 'transform';
298
335
  var ACTION_ZOOM = 'zoom';
299
336
 
300
337
  // Classes
@@ -328,11 +365,14 @@ var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';
328
365
  var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';
329
366
  var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';
330
367
  var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;
368
+ var EVENT_POINTER_ENTER = HAS_POINTER_EVENT ? 'pointerenter' : 'mouseenter';
369
+ var EVENT_POINTER_LEAVE = HAS_POINTER_EVENT ? 'pointerleave' : 'mouseleave';
331
370
  var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;
332
371
  var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;
333
372
  var EVENT_RESIZE = 'resize';
334
373
  var EVENT_TRANSITION_END = 'transitionend';
335
374
  var EVENT_WHEEL = 'wheel';
375
+ var EVENT_GESTURE = 'gesturestart gesturechange gestureend';
336
376
 
337
377
  // Custom events
338
378
  var EVENT_READY = 'ready';
@@ -456,6 +496,32 @@ function forEach(data, callback) {
456
496
  return data;
457
497
  }
458
498
 
499
+ /**
500
+ * Inherit attributes from the original image.
501
+ * @param {Element} image - The target image.
502
+ * @param {Element} originalImage - The original image.
503
+ * @param {Array} inheritedAttributes - The attributes to inherit.
504
+ */
505
+ function inheritAttributes(image, originalImage, inheritedAttributes) {
506
+ forEach(inheritedAttributes, function (name) {
507
+ var value = originalImage.getAttribute(name);
508
+ if (value !== null) {
509
+ image.setAttribute(name, value);
510
+ }
511
+ });
512
+ }
513
+
514
+ /**
515
+ * Check if transition is enabled for the given action.
516
+ * @param {Object} options - The viewer options.
517
+ * @param {string} action - The transition action.
518
+ * @returns {boolean} Returns `true` if transition is enabled.
519
+ */
520
+ function isTransitionEnabled(options, action) {
521
+ var transition = options.transition;
522
+ return transition && transition[action] !== false;
523
+ }
524
+
459
525
  /**
460
526
  * Extend the given object.
461
527
  * @param {*} obj - The object to be extended.
@@ -785,10 +851,8 @@ function getTransforms(_ref) {
785
851
  if (isNumber(rotate) && rotate !== 0) {
786
852
  values.push("rotate(".concat(rotate, "deg)"));
787
853
  }
788
- if (isNumber(scaleX) && scaleX !== 1) {
854
+ if (isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1)) {
789
855
  values.push("scaleX(".concat(scaleX, ")"));
790
- }
791
- if (isNumber(scaleY) && scaleY !== 1) {
792
856
  values.push("scaleY(".concat(scaleY, ")"));
793
857
  }
794
858
  var transform = values.length ? values.join(' ') : 'none';
@@ -834,12 +898,7 @@ function getImageNaturalSizes(image, options, callback) {
834
898
  body.removeChild(newImage);
835
899
  }
836
900
  };
837
- forEach(options.inheritedAttributes, function (name) {
838
- var value = image.getAttribute(name);
839
- if (value !== null) {
840
- newImage.setAttribute(name, value);
841
- }
842
- });
901
+ inheritAttributes(newImage, image, options.inheritedAttributes);
843
902
  newImage.src = image.src;
844
903
 
845
904
  // iOS Safari will convert the image automatically
@@ -896,6 +955,34 @@ function getMaxZoomRatio(pointers) {
896
955
  return ratios[0];
897
956
  }
898
957
 
958
+ /**
959
+ * Get the max rotation degree of a group of pointers.
960
+ * @param {Object} pointers - The target pointers.
961
+ * @returns {number} The result degree.
962
+ */
963
+ function getMaxRotateDegree(pointers) {
964
+ var pointers2 = _objectSpread2({}, pointers);
965
+ var degrees = [];
966
+ forEach(pointers, function (pointer, pointerId) {
967
+ delete pointers2[pointerId];
968
+ forEach(pointers2, function (pointer2) {
969
+ var start = Math.atan2(pointer2.startY - pointer.startY, pointer2.startX - pointer.startX);
970
+ var end = Math.atan2(pointer2.endY - pointer.endY, pointer2.endX - pointer.endX);
971
+ var radians = end - start;
972
+ if (radians > Math.PI) {
973
+ radians -= Math.PI * 2;
974
+ } else if (radians < -Math.PI) {
975
+ radians += Math.PI * 2;
976
+ }
977
+ degrees.push(radians * 180 / Math.PI);
978
+ });
979
+ });
980
+ degrees.sort(function (a, b) {
981
+ return Math.abs(b) - Math.abs(a);
982
+ });
983
+ return degrees[0] || 0;
984
+ }
985
+
899
986
  /**
900
987
  * Get a pointer from an event object.
901
988
  * @param {Object} event - The target event object.
@@ -948,7 +1035,7 @@ var render = {
948
1035
  this.renderViewer();
949
1036
  },
950
1037
  initBody: function initBody() {
951
- var ownerDocument = this.element.ownerDocument;
1038
+ var ownerDocument = this.ownerDocument;
952
1039
  var body = ownerDocument.body || ownerDocument.documentElement;
953
1040
  this.body = body;
954
1041
  this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
@@ -984,26 +1071,34 @@ var render = {
984
1071
  },
985
1072
  initList: function initList() {
986
1073
  var _this = this;
1074
+ var viewIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.index;
987
1075
  var element = this.element,
988
1076
  options = this.options,
989
1077
  list = this.list;
990
1078
  var items = [];
1079
+ var navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
1080
+ var probe = document.createElement('li');
1081
+ list.appendChild(probe);
1082
+ var itemWidth = probe.offsetWidth + parseInt(window.getComputedStyle(probe).marginLeft, 10);
1083
+ list.removeChild(probe);
1084
+ var visibleItemCount = isNumber(navbarOptions.visibleItemCount) ? Math.floor(navbarOptions.visibleItemCount) : Math.floor(this.containerData.width / itemWidth);
1085
+ visibleItemCount = Math.min(visibleItemCount, this.length);
1086
+ var start = visibleItemCount > 0 ? Math.min(Math.max(0, viewIndex - Math.floor(visibleItemCount / 2)), Math.max(0, this.length - visibleItemCount)) : 0;
1087
+ var end = visibleItemCount > 0 ? Math.min(this.length, start + visibleItemCount) : this.length;
991
1088
 
992
1089
  // initList may be called in this.update, so should keep idempotent
993
1090
  list.innerHTML = '';
994
1091
  forEach(this.images, function (image, index) {
1092
+ if (visibleItemCount > 0 && (index < start || index >= end)) {
1093
+ return;
1094
+ }
995
1095
  var src = image.src;
996
1096
  var alt = image.alt || getImageNameFromURL(src);
997
1097
  var url = _this.getImageURL(image);
998
1098
  if (src || url) {
999
1099
  var item = document.createElement('li');
1000
1100
  var img = document.createElement('img');
1001
- forEach(options.inheritedAttributes, function (name) {
1002
- var value = image.getAttribute(name);
1003
- if (value !== null) {
1004
- img.setAttribute(name, value);
1005
- }
1006
- });
1101
+ inheritAttributes(img, image, options.inheritedAttributes);
1007
1102
  if (options.navbar) {
1008
1103
  img.src = src || url;
1009
1104
  }
@@ -1021,6 +1116,13 @@ var render = {
1021
1116
  }
1022
1117
  });
1023
1118
  this.items = items;
1119
+ if (this.viewed) {
1120
+ var activeItem = this.getItem(this.index);
1121
+ if (activeItem) {
1122
+ addClass(activeItem, CLASS_ACTIVE);
1123
+ activeItem.setAttribute('aria-selected', true);
1124
+ }
1125
+ }
1024
1126
  forEach(items, function (item) {
1025
1127
  var image = item.firstElementChild;
1026
1128
  var onLoad;
@@ -1047,7 +1149,7 @@ var render = {
1047
1149
  once: true
1048
1150
  });
1049
1151
  });
1050
- if (options.transition) {
1152
+ if (isTransitionEnabled(options, 'view')) {
1051
1153
  addListener(element, EVENT_VIEWED, function () {
1052
1154
  addClass(list, CLASS_TRANSITION);
1053
1155
  }, {
@@ -1055,9 +1157,20 @@ var render = {
1055
1157
  });
1056
1158
  }
1057
1159
  },
1160
+ getItem: function getItem(index) {
1161
+ var item;
1162
+ forEach(this.items, function (candidate) {
1163
+ if (Number(getData(candidate, 'index')) === index) {
1164
+ item = candidate;
1165
+ return false;
1166
+ }
1167
+ return true;
1168
+ });
1169
+ return item;
1170
+ },
1058
1171
  renderList: function renderList() {
1059
1172
  var index = this.index;
1060
- var item = this.items[index];
1173
+ var item = this.getItem(index);
1061
1174
  if (!item) {
1062
1175
  return;
1063
1176
  }
@@ -1068,9 +1181,9 @@ var render = {
1068
1181
 
1069
1182
  // Place the active item in the center of the screen
1070
1183
  setStyle(this.list, assign({
1071
- width: outerWidth * this.length - gutter
1184
+ width: outerWidth * this.items.length - gutter
1072
1185
  }, getTransforms({
1073
- translateX: (this.viewerData.width - offsetWidth) / 2 - outerWidth * index
1186
+ translateX: (this.viewerData.width - offsetWidth) / 2 - item.offsetLeft
1074
1187
  })));
1075
1188
  },
1076
1189
  resetList: function resetList() {
@@ -1156,8 +1269,23 @@ var render = {
1156
1269
  marginLeft: imageData.x,
1157
1270
  marginTop: imageData.y
1158
1271
  }, getTransforms(imageData)));
1272
+ if (this.magnifierPoint) {
1273
+ this.renderMagnifier();
1274
+ }
1159
1275
  if (done) {
1160
- if ((this.viewing || this.moving || this.rotating || this.scaling || this.zooming) && this.options.transition && hasClass(image, CLASS_TRANSITION)) {
1276
+ var action = false;
1277
+ if (this.viewing) {
1278
+ action = 'view';
1279
+ } else if (this.moving) {
1280
+ action = 'move';
1281
+ } else if (this.rotating) {
1282
+ action = 'rotate';
1283
+ } else if (this.scaling) {
1284
+ action = 'scale';
1285
+ } else if (this.zooming) {
1286
+ action = 'zoom';
1287
+ }
1288
+ if (action && isTransitionEnabled(this.options, action) && hasClass(image, CLASS_TRANSITION)) {
1161
1289
  var onTransitionEnd = function onTransitionEnd() {
1162
1290
  _this3.imageRendering = false;
1163
1291
  done();
@@ -1193,9 +1321,12 @@ var events = {
1193
1321
  var options = this.options,
1194
1322
  viewer = this.viewer,
1195
1323
  canvas = this.canvas;
1196
- var document = this.element.ownerDocument;
1324
+ var document = this.ownerDocument;
1197
1325
  addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
1198
1326
  addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
1327
+ addListener(viewer, EVENT_POINTER_ENTER, this.onMagnifyEnter = this.magnify.bind(this));
1328
+ addListener(viewer, EVENT_POINTER_MOVE, this.onMagnify = this.magnify.bind(this));
1329
+ addListener(viewer, EVENT_POINTER_LEAVE, this.onMagnifierLeave = this.hideMagnifier.bind(this));
1199
1330
  addListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
1200
1331
  addListener(document, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
1201
1332
  addListener(document, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
@@ -1207,6 +1338,11 @@ var events = {
1207
1338
  capture: true
1208
1339
  });
1209
1340
  }
1341
+ if (options.zoomable && options.zoomOnGesture || options.rotatable && options.rotateOnGesture) {
1342
+ addListener(viewer, EVENT_GESTURE, this.onGesture = this.gesture.bind(this), {
1343
+ passive: false
1344
+ });
1345
+ }
1210
1346
  if (options.toggleOnDblclick) {
1211
1347
  addListener(canvas, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
1212
1348
  }
@@ -1215,9 +1351,12 @@ var events = {
1215
1351
  var options = this.options,
1216
1352
  viewer = this.viewer,
1217
1353
  canvas = this.canvas;
1218
- var document = this.element.ownerDocument;
1354
+ var document = this.ownerDocument;
1219
1355
  removeListener(viewer, EVENT_CLICK, this.onClick);
1220
1356
  removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
1357
+ removeListener(viewer, EVENT_POINTER_ENTER, this.onMagnifyEnter);
1358
+ removeListener(viewer, EVENT_POINTER_MOVE, this.onMagnify);
1359
+ removeListener(viewer, EVENT_POINTER_LEAVE, this.onMagnifierLeave);
1221
1360
  removeListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown);
1222
1361
  removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove);
1223
1362
  removeListener(document, EVENT_POINTER_UP, this.onPointerUp);
@@ -1229,6 +1368,11 @@ var events = {
1229
1368
  capture: true
1230
1369
  });
1231
1370
  }
1371
+ if (options.zoomable && options.zoomOnGesture || options.rotatable && options.rotateOnGesture) {
1372
+ removeListener(viewer, EVENT_GESTURE, this.onGesture, {
1373
+ passive: false
1374
+ });
1375
+ }
1232
1376
  if (options.toggleOnDblclick) {
1233
1377
  removeListener(canvas, EVENT_DBLCLICK, this.onDblclick);
1234
1378
  }
@@ -1341,10 +1485,13 @@ var handlers = {
1341
1485
  image.style.cssText = 'height:0;' + "margin-left:".concat(viewerData.width / 2, "px;") + "margin-top:".concat(viewerData.height / 2, "px;") + 'max-width:none!important;' + 'position:relative;' + 'width:0;';
1342
1486
  this.initImage(function () {
1343
1487
  toggleClass(image, CLASS_MOVE, options.movable);
1344
- toggleClass(image, CLASS_TRANSITION, options.transition);
1488
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'view'));
1345
1489
  _this.renderImage(function () {
1346
1490
  _this.viewed = true;
1347
1491
  _this.viewing = false;
1492
+ setTimeout(function () {
1493
+ toggleClass(image, CLASS_TRANSITION, options.transition);
1494
+ }, 300);
1348
1495
  if (isFunction(options.viewed)) {
1349
1496
  addListener(element, EVENT_VIEWED, options.viewed, {
1350
1497
  once: true
@@ -1483,6 +1630,88 @@ var handlers = {
1483
1630
  event.preventDefault();
1484
1631
  }
1485
1632
  },
1633
+ magnify: function magnify(event) {
1634
+ if (event.changedTouches || event.pointerType && event.pointerType !== 'mouse') {
1635
+ this.hideMagnifier();
1636
+ return;
1637
+ }
1638
+ this.magnifierPoint = {
1639
+ clientX: event.clientX,
1640
+ clientY: event.clientY
1641
+ };
1642
+ this.renderMagnifier();
1643
+ },
1644
+ renderMagnifier: function renderMagnifier() {
1645
+ var options = this.options,
1646
+ imageData = this.imageData,
1647
+ magnifier = this.magnifier,
1648
+ magnifierImage = this.magnifierImage,
1649
+ viewer = this.viewer;
1650
+ var config = isPlainObject(options.magnifier) ? options.magnifier : {};
1651
+ var point = this.magnifierPoint;
1652
+ if (!options.magnifier || !this.fulled || !this.viewed || !magnifier || !magnifierImage || !point) {
1653
+ this.hideMagnifier();
1654
+ return;
1655
+ }
1656
+ var size = Math.max(1, Number(config.size) || 100);
1657
+ var zoomRatio = Math.max(1, Number(config.zoomRatio) || 2);
1658
+ var opacity = Number(config.opacity);
1659
+ var rect = viewer.getBoundingClientRect();
1660
+ var x = point.clientX - rect.left;
1661
+ var y = point.clientY - rect.top;
1662
+ var imageURL = this.image.currentSrc || this.image.src;
1663
+ var imageRect = this.image.getBoundingClientRect();
1664
+ if (point.clientX < imageRect.left || point.clientX > imageRect.right || point.clientY < imageRect.top || point.clientY > imageRect.bottom) {
1665
+ this.hideMagnifier();
1666
+ return;
1667
+ }
1668
+ var scaleX = isNumber(imageData.scaleX) ? imageData.scaleX : 1;
1669
+ var scaleY = isNumber(imageData.scaleY) ? imageData.scaleY : 1;
1670
+ var rotate = (imageData.rotate || 0) * Math.PI / 180;
1671
+ var cos = Math.cos(rotate);
1672
+ var sin = Math.sin(rotate);
1673
+ var matrixA = cos * scaleX;
1674
+ var matrixB = sin * scaleX;
1675
+ var matrixC = -sin * scaleY;
1676
+ var matrixD = cos * scaleY;
1677
+ var determinant = scaleX * scaleY;
1678
+ if (determinant === 0) {
1679
+ this.hideMagnifier();
1680
+ return;
1681
+ }
1682
+ var centerX = imageData.x + imageData.width / 2;
1683
+ var centerY = imageData.y + imageData.height / 2;
1684
+ var localX = (matrixD * (x - centerX) - matrixC * (y - centerY)) / determinant;
1685
+ var localY = (-matrixB * (x - centerX) + matrixA * (y - centerY)) / determinant;
1686
+ var sourceX = localX + imageData.width / 2;
1687
+ var sourceY = localY + imageData.height / 2;
1688
+ var magnifiedWidth = imageData.width * zoomRatio;
1689
+ var magnifiedHeight = imageData.height * zoomRatio;
1690
+ var transformedX = matrixA * (sourceX * zoomRatio - magnifiedWidth / 2) + matrixC * (sourceY * zoomRatio - magnifiedHeight / 2);
1691
+ var transformedY = matrixB * (sourceX * zoomRatio - magnifiedWidth / 2) + matrixD * (sourceY * zoomRatio - magnifiedHeight / 2);
1692
+ this.magnifierSourcePoint = {
1693
+ x: sourceX,
1694
+ y: sourceY
1695
+ };
1696
+ magnifier.style.width = "".concat(size, "px");
1697
+ magnifier.style.height = "".concat(size, "px");
1698
+ magnifier.style.opacity = "".concat(Math.max(0, Math.min(1, isNumber(opacity) ? opacity : 1)));
1699
+ magnifierImage.src = imageURL;
1700
+ magnifierImage.style.width = "".concat(magnifiedWidth, "px");
1701
+ magnifierImage.style.height = "".concat(magnifiedHeight, "px");
1702
+ magnifierImage.style.left = "".concat(size / 2 - magnifiedWidth / 2 - transformedX, "px");
1703
+ magnifierImage.style.top = "".concat(size / 2 - magnifiedHeight / 2 - transformedY, "px");
1704
+ magnifierImage.style.transform = "rotate(".concat(imageData.rotate || 0, "deg) scaleX(").concat(scaleX, ") scaleY(").concat(scaleY, ")");
1705
+ magnifier.removeAttribute('aria-hidden');
1706
+ addClass(magnifier, 'viewer-show');
1707
+ },
1708
+ hideMagnifier: function hideMagnifier() {
1709
+ if (this.magnifier) {
1710
+ removeClass(this.magnifier, 'viewer-show');
1711
+ this.magnifier.setAttribute('aria-hidden', true);
1712
+ this.magnifierPoint = null;
1713
+ }
1714
+ },
1486
1715
  pointerdown: function pointerdown(event) {
1487
1716
  var options = this.options,
1488
1717
  pointers = this.pointers;
@@ -1511,12 +1740,14 @@ var handlers = {
1511
1740
  pointers[event.pointerId || 0] = getPointer(event);
1512
1741
  }
1513
1742
  var action = options.movable ? ACTION_MOVE : false;
1514
- if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) {
1515
- action = ACTION_ZOOM;
1743
+ if ((options.zoomable && options.zoomOnTouch || options.rotatable && options.rotateOnTouch) && Object.keys(pointers).length > 1) {
1744
+ // action = ACTION_ZOOM;
1745
+ // action = ACTION_ROTATE;
1746
+ action = ACTION_TRANSFORM;
1516
1747
  } else if (options.slideOnTouch && (event.pointerType === 'touch' || event.type === 'touchstart') && this.isSwitchable()) {
1517
1748
  action = ACTION_SWITCH;
1518
1749
  }
1519
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1750
+ if (action === ACTION_MOVE || action === ACTION_ZOOM || action === ACTION_ROTATE || action === ACTION_TRANSFORM) {
1520
1751
  removeClass(this.image, CLASS_TRANSITION);
1521
1752
  }
1522
1753
  this.action = action;
@@ -1556,13 +1787,14 @@ var handlers = {
1556
1787
  return;
1557
1788
  }
1558
1789
  event.preventDefault();
1559
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1560
- addClass(this.image, CLASS_TRANSITION);
1790
+ if (action === ACTION_MOVE || action === ACTION_ZOOM || action === ACTION_ROTATE || action === ACTION_TRANSFORM) {
1791
+ var transition = action === ACTION_TRANSFORM ? isTransitionEnabled(options, ACTION_ZOOM) || isTransitionEnabled(options, ACTION_ROTATE) : isTransitionEnabled(options, action);
1792
+ toggleClass(this.image, CLASS_TRANSITION, transition);
1561
1793
  }
1562
1794
  this.action = false;
1563
1795
 
1564
1796
  // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
1565
- if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && pointer && Date.now() - pointer.timeStamp < 500) {
1797
+ if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && action !== ACTION_TRANSFORM && pointer && Date.now() - pointer.timeStamp < 500) {
1566
1798
  clearTimeout(this.clickCanvasTimeout);
1567
1799
  clearTimeout(this.doubleClickImageTimeout);
1568
1800
  if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
@@ -1611,6 +1843,10 @@ var handlers = {
1611
1843
  this.initContainer();
1612
1844
  this.initViewer();
1613
1845
  this.renderViewer();
1846
+ var navbarOptions = isPlainObject(this.options.navbar) ? this.options.navbar : {};
1847
+ if (isUndefined(navbarOptions.visibleItemCount)) {
1848
+ this.initList(this.index);
1849
+ }
1614
1850
  this.renderList();
1615
1851
  if (this.viewed) {
1616
1852
  this.initImage(function () {
@@ -1636,6 +1872,9 @@ var handlers = {
1636
1872
  return;
1637
1873
  }
1638
1874
  event.preventDefault();
1875
+ if (this.gesturing) {
1876
+ return;
1877
+ }
1639
1878
 
1640
1879
  // Limit wheel speed to prevent zoom too fast
1641
1880
  if (this.wheeling) {
@@ -1655,6 +1894,43 @@ var handlers = {
1655
1894
  delta = event.detail > 0 ? 1 : -1;
1656
1895
  }
1657
1896
  this.zoom(-delta * ratio, true, null, event);
1897
+ },
1898
+ gesture: function gesture(event) {
1899
+ var options = this.options;
1900
+ if (!this.viewed || !options.zoomOnGesture && !options.rotateOnGesture) {
1901
+ return;
1902
+ }
1903
+ event.preventDefault();
1904
+ switch (event.type) {
1905
+ case 'gesturestart':
1906
+ this.gesturing = true;
1907
+ this.gestureScale = event.scale || 1;
1908
+ this.gestureRotation = event.rotation || 0;
1909
+ break;
1910
+ case 'gesturechange':
1911
+ {
1912
+ var scale = event.scale || 1;
1913
+ var ratio = scale / (this.gestureScale || 1);
1914
+ var rotation = Number(event.rotation);
1915
+ var degree = rotation - (this.gestureRotation || 0);
1916
+ this.gestureScale = scale;
1917
+ if (options.zoomable && options.zoomOnGesture && ratio !== 1) {
1918
+ this.zoom(ratio >= 1 ? ratio - 1 : 1 - 1 / ratio, false, null, event);
1919
+ }
1920
+ if (options.rotatable && options.rotateOnGesture && isNumber(rotation)) {
1921
+ this.gestureRotation = rotation;
1922
+ if (degree !== 0) {
1923
+ this.rotate(degree);
1924
+ }
1925
+ }
1926
+ break;
1927
+ }
1928
+ case 'gestureend':
1929
+ this.gesturing = false;
1930
+ this.gestureScale = 1;
1931
+ this.gestureRotation = 0;
1932
+ break;
1933
+ }
1658
1934
  }
1659
1935
  };
1660
1936
 
@@ -1696,7 +1972,7 @@ var methods = {
1696
1972
  viewer.setAttribute('aria-labelledby', this.title.id);
1697
1973
  viewer.setAttribute('aria-modal', true);
1698
1974
  viewer.removeAttribute('aria-hidden');
1699
- if (options.transition && !immediate) {
1975
+ if (isTransitionEnabled(options, 'show') && !immediate) {
1700
1976
  var shown = this.shown.bind(this);
1701
1977
  this.transitioning = {
1702
1978
  abort: function abort() {
@@ -1736,7 +2012,7 @@ var methods = {
1736
2012
  once: true
1737
2013
  });
1738
2014
  }
1739
- if (dispatchEvent(element, EVENT_HIDE) === false) {
2015
+ if (dispatchEvent(element, EVENT_HIDE) === false || this.destroyed) {
1740
2016
  return this;
1741
2017
  }
1742
2018
  if (this.showing) {
@@ -1754,7 +2030,7 @@ var methods = {
1754
2030
  removeClass(viewer, CLASS_IN);
1755
2031
  _this.hidden();
1756
2032
  };
1757
- if (options.transition && !immediate) {
2033
+ if (isTransitionEnabled(options, 'hide') && !immediate) {
1758
2034
  var _onViewerTransitionEnd = function onViewerTransitionEnd(event) {
1759
2035
  // Ignore all propagating `transitionend` events (#275).
1760
2036
  if (event && event.target === viewer) {
@@ -1804,8 +2080,9 @@ var methods = {
1804
2080
  view: function view() {
1805
2081
  var _this2 = this;
1806
2082
  var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.initialViewIndex;
2083
+ var previousIndex = this.index;
1807
2084
  index = Number(index) || 0;
1808
- if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) {
2085
+ if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === previousIndex) {
1809
2086
  return this;
1810
2087
  }
1811
2088
  if (!this.isShown) {
@@ -1819,17 +2096,16 @@ var methods = {
1819
2096
  options = this.options,
1820
2097
  title = this.title,
1821
2098
  canvas = this.canvas;
1822
- var item = this.items[index];
2099
+ var item = this.getItem(index);
2100
+ if (!item) {
2101
+ this.initList(index);
2102
+ item = this.getItem(index);
2103
+ }
1823
2104
  var img = item.querySelector('img');
1824
2105
  var url = getData(img, 'originalUrl');
1825
2106
  var alt = img.getAttribute('alt');
1826
2107
  var image = document.createElement('img');
1827
- forEach(options.inheritedAttributes, function (name) {
1828
- var value = img.getAttribute(name);
1829
- if (value !== null) {
1830
- image.setAttribute(name, value);
1831
- }
1832
- });
2108
+ inheritAttributes(image, img, options.inheritedAttributes);
1833
2109
  image.src = url;
1834
2110
  image.alt = alt;
1835
2111
  if (isFunction(options.view)) {
@@ -1844,7 +2120,8 @@ var methods = {
1844
2120
  }) === false || !this.isShown || this.hiding || this.played) {
1845
2121
  return this;
1846
2122
  }
1847
- var activeItem = this.items[this.index];
2123
+ this.hideMagnifier();
2124
+ var activeItem = this.getItem(previousIndex);
1848
2125
  if (activeItem) {
1849
2126
  removeClass(activeItem, CLASS_ACTIVE);
1850
2127
  activeItem.removeAttribute('aria-selected');
@@ -1876,6 +2153,25 @@ var methods = {
1876
2153
  var imageData = _this2.imageData;
1877
2154
  var render = Array.isArray(options.title) ? options.title[1] : options.title;
1878
2155
  title.innerHTML = escapeHTMLEntities(isFunction(render) ? render.call(_this2, image, imageData) : "".concat(alt, " (").concat(imageData.naturalWidth, " \xD7 ").concat(imageData.naturalHeight, ")"));
2156
+
2157
+ // Preload the image in the direction of navigation
2158
+ if (options.preload) {
2159
+ var direction = index < previousIndex ? -1 : 1;
2160
+ var nextIndex = index + direction;
2161
+ if (nextIndex < 0 || nextIndex >= _this2.length) {
2162
+ if (options.loop) {
2163
+ nextIndex = direction > 0 ? 0 : _this2.length - 1;
2164
+ } else {
2165
+ nextIndex = -1;
2166
+ }
2167
+ }
2168
+ if (nextIndex !== index) {
2169
+ var nextImage = _this2.images[nextIndex];
2170
+ var preloadedImage = document.createElement('img');
2171
+ inheritAttributes(preloadedImage, nextImage, options.inheritedAttributes);
2172
+ preloadedImage.src = _this2.getImageURL(nextImage) || nextImage.src;
2173
+ }
2174
+ }
1879
2175
  };
1880
2176
  var onLoad;
1881
2177
  var onError;
@@ -2027,6 +2323,7 @@ var methods = {
2027
2323
  imageData.y = y;
2028
2324
  imageData.left = x;
2029
2325
  imageData.top = y;
2326
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'move'));
2030
2327
  this.moving = true;
2031
2328
  this.renderImage(function () {
2032
2329
  _this3.moving = false;
@@ -2083,6 +2380,7 @@ var methods = {
2083
2380
  return this;
2084
2381
  }
2085
2382
  imageData.rotate = degree;
2383
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'rotate'));
2086
2384
  this.rotating = true;
2087
2385
  this.renderImage(function () {
2088
2386
  _this4.rotating = false;
@@ -2163,6 +2461,7 @@ var methods = {
2163
2461
  }
2164
2462
  imageData.scaleX = scaleX;
2165
2463
  imageData.scaleY = scaleY;
2464
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'scale'));
2166
2465
  this.scaling = true;
2167
2466
  this.renderImage(function () {
2168
2467
  _this5.scaling = false;
@@ -2234,8 +2533,8 @@ var methods = {
2234
2533
  ratio = Math.max(0, ratio);
2235
2534
  if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
2236
2535
  if (!_zoomable) {
2237
- var minZoomRatio = Math.max(0.01, options.minZoomRatio);
2238
- var maxZoomRatio = Math.min(100, options.maxZoomRatio);
2536
+ var minZoomRatio = Math.max(0.01, isFunction(options.minZoomRatio) ? options.minZoomRatio.call(this, this.image, imageData) : options.minZoomRatio);
2537
+ var maxZoomRatio = Math.min(100, isFunction(options.maxZoomRatio) ? options.maxZoomRatio.call(this, this.image, imageData) : options.maxZoomRatio);
2239
2538
  ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
2240
2539
  }
2241
2540
  if (_originalEvent) {
@@ -2296,6 +2595,7 @@ var methods = {
2296
2595
  imageData.height = newHeight;
2297
2596
  imageData.oldRatio = oldRatio;
2298
2597
  imageData.ratio = ratio;
2598
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'zoom'));
2299
2599
  this.renderImage(function () {
2300
2600
  _this6.zooming = false;
2301
2601
  if (isFunction(options.zoomed)) {
@@ -2349,16 +2649,15 @@ var methods = {
2349
2649
  this.requestFullscreen(fullscreen);
2350
2650
  }
2351
2651
  addClass(player, CLASS_SHOW);
2352
- forEach(this.items, function (item, i) {
2353
- var img = item.querySelector('img');
2652
+ forEach(this.images, function (originalImage, i) {
2354
2653
  var image = document.createElement('img');
2355
- image.src = getData(img, 'originalUrl');
2356
- image.alt = img.getAttribute('alt');
2357
- image.referrerPolicy = img.referrerPolicy;
2654
+ image.src = _this7.getImageURL(originalImage) || originalImage.src;
2655
+ image.alt = originalImage.alt || '';
2656
+ image.referrerPolicy = originalImage.referrerPolicy;
2358
2657
  total += 1;
2359
2658
  addClass(image, CLASS_FADE);
2360
- toggleClass(image, CLASS_TRANSITION, options.transition);
2361
- if (hasClass(item, CLASS_ACTIVE)) {
2659
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'play'));
2660
+ if (i === _this7.index) {
2362
2661
  addClass(image, CLASS_IN);
2363
2662
  index = i;
2364
2663
  }
@@ -2375,7 +2674,7 @@ var methods = {
2375
2674
  index -= 1;
2376
2675
  index = index >= 0 ? index : total - 1;
2377
2676
  addClass(list[index], CLASS_IN);
2378
- _this7.playing.timeout = setTimeout(_prev, options.interval);
2677
+ _this7.playing.timeout = options.autoplay ? setTimeout(_prev, options.interval) : null;
2379
2678
  };
2380
2679
  var _next = function next() {
2381
2680
  clearTimeout(_this7.playing.timeout);
@@ -2383,13 +2682,13 @@ var methods = {
2383
2682
  index += 1;
2384
2683
  index = index < total ? index : 0;
2385
2684
  addClass(list[index], CLASS_IN);
2386
- _this7.playing.timeout = setTimeout(_next, options.interval);
2685
+ _this7.playing.timeout = options.autoplay ? setTimeout(_next, options.interval) : null;
2387
2686
  };
2388
2687
  if (total > 1) {
2389
2688
  this.playing = {
2390
2689
  prev: _prev,
2391
2690
  next: _next,
2392
- timeout: setTimeout(_next, options.interval)
2691
+ timeout: options.autoplay ? setTimeout(_next, options.interval) : null
2393
2692
  };
2394
2693
  }
2395
2694
  }
@@ -2436,11 +2735,9 @@ var methods = {
2436
2735
  this.fulled = true;
2437
2736
  this.open();
2438
2737
  addClass(this.button, CLASS_FULLSCREEN_EXIT);
2439
- if (options.transition) {
2440
- removeClass(list, CLASS_TRANSITION);
2441
- if (this.viewed) {
2442
- removeClass(image, CLASS_TRANSITION);
2443
- }
2738
+ removeClass(list, CLASS_TRANSITION);
2739
+ if (this.viewed) {
2740
+ removeClass(image, CLASS_TRANSITION);
2444
2741
  }
2445
2742
  addClass(viewer, CLASS_FIXED);
2446
2743
  viewer.setAttribute('role', 'dialog');
@@ -2458,14 +2755,7 @@ var methods = {
2458
2755
  this.renderList();
2459
2756
  if (this.viewed) {
2460
2757
  this.initImage(function () {
2461
- _this9.renderImage(function () {
2462
- if (options.transition) {
2463
- setTimeout(function () {
2464
- addClass(image, CLASS_TRANSITION);
2465
- addClass(list, CLASS_TRANSITION);
2466
- }, 0);
2467
- }
2468
- });
2758
+ _this9.renderImage();
2469
2759
  });
2470
2760
  }
2471
2761
  return this;
@@ -2483,11 +2773,9 @@ var methods = {
2483
2773
  this.fulled = false;
2484
2774
  this.close();
2485
2775
  removeClass(this.button, CLASS_FULLSCREEN_EXIT);
2486
- if (options.transition) {
2487
- removeClass(list, CLASS_TRANSITION);
2488
- if (this.viewed) {
2489
- removeClass(image, CLASS_TRANSITION);
2490
- }
2776
+ removeClass(list, CLASS_TRANSITION);
2777
+ if (this.viewed) {
2778
+ removeClass(image, CLASS_TRANSITION);
2491
2779
  }
2492
2780
  if (options.focus) {
2493
2781
  this.clearEnforceFocus();
@@ -2504,14 +2792,7 @@ var methods = {
2504
2792
  this.renderList();
2505
2793
  if (this.viewed) {
2506
2794
  this.initImage(function () {
2507
- _this0.renderImage(function () {
2508
- if (options.transition) {
2509
- setTimeout(function () {
2510
- addClass(image, CLASS_TRANSITION);
2511
- addClass(list, CLASS_TRANSITION);
2512
- }, 0);
2513
- }
2514
- });
2795
+ _this0.renderImage();
2515
2796
  });
2516
2797
  }
2517
2798
  return this;
@@ -2527,7 +2808,7 @@ var methods = {
2527
2808
  }
2528
2809
  tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
2529
2810
  if (!this.tooltipping) {
2530
- if (options.transition) {
2811
+ if (isTransitionEnabled(options, 'tooltip')) {
2531
2812
  if (this.fading) {
2532
2813
  dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
2533
2814
  }
@@ -2547,7 +2828,7 @@ var methods = {
2547
2828
  clearTimeout(this.tooltipping);
2548
2829
  }
2549
2830
  this.tooltipping = setTimeout(function () {
2550
- if (options.transition) {
2831
+ if (isTransitionEnabled(options, 'tooltip')) {
2551
2832
  addListener(tooltipBox, EVENT_TRANSITION_END, function () {
2552
2833
  removeClass(tooltipBox, CLASS_SHOW);
2553
2834
  removeClass(tooltipBox, CLASS_FADE);
@@ -2589,12 +2870,19 @@ var methods = {
2589
2870
  }
2590
2871
  return this;
2591
2872
  },
2592
- // Update viewer when images changed
2593
- update: function update() {
2873
+ /**
2874
+ * Update the viewer when images or options changed.
2875
+ * @param {Object} [updateOptions] - The options to update.
2876
+ * @returns {Viewer} this
2877
+ */
2878
+ update: function update(updateOptions) {
2594
2879
  var _this10 = this;
2595
2880
  var element = this.element,
2596
2881
  options = this.options,
2597
2882
  isImg = this.isImg;
2883
+ if (isPlainObject(updateOptions)) {
2884
+ assign(options, updateOptions);
2885
+ }
2598
2886
 
2599
2887
  // Destroy viewer if the target image was deleted
2600
2888
  if (isImg && !element.parentNode) {
@@ -2617,18 +2905,19 @@ var methods = {
2617
2905
  this.length = images.length;
2618
2906
  if (this.ready) {
2619
2907
  var changedIndexes = [];
2620
- forEach(this.items, function (item, i) {
2908
+ forEach(this.items, function (item) {
2621
2909
  var img = item.querySelector('img');
2622
- var image = images[i];
2910
+ var index = Number(getData(item, 'index'));
2911
+ var image = images[index];
2623
2912
  if (image && img) {
2624
2913
  if (image.src !== img.src
2625
2914
 
2626
2915
  // Title changed (#408)
2627
2916
  || image.alt !== img.alt) {
2628
- changedIndexes.push(i);
2917
+ changedIndexes.push(index);
2629
2918
  }
2630
2919
  } else {
2631
- changedIndexes.push(i);
2920
+ changedIndexes.push(index);
2632
2921
  }
2633
2922
  });
2634
2923
  setStyle(this.list, {
@@ -2643,7 +2932,7 @@ var methods = {
2643
2932
  this.viewed = false;
2644
2933
  this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
2645
2934
  } else {
2646
- var activeItem = this.items[this.index];
2935
+ var activeItem = this.getItem(this.index);
2647
2936
 
2648
2937
  // Reactivate the current viewing item after reset the list.
2649
2938
  addClass(activeItem, CLASS_ACTIVE);
@@ -2829,7 +3118,7 @@ var others = {
2829
3118
  }
2830
3119
  },
2831
3120
  requestFullscreen: function requestFullscreen(options) {
2832
- var document = this.element.ownerDocument;
3121
+ var document = this.ownerDocument;
2833
3122
  if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2834
3123
  var documentElement = document.documentElement;
2835
3124
 
@@ -2851,7 +3140,7 @@ var others = {
2851
3140
  }
2852
3141
  },
2853
3142
  exitFullscreen: function exitFullscreen() {
2854
- var document = this.element.ownerDocument;
3143
+ var document = this.ownerDocument;
2855
3144
  if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2856
3145
  // Document.exitFullscreen()
2857
3146
  if (document.exitFullscreen) {
@@ -2887,7 +3176,38 @@ var others = {
2887
3176
 
2888
3177
  // Zoom the current image
2889
3178
  case ACTION_ZOOM:
2890
- this.zoom(getMaxZoomRatio(pointers), false, null, event);
3179
+ if (options.zoomable && options.zoomOnTouch) {
3180
+ var zoomRatio = getMaxZoomRatio(pointers);
3181
+ if (zoomRatio !== 0) {
3182
+ this.zoom(zoomRatio, false, null, event);
3183
+ }
3184
+ }
3185
+ break;
3186
+
3187
+ // Rotate the current image
3188
+ case ACTION_ROTATE:
3189
+ if (options.rotatable && options.rotateOnTouch) {
3190
+ var rotateDegree = getMaxRotateDegree(pointers);
3191
+ if (rotateDegree !== 0) {
3192
+ this.rotate(rotateDegree, event);
3193
+ }
3194
+ }
3195
+ break;
3196
+
3197
+ // Transform the current image
3198
+ case ACTION_TRANSFORM:
3199
+ if (options.zoomable && options.zoomOnTouch) {
3200
+ var _zoomRatio = getMaxZoomRatio(pointers);
3201
+ if (_zoomRatio !== 0) {
3202
+ this.zoom(_zoomRatio, false, null, event);
3203
+ }
3204
+ }
3205
+ if (options.rotatable && options.rotateOnTouch) {
3206
+ var _rotateDegree = getMaxRotateDegree(pointers);
3207
+ if (_rotateDegree !== 0) {
3208
+ this.rotate(_rotateDegree, event);
3209
+ }
3210
+ }
2891
3211
  break;
2892
3212
  case ACTION_SWITCH:
2893
3213
  {
@@ -2929,16 +3249,17 @@ var getUniqueID = function (id) {
2929
3249
  var Viewer = /*#__PURE__*/function () {
2930
3250
  /**
2931
3251
  * Create a new Viewer.
2932
- * @param {Element} element - The target element for viewing.
3252
+ * @param {Element} element - The target image, or a container of images, to view.
2933
3253
  * @param {Object} [options={}] - The configuration options.
2934
3254
  */
2935
3255
  function Viewer(element) {
2936
3256
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2937
3257
  _classCallCheck(this, Viewer);
2938
- if (!element || element.nodeType !== 1) {
3258
+ if (!element || element.nodeType !== 1 && element.nodeType !== 11) {
2939
3259
  throw new Error('The first argument is required and must be an element.');
2940
3260
  }
2941
3261
  this.element = element;
3262
+ this.ownerDocument = element.ownerDocument || element.host.ownerDocument;
2942
3263
  this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
2943
3264
  this.action = false;
2944
3265
  this.fading = false;
@@ -3000,7 +3321,7 @@ var Viewer = /*#__PURE__*/function () {
3000
3321
  this.initBody();
3001
3322
 
3002
3323
  // Override `transition` option if it is not supported
3003
- if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
3324
+ if (isUndefined(this.ownerDocument.createElement(NAMESPACE).style.transition)) {
3004
3325
  options.transition = false;
3005
3326
  }
3006
3327
  if (options.inline) {
@@ -3087,6 +3408,7 @@ var Viewer = /*#__PURE__*/function () {
3087
3408
  var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
3088
3409
  var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
3089
3410
  var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
3411
+ var navigation = viewer.querySelector(".".concat(NAMESPACE, "-navigation"));
3090
3412
  var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
3091
3413
  var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
3092
3414
  this.parent = parent;
@@ -3094,19 +3416,52 @@ var Viewer = /*#__PURE__*/function () {
3094
3416
  this.title = title;
3095
3417
  this.toolbar = toolbar;
3096
3418
  this.navbar = navbar;
3419
+ this.navigation = navigation;
3097
3420
  this.button = button;
3098
3421
  this.canvas = canvas;
3099
3422
  this.footer = viewer.querySelector(".".concat(NAMESPACE, "-footer"));
3423
+ this.magnifier = viewer.querySelector(".".concat(NAMESPACE, "-magnifier"));
3424
+ this.magnifierImage = viewer.querySelector(".".concat(NAMESPACE, "-magnifier-image"));
3100
3425
  this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
3101
3426
  this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
3102
3427
  this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
3103
3428
  viewer.id = "".concat(NAMESPACE).concat(this.id);
3104
3429
  title.id = "".concat(NAMESPACE, "Title").concat(this.id);
3105
3430
  addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
3106
- addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
3431
+ var navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
3432
+ var navbarShow = options.navbar;
3433
+ var navbarSize = !isUndefined(navbarOptions.size) ? navbarOptions.size : options.navbar;
3434
+ if (isPlainObject(options.navbar)) {
3435
+ navbarShow = !isUndefined(navbarOptions.show) ? navbarOptions.show : true;
3436
+ }
3437
+ addClass(navbar, !navbarShow ? CLASS_HIDE : getResponsiveClass(navbarShow));
3438
+ if (['small', 'medium', 'large'].indexOf(navbarSize) !== -1) {
3439
+ addClass(navbar, "".concat(NAMESPACE, "-").concat(navbarSize));
3440
+ }
3441
+ if (isPlainObject(options.navigation)) {
3442
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3443
+ var name = getData(item, DATA_ACTION);
3444
+ var value = options.navigation[name];
3445
+ var deep = isPlainObject(value);
3446
+ var show = deep && !isUndefined(value.show) ? value.show : value;
3447
+ var size = deep && !isUndefined(value.size) ? value.size : value;
3448
+ toggleClass(item, CLASS_HIDE, !show);
3449
+ if (isNumber(show)) {
3450
+ addClass(item, getResponsiveClass(show));
3451
+ }
3452
+ if (['small', 'large'].indexOf(size) !== -1) {
3453
+ addClass(item, "".concat(NAMESPACE, "-").concat(size));
3454
+ }
3455
+ });
3456
+ } else {
3457
+ addClass(navigation, !options.navigation ? CLASS_HIDE : getResponsiveClass(options.navigation));
3458
+ }
3107
3459
  toggleClass(button, CLASS_HIDE, !options.button);
3108
3460
  if (options.keyboard) {
3109
3461
  button.setAttribute('tabindex', 0);
3462
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3463
+ item.setAttribute('tabindex', 0);
3464
+ });
3110
3465
  }
3111
3466
  if (options.backdrop) {
3112
3467
  addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
@@ -3192,7 +3547,7 @@ var Viewer = /*#__PURE__*/function () {
3192
3547
  });
3193
3548
  var container = options.container;
3194
3549
  if (isString(container)) {
3195
- container = element.ownerDocument.querySelector(container);
3550
+ container = this.ownerDocument.querySelector(container);
3196
3551
  }
3197
3552
  if (!container) {
3198
3553
  container = this.body;
@@ -3220,14 +3575,15 @@ var Viewer = /*#__PURE__*/function () {
3220
3575
  }
3221
3576
 
3222
3577
  /**
3223
- * Get the no conflict viewer class.
3224
- * @returns {Viewer} The viewer class.
3578
+ * Create a new Viewer instance.
3579
+ * @param {Element} element - The target image, or a container of images, to view.
3580
+ * @param {Object} [options={}] - The configuration options.
3581
+ * @returns {Viewer} A new Viewer instance.
3225
3582
  */
3226
3583
  }], [{
3227
- key: "noConflict",
3228
- value: function noConflict() {
3229
- window.Viewer = AnotherViewer;
3230
- return Viewer;
3584
+ key: "create",
3585
+ value: function create(element, options) {
3586
+ return new Viewer(element, options);
3231
3587
  }
3232
3588
 
3233
3589
  /**
@@ -3239,6 +3595,17 @@ var Viewer = /*#__PURE__*/function () {
3239
3595
  value: function setDefaults(options) {
3240
3596
  assign(DEFAULTS, isPlainObject(options) && options);
3241
3597
  }
3598
+
3599
+ /**
3600
+ * Get the no conflict viewer class.
3601
+ * @returns {Viewer} The viewer class.
3602
+ */
3603
+ }, {
3604
+ key: "noConflict",
3605
+ value: function noConflict() {
3606
+ window.Viewer = AnotherViewer;
3607
+ return Viewer;
3608
+ }
3242
3609
  }]);
3243
3610
  }();
3244
3611
  assign(Viewer.prototype, render, events, handlers, methods, others);