viewerjs 1.11.9 → 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.11.9
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-21T13:29:40.787Z
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.
@@ -517,7 +583,7 @@
517
583
  if (!element || !value) {
518
584
  return false;
519
585
  }
520
- return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
586
+ return element.classList ? element.classList.contains(value) : element.className.split(REGEXP_SPACES).indexOf(value) > -1;
521
587
  }
522
588
 
523
589
  /**
@@ -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,17 +1791,20 @@
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) {
1573
- if (this.imageClicked) {
1805
+ if (this.pointerMoved) {
1806
+ this.imageClicked = false;
1807
+ } else if (this.imageClicked) {
1574
1808
  this.imageClicked = false;
1575
1809
 
1576
1810
  // This timeout will be cleared later when a native dblclick event is triggering
@@ -1613,6 +1847,10 @@
1613
1847
  this.initContainer();
1614
1848
  this.initViewer();
1615
1849
  this.renderViewer();
1850
+ var navbarOptions = isPlainObject(this.options.navbar) ? this.options.navbar : {};
1851
+ if (isUndefined(navbarOptions.visibleItemCount)) {
1852
+ this.initList(this.index);
1853
+ }
1616
1854
  this.renderList();
1617
1855
  if (this.viewed) {
1618
1856
  this.initImage(function () {
@@ -1638,6 +1876,9 @@
1638
1876
  return;
1639
1877
  }
1640
1878
  event.preventDefault();
1879
+ if (this.gesturing) {
1880
+ return;
1881
+ }
1641
1882
 
1642
1883
  // Limit wheel speed to prevent zoom too fast
1643
1884
  if (this.wheeling) {
@@ -1657,6 +1898,43 @@
1657
1898
  delta = event.detail > 0 ? 1 : -1;
1658
1899
  }
1659
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
+ }
1660
1938
  }
1661
1939
  };
1662
1940
 
@@ -1698,7 +1976,7 @@
1698
1976
  viewer.setAttribute('aria-labelledby', this.title.id);
1699
1977
  viewer.setAttribute('aria-modal', true);
1700
1978
  viewer.removeAttribute('aria-hidden');
1701
- if (options.transition && !immediate) {
1979
+ if (isTransitionEnabled(options, 'show') && !immediate) {
1702
1980
  var shown = this.shown.bind(this);
1703
1981
  this.transitioning = {
1704
1982
  abort: function abort() {
@@ -1738,7 +2016,7 @@
1738
2016
  once: true
1739
2017
  });
1740
2018
  }
1741
- if (dispatchEvent(element, EVENT_HIDE) === false) {
2019
+ if (dispatchEvent(element, EVENT_HIDE) === false || this.destroyed) {
1742
2020
  return this;
1743
2021
  }
1744
2022
  if (this.showing) {
@@ -1756,7 +2034,7 @@
1756
2034
  removeClass(viewer, CLASS_IN);
1757
2035
  _this.hidden();
1758
2036
  };
1759
- if (options.transition && !immediate) {
2037
+ if (isTransitionEnabled(options, 'hide') && !immediate) {
1760
2038
  var _onViewerTransitionEnd = function onViewerTransitionEnd(event) {
1761
2039
  // Ignore all propagating `transitionend` events (#275).
1762
2040
  if (event && event.target === viewer) {
@@ -1806,8 +2084,9 @@
1806
2084
  view: function view() {
1807
2085
  var _this2 = this;
1808
2086
  var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.initialViewIndex;
2087
+ var previousIndex = this.index;
1809
2088
  index = Number(index) || 0;
1810
- 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) {
1811
2090
  return this;
1812
2091
  }
1813
2092
  if (!this.isShown) {
@@ -1821,17 +2100,16 @@
1821
2100
  options = this.options,
1822
2101
  title = this.title,
1823
2102
  canvas = this.canvas;
1824
- var item = this.items[index];
2103
+ var item = this.getItem(index);
2104
+ if (!item) {
2105
+ this.initList(index);
2106
+ item = this.getItem(index);
2107
+ }
1825
2108
  var img = item.querySelector('img');
1826
2109
  var url = getData(img, 'originalUrl');
1827
2110
  var alt = img.getAttribute('alt');
1828
2111
  var image = document.createElement('img');
1829
- forEach(options.inheritedAttributes, function (name) {
1830
- var value = img.getAttribute(name);
1831
- if (value !== null) {
1832
- image.setAttribute(name, value);
1833
- }
1834
- });
2112
+ inheritAttributes(image, img, options.inheritedAttributes);
1835
2113
  image.src = url;
1836
2114
  image.alt = alt;
1837
2115
  if (isFunction(options.view)) {
@@ -1846,7 +2124,8 @@
1846
2124
  }) === false || !this.isShown || this.hiding || this.played) {
1847
2125
  return this;
1848
2126
  }
1849
- var activeItem = this.items[this.index];
2127
+ this.hideMagnifier();
2128
+ var activeItem = this.getItem(previousIndex);
1850
2129
  if (activeItem) {
1851
2130
  removeClass(activeItem, CLASS_ACTIVE);
1852
2131
  activeItem.removeAttribute('aria-selected');
@@ -1878,6 +2157,25 @@
1878
2157
  var imageData = _this2.imageData;
1879
2158
  var render = Array.isArray(options.title) ? options.title[1] : options.title;
1880
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
+ }
1881
2179
  };
1882
2180
  var onLoad;
1883
2181
  var onError;
@@ -2029,6 +2327,7 @@
2029
2327
  imageData.y = y;
2030
2328
  imageData.left = x;
2031
2329
  imageData.top = y;
2330
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'move'));
2032
2331
  this.moving = true;
2033
2332
  this.renderImage(function () {
2034
2333
  _this3.moving = false;
@@ -2085,6 +2384,7 @@
2085
2384
  return this;
2086
2385
  }
2087
2386
  imageData.rotate = degree;
2387
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'rotate'));
2088
2388
  this.rotating = true;
2089
2389
  this.renderImage(function () {
2090
2390
  _this4.rotating = false;
@@ -2165,6 +2465,7 @@
2165
2465
  }
2166
2466
  imageData.scaleX = scaleX;
2167
2467
  imageData.scaleY = scaleY;
2468
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'scale'));
2168
2469
  this.scaling = true;
2169
2470
  this.renderImage(function () {
2170
2471
  _this5.scaling = false;
@@ -2236,8 +2537,8 @@
2236
2537
  ratio = Math.max(0, ratio);
2237
2538
  if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
2238
2539
  if (!_zoomable) {
2239
- var minZoomRatio = Math.max(0.01, options.minZoomRatio);
2240
- 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);
2241
2542
  ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
2242
2543
  }
2243
2544
  if (_originalEvent) {
@@ -2298,6 +2599,7 @@
2298
2599
  imageData.height = newHeight;
2299
2600
  imageData.oldRatio = oldRatio;
2300
2601
  imageData.ratio = ratio;
2602
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'zoom'));
2301
2603
  this.renderImage(function () {
2302
2604
  _this6.zooming = false;
2303
2605
  if (isFunction(options.zoomed)) {
@@ -2351,16 +2653,15 @@
2351
2653
  this.requestFullscreen(fullscreen);
2352
2654
  }
2353
2655
  addClass(player, CLASS_SHOW);
2354
- forEach(this.items, function (item, i) {
2355
- var img = item.querySelector('img');
2656
+ forEach(this.images, function (originalImage, i) {
2356
2657
  var image = document.createElement('img');
2357
- image.src = getData(img, 'originalUrl');
2358
- image.alt = img.getAttribute('alt');
2359
- image.referrerPolicy = img.referrerPolicy;
2658
+ image.src = _this7.getImageURL(originalImage) || originalImage.src;
2659
+ image.alt = originalImage.alt || '';
2660
+ image.referrerPolicy = originalImage.referrerPolicy;
2360
2661
  total += 1;
2361
2662
  addClass(image, CLASS_FADE);
2362
- toggleClass(image, CLASS_TRANSITION, options.transition);
2363
- if (hasClass(item, CLASS_ACTIVE)) {
2663
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'play'));
2664
+ if (i === _this7.index) {
2364
2665
  addClass(image, CLASS_IN);
2365
2666
  index = i;
2366
2667
  }
@@ -2377,7 +2678,7 @@
2377
2678
  index -= 1;
2378
2679
  index = index >= 0 ? index : total - 1;
2379
2680
  addClass(list[index], CLASS_IN);
2380
- _this7.playing.timeout = setTimeout(_prev, options.interval);
2681
+ _this7.playing.timeout = options.autoplay ? setTimeout(_prev, options.interval) : null;
2381
2682
  };
2382
2683
  var _next = function next() {
2383
2684
  clearTimeout(_this7.playing.timeout);
@@ -2385,13 +2686,13 @@
2385
2686
  index += 1;
2386
2687
  index = index < total ? index : 0;
2387
2688
  addClass(list[index], CLASS_IN);
2388
- _this7.playing.timeout = setTimeout(_next, options.interval);
2689
+ _this7.playing.timeout = options.autoplay ? setTimeout(_next, options.interval) : null;
2389
2690
  };
2390
2691
  if (total > 1) {
2391
2692
  this.playing = {
2392
2693
  prev: _prev,
2393
2694
  next: _next,
2394
- timeout: setTimeout(_next, options.interval)
2695
+ timeout: options.autoplay ? setTimeout(_next, options.interval) : null
2395
2696
  };
2396
2697
  }
2397
2698
  }
@@ -2438,11 +2739,9 @@
2438
2739
  this.fulled = true;
2439
2740
  this.open();
2440
2741
  addClass(this.button, CLASS_FULLSCREEN_EXIT);
2441
- if (options.transition) {
2442
- removeClass(list, CLASS_TRANSITION);
2443
- if (this.viewed) {
2444
- removeClass(image, CLASS_TRANSITION);
2445
- }
2742
+ removeClass(list, CLASS_TRANSITION);
2743
+ if (this.viewed) {
2744
+ removeClass(image, CLASS_TRANSITION);
2446
2745
  }
2447
2746
  addClass(viewer, CLASS_FIXED);
2448
2747
  viewer.setAttribute('role', 'dialog');
@@ -2460,14 +2759,7 @@
2460
2759
  this.renderList();
2461
2760
  if (this.viewed) {
2462
2761
  this.initImage(function () {
2463
- _this9.renderImage(function () {
2464
- if (options.transition) {
2465
- setTimeout(function () {
2466
- addClass(image, CLASS_TRANSITION);
2467
- addClass(list, CLASS_TRANSITION);
2468
- }, 0);
2469
- }
2470
- });
2762
+ _this9.renderImage();
2471
2763
  });
2472
2764
  }
2473
2765
  return this;
@@ -2485,11 +2777,9 @@
2485
2777
  this.fulled = false;
2486
2778
  this.close();
2487
2779
  removeClass(this.button, CLASS_FULLSCREEN_EXIT);
2488
- if (options.transition) {
2489
- removeClass(list, CLASS_TRANSITION);
2490
- if (this.viewed) {
2491
- removeClass(image, CLASS_TRANSITION);
2492
- }
2780
+ removeClass(list, CLASS_TRANSITION);
2781
+ if (this.viewed) {
2782
+ removeClass(image, CLASS_TRANSITION);
2493
2783
  }
2494
2784
  if (options.focus) {
2495
2785
  this.clearEnforceFocus();
@@ -2506,14 +2796,7 @@
2506
2796
  this.renderList();
2507
2797
  if (this.viewed) {
2508
2798
  this.initImage(function () {
2509
- _this0.renderImage(function () {
2510
- if (options.transition) {
2511
- setTimeout(function () {
2512
- addClass(image, CLASS_TRANSITION);
2513
- addClass(list, CLASS_TRANSITION);
2514
- }, 0);
2515
- }
2516
- });
2799
+ _this0.renderImage();
2517
2800
  });
2518
2801
  }
2519
2802
  return this;
@@ -2529,7 +2812,7 @@
2529
2812
  }
2530
2813
  tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
2531
2814
  if (!this.tooltipping) {
2532
- if (options.transition) {
2815
+ if (isTransitionEnabled(options, 'tooltip')) {
2533
2816
  if (this.fading) {
2534
2817
  dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
2535
2818
  }
@@ -2549,7 +2832,7 @@
2549
2832
  clearTimeout(this.tooltipping);
2550
2833
  }
2551
2834
  this.tooltipping = setTimeout(function () {
2552
- if (options.transition) {
2835
+ if (isTransitionEnabled(options, 'tooltip')) {
2553
2836
  addListener(tooltipBox, EVENT_TRANSITION_END, function () {
2554
2837
  removeClass(tooltipBox, CLASS_SHOW);
2555
2838
  removeClass(tooltipBox, CLASS_FADE);
@@ -2591,12 +2874,19 @@
2591
2874
  }
2592
2875
  return this;
2593
2876
  },
2594
- // Update viewer when images changed
2595
- 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) {
2596
2883
  var _this10 = this;
2597
2884
  var element = this.element,
2598
2885
  options = this.options,
2599
2886
  isImg = this.isImg;
2887
+ if (isPlainObject(updateOptions)) {
2888
+ assign(options, updateOptions);
2889
+ }
2600
2890
 
2601
2891
  // Destroy viewer if the target image was deleted
2602
2892
  if (isImg && !element.parentNode) {
@@ -2619,18 +2909,19 @@
2619
2909
  this.length = images.length;
2620
2910
  if (this.ready) {
2621
2911
  var changedIndexes = [];
2622
- forEach(this.items, function (item, i) {
2912
+ forEach(this.items, function (item) {
2623
2913
  var img = item.querySelector('img');
2624
- var image = images[i];
2914
+ var index = Number(getData(item, 'index'));
2915
+ var image = images[index];
2625
2916
  if (image && img) {
2626
2917
  if (image.src !== img.src
2627
2918
 
2628
2919
  // Title changed (#408)
2629
2920
  || image.alt !== img.alt) {
2630
- changedIndexes.push(i);
2921
+ changedIndexes.push(index);
2631
2922
  }
2632
2923
  } else {
2633
- changedIndexes.push(i);
2924
+ changedIndexes.push(index);
2634
2925
  }
2635
2926
  });
2636
2927
  setStyle(this.list, {
@@ -2645,7 +2936,7 @@
2645
2936
  this.viewed = false;
2646
2937
  this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
2647
2938
  } else {
2648
- var activeItem = this.items[this.index];
2939
+ var activeItem = this.getItem(this.index);
2649
2940
 
2650
2941
  // Reactivate the current viewing item after reset the list.
2651
2942
  addClass(activeItem, CLASS_ACTIVE);
@@ -2709,7 +3000,8 @@
2709
3000
  }
2710
3001
  }
2711
3002
  if (!options.inline) {
2712
- removeListener(element, EVENT_CLICK, this.onStart);
3003
+ removeListener(element, EVENT_CLICK, this.onElementClick);
3004
+ removeListener(element, EVENT_KEY_DOWN, this.onElementKeyDown);
2713
3005
  }
2714
3006
  element[NAMESPACE] = undefined;
2715
3007
  return this;
@@ -2796,6 +3088,12 @@
2796
3088
  var element = this.element,
2797
3089
  options = this.options,
2798
3090
  viewer = this.viewer;
3091
+ var activeElement = viewer.ownerDocument.activeElement;
3092
+
3093
+ // Avoid keeping focus inside the dialog before setting `aria-hidden`.
3094
+ if (activeElement && viewer.contains(activeElement) && isFunction(activeElement.blur)) {
3095
+ activeElement.blur();
3096
+ }
2799
3097
  if (options.focus) {
2800
3098
  this.clearEnforceFocus();
2801
3099
  }
@@ -2824,7 +3122,7 @@
2824
3122
  }
2825
3123
  },
2826
3124
  requestFullscreen: function requestFullscreen(options) {
2827
- var document = this.element.ownerDocument;
3125
+ var document = this.ownerDocument;
2828
3126
  if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2829
3127
  var documentElement = document.documentElement;
2830
3128
 
@@ -2846,7 +3144,7 @@
2846
3144
  }
2847
3145
  },
2848
3146
  exitFullscreen: function exitFullscreen() {
2849
- var document = this.element.ownerDocument;
3147
+ var document = this.ownerDocument;
2850
3148
  if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2851
3149
  // Document.exitFullscreen()
2852
3150
  if (document.exitFullscreen) {
@@ -2882,7 +3180,38 @@
2882
3180
 
2883
3181
  // Zoom the current image
2884
3182
  case ACTION_ZOOM:
2885
- 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
+ }
2886
3215
  break;
2887
3216
  case ACTION_SWITCH:
2888
3217
  {
@@ -2924,16 +3253,17 @@
2924
3253
  var Viewer = /*#__PURE__*/function () {
2925
3254
  /**
2926
3255
  * Create a new Viewer.
2927
- * @param {Element} element - The target element for viewing.
3256
+ * @param {Element} element - The target image, or a container of images, to view.
2928
3257
  * @param {Object} [options={}] - The configuration options.
2929
3258
  */
2930
3259
  function Viewer(element) {
2931
3260
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2932
3261
  _classCallCheck(this, Viewer);
2933
- if (!element || element.nodeType !== 1) {
3262
+ if (!element || element.nodeType !== 1 && element.nodeType !== 11) {
2934
3263
  throw new Error('The first argument is required and must be an element.');
2935
3264
  }
2936
3265
  this.element = element;
3266
+ this.ownerDocument = element.ownerDocument || element.host.ownerDocument;
2937
3267
  this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
2938
3268
  this.action = false;
2939
3269
  this.fading = false;
@@ -2995,7 +3325,7 @@
2995
3325
  this.initBody();
2996
3326
 
2997
3327
  // Override `transition` option if it is not supported
2998
- if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
3328
+ if (isUndefined(this.ownerDocument.createElement(NAMESPACE).style.transition)) {
2999
3329
  options.transition = false;
3000
3330
  }
3001
3331
  if (options.inline) {
@@ -3049,12 +3379,22 @@
3049
3379
  }
3050
3380
  });
3051
3381
  } else {
3052
- addListener(element, EVENT_CLICK, this.onStart = function (_ref) {
3382
+ addListener(element, EVENT_CLICK, this.onElementClick = function (_ref) {
3053
3383
  var target = _ref.target;
3054
3384
  if (target.localName === 'img' && (!isFunction(options.filter) || options.filter.call(_this, target))) {
3055
3385
  _this.view(_this.images.indexOf(target));
3056
3386
  }
3057
3387
  });
3388
+ addListener(element, EVENT_KEY_DOWN, this.onElementKeyDown = function (event) {
3389
+ var target = event.target,
3390
+ key = event.key;
3391
+ if (target.localName === 'img' && (key === 'Enter' || key === ' ')) {
3392
+ event.preventDefault();
3393
+ if (!isFunction(options.filter) || options.filter.call(_this, target)) {
3394
+ _this.view(_this.images.indexOf(target));
3395
+ }
3396
+ }
3397
+ });
3058
3398
  }
3059
3399
  }
3060
3400
  }, {
@@ -3072,6 +3412,7 @@
3072
3412
  var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
3073
3413
  var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
3074
3414
  var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
3415
+ var navigation = viewer.querySelector(".".concat(NAMESPACE, "-navigation"));
3075
3416
  var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
3076
3417
  var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
3077
3418
  this.parent = parent;
@@ -3079,19 +3420,52 @@
3079
3420
  this.title = title;
3080
3421
  this.toolbar = toolbar;
3081
3422
  this.navbar = navbar;
3423
+ this.navigation = navigation;
3082
3424
  this.button = button;
3083
3425
  this.canvas = canvas;
3084
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"));
3085
3429
  this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
3086
3430
  this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
3087
3431
  this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
3088
3432
  viewer.id = "".concat(NAMESPACE).concat(this.id);
3089
3433
  title.id = "".concat(NAMESPACE, "Title").concat(this.id);
3090
3434
  addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
3091
- 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
+ }
3092
3463
  toggleClass(button, CLASS_HIDE, !options.button);
3093
3464
  if (options.keyboard) {
3094
3465
  button.setAttribute('tabindex', 0);
3466
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3467
+ item.setAttribute('tabindex', 0);
3468
+ });
3095
3469
  }
3096
3470
  if (options.backdrop) {
3097
3471
  addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
@@ -3177,7 +3551,7 @@
3177
3551
  });
3178
3552
  var container = options.container;
3179
3553
  if (isString(container)) {
3180
- container = element.ownerDocument.querySelector(container);
3554
+ container = this.ownerDocument.querySelector(container);
3181
3555
  }
3182
3556
  if (!container) {
3183
3557
  container = this.body;
@@ -3205,14 +3579,15 @@
3205
3579
  }
3206
3580
 
3207
3581
  /**
3208
- * Get the no conflict viewer class.
3209
- * @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.
3210
3586
  */
3211
3587
  }], [{
3212
- key: "noConflict",
3213
- value: function noConflict() {
3214
- window.Viewer = AnotherViewer;
3215
- return Viewer;
3588
+ key: "create",
3589
+ value: function create(element, options) {
3590
+ return new Viewer(element, options);
3216
3591
  }
3217
3592
 
3218
3593
  /**
@@ -3224,6 +3599,17 @@
3224
3599
  value: function setDefaults(options) {
3225
3600
  assign(DEFAULTS, isPlainObject(options) && options);
3226
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
+ }
3227
3613
  }]);
3228
3614
  }();
3229
3615
  assign(Viewer.prototype, render, events, handlers, methods, others);