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.
package/dist/viewer.js CHANGED
@@ -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
  (function (global, factory) {
@@ -95,9 +95,14 @@
95
95
  button: true,
96
96
  /**
97
97
  * Show the navbar.
98
- * @type {boolean | number}
98
+ * @type {boolean | number | string | Object}
99
99
  */
100
100
  navbar: true,
101
+ /**
102
+ * Show the navigation buttons.
103
+ * @type {boolean | number | Object}
104
+ */
105
+ navigation: false,
101
106
  /**
102
107
  * Specify the visibility and the content of the title.
103
108
  * @type {boolean | number | Function | Array}
@@ -149,6 +154,11 @@
149
154
  * @type {boolean}
150
155
  */
151
156
  inline: false,
157
+ /**
158
+ * Enable to automatically play the images when playing.
159
+ * @type {boolean}
160
+ */
161
+ autoplay: true,
152
162
  /**
153
163
  * The amount of time to delay between automatically cycling an image when playing.
154
164
  * @type {number}
@@ -174,6 +184,11 @@
174
184
  * @type {boolean}
175
185
  */
176
186
  loop: true,
187
+ /**
188
+ * Enable to preload the next or previous image before viewing it.
189
+ * @type {boolean}
190
+ */
191
+ preload: true,
177
192
  /**
178
193
  * Min width of the viewer in inline mode.
179
194
  * @type {number}
@@ -189,11 +204,26 @@
189
204
  * @type {boolean}
190
205
  */
191
206
  movable: true,
207
+ /**
208
+ * Show a magnifier when hovering over the image in fullscreen mode.
209
+ * @type {boolean | Object}
210
+ */
211
+ magnifier: false,
192
212
  /**
193
213
  * Enable to rotate the image.
194
214
  * @type {boolean}
195
215
  */
196
216
  rotatable: true,
217
+ /**
218
+ * Enable to rotate the current image by gesture.
219
+ * @type {boolean}
220
+ */
221
+ rotateOnGesture: true,
222
+ /**
223
+ * Enable to rotate the current image by dragging on the touch screen.
224
+ * @type {boolean}
225
+ */
226
+ rotateOnTouch: true,
197
227
  /**
198
228
  * Enable to scale the image.
199
229
  * @type {boolean}
@@ -209,6 +239,11 @@
209
239
  * @type {boolean}
210
240
  */
211
241
  zoomOnTouch: true,
242
+ /**
243
+ * Enable to zoom the current image by gesture.
244
+ * @type {boolean}
245
+ */
246
+ zoomOnGesture: true,
212
247
  /**
213
248
  * Enable to zoom the image by wheeling mouse.
214
249
  * @type {boolean}
@@ -232,7 +267,7 @@
232
267
  tooltip: true,
233
268
  /**
234
269
  * Enable CSS3 Transition for some special elements.
235
- * @type {boolean}
270
+ * @type {boolean | Object}
236
271
  */
237
272
  transition: true,
238
273
  /**
@@ -252,12 +287,12 @@
252
287
  zoomRatio: 0.1,
253
288
  /**
254
289
  * Define the min ratio of the image when zoom out.
255
- * @type {number}
290
+ * @type {number | Function}
256
291
  */
257
292
  minZoomRatio: 0.01,
258
293
  /**
259
294
  * Define the max ratio of the image when zoom in.
260
- * @type {number}
295
+ * @type {number | Function}
261
296
  */
262
297
  maxZoomRatio: 100,
263
298
  /**
@@ -288,7 +323,7 @@
288
323
  stop: null
289
324
  };
290
325
 
291
- 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>';
326
+ 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>';
292
327
 
293
328
  var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
294
329
  var WINDOW = IS_BROWSER ? window : {};
@@ -298,7 +333,9 @@
298
333
 
299
334
  // Actions
300
335
  var ACTION_MOVE = 'move';
336
+ var ACTION_ROTATE = 'rotate';
301
337
  var ACTION_SWITCH = 'switch';
338
+ var ACTION_TRANSFORM = 'transform';
302
339
  var ACTION_ZOOM = 'zoom';
303
340
 
304
341
  // Classes
@@ -332,11 +369,14 @@
332
369
  var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';
333
370
  var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';
334
371
  var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;
372
+ var EVENT_POINTER_ENTER = HAS_POINTER_EVENT ? 'pointerenter' : 'mouseenter';
373
+ var EVENT_POINTER_LEAVE = HAS_POINTER_EVENT ? 'pointerleave' : 'mouseleave';
335
374
  var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;
336
375
  var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;
337
376
  var EVENT_RESIZE = 'resize';
338
377
  var EVENT_TRANSITION_END = 'transitionend';
339
378
  var EVENT_WHEEL = 'wheel';
379
+ var EVENT_GESTURE = 'gesturestart gesturechange gestureend';
340
380
 
341
381
  // Custom events
342
382
  var EVENT_READY = 'ready';
@@ -460,6 +500,32 @@
460
500
  return data;
461
501
  }
462
502
 
503
+ /**
504
+ * Inherit attributes from the original image.
505
+ * @param {Element} image - The target image.
506
+ * @param {Element} originalImage - The original image.
507
+ * @param {Array} inheritedAttributes - The attributes to inherit.
508
+ */
509
+ function inheritAttributes(image, originalImage, inheritedAttributes) {
510
+ forEach(inheritedAttributes, function (name) {
511
+ var value = originalImage.getAttribute(name);
512
+ if (value !== null) {
513
+ image.setAttribute(name, value);
514
+ }
515
+ });
516
+ }
517
+
518
+ /**
519
+ * Check if transition is enabled for the given action.
520
+ * @param {Object} options - The viewer options.
521
+ * @param {string} action - The transition action.
522
+ * @returns {boolean} Returns `true` if transition is enabled.
523
+ */
524
+ function isTransitionEnabled(options, action) {
525
+ var transition = options.transition;
526
+ return transition && transition[action] !== false;
527
+ }
528
+
463
529
  /**
464
530
  * Extend the given object.
465
531
  * @param {*} obj - The object to be extended.
@@ -789,10 +855,8 @@
789
855
  if (isNumber(rotate) && rotate !== 0) {
790
856
  values.push("rotate(".concat(rotate, "deg)"));
791
857
  }
792
- if (isNumber(scaleX) && scaleX !== 1) {
858
+ if (isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1)) {
793
859
  values.push("scaleX(".concat(scaleX, ")"));
794
- }
795
- if (isNumber(scaleY) && scaleY !== 1) {
796
860
  values.push("scaleY(".concat(scaleY, ")"));
797
861
  }
798
862
  var transform = values.length ? values.join(' ') : 'none';
@@ -838,12 +902,7 @@
838
902
  body.removeChild(newImage);
839
903
  }
840
904
  };
841
- forEach(options.inheritedAttributes, function (name) {
842
- var value = image.getAttribute(name);
843
- if (value !== null) {
844
- newImage.setAttribute(name, value);
845
- }
846
- });
905
+ inheritAttributes(newImage, image, options.inheritedAttributes);
847
906
  newImage.src = image.src;
848
907
 
849
908
  // iOS Safari will convert the image automatically
@@ -900,6 +959,34 @@
900
959
  return ratios[0];
901
960
  }
902
961
 
962
+ /**
963
+ * Get the max rotation degree of a group of pointers.
964
+ * @param {Object} pointers - The target pointers.
965
+ * @returns {number} The result degree.
966
+ */
967
+ function getMaxRotateDegree(pointers) {
968
+ var pointers2 = _objectSpread2({}, pointers);
969
+ var degrees = [];
970
+ forEach(pointers, function (pointer, pointerId) {
971
+ delete pointers2[pointerId];
972
+ forEach(pointers2, function (pointer2) {
973
+ var start = Math.atan2(pointer2.startY - pointer.startY, pointer2.startX - pointer.startX);
974
+ var end = Math.atan2(pointer2.endY - pointer.endY, pointer2.endX - pointer.endX);
975
+ var radians = end - start;
976
+ if (radians > Math.PI) {
977
+ radians -= Math.PI * 2;
978
+ } else if (radians < -Math.PI) {
979
+ radians += Math.PI * 2;
980
+ }
981
+ degrees.push(radians * 180 / Math.PI);
982
+ });
983
+ });
984
+ degrees.sort(function (a, b) {
985
+ return Math.abs(b) - Math.abs(a);
986
+ });
987
+ return degrees[0] || 0;
988
+ }
989
+
903
990
  /**
904
991
  * Get a pointer from an event object.
905
992
  * @param {Object} event - The target event object.
@@ -952,7 +1039,7 @@
952
1039
  this.renderViewer();
953
1040
  },
954
1041
  initBody: function initBody() {
955
- var ownerDocument = this.element.ownerDocument;
1042
+ var ownerDocument = this.ownerDocument;
956
1043
  var body = ownerDocument.body || ownerDocument.documentElement;
957
1044
  this.body = body;
958
1045
  this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
@@ -988,26 +1075,34 @@
988
1075
  },
989
1076
  initList: function initList() {
990
1077
  var _this = this;
1078
+ var viewIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.index;
991
1079
  var element = this.element,
992
1080
  options = this.options,
993
1081
  list = this.list;
994
1082
  var items = [];
1083
+ var navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
1084
+ var probe = document.createElement('li');
1085
+ list.appendChild(probe);
1086
+ var itemWidth = probe.offsetWidth + parseInt(window.getComputedStyle(probe).marginLeft, 10);
1087
+ list.removeChild(probe);
1088
+ var visibleItemCount = isNumber(navbarOptions.visibleItemCount) ? Math.floor(navbarOptions.visibleItemCount) : Math.floor(this.containerData.width / itemWidth);
1089
+ visibleItemCount = Math.min(visibleItemCount, this.length);
1090
+ var start = visibleItemCount > 0 ? Math.min(Math.max(0, viewIndex - Math.floor(visibleItemCount / 2)), Math.max(0, this.length - visibleItemCount)) : 0;
1091
+ var end = visibleItemCount > 0 ? Math.min(this.length, start + visibleItemCount) : this.length;
995
1092
 
996
1093
  // initList may be called in this.update, so should keep idempotent
997
1094
  list.innerHTML = '';
998
1095
  forEach(this.images, function (image, index) {
1096
+ if (visibleItemCount > 0 && (index < start || index >= end)) {
1097
+ return;
1098
+ }
999
1099
  var src = image.src;
1000
1100
  var alt = image.alt || getImageNameFromURL(src);
1001
1101
  var url = _this.getImageURL(image);
1002
1102
  if (src || url) {
1003
1103
  var item = document.createElement('li');
1004
1104
  var img = document.createElement('img');
1005
- forEach(options.inheritedAttributes, function (name) {
1006
- var value = image.getAttribute(name);
1007
- if (value !== null) {
1008
- img.setAttribute(name, value);
1009
- }
1010
- });
1105
+ inheritAttributes(img, image, options.inheritedAttributes);
1011
1106
  if (options.navbar) {
1012
1107
  img.src = src || url;
1013
1108
  }
@@ -1025,6 +1120,13 @@
1025
1120
  }
1026
1121
  });
1027
1122
  this.items = items;
1123
+ if (this.viewed) {
1124
+ var activeItem = this.getItem(this.index);
1125
+ if (activeItem) {
1126
+ addClass(activeItem, CLASS_ACTIVE);
1127
+ activeItem.setAttribute('aria-selected', true);
1128
+ }
1129
+ }
1028
1130
  forEach(items, function (item) {
1029
1131
  var image = item.firstElementChild;
1030
1132
  var onLoad;
@@ -1051,7 +1153,7 @@
1051
1153
  once: true
1052
1154
  });
1053
1155
  });
1054
- if (options.transition) {
1156
+ if (isTransitionEnabled(options, 'view')) {
1055
1157
  addListener(element, EVENT_VIEWED, function () {
1056
1158
  addClass(list, CLASS_TRANSITION);
1057
1159
  }, {
@@ -1059,9 +1161,20 @@
1059
1161
  });
1060
1162
  }
1061
1163
  },
1164
+ getItem: function getItem(index) {
1165
+ var item;
1166
+ forEach(this.items, function (candidate) {
1167
+ if (Number(getData(candidate, 'index')) === index) {
1168
+ item = candidate;
1169
+ return false;
1170
+ }
1171
+ return true;
1172
+ });
1173
+ return item;
1174
+ },
1062
1175
  renderList: function renderList() {
1063
1176
  var index = this.index;
1064
- var item = this.items[index];
1177
+ var item = this.getItem(index);
1065
1178
  if (!item) {
1066
1179
  return;
1067
1180
  }
@@ -1072,9 +1185,9 @@
1072
1185
 
1073
1186
  // Place the active item in the center of the screen
1074
1187
  setStyle(this.list, assign({
1075
- width: outerWidth * this.length - gutter
1188
+ width: outerWidth * this.items.length - gutter
1076
1189
  }, getTransforms({
1077
- translateX: (this.viewerData.width - offsetWidth) / 2 - outerWidth * index
1190
+ translateX: (this.viewerData.width - offsetWidth) / 2 - item.offsetLeft
1078
1191
  })));
1079
1192
  },
1080
1193
  resetList: function resetList() {
@@ -1160,8 +1273,23 @@
1160
1273
  marginLeft: imageData.x,
1161
1274
  marginTop: imageData.y
1162
1275
  }, getTransforms(imageData)));
1276
+ if (this.magnifierPoint) {
1277
+ this.renderMagnifier();
1278
+ }
1163
1279
  if (done) {
1164
- if ((this.viewing || this.moving || this.rotating || this.scaling || this.zooming) && this.options.transition && hasClass(image, CLASS_TRANSITION)) {
1280
+ var action = false;
1281
+ if (this.viewing) {
1282
+ action = 'view';
1283
+ } else if (this.moving) {
1284
+ action = 'move';
1285
+ } else if (this.rotating) {
1286
+ action = 'rotate';
1287
+ } else if (this.scaling) {
1288
+ action = 'scale';
1289
+ } else if (this.zooming) {
1290
+ action = 'zoom';
1291
+ }
1292
+ if (action && isTransitionEnabled(this.options, action) && hasClass(image, CLASS_TRANSITION)) {
1165
1293
  var onTransitionEnd = function onTransitionEnd() {
1166
1294
  _this3.imageRendering = false;
1167
1295
  done();
@@ -1197,9 +1325,12 @@
1197
1325
  var options = this.options,
1198
1326
  viewer = this.viewer,
1199
1327
  canvas = this.canvas;
1200
- var document = this.element.ownerDocument;
1328
+ var document = this.ownerDocument;
1201
1329
  addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
1202
1330
  addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
1331
+ addListener(viewer, EVENT_POINTER_ENTER, this.onMagnifyEnter = this.magnify.bind(this));
1332
+ addListener(viewer, EVENT_POINTER_MOVE, this.onMagnify = this.magnify.bind(this));
1333
+ addListener(viewer, EVENT_POINTER_LEAVE, this.onMagnifierLeave = this.hideMagnifier.bind(this));
1203
1334
  addListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
1204
1335
  addListener(document, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
1205
1336
  addListener(document, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
@@ -1211,6 +1342,11 @@
1211
1342
  capture: true
1212
1343
  });
1213
1344
  }
1345
+ if (options.zoomable && options.zoomOnGesture || options.rotatable && options.rotateOnGesture) {
1346
+ addListener(viewer, EVENT_GESTURE, this.onGesture = this.gesture.bind(this), {
1347
+ passive: false
1348
+ });
1349
+ }
1214
1350
  if (options.toggleOnDblclick) {
1215
1351
  addListener(canvas, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
1216
1352
  }
@@ -1219,9 +1355,12 @@
1219
1355
  var options = this.options,
1220
1356
  viewer = this.viewer,
1221
1357
  canvas = this.canvas;
1222
- var document = this.element.ownerDocument;
1358
+ var document = this.ownerDocument;
1223
1359
  removeListener(viewer, EVENT_CLICK, this.onClick);
1224
1360
  removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
1361
+ removeListener(viewer, EVENT_POINTER_ENTER, this.onMagnifyEnter);
1362
+ removeListener(viewer, EVENT_POINTER_MOVE, this.onMagnify);
1363
+ removeListener(viewer, EVENT_POINTER_LEAVE, this.onMagnifierLeave);
1225
1364
  removeListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown);
1226
1365
  removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove);
1227
1366
  removeListener(document, EVENT_POINTER_UP, this.onPointerUp);
@@ -1233,6 +1372,11 @@
1233
1372
  capture: true
1234
1373
  });
1235
1374
  }
1375
+ if (options.zoomable && options.zoomOnGesture || options.rotatable && options.rotateOnGesture) {
1376
+ removeListener(viewer, EVENT_GESTURE, this.onGesture, {
1377
+ passive: false
1378
+ });
1379
+ }
1236
1380
  if (options.toggleOnDblclick) {
1237
1381
  removeListener(canvas, EVENT_DBLCLICK, this.onDblclick);
1238
1382
  }
@@ -1345,10 +1489,13 @@
1345
1489
  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;';
1346
1490
  this.initImage(function () {
1347
1491
  toggleClass(image, CLASS_MOVE, options.movable);
1348
- toggleClass(image, CLASS_TRANSITION, options.transition);
1492
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'view'));
1349
1493
  _this.renderImage(function () {
1350
1494
  _this.viewed = true;
1351
1495
  _this.viewing = false;
1496
+ setTimeout(function () {
1497
+ toggleClass(image, CLASS_TRANSITION, options.transition);
1498
+ }, 300);
1352
1499
  if (isFunction(options.viewed)) {
1353
1500
  addListener(element, EVENT_VIEWED, options.viewed, {
1354
1501
  once: true
@@ -1487,6 +1634,88 @@
1487
1634
  event.preventDefault();
1488
1635
  }
1489
1636
  },
1637
+ magnify: function magnify(event) {
1638
+ if (event.changedTouches || event.pointerType && event.pointerType !== 'mouse') {
1639
+ this.hideMagnifier();
1640
+ return;
1641
+ }
1642
+ this.magnifierPoint = {
1643
+ clientX: event.clientX,
1644
+ clientY: event.clientY
1645
+ };
1646
+ this.renderMagnifier();
1647
+ },
1648
+ renderMagnifier: function renderMagnifier() {
1649
+ var options = this.options,
1650
+ imageData = this.imageData,
1651
+ magnifier = this.magnifier,
1652
+ magnifierImage = this.magnifierImage,
1653
+ viewer = this.viewer;
1654
+ var config = isPlainObject(options.magnifier) ? options.magnifier : {};
1655
+ var point = this.magnifierPoint;
1656
+ if (!options.magnifier || !this.fulled || !this.viewed || !magnifier || !magnifierImage || !point) {
1657
+ this.hideMagnifier();
1658
+ return;
1659
+ }
1660
+ var size = Math.max(1, Number(config.size) || 100);
1661
+ var zoomRatio = Math.max(1, Number(config.zoomRatio) || 2);
1662
+ var opacity = Number(config.opacity);
1663
+ var rect = viewer.getBoundingClientRect();
1664
+ var x = point.clientX - rect.left;
1665
+ var y = point.clientY - rect.top;
1666
+ var imageURL = this.image.currentSrc || this.image.src;
1667
+ var imageRect = this.image.getBoundingClientRect();
1668
+ if (point.clientX < imageRect.left || point.clientX > imageRect.right || point.clientY < imageRect.top || point.clientY > imageRect.bottom) {
1669
+ this.hideMagnifier();
1670
+ return;
1671
+ }
1672
+ var scaleX = isNumber(imageData.scaleX) ? imageData.scaleX : 1;
1673
+ var scaleY = isNumber(imageData.scaleY) ? imageData.scaleY : 1;
1674
+ var rotate = (imageData.rotate || 0) * Math.PI / 180;
1675
+ var cos = Math.cos(rotate);
1676
+ var sin = Math.sin(rotate);
1677
+ var matrixA = cos * scaleX;
1678
+ var matrixB = sin * scaleX;
1679
+ var matrixC = -sin * scaleY;
1680
+ var matrixD = cos * scaleY;
1681
+ var determinant = scaleX * scaleY;
1682
+ if (determinant === 0) {
1683
+ this.hideMagnifier();
1684
+ return;
1685
+ }
1686
+ var centerX = imageData.x + imageData.width / 2;
1687
+ var centerY = imageData.y + imageData.height / 2;
1688
+ var localX = (matrixD * (x - centerX) - matrixC * (y - centerY)) / determinant;
1689
+ var localY = (-matrixB * (x - centerX) + matrixA * (y - centerY)) / determinant;
1690
+ var sourceX = localX + imageData.width / 2;
1691
+ var sourceY = localY + imageData.height / 2;
1692
+ var magnifiedWidth = imageData.width * zoomRatio;
1693
+ var magnifiedHeight = imageData.height * zoomRatio;
1694
+ var transformedX = matrixA * (sourceX * zoomRatio - magnifiedWidth / 2) + matrixC * (sourceY * zoomRatio - magnifiedHeight / 2);
1695
+ var transformedY = matrixB * (sourceX * zoomRatio - magnifiedWidth / 2) + matrixD * (sourceY * zoomRatio - magnifiedHeight / 2);
1696
+ this.magnifierSourcePoint = {
1697
+ x: sourceX,
1698
+ y: sourceY
1699
+ };
1700
+ magnifier.style.width = "".concat(size, "px");
1701
+ magnifier.style.height = "".concat(size, "px");
1702
+ magnifier.style.opacity = "".concat(Math.max(0, Math.min(1, isNumber(opacity) ? opacity : 1)));
1703
+ magnifierImage.src = imageURL;
1704
+ magnifierImage.style.width = "".concat(magnifiedWidth, "px");
1705
+ magnifierImage.style.height = "".concat(magnifiedHeight, "px");
1706
+ magnifierImage.style.left = "".concat(size / 2 - magnifiedWidth / 2 - transformedX, "px");
1707
+ magnifierImage.style.top = "".concat(size / 2 - magnifiedHeight / 2 - transformedY, "px");
1708
+ magnifierImage.style.transform = "rotate(".concat(imageData.rotate || 0, "deg) scaleX(").concat(scaleX, ") scaleY(").concat(scaleY, ")");
1709
+ magnifier.removeAttribute('aria-hidden');
1710
+ addClass(magnifier, 'viewer-show');
1711
+ },
1712
+ hideMagnifier: function hideMagnifier() {
1713
+ if (this.magnifier) {
1714
+ removeClass(this.magnifier, 'viewer-show');
1715
+ this.magnifier.setAttribute('aria-hidden', true);
1716
+ this.magnifierPoint = null;
1717
+ }
1718
+ },
1490
1719
  pointerdown: function pointerdown(event) {
1491
1720
  var options = this.options,
1492
1721
  pointers = this.pointers;
@@ -1515,12 +1744,14 @@
1515
1744
  pointers[event.pointerId || 0] = getPointer(event);
1516
1745
  }
1517
1746
  var action = options.movable ? ACTION_MOVE : false;
1518
- if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) {
1519
- action = ACTION_ZOOM;
1747
+ if ((options.zoomable && options.zoomOnTouch || options.rotatable && options.rotateOnTouch) && Object.keys(pointers).length > 1) {
1748
+ // action = ACTION_ZOOM;
1749
+ // action = ACTION_ROTATE;
1750
+ action = ACTION_TRANSFORM;
1520
1751
  } else if (options.slideOnTouch && (event.pointerType === 'touch' || event.type === 'touchstart') && this.isSwitchable()) {
1521
1752
  action = ACTION_SWITCH;
1522
1753
  }
1523
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1754
+ if (action === ACTION_MOVE || action === ACTION_ZOOM || action === ACTION_ROTATE || action === ACTION_TRANSFORM) {
1524
1755
  removeClass(this.image, CLASS_TRANSITION);
1525
1756
  }
1526
1757
  this.action = action;
@@ -1560,13 +1791,14 @@
1560
1791
  return;
1561
1792
  }
1562
1793
  event.preventDefault();
1563
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1564
- addClass(this.image, CLASS_TRANSITION);
1794
+ if (action === ACTION_MOVE || action === ACTION_ZOOM || action === ACTION_ROTATE || action === ACTION_TRANSFORM) {
1795
+ var transition = action === ACTION_TRANSFORM ? isTransitionEnabled(options, ACTION_ZOOM) || isTransitionEnabled(options, ACTION_ROTATE) : isTransitionEnabled(options, action);
1796
+ toggleClass(this.image, CLASS_TRANSITION, transition);
1565
1797
  }
1566
1798
  this.action = false;
1567
1799
 
1568
1800
  // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
1569
- if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && pointer && Date.now() - pointer.timeStamp < 500) {
1801
+ if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && action !== ACTION_TRANSFORM && pointer && Date.now() - pointer.timeStamp < 500) {
1570
1802
  clearTimeout(this.clickCanvasTimeout);
1571
1803
  clearTimeout(this.doubleClickImageTimeout);
1572
1804
  if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
@@ -1615,6 +1847,10 @@
1615
1847
  this.initContainer();
1616
1848
  this.initViewer();
1617
1849
  this.renderViewer();
1850
+ var navbarOptions = isPlainObject(this.options.navbar) ? this.options.navbar : {};
1851
+ if (isUndefined(navbarOptions.visibleItemCount)) {
1852
+ this.initList(this.index);
1853
+ }
1618
1854
  this.renderList();
1619
1855
  if (this.viewed) {
1620
1856
  this.initImage(function () {
@@ -1640,6 +1876,9 @@
1640
1876
  return;
1641
1877
  }
1642
1878
  event.preventDefault();
1879
+ if (this.gesturing) {
1880
+ return;
1881
+ }
1643
1882
 
1644
1883
  // Limit wheel speed to prevent zoom too fast
1645
1884
  if (this.wheeling) {
@@ -1659,6 +1898,43 @@
1659
1898
  delta = event.detail > 0 ? 1 : -1;
1660
1899
  }
1661
1900
  this.zoom(-delta * ratio, true, null, event);
1901
+ },
1902
+ gesture: function gesture(event) {
1903
+ var options = this.options;
1904
+ if (!this.viewed || !options.zoomOnGesture && !options.rotateOnGesture) {
1905
+ return;
1906
+ }
1907
+ event.preventDefault();
1908
+ switch (event.type) {
1909
+ case 'gesturestart':
1910
+ this.gesturing = true;
1911
+ this.gestureScale = event.scale || 1;
1912
+ this.gestureRotation = event.rotation || 0;
1913
+ break;
1914
+ case 'gesturechange':
1915
+ {
1916
+ var scale = event.scale || 1;
1917
+ var ratio = scale / (this.gestureScale || 1);
1918
+ var rotation = Number(event.rotation);
1919
+ var degree = rotation - (this.gestureRotation || 0);
1920
+ this.gestureScale = scale;
1921
+ if (options.zoomable && options.zoomOnGesture && ratio !== 1) {
1922
+ this.zoom(ratio >= 1 ? ratio - 1 : 1 - 1 / ratio, false, null, event);
1923
+ }
1924
+ if (options.rotatable && options.rotateOnGesture && isNumber(rotation)) {
1925
+ this.gestureRotation = rotation;
1926
+ if (degree !== 0) {
1927
+ this.rotate(degree);
1928
+ }
1929
+ }
1930
+ break;
1931
+ }
1932
+ case 'gestureend':
1933
+ this.gesturing = false;
1934
+ this.gestureScale = 1;
1935
+ this.gestureRotation = 0;
1936
+ break;
1937
+ }
1662
1938
  }
1663
1939
  };
1664
1940
 
@@ -1700,7 +1976,7 @@
1700
1976
  viewer.setAttribute('aria-labelledby', this.title.id);
1701
1977
  viewer.setAttribute('aria-modal', true);
1702
1978
  viewer.removeAttribute('aria-hidden');
1703
- if (options.transition && !immediate) {
1979
+ if (isTransitionEnabled(options, 'show') && !immediate) {
1704
1980
  var shown = this.shown.bind(this);
1705
1981
  this.transitioning = {
1706
1982
  abort: function abort() {
@@ -1740,7 +2016,7 @@
1740
2016
  once: true
1741
2017
  });
1742
2018
  }
1743
- if (dispatchEvent(element, EVENT_HIDE) === false) {
2019
+ if (dispatchEvent(element, EVENT_HIDE) === false || this.destroyed) {
1744
2020
  return this;
1745
2021
  }
1746
2022
  if (this.showing) {
@@ -1758,7 +2034,7 @@
1758
2034
  removeClass(viewer, CLASS_IN);
1759
2035
  _this.hidden();
1760
2036
  };
1761
- if (options.transition && !immediate) {
2037
+ if (isTransitionEnabled(options, 'hide') && !immediate) {
1762
2038
  var _onViewerTransitionEnd = function onViewerTransitionEnd(event) {
1763
2039
  // Ignore all propagating `transitionend` events (#275).
1764
2040
  if (event && event.target === viewer) {
@@ -1808,8 +2084,9 @@
1808
2084
  view: function view() {
1809
2085
  var _this2 = this;
1810
2086
  var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.initialViewIndex;
2087
+ var previousIndex = this.index;
1811
2088
  index = Number(index) || 0;
1812
- if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) {
2089
+ if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === previousIndex) {
1813
2090
  return this;
1814
2091
  }
1815
2092
  if (!this.isShown) {
@@ -1823,17 +2100,16 @@
1823
2100
  options = this.options,
1824
2101
  title = this.title,
1825
2102
  canvas = this.canvas;
1826
- var item = this.items[index];
2103
+ var item = this.getItem(index);
2104
+ if (!item) {
2105
+ this.initList(index);
2106
+ item = this.getItem(index);
2107
+ }
1827
2108
  var img = item.querySelector('img');
1828
2109
  var url = getData(img, 'originalUrl');
1829
2110
  var alt = img.getAttribute('alt');
1830
2111
  var image = document.createElement('img');
1831
- forEach(options.inheritedAttributes, function (name) {
1832
- var value = img.getAttribute(name);
1833
- if (value !== null) {
1834
- image.setAttribute(name, value);
1835
- }
1836
- });
2112
+ inheritAttributes(image, img, options.inheritedAttributes);
1837
2113
  image.src = url;
1838
2114
  image.alt = alt;
1839
2115
  if (isFunction(options.view)) {
@@ -1848,7 +2124,8 @@
1848
2124
  }) === false || !this.isShown || this.hiding || this.played) {
1849
2125
  return this;
1850
2126
  }
1851
- var activeItem = this.items[this.index];
2127
+ this.hideMagnifier();
2128
+ var activeItem = this.getItem(previousIndex);
1852
2129
  if (activeItem) {
1853
2130
  removeClass(activeItem, CLASS_ACTIVE);
1854
2131
  activeItem.removeAttribute('aria-selected');
@@ -1880,6 +2157,25 @@
1880
2157
  var imageData = _this2.imageData;
1881
2158
  var render = Array.isArray(options.title) ? options.title[1] : options.title;
1882
2159
  title.innerHTML = escapeHTMLEntities(isFunction(render) ? render.call(_this2, image, imageData) : "".concat(alt, " (").concat(imageData.naturalWidth, " \xD7 ").concat(imageData.naturalHeight, ")"));
2160
+
2161
+ // Preload the image in the direction of navigation
2162
+ if (options.preload) {
2163
+ var direction = index < previousIndex ? -1 : 1;
2164
+ var nextIndex = index + direction;
2165
+ if (nextIndex < 0 || nextIndex >= _this2.length) {
2166
+ if (options.loop) {
2167
+ nextIndex = direction > 0 ? 0 : _this2.length - 1;
2168
+ } else {
2169
+ nextIndex = -1;
2170
+ }
2171
+ }
2172
+ if (nextIndex !== index) {
2173
+ var nextImage = _this2.images[nextIndex];
2174
+ var preloadedImage = document.createElement('img');
2175
+ inheritAttributes(preloadedImage, nextImage, options.inheritedAttributes);
2176
+ preloadedImage.src = _this2.getImageURL(nextImage) || nextImage.src;
2177
+ }
2178
+ }
1883
2179
  };
1884
2180
  var onLoad;
1885
2181
  var onError;
@@ -2031,6 +2327,7 @@
2031
2327
  imageData.y = y;
2032
2328
  imageData.left = x;
2033
2329
  imageData.top = y;
2330
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'move'));
2034
2331
  this.moving = true;
2035
2332
  this.renderImage(function () {
2036
2333
  _this3.moving = false;
@@ -2087,6 +2384,7 @@
2087
2384
  return this;
2088
2385
  }
2089
2386
  imageData.rotate = degree;
2387
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'rotate'));
2090
2388
  this.rotating = true;
2091
2389
  this.renderImage(function () {
2092
2390
  _this4.rotating = false;
@@ -2167,6 +2465,7 @@
2167
2465
  }
2168
2466
  imageData.scaleX = scaleX;
2169
2467
  imageData.scaleY = scaleY;
2468
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'scale'));
2170
2469
  this.scaling = true;
2171
2470
  this.renderImage(function () {
2172
2471
  _this5.scaling = false;
@@ -2238,8 +2537,8 @@
2238
2537
  ratio = Math.max(0, ratio);
2239
2538
  if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
2240
2539
  if (!_zoomable) {
2241
- var minZoomRatio = Math.max(0.01, options.minZoomRatio);
2242
- var maxZoomRatio = Math.min(100, options.maxZoomRatio);
2540
+ var minZoomRatio = Math.max(0.01, isFunction(options.minZoomRatio) ? options.minZoomRatio.call(this, this.image, imageData) : options.minZoomRatio);
2541
+ var maxZoomRatio = Math.min(100, isFunction(options.maxZoomRatio) ? options.maxZoomRatio.call(this, this.image, imageData) : options.maxZoomRatio);
2243
2542
  ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
2244
2543
  }
2245
2544
  if (_originalEvent) {
@@ -2300,6 +2599,7 @@
2300
2599
  imageData.height = newHeight;
2301
2600
  imageData.oldRatio = oldRatio;
2302
2601
  imageData.ratio = ratio;
2602
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'zoom'));
2303
2603
  this.renderImage(function () {
2304
2604
  _this6.zooming = false;
2305
2605
  if (isFunction(options.zoomed)) {
@@ -2353,16 +2653,15 @@
2353
2653
  this.requestFullscreen(fullscreen);
2354
2654
  }
2355
2655
  addClass(player, CLASS_SHOW);
2356
- forEach(this.items, function (item, i) {
2357
- var img = item.querySelector('img');
2656
+ forEach(this.images, function (originalImage, i) {
2358
2657
  var image = document.createElement('img');
2359
- image.src = getData(img, 'originalUrl');
2360
- image.alt = img.getAttribute('alt');
2361
- image.referrerPolicy = img.referrerPolicy;
2658
+ image.src = _this7.getImageURL(originalImage) || originalImage.src;
2659
+ image.alt = originalImage.alt || '';
2660
+ image.referrerPolicy = originalImage.referrerPolicy;
2362
2661
  total += 1;
2363
2662
  addClass(image, CLASS_FADE);
2364
- toggleClass(image, CLASS_TRANSITION, options.transition);
2365
- if (hasClass(item, CLASS_ACTIVE)) {
2663
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'play'));
2664
+ if (i === _this7.index) {
2366
2665
  addClass(image, CLASS_IN);
2367
2666
  index = i;
2368
2667
  }
@@ -2379,7 +2678,7 @@
2379
2678
  index -= 1;
2380
2679
  index = index >= 0 ? index : total - 1;
2381
2680
  addClass(list[index], CLASS_IN);
2382
- _this7.playing.timeout = setTimeout(_prev, options.interval);
2681
+ _this7.playing.timeout = options.autoplay ? setTimeout(_prev, options.interval) : null;
2383
2682
  };
2384
2683
  var _next = function next() {
2385
2684
  clearTimeout(_this7.playing.timeout);
@@ -2387,13 +2686,13 @@
2387
2686
  index += 1;
2388
2687
  index = index < total ? index : 0;
2389
2688
  addClass(list[index], CLASS_IN);
2390
- _this7.playing.timeout = setTimeout(_next, options.interval);
2689
+ _this7.playing.timeout = options.autoplay ? setTimeout(_next, options.interval) : null;
2391
2690
  };
2392
2691
  if (total > 1) {
2393
2692
  this.playing = {
2394
2693
  prev: _prev,
2395
2694
  next: _next,
2396
- timeout: setTimeout(_next, options.interval)
2695
+ timeout: options.autoplay ? setTimeout(_next, options.interval) : null
2397
2696
  };
2398
2697
  }
2399
2698
  }
@@ -2440,11 +2739,9 @@
2440
2739
  this.fulled = true;
2441
2740
  this.open();
2442
2741
  addClass(this.button, CLASS_FULLSCREEN_EXIT);
2443
- if (options.transition) {
2444
- removeClass(list, CLASS_TRANSITION);
2445
- if (this.viewed) {
2446
- removeClass(image, CLASS_TRANSITION);
2447
- }
2742
+ removeClass(list, CLASS_TRANSITION);
2743
+ if (this.viewed) {
2744
+ removeClass(image, CLASS_TRANSITION);
2448
2745
  }
2449
2746
  addClass(viewer, CLASS_FIXED);
2450
2747
  viewer.setAttribute('role', 'dialog');
@@ -2462,14 +2759,7 @@
2462
2759
  this.renderList();
2463
2760
  if (this.viewed) {
2464
2761
  this.initImage(function () {
2465
- _this9.renderImage(function () {
2466
- if (options.transition) {
2467
- setTimeout(function () {
2468
- addClass(image, CLASS_TRANSITION);
2469
- addClass(list, CLASS_TRANSITION);
2470
- }, 0);
2471
- }
2472
- });
2762
+ _this9.renderImage();
2473
2763
  });
2474
2764
  }
2475
2765
  return this;
@@ -2487,11 +2777,9 @@
2487
2777
  this.fulled = false;
2488
2778
  this.close();
2489
2779
  removeClass(this.button, CLASS_FULLSCREEN_EXIT);
2490
- if (options.transition) {
2491
- removeClass(list, CLASS_TRANSITION);
2492
- if (this.viewed) {
2493
- removeClass(image, CLASS_TRANSITION);
2494
- }
2780
+ removeClass(list, CLASS_TRANSITION);
2781
+ if (this.viewed) {
2782
+ removeClass(image, CLASS_TRANSITION);
2495
2783
  }
2496
2784
  if (options.focus) {
2497
2785
  this.clearEnforceFocus();
@@ -2508,14 +2796,7 @@
2508
2796
  this.renderList();
2509
2797
  if (this.viewed) {
2510
2798
  this.initImage(function () {
2511
- _this0.renderImage(function () {
2512
- if (options.transition) {
2513
- setTimeout(function () {
2514
- addClass(image, CLASS_TRANSITION);
2515
- addClass(list, CLASS_TRANSITION);
2516
- }, 0);
2517
- }
2518
- });
2799
+ _this0.renderImage();
2519
2800
  });
2520
2801
  }
2521
2802
  return this;
@@ -2531,7 +2812,7 @@
2531
2812
  }
2532
2813
  tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
2533
2814
  if (!this.tooltipping) {
2534
- if (options.transition) {
2815
+ if (isTransitionEnabled(options, 'tooltip')) {
2535
2816
  if (this.fading) {
2536
2817
  dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
2537
2818
  }
@@ -2551,7 +2832,7 @@
2551
2832
  clearTimeout(this.tooltipping);
2552
2833
  }
2553
2834
  this.tooltipping = setTimeout(function () {
2554
- if (options.transition) {
2835
+ if (isTransitionEnabled(options, 'tooltip')) {
2555
2836
  addListener(tooltipBox, EVENT_TRANSITION_END, function () {
2556
2837
  removeClass(tooltipBox, CLASS_SHOW);
2557
2838
  removeClass(tooltipBox, CLASS_FADE);
@@ -2593,12 +2874,19 @@
2593
2874
  }
2594
2875
  return this;
2595
2876
  },
2596
- // Update viewer when images changed
2597
- update: function update() {
2877
+ /**
2878
+ * Update the viewer when images or options changed.
2879
+ * @param {Object} [updateOptions] - The options to update.
2880
+ * @returns {Viewer} this
2881
+ */
2882
+ update: function update(updateOptions) {
2598
2883
  var _this10 = this;
2599
2884
  var element = this.element,
2600
2885
  options = this.options,
2601
2886
  isImg = this.isImg;
2887
+ if (isPlainObject(updateOptions)) {
2888
+ assign(options, updateOptions);
2889
+ }
2602
2890
 
2603
2891
  // Destroy viewer if the target image was deleted
2604
2892
  if (isImg && !element.parentNode) {
@@ -2621,18 +2909,19 @@
2621
2909
  this.length = images.length;
2622
2910
  if (this.ready) {
2623
2911
  var changedIndexes = [];
2624
- forEach(this.items, function (item, i) {
2912
+ forEach(this.items, function (item) {
2625
2913
  var img = item.querySelector('img');
2626
- var image = images[i];
2914
+ var index = Number(getData(item, 'index'));
2915
+ var image = images[index];
2627
2916
  if (image && img) {
2628
2917
  if (image.src !== img.src
2629
2918
 
2630
2919
  // Title changed (#408)
2631
2920
  || image.alt !== img.alt) {
2632
- changedIndexes.push(i);
2921
+ changedIndexes.push(index);
2633
2922
  }
2634
2923
  } else {
2635
- changedIndexes.push(i);
2924
+ changedIndexes.push(index);
2636
2925
  }
2637
2926
  });
2638
2927
  setStyle(this.list, {
@@ -2647,7 +2936,7 @@
2647
2936
  this.viewed = false;
2648
2937
  this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
2649
2938
  } else {
2650
- var activeItem = this.items[this.index];
2939
+ var activeItem = this.getItem(this.index);
2651
2940
 
2652
2941
  // Reactivate the current viewing item after reset the list.
2653
2942
  addClass(activeItem, CLASS_ACTIVE);
@@ -2833,7 +3122,7 @@
2833
3122
  }
2834
3123
  },
2835
3124
  requestFullscreen: function requestFullscreen(options) {
2836
- var document = this.element.ownerDocument;
3125
+ var document = this.ownerDocument;
2837
3126
  if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2838
3127
  var documentElement = document.documentElement;
2839
3128
 
@@ -2855,7 +3144,7 @@
2855
3144
  }
2856
3145
  },
2857
3146
  exitFullscreen: function exitFullscreen() {
2858
- var document = this.element.ownerDocument;
3147
+ var document = this.ownerDocument;
2859
3148
  if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2860
3149
  // Document.exitFullscreen()
2861
3150
  if (document.exitFullscreen) {
@@ -2891,7 +3180,38 @@
2891
3180
 
2892
3181
  // Zoom the current image
2893
3182
  case ACTION_ZOOM:
2894
- this.zoom(getMaxZoomRatio(pointers), false, null, event);
3183
+ if (options.zoomable && options.zoomOnTouch) {
3184
+ var zoomRatio = getMaxZoomRatio(pointers);
3185
+ if (zoomRatio !== 0) {
3186
+ this.zoom(zoomRatio, false, null, event);
3187
+ }
3188
+ }
3189
+ break;
3190
+
3191
+ // Rotate the current image
3192
+ case ACTION_ROTATE:
3193
+ if (options.rotatable && options.rotateOnTouch) {
3194
+ var rotateDegree = getMaxRotateDegree(pointers);
3195
+ if (rotateDegree !== 0) {
3196
+ this.rotate(rotateDegree, event);
3197
+ }
3198
+ }
3199
+ break;
3200
+
3201
+ // Transform the current image
3202
+ case ACTION_TRANSFORM:
3203
+ if (options.zoomable && options.zoomOnTouch) {
3204
+ var _zoomRatio = getMaxZoomRatio(pointers);
3205
+ if (_zoomRatio !== 0) {
3206
+ this.zoom(_zoomRatio, false, null, event);
3207
+ }
3208
+ }
3209
+ if (options.rotatable && options.rotateOnTouch) {
3210
+ var _rotateDegree = getMaxRotateDegree(pointers);
3211
+ if (_rotateDegree !== 0) {
3212
+ this.rotate(_rotateDegree, event);
3213
+ }
3214
+ }
2895
3215
  break;
2896
3216
  case ACTION_SWITCH:
2897
3217
  {
@@ -2933,16 +3253,17 @@
2933
3253
  var Viewer = /*#__PURE__*/function () {
2934
3254
  /**
2935
3255
  * Create a new Viewer.
2936
- * @param {Element} element - The target element for viewing.
3256
+ * @param {Element} element - The target image, or a container of images, to view.
2937
3257
  * @param {Object} [options={}] - The configuration options.
2938
3258
  */
2939
3259
  function Viewer(element) {
2940
3260
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2941
3261
  _classCallCheck(this, Viewer);
2942
- if (!element || element.nodeType !== 1) {
3262
+ if (!element || element.nodeType !== 1 && element.nodeType !== 11) {
2943
3263
  throw new Error('The first argument is required and must be an element.');
2944
3264
  }
2945
3265
  this.element = element;
3266
+ this.ownerDocument = element.ownerDocument || element.host.ownerDocument;
2946
3267
  this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
2947
3268
  this.action = false;
2948
3269
  this.fading = false;
@@ -3004,7 +3325,7 @@
3004
3325
  this.initBody();
3005
3326
 
3006
3327
  // Override `transition` option if it is not supported
3007
- if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
3328
+ if (isUndefined(this.ownerDocument.createElement(NAMESPACE).style.transition)) {
3008
3329
  options.transition = false;
3009
3330
  }
3010
3331
  if (options.inline) {
@@ -3091,6 +3412,7 @@
3091
3412
  var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
3092
3413
  var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
3093
3414
  var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
3415
+ var navigation = viewer.querySelector(".".concat(NAMESPACE, "-navigation"));
3094
3416
  var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
3095
3417
  var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
3096
3418
  this.parent = parent;
@@ -3098,19 +3420,52 @@
3098
3420
  this.title = title;
3099
3421
  this.toolbar = toolbar;
3100
3422
  this.navbar = navbar;
3423
+ this.navigation = navigation;
3101
3424
  this.button = button;
3102
3425
  this.canvas = canvas;
3103
3426
  this.footer = viewer.querySelector(".".concat(NAMESPACE, "-footer"));
3427
+ this.magnifier = viewer.querySelector(".".concat(NAMESPACE, "-magnifier"));
3428
+ this.magnifierImage = viewer.querySelector(".".concat(NAMESPACE, "-magnifier-image"));
3104
3429
  this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
3105
3430
  this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
3106
3431
  this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
3107
3432
  viewer.id = "".concat(NAMESPACE).concat(this.id);
3108
3433
  title.id = "".concat(NAMESPACE, "Title").concat(this.id);
3109
3434
  addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
3110
- addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
3435
+ var navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
3436
+ var navbarShow = options.navbar;
3437
+ var navbarSize = !isUndefined(navbarOptions.size) ? navbarOptions.size : options.navbar;
3438
+ if (isPlainObject(options.navbar)) {
3439
+ navbarShow = !isUndefined(navbarOptions.show) ? navbarOptions.show : true;
3440
+ }
3441
+ addClass(navbar, !navbarShow ? CLASS_HIDE : getResponsiveClass(navbarShow));
3442
+ if (['small', 'medium', 'large'].indexOf(navbarSize) !== -1) {
3443
+ addClass(navbar, "".concat(NAMESPACE, "-").concat(navbarSize));
3444
+ }
3445
+ if (isPlainObject(options.navigation)) {
3446
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3447
+ var name = getData(item, DATA_ACTION);
3448
+ var value = options.navigation[name];
3449
+ var deep = isPlainObject(value);
3450
+ var show = deep && !isUndefined(value.show) ? value.show : value;
3451
+ var size = deep && !isUndefined(value.size) ? value.size : value;
3452
+ toggleClass(item, CLASS_HIDE, !show);
3453
+ if (isNumber(show)) {
3454
+ addClass(item, getResponsiveClass(show));
3455
+ }
3456
+ if (['small', 'large'].indexOf(size) !== -1) {
3457
+ addClass(item, "".concat(NAMESPACE, "-").concat(size));
3458
+ }
3459
+ });
3460
+ } else {
3461
+ addClass(navigation, !options.navigation ? CLASS_HIDE : getResponsiveClass(options.navigation));
3462
+ }
3111
3463
  toggleClass(button, CLASS_HIDE, !options.button);
3112
3464
  if (options.keyboard) {
3113
3465
  button.setAttribute('tabindex', 0);
3466
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3467
+ item.setAttribute('tabindex', 0);
3468
+ });
3114
3469
  }
3115
3470
  if (options.backdrop) {
3116
3471
  addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
@@ -3196,7 +3551,7 @@
3196
3551
  });
3197
3552
  var container = options.container;
3198
3553
  if (isString(container)) {
3199
- container = element.ownerDocument.querySelector(container);
3554
+ container = this.ownerDocument.querySelector(container);
3200
3555
  }
3201
3556
  if (!container) {
3202
3557
  container = this.body;
@@ -3224,14 +3579,15 @@
3224
3579
  }
3225
3580
 
3226
3581
  /**
3227
- * Get the no conflict viewer class.
3228
- * @returns {Viewer} The viewer class.
3582
+ * Create a new Viewer instance.
3583
+ * @param {Element} element - The target image, or a container of images, to view.
3584
+ * @param {Object} [options={}] - The configuration options.
3585
+ * @returns {Viewer} A new Viewer instance.
3229
3586
  */
3230
3587
  }], [{
3231
- key: "noConflict",
3232
- value: function noConflict() {
3233
- window.Viewer = AnotherViewer;
3234
- return Viewer;
3588
+ key: "create",
3589
+ value: function create(element, options) {
3590
+ return new Viewer(element, options);
3235
3591
  }
3236
3592
 
3237
3593
  /**
@@ -3243,6 +3599,17 @@
3243
3599
  value: function setDefaults(options) {
3244
3600
  assign(DEFAULTS, isPlainObject(options) && options);
3245
3601
  }
3602
+
3603
+ /**
3604
+ * Get the no conflict viewer class.
3605
+ * @returns {Viewer} The viewer class.
3606
+ */
3607
+ }, {
3608
+ key: "noConflict",
3609
+ value: function noConflict() {
3610
+ window.Viewer = AnotherViewer;
3611
+ return Viewer;
3612
+ }
3246
3613
  }]);
3247
3614
  }();
3248
3615
  assign(Viewer.prototype, render, events, handlers, methods, others);