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.
@@ -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 _classCallCheck(a, n) {
@@ -89,9 +89,14 @@ var DEFAULTS = {
89
89
  button: true,
90
90
  /**
91
91
  * Show the navbar.
92
- * @type {boolean | number}
92
+ * @type {boolean | number | string | Object}
93
93
  */
94
94
  navbar: true,
95
+ /**
96
+ * Show the navigation buttons.
97
+ * @type {boolean | number | Object}
98
+ */
99
+ navigation: false,
95
100
  /**
96
101
  * Specify the visibility and the content of the title.
97
102
  * @type {boolean | number | Function | Array}
@@ -143,6 +148,11 @@ var DEFAULTS = {
143
148
  * @type {boolean}
144
149
  */
145
150
  inline: false,
151
+ /**
152
+ * Enable to automatically play the images when playing.
153
+ * @type {boolean}
154
+ */
155
+ autoplay: true,
146
156
  /**
147
157
  * The amount of time to delay between automatically cycling an image when playing.
148
158
  * @type {number}
@@ -168,6 +178,11 @@ var DEFAULTS = {
168
178
  * @type {boolean}
169
179
  */
170
180
  loop: true,
181
+ /**
182
+ * Enable to preload the next or previous image before viewing it.
183
+ * @type {boolean}
184
+ */
185
+ preload: true,
171
186
  /**
172
187
  * Min width of the viewer in inline mode.
173
188
  * @type {number}
@@ -183,11 +198,26 @@ var DEFAULTS = {
183
198
  * @type {boolean}
184
199
  */
185
200
  movable: true,
201
+ /**
202
+ * Show a magnifier when hovering over the image in fullscreen mode.
203
+ * @type {boolean | Object}
204
+ */
205
+ magnifier: false,
186
206
  /**
187
207
  * Enable to rotate the image.
188
208
  * @type {boolean}
189
209
  */
190
210
  rotatable: true,
211
+ /**
212
+ * Enable to rotate the current image by gesture.
213
+ * @type {boolean}
214
+ */
215
+ rotateOnGesture: true,
216
+ /**
217
+ * Enable to rotate the current image by dragging on the touch screen.
218
+ * @type {boolean}
219
+ */
220
+ rotateOnTouch: true,
191
221
  /**
192
222
  * Enable to scale the image.
193
223
  * @type {boolean}
@@ -203,6 +233,11 @@ var DEFAULTS = {
203
233
  * @type {boolean}
204
234
  */
205
235
  zoomOnTouch: true,
236
+ /**
237
+ * Enable to zoom the current image by gesture.
238
+ * @type {boolean}
239
+ */
240
+ zoomOnGesture: true,
206
241
  /**
207
242
  * Enable to zoom the image by wheeling mouse.
208
243
  * @type {boolean}
@@ -226,7 +261,7 @@ var DEFAULTS = {
226
261
  tooltip: true,
227
262
  /**
228
263
  * Enable CSS3 Transition for some special elements.
229
- * @type {boolean}
264
+ * @type {boolean | Object}
230
265
  */
231
266
  transition: true,
232
267
  /**
@@ -246,12 +281,12 @@ var DEFAULTS = {
246
281
  zoomRatio: 0.1,
247
282
  /**
248
283
  * Define the min ratio of the image when zoom out.
249
- * @type {number}
284
+ * @type {number | Function}
250
285
  */
251
286
  minZoomRatio: 0.01,
252
287
  /**
253
288
  * Define the max ratio of the image when zoom in.
254
- * @type {number}
289
+ * @type {number | Function}
255
290
  */
256
291
  maxZoomRatio: 100,
257
292
  /**
@@ -282,7 +317,7 @@ var DEFAULTS = {
282
317
  stop: null
283
318
  };
284
319
 
285
- 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>';
320
+ 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>';
286
321
 
287
322
  var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
288
323
  var WINDOW = IS_BROWSER ? window : {};
@@ -292,7 +327,9 @@ var NAMESPACE = 'viewer';
292
327
 
293
328
  // Actions
294
329
  var ACTION_MOVE = 'move';
330
+ var ACTION_ROTATE = 'rotate';
295
331
  var ACTION_SWITCH = 'switch';
332
+ var ACTION_TRANSFORM = 'transform';
296
333
  var ACTION_ZOOM = 'zoom';
297
334
 
298
335
  // Classes
@@ -326,11 +363,14 @@ var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';
326
363
  var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';
327
364
  var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';
328
365
  var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;
366
+ var EVENT_POINTER_ENTER = HAS_POINTER_EVENT ? 'pointerenter' : 'mouseenter';
367
+ var EVENT_POINTER_LEAVE = HAS_POINTER_EVENT ? 'pointerleave' : 'mouseleave';
329
368
  var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;
330
369
  var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;
331
370
  var EVENT_RESIZE = 'resize';
332
371
  var EVENT_TRANSITION_END = 'transitionend';
333
372
  var EVENT_WHEEL = 'wheel';
373
+ var EVENT_GESTURE = 'gesturestart gesturechange gestureend';
334
374
 
335
375
  // Custom events
336
376
  var EVENT_READY = 'ready';
@@ -454,6 +494,32 @@ function forEach(data, callback) {
454
494
  return data;
455
495
  }
456
496
 
497
+ /**
498
+ * Inherit attributes from the original image.
499
+ * @param {Element} image - The target image.
500
+ * @param {Element} originalImage - The original image.
501
+ * @param {Array} inheritedAttributes - The attributes to inherit.
502
+ */
503
+ function inheritAttributes(image, originalImage, inheritedAttributes) {
504
+ forEach(inheritedAttributes, function (name) {
505
+ var value = originalImage.getAttribute(name);
506
+ if (value !== null) {
507
+ image.setAttribute(name, value);
508
+ }
509
+ });
510
+ }
511
+
512
+ /**
513
+ * Check if transition is enabled for the given action.
514
+ * @param {Object} options - The viewer options.
515
+ * @param {string} action - The transition action.
516
+ * @returns {boolean} Returns `true` if transition is enabled.
517
+ */
518
+ function isTransitionEnabled(options, action) {
519
+ var transition = options.transition;
520
+ return transition && transition[action] !== false;
521
+ }
522
+
457
523
  /**
458
524
  * Extend the given object.
459
525
  * @param {*} obj - The object to be extended.
@@ -511,7 +577,7 @@ function hasClass(element, value) {
511
577
  if (!element || !value) {
512
578
  return false;
513
579
  }
514
- return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
580
+ return element.classList ? element.classList.contains(value) : element.className.split(REGEXP_SPACES).indexOf(value) > -1;
515
581
  }
516
582
 
517
583
  /**
@@ -783,10 +849,8 @@ function getTransforms(_ref) {
783
849
  if (isNumber(rotate) && rotate !== 0) {
784
850
  values.push("rotate(".concat(rotate, "deg)"));
785
851
  }
786
- if (isNumber(scaleX) && scaleX !== 1) {
852
+ if (isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1)) {
787
853
  values.push("scaleX(".concat(scaleX, ")"));
788
- }
789
- if (isNumber(scaleY) && scaleY !== 1) {
790
854
  values.push("scaleY(".concat(scaleY, ")"));
791
855
  }
792
856
  var transform = values.length ? values.join(' ') : 'none';
@@ -832,12 +896,7 @@ function getImageNaturalSizes(image, options, callback) {
832
896
  body.removeChild(newImage);
833
897
  }
834
898
  };
835
- forEach(options.inheritedAttributes, function (name) {
836
- var value = image.getAttribute(name);
837
- if (value !== null) {
838
- newImage.setAttribute(name, value);
839
- }
840
- });
899
+ inheritAttributes(newImage, image, options.inheritedAttributes);
841
900
  newImage.src = image.src;
842
901
 
843
902
  // iOS Safari will convert the image automatically
@@ -894,6 +953,34 @@ function getMaxZoomRatio(pointers) {
894
953
  return ratios[0];
895
954
  }
896
955
 
956
+ /**
957
+ * Get the max rotation degree of a group of pointers.
958
+ * @param {Object} pointers - The target pointers.
959
+ * @returns {number} The result degree.
960
+ */
961
+ function getMaxRotateDegree(pointers) {
962
+ var pointers2 = _objectSpread2({}, pointers);
963
+ var degrees = [];
964
+ forEach(pointers, function (pointer, pointerId) {
965
+ delete pointers2[pointerId];
966
+ forEach(pointers2, function (pointer2) {
967
+ var start = Math.atan2(pointer2.startY - pointer.startY, pointer2.startX - pointer.startX);
968
+ var end = Math.atan2(pointer2.endY - pointer.endY, pointer2.endX - pointer.endX);
969
+ var radians = end - start;
970
+ if (radians > Math.PI) {
971
+ radians -= Math.PI * 2;
972
+ } else if (radians < -Math.PI) {
973
+ radians += Math.PI * 2;
974
+ }
975
+ degrees.push(radians * 180 / Math.PI);
976
+ });
977
+ });
978
+ degrees.sort(function (a, b) {
979
+ return Math.abs(b) - Math.abs(a);
980
+ });
981
+ return degrees[0] || 0;
982
+ }
983
+
897
984
  /**
898
985
  * Get a pointer from an event object.
899
986
  * @param {Object} event - The target event object.
@@ -946,7 +1033,7 @@ var render = {
946
1033
  this.renderViewer();
947
1034
  },
948
1035
  initBody: function initBody() {
949
- var ownerDocument = this.element.ownerDocument;
1036
+ var ownerDocument = this.ownerDocument;
950
1037
  var body = ownerDocument.body || ownerDocument.documentElement;
951
1038
  this.body = body;
952
1039
  this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
@@ -982,26 +1069,34 @@ var render = {
982
1069
  },
983
1070
  initList: function initList() {
984
1071
  var _this = this;
1072
+ var viewIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.index;
985
1073
  var element = this.element,
986
1074
  options = this.options,
987
1075
  list = this.list;
988
1076
  var items = [];
1077
+ var navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
1078
+ var probe = document.createElement('li');
1079
+ list.appendChild(probe);
1080
+ var itemWidth = probe.offsetWidth + parseInt(window.getComputedStyle(probe).marginLeft, 10);
1081
+ list.removeChild(probe);
1082
+ var visibleItemCount = isNumber(navbarOptions.visibleItemCount) ? Math.floor(navbarOptions.visibleItemCount) : Math.floor(this.containerData.width / itemWidth);
1083
+ visibleItemCount = Math.min(visibleItemCount, this.length);
1084
+ var start = visibleItemCount > 0 ? Math.min(Math.max(0, viewIndex - Math.floor(visibleItemCount / 2)), Math.max(0, this.length - visibleItemCount)) : 0;
1085
+ var end = visibleItemCount > 0 ? Math.min(this.length, start + visibleItemCount) : this.length;
989
1086
 
990
1087
  // initList may be called in this.update, so should keep idempotent
991
1088
  list.innerHTML = '';
992
1089
  forEach(this.images, function (image, index) {
1090
+ if (visibleItemCount > 0 && (index < start || index >= end)) {
1091
+ return;
1092
+ }
993
1093
  var src = image.src;
994
1094
  var alt = image.alt || getImageNameFromURL(src);
995
1095
  var url = _this.getImageURL(image);
996
1096
  if (src || url) {
997
1097
  var item = document.createElement('li');
998
1098
  var img = document.createElement('img');
999
- forEach(options.inheritedAttributes, function (name) {
1000
- var value = image.getAttribute(name);
1001
- if (value !== null) {
1002
- img.setAttribute(name, value);
1003
- }
1004
- });
1099
+ inheritAttributes(img, image, options.inheritedAttributes);
1005
1100
  if (options.navbar) {
1006
1101
  img.src = src || url;
1007
1102
  }
@@ -1019,6 +1114,13 @@ var render = {
1019
1114
  }
1020
1115
  });
1021
1116
  this.items = items;
1117
+ if (this.viewed) {
1118
+ var activeItem = this.getItem(this.index);
1119
+ if (activeItem) {
1120
+ addClass(activeItem, CLASS_ACTIVE);
1121
+ activeItem.setAttribute('aria-selected', true);
1122
+ }
1123
+ }
1022
1124
  forEach(items, function (item) {
1023
1125
  var image = item.firstElementChild;
1024
1126
  var onLoad;
@@ -1045,7 +1147,7 @@ var render = {
1045
1147
  once: true
1046
1148
  });
1047
1149
  });
1048
- if (options.transition) {
1150
+ if (isTransitionEnabled(options, 'view')) {
1049
1151
  addListener(element, EVENT_VIEWED, function () {
1050
1152
  addClass(list, CLASS_TRANSITION);
1051
1153
  }, {
@@ -1053,9 +1155,20 @@ var render = {
1053
1155
  });
1054
1156
  }
1055
1157
  },
1158
+ getItem: function getItem(index) {
1159
+ var item;
1160
+ forEach(this.items, function (candidate) {
1161
+ if (Number(getData(candidate, 'index')) === index) {
1162
+ item = candidate;
1163
+ return false;
1164
+ }
1165
+ return true;
1166
+ });
1167
+ return item;
1168
+ },
1056
1169
  renderList: function renderList() {
1057
1170
  var index = this.index;
1058
- var item = this.items[index];
1171
+ var item = this.getItem(index);
1059
1172
  if (!item) {
1060
1173
  return;
1061
1174
  }
@@ -1066,9 +1179,9 @@ var render = {
1066
1179
 
1067
1180
  // Place the active item in the center of the screen
1068
1181
  setStyle(this.list, assign({
1069
- width: outerWidth * this.length - gutter
1182
+ width: outerWidth * this.items.length - gutter
1070
1183
  }, getTransforms({
1071
- translateX: (this.viewerData.width - offsetWidth) / 2 - outerWidth * index
1184
+ translateX: (this.viewerData.width - offsetWidth) / 2 - item.offsetLeft
1072
1185
  })));
1073
1186
  },
1074
1187
  resetList: function resetList() {
@@ -1154,8 +1267,23 @@ var render = {
1154
1267
  marginLeft: imageData.x,
1155
1268
  marginTop: imageData.y
1156
1269
  }, getTransforms(imageData)));
1270
+ if (this.magnifierPoint) {
1271
+ this.renderMagnifier();
1272
+ }
1157
1273
  if (done) {
1158
- if ((this.viewing || this.moving || this.rotating || this.scaling || this.zooming) && this.options.transition && hasClass(image, CLASS_TRANSITION)) {
1274
+ var action = false;
1275
+ if (this.viewing) {
1276
+ action = 'view';
1277
+ } else if (this.moving) {
1278
+ action = 'move';
1279
+ } else if (this.rotating) {
1280
+ action = 'rotate';
1281
+ } else if (this.scaling) {
1282
+ action = 'scale';
1283
+ } else if (this.zooming) {
1284
+ action = 'zoom';
1285
+ }
1286
+ if (action && isTransitionEnabled(this.options, action) && hasClass(image, CLASS_TRANSITION)) {
1159
1287
  var onTransitionEnd = function onTransitionEnd() {
1160
1288
  _this3.imageRendering = false;
1161
1289
  done();
@@ -1191,9 +1319,12 @@ var events = {
1191
1319
  var options = this.options,
1192
1320
  viewer = this.viewer,
1193
1321
  canvas = this.canvas;
1194
- var document = this.element.ownerDocument;
1322
+ var document = this.ownerDocument;
1195
1323
  addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
1196
1324
  addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
1325
+ addListener(viewer, EVENT_POINTER_ENTER, this.onMagnifyEnter = this.magnify.bind(this));
1326
+ addListener(viewer, EVENT_POINTER_MOVE, this.onMagnify = this.magnify.bind(this));
1327
+ addListener(viewer, EVENT_POINTER_LEAVE, this.onMagnifierLeave = this.hideMagnifier.bind(this));
1197
1328
  addListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
1198
1329
  addListener(document, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
1199
1330
  addListener(document, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
@@ -1205,6 +1336,11 @@ var events = {
1205
1336
  capture: true
1206
1337
  });
1207
1338
  }
1339
+ if (options.zoomable && options.zoomOnGesture || options.rotatable && options.rotateOnGesture) {
1340
+ addListener(viewer, EVENT_GESTURE, this.onGesture = this.gesture.bind(this), {
1341
+ passive: false
1342
+ });
1343
+ }
1208
1344
  if (options.toggleOnDblclick) {
1209
1345
  addListener(canvas, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
1210
1346
  }
@@ -1213,9 +1349,12 @@ var events = {
1213
1349
  var options = this.options,
1214
1350
  viewer = this.viewer,
1215
1351
  canvas = this.canvas;
1216
- var document = this.element.ownerDocument;
1352
+ var document = this.ownerDocument;
1217
1353
  removeListener(viewer, EVENT_CLICK, this.onClick);
1218
1354
  removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
1355
+ removeListener(viewer, EVENT_POINTER_ENTER, this.onMagnifyEnter);
1356
+ removeListener(viewer, EVENT_POINTER_MOVE, this.onMagnify);
1357
+ removeListener(viewer, EVENT_POINTER_LEAVE, this.onMagnifierLeave);
1219
1358
  removeListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown);
1220
1359
  removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove);
1221
1360
  removeListener(document, EVENT_POINTER_UP, this.onPointerUp);
@@ -1227,6 +1366,11 @@ var events = {
1227
1366
  capture: true
1228
1367
  });
1229
1368
  }
1369
+ if (options.zoomable && options.zoomOnGesture || options.rotatable && options.rotateOnGesture) {
1370
+ removeListener(viewer, EVENT_GESTURE, this.onGesture, {
1371
+ passive: false
1372
+ });
1373
+ }
1230
1374
  if (options.toggleOnDblclick) {
1231
1375
  removeListener(canvas, EVENT_DBLCLICK, this.onDblclick);
1232
1376
  }
@@ -1339,10 +1483,13 @@ var handlers = {
1339
1483
  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;';
1340
1484
  this.initImage(function () {
1341
1485
  toggleClass(image, CLASS_MOVE, options.movable);
1342
- toggleClass(image, CLASS_TRANSITION, options.transition);
1486
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'view'));
1343
1487
  _this.renderImage(function () {
1344
1488
  _this.viewed = true;
1345
1489
  _this.viewing = false;
1490
+ setTimeout(function () {
1491
+ toggleClass(image, CLASS_TRANSITION, options.transition);
1492
+ }, 300);
1346
1493
  if (isFunction(options.viewed)) {
1347
1494
  addListener(element, EVENT_VIEWED, options.viewed, {
1348
1495
  once: true
@@ -1481,6 +1628,88 @@ var handlers = {
1481
1628
  event.preventDefault();
1482
1629
  }
1483
1630
  },
1631
+ magnify: function magnify(event) {
1632
+ if (event.changedTouches || event.pointerType && event.pointerType !== 'mouse') {
1633
+ this.hideMagnifier();
1634
+ return;
1635
+ }
1636
+ this.magnifierPoint = {
1637
+ clientX: event.clientX,
1638
+ clientY: event.clientY
1639
+ };
1640
+ this.renderMagnifier();
1641
+ },
1642
+ renderMagnifier: function renderMagnifier() {
1643
+ var options = this.options,
1644
+ imageData = this.imageData,
1645
+ magnifier = this.magnifier,
1646
+ magnifierImage = this.magnifierImage,
1647
+ viewer = this.viewer;
1648
+ var config = isPlainObject(options.magnifier) ? options.magnifier : {};
1649
+ var point = this.magnifierPoint;
1650
+ if (!options.magnifier || !this.fulled || !this.viewed || !magnifier || !magnifierImage || !point) {
1651
+ this.hideMagnifier();
1652
+ return;
1653
+ }
1654
+ var size = Math.max(1, Number(config.size) || 100);
1655
+ var zoomRatio = Math.max(1, Number(config.zoomRatio) || 2);
1656
+ var opacity = Number(config.opacity);
1657
+ var rect = viewer.getBoundingClientRect();
1658
+ var x = point.clientX - rect.left;
1659
+ var y = point.clientY - rect.top;
1660
+ var imageURL = this.image.currentSrc || this.image.src;
1661
+ var imageRect = this.image.getBoundingClientRect();
1662
+ if (point.clientX < imageRect.left || point.clientX > imageRect.right || point.clientY < imageRect.top || point.clientY > imageRect.bottom) {
1663
+ this.hideMagnifier();
1664
+ return;
1665
+ }
1666
+ var scaleX = isNumber(imageData.scaleX) ? imageData.scaleX : 1;
1667
+ var scaleY = isNumber(imageData.scaleY) ? imageData.scaleY : 1;
1668
+ var rotate = (imageData.rotate || 0) * Math.PI / 180;
1669
+ var cos = Math.cos(rotate);
1670
+ var sin = Math.sin(rotate);
1671
+ var matrixA = cos * scaleX;
1672
+ var matrixB = sin * scaleX;
1673
+ var matrixC = -sin * scaleY;
1674
+ var matrixD = cos * scaleY;
1675
+ var determinant = scaleX * scaleY;
1676
+ if (determinant === 0) {
1677
+ this.hideMagnifier();
1678
+ return;
1679
+ }
1680
+ var centerX = imageData.x + imageData.width / 2;
1681
+ var centerY = imageData.y + imageData.height / 2;
1682
+ var localX = (matrixD * (x - centerX) - matrixC * (y - centerY)) / determinant;
1683
+ var localY = (-matrixB * (x - centerX) + matrixA * (y - centerY)) / determinant;
1684
+ var sourceX = localX + imageData.width / 2;
1685
+ var sourceY = localY + imageData.height / 2;
1686
+ var magnifiedWidth = imageData.width * zoomRatio;
1687
+ var magnifiedHeight = imageData.height * zoomRatio;
1688
+ var transformedX = matrixA * (sourceX * zoomRatio - magnifiedWidth / 2) + matrixC * (sourceY * zoomRatio - magnifiedHeight / 2);
1689
+ var transformedY = matrixB * (sourceX * zoomRatio - magnifiedWidth / 2) + matrixD * (sourceY * zoomRatio - magnifiedHeight / 2);
1690
+ this.magnifierSourcePoint = {
1691
+ x: sourceX,
1692
+ y: sourceY
1693
+ };
1694
+ magnifier.style.width = "".concat(size, "px");
1695
+ magnifier.style.height = "".concat(size, "px");
1696
+ magnifier.style.opacity = "".concat(Math.max(0, Math.min(1, isNumber(opacity) ? opacity : 1)));
1697
+ magnifierImage.src = imageURL;
1698
+ magnifierImage.style.width = "".concat(magnifiedWidth, "px");
1699
+ magnifierImage.style.height = "".concat(magnifiedHeight, "px");
1700
+ magnifierImage.style.left = "".concat(size / 2 - magnifiedWidth / 2 - transformedX, "px");
1701
+ magnifierImage.style.top = "".concat(size / 2 - magnifiedHeight / 2 - transformedY, "px");
1702
+ magnifierImage.style.transform = "rotate(".concat(imageData.rotate || 0, "deg) scaleX(").concat(scaleX, ") scaleY(").concat(scaleY, ")");
1703
+ magnifier.removeAttribute('aria-hidden');
1704
+ addClass(magnifier, 'viewer-show');
1705
+ },
1706
+ hideMagnifier: function hideMagnifier() {
1707
+ if (this.magnifier) {
1708
+ removeClass(this.magnifier, 'viewer-show');
1709
+ this.magnifier.setAttribute('aria-hidden', true);
1710
+ this.magnifierPoint = null;
1711
+ }
1712
+ },
1484
1713
  pointerdown: function pointerdown(event) {
1485
1714
  var options = this.options,
1486
1715
  pointers = this.pointers;
@@ -1509,12 +1738,14 @@ var handlers = {
1509
1738
  pointers[event.pointerId || 0] = getPointer(event);
1510
1739
  }
1511
1740
  var action = options.movable ? ACTION_MOVE : false;
1512
- if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) {
1513
- action = ACTION_ZOOM;
1741
+ if ((options.zoomable && options.zoomOnTouch || options.rotatable && options.rotateOnTouch) && Object.keys(pointers).length > 1) {
1742
+ // action = ACTION_ZOOM;
1743
+ // action = ACTION_ROTATE;
1744
+ action = ACTION_TRANSFORM;
1514
1745
  } else if (options.slideOnTouch && (event.pointerType === 'touch' || event.type === 'touchstart') && this.isSwitchable()) {
1515
1746
  action = ACTION_SWITCH;
1516
1747
  }
1517
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1748
+ if (action === ACTION_MOVE || action === ACTION_ZOOM || action === ACTION_ROTATE || action === ACTION_TRANSFORM) {
1518
1749
  removeClass(this.image, CLASS_TRANSITION);
1519
1750
  }
1520
1751
  this.action = action;
@@ -1554,17 +1785,20 @@ var handlers = {
1554
1785
  return;
1555
1786
  }
1556
1787
  event.preventDefault();
1557
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1558
- addClass(this.image, CLASS_TRANSITION);
1788
+ if (action === ACTION_MOVE || action === ACTION_ZOOM || action === ACTION_ROTATE || action === ACTION_TRANSFORM) {
1789
+ var transition = action === ACTION_TRANSFORM ? isTransitionEnabled(options, ACTION_ZOOM) || isTransitionEnabled(options, ACTION_ROTATE) : isTransitionEnabled(options, action);
1790
+ toggleClass(this.image, CLASS_TRANSITION, transition);
1559
1791
  }
1560
1792
  this.action = false;
1561
1793
 
1562
1794
  // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
1563
- if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && pointer && Date.now() - pointer.timeStamp < 500) {
1795
+ if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && action !== ACTION_TRANSFORM && pointer && Date.now() - pointer.timeStamp < 500) {
1564
1796
  clearTimeout(this.clickCanvasTimeout);
1565
1797
  clearTimeout(this.doubleClickImageTimeout);
1566
1798
  if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
1567
- if (this.imageClicked) {
1799
+ if (this.pointerMoved) {
1800
+ this.imageClicked = false;
1801
+ } else if (this.imageClicked) {
1568
1802
  this.imageClicked = false;
1569
1803
 
1570
1804
  // This timeout will be cleared later when a native dblclick event is triggering
@@ -1607,6 +1841,10 @@ var handlers = {
1607
1841
  this.initContainer();
1608
1842
  this.initViewer();
1609
1843
  this.renderViewer();
1844
+ var navbarOptions = isPlainObject(this.options.navbar) ? this.options.navbar : {};
1845
+ if (isUndefined(navbarOptions.visibleItemCount)) {
1846
+ this.initList(this.index);
1847
+ }
1610
1848
  this.renderList();
1611
1849
  if (this.viewed) {
1612
1850
  this.initImage(function () {
@@ -1632,6 +1870,9 @@ var handlers = {
1632
1870
  return;
1633
1871
  }
1634
1872
  event.preventDefault();
1873
+ if (this.gesturing) {
1874
+ return;
1875
+ }
1635
1876
 
1636
1877
  // Limit wheel speed to prevent zoom too fast
1637
1878
  if (this.wheeling) {
@@ -1651,6 +1892,43 @@ var handlers = {
1651
1892
  delta = event.detail > 0 ? 1 : -1;
1652
1893
  }
1653
1894
  this.zoom(-delta * ratio, true, null, event);
1895
+ },
1896
+ gesture: function gesture(event) {
1897
+ var options = this.options;
1898
+ if (!this.viewed || !options.zoomOnGesture && !options.rotateOnGesture) {
1899
+ return;
1900
+ }
1901
+ event.preventDefault();
1902
+ switch (event.type) {
1903
+ case 'gesturestart':
1904
+ this.gesturing = true;
1905
+ this.gestureScale = event.scale || 1;
1906
+ this.gestureRotation = event.rotation || 0;
1907
+ break;
1908
+ case 'gesturechange':
1909
+ {
1910
+ var scale = event.scale || 1;
1911
+ var ratio = scale / (this.gestureScale || 1);
1912
+ var rotation = Number(event.rotation);
1913
+ var degree = rotation - (this.gestureRotation || 0);
1914
+ this.gestureScale = scale;
1915
+ if (options.zoomable && options.zoomOnGesture && ratio !== 1) {
1916
+ this.zoom(ratio >= 1 ? ratio - 1 : 1 - 1 / ratio, false, null, event);
1917
+ }
1918
+ if (options.rotatable && options.rotateOnGesture && isNumber(rotation)) {
1919
+ this.gestureRotation = rotation;
1920
+ if (degree !== 0) {
1921
+ this.rotate(degree);
1922
+ }
1923
+ }
1924
+ break;
1925
+ }
1926
+ case 'gestureend':
1927
+ this.gesturing = false;
1928
+ this.gestureScale = 1;
1929
+ this.gestureRotation = 0;
1930
+ break;
1931
+ }
1654
1932
  }
1655
1933
  };
1656
1934
 
@@ -1692,7 +1970,7 @@ var methods = {
1692
1970
  viewer.setAttribute('aria-labelledby', this.title.id);
1693
1971
  viewer.setAttribute('aria-modal', true);
1694
1972
  viewer.removeAttribute('aria-hidden');
1695
- if (options.transition && !immediate) {
1973
+ if (isTransitionEnabled(options, 'show') && !immediate) {
1696
1974
  var shown = this.shown.bind(this);
1697
1975
  this.transitioning = {
1698
1976
  abort: function abort() {
@@ -1732,7 +2010,7 @@ var methods = {
1732
2010
  once: true
1733
2011
  });
1734
2012
  }
1735
- if (dispatchEvent(element, EVENT_HIDE) === false) {
2013
+ if (dispatchEvent(element, EVENT_HIDE) === false || this.destroyed) {
1736
2014
  return this;
1737
2015
  }
1738
2016
  if (this.showing) {
@@ -1750,7 +2028,7 @@ var methods = {
1750
2028
  removeClass(viewer, CLASS_IN);
1751
2029
  _this.hidden();
1752
2030
  };
1753
- if (options.transition && !immediate) {
2031
+ if (isTransitionEnabled(options, 'hide') && !immediate) {
1754
2032
  var _onViewerTransitionEnd = function onViewerTransitionEnd(event) {
1755
2033
  // Ignore all propagating `transitionend` events (#275).
1756
2034
  if (event && event.target === viewer) {
@@ -1800,8 +2078,9 @@ var methods = {
1800
2078
  view: function view() {
1801
2079
  var _this2 = this;
1802
2080
  var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.initialViewIndex;
2081
+ var previousIndex = this.index;
1803
2082
  index = Number(index) || 0;
1804
- if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) {
2083
+ if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === previousIndex) {
1805
2084
  return this;
1806
2085
  }
1807
2086
  if (!this.isShown) {
@@ -1815,17 +2094,16 @@ var methods = {
1815
2094
  options = this.options,
1816
2095
  title = this.title,
1817
2096
  canvas = this.canvas;
1818
- var item = this.items[index];
2097
+ var item = this.getItem(index);
2098
+ if (!item) {
2099
+ this.initList(index);
2100
+ item = this.getItem(index);
2101
+ }
1819
2102
  var img = item.querySelector('img');
1820
2103
  var url = getData(img, 'originalUrl');
1821
2104
  var alt = img.getAttribute('alt');
1822
2105
  var image = document.createElement('img');
1823
- forEach(options.inheritedAttributes, function (name) {
1824
- var value = img.getAttribute(name);
1825
- if (value !== null) {
1826
- image.setAttribute(name, value);
1827
- }
1828
- });
2106
+ inheritAttributes(image, img, options.inheritedAttributes);
1829
2107
  image.src = url;
1830
2108
  image.alt = alt;
1831
2109
  if (isFunction(options.view)) {
@@ -1840,7 +2118,8 @@ var methods = {
1840
2118
  }) === false || !this.isShown || this.hiding || this.played) {
1841
2119
  return this;
1842
2120
  }
1843
- var activeItem = this.items[this.index];
2121
+ this.hideMagnifier();
2122
+ var activeItem = this.getItem(previousIndex);
1844
2123
  if (activeItem) {
1845
2124
  removeClass(activeItem, CLASS_ACTIVE);
1846
2125
  activeItem.removeAttribute('aria-selected');
@@ -1872,6 +2151,25 @@ var methods = {
1872
2151
  var imageData = _this2.imageData;
1873
2152
  var render = Array.isArray(options.title) ? options.title[1] : options.title;
1874
2153
  title.innerHTML = escapeHTMLEntities(isFunction(render) ? render.call(_this2, image, imageData) : "".concat(alt, " (").concat(imageData.naturalWidth, " \xD7 ").concat(imageData.naturalHeight, ")"));
2154
+
2155
+ // Preload the image in the direction of navigation
2156
+ if (options.preload) {
2157
+ var direction = index < previousIndex ? -1 : 1;
2158
+ var nextIndex = index + direction;
2159
+ if (nextIndex < 0 || nextIndex >= _this2.length) {
2160
+ if (options.loop) {
2161
+ nextIndex = direction > 0 ? 0 : _this2.length - 1;
2162
+ } else {
2163
+ nextIndex = -1;
2164
+ }
2165
+ }
2166
+ if (nextIndex !== index) {
2167
+ var nextImage = _this2.images[nextIndex];
2168
+ var preloadedImage = document.createElement('img');
2169
+ inheritAttributes(preloadedImage, nextImage, options.inheritedAttributes);
2170
+ preloadedImage.src = _this2.getImageURL(nextImage) || nextImage.src;
2171
+ }
2172
+ }
1875
2173
  };
1876
2174
  var onLoad;
1877
2175
  var onError;
@@ -2023,6 +2321,7 @@ var methods = {
2023
2321
  imageData.y = y;
2024
2322
  imageData.left = x;
2025
2323
  imageData.top = y;
2324
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'move'));
2026
2325
  this.moving = true;
2027
2326
  this.renderImage(function () {
2028
2327
  _this3.moving = false;
@@ -2079,6 +2378,7 @@ var methods = {
2079
2378
  return this;
2080
2379
  }
2081
2380
  imageData.rotate = degree;
2381
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'rotate'));
2082
2382
  this.rotating = true;
2083
2383
  this.renderImage(function () {
2084
2384
  _this4.rotating = false;
@@ -2159,6 +2459,7 @@ var methods = {
2159
2459
  }
2160
2460
  imageData.scaleX = scaleX;
2161
2461
  imageData.scaleY = scaleY;
2462
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'scale'));
2162
2463
  this.scaling = true;
2163
2464
  this.renderImage(function () {
2164
2465
  _this5.scaling = false;
@@ -2230,8 +2531,8 @@ var methods = {
2230
2531
  ratio = Math.max(0, ratio);
2231
2532
  if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
2232
2533
  if (!_zoomable) {
2233
- var minZoomRatio = Math.max(0.01, options.minZoomRatio);
2234
- var maxZoomRatio = Math.min(100, options.maxZoomRatio);
2534
+ var minZoomRatio = Math.max(0.01, isFunction(options.minZoomRatio) ? options.minZoomRatio.call(this, this.image, imageData) : options.minZoomRatio);
2535
+ var maxZoomRatio = Math.min(100, isFunction(options.maxZoomRatio) ? options.maxZoomRatio.call(this, this.image, imageData) : options.maxZoomRatio);
2235
2536
  ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
2236
2537
  }
2237
2538
  if (_originalEvent) {
@@ -2292,6 +2593,7 @@ var methods = {
2292
2593
  imageData.height = newHeight;
2293
2594
  imageData.oldRatio = oldRatio;
2294
2595
  imageData.ratio = ratio;
2596
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'zoom'));
2295
2597
  this.renderImage(function () {
2296
2598
  _this6.zooming = false;
2297
2599
  if (isFunction(options.zoomed)) {
@@ -2345,16 +2647,15 @@ var methods = {
2345
2647
  this.requestFullscreen(fullscreen);
2346
2648
  }
2347
2649
  addClass(player, CLASS_SHOW);
2348
- forEach(this.items, function (item, i) {
2349
- var img = item.querySelector('img');
2650
+ forEach(this.images, function (originalImage, i) {
2350
2651
  var image = document.createElement('img');
2351
- image.src = getData(img, 'originalUrl');
2352
- image.alt = img.getAttribute('alt');
2353
- image.referrerPolicy = img.referrerPolicy;
2652
+ image.src = _this7.getImageURL(originalImage) || originalImage.src;
2653
+ image.alt = originalImage.alt || '';
2654
+ image.referrerPolicy = originalImage.referrerPolicy;
2354
2655
  total += 1;
2355
2656
  addClass(image, CLASS_FADE);
2356
- toggleClass(image, CLASS_TRANSITION, options.transition);
2357
- if (hasClass(item, CLASS_ACTIVE)) {
2657
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'play'));
2658
+ if (i === _this7.index) {
2358
2659
  addClass(image, CLASS_IN);
2359
2660
  index = i;
2360
2661
  }
@@ -2371,7 +2672,7 @@ var methods = {
2371
2672
  index -= 1;
2372
2673
  index = index >= 0 ? index : total - 1;
2373
2674
  addClass(list[index], CLASS_IN);
2374
- _this7.playing.timeout = setTimeout(_prev, options.interval);
2675
+ _this7.playing.timeout = options.autoplay ? setTimeout(_prev, options.interval) : null;
2375
2676
  };
2376
2677
  var _next = function next() {
2377
2678
  clearTimeout(_this7.playing.timeout);
@@ -2379,13 +2680,13 @@ var methods = {
2379
2680
  index += 1;
2380
2681
  index = index < total ? index : 0;
2381
2682
  addClass(list[index], CLASS_IN);
2382
- _this7.playing.timeout = setTimeout(_next, options.interval);
2683
+ _this7.playing.timeout = options.autoplay ? setTimeout(_next, options.interval) : null;
2383
2684
  };
2384
2685
  if (total > 1) {
2385
2686
  this.playing = {
2386
2687
  prev: _prev,
2387
2688
  next: _next,
2388
- timeout: setTimeout(_next, options.interval)
2689
+ timeout: options.autoplay ? setTimeout(_next, options.interval) : null
2389
2690
  };
2390
2691
  }
2391
2692
  }
@@ -2432,11 +2733,9 @@ var methods = {
2432
2733
  this.fulled = true;
2433
2734
  this.open();
2434
2735
  addClass(this.button, CLASS_FULLSCREEN_EXIT);
2435
- if (options.transition) {
2436
- removeClass(list, CLASS_TRANSITION);
2437
- if (this.viewed) {
2438
- removeClass(image, CLASS_TRANSITION);
2439
- }
2736
+ removeClass(list, CLASS_TRANSITION);
2737
+ if (this.viewed) {
2738
+ removeClass(image, CLASS_TRANSITION);
2440
2739
  }
2441
2740
  addClass(viewer, CLASS_FIXED);
2442
2741
  viewer.setAttribute('role', 'dialog');
@@ -2454,14 +2753,7 @@ var methods = {
2454
2753
  this.renderList();
2455
2754
  if (this.viewed) {
2456
2755
  this.initImage(function () {
2457
- _this9.renderImage(function () {
2458
- if (options.transition) {
2459
- setTimeout(function () {
2460
- addClass(image, CLASS_TRANSITION);
2461
- addClass(list, CLASS_TRANSITION);
2462
- }, 0);
2463
- }
2464
- });
2756
+ _this9.renderImage();
2465
2757
  });
2466
2758
  }
2467
2759
  return this;
@@ -2479,11 +2771,9 @@ var methods = {
2479
2771
  this.fulled = false;
2480
2772
  this.close();
2481
2773
  removeClass(this.button, CLASS_FULLSCREEN_EXIT);
2482
- if (options.transition) {
2483
- removeClass(list, CLASS_TRANSITION);
2484
- if (this.viewed) {
2485
- removeClass(image, CLASS_TRANSITION);
2486
- }
2774
+ removeClass(list, CLASS_TRANSITION);
2775
+ if (this.viewed) {
2776
+ removeClass(image, CLASS_TRANSITION);
2487
2777
  }
2488
2778
  if (options.focus) {
2489
2779
  this.clearEnforceFocus();
@@ -2500,14 +2790,7 @@ var methods = {
2500
2790
  this.renderList();
2501
2791
  if (this.viewed) {
2502
2792
  this.initImage(function () {
2503
- _this0.renderImage(function () {
2504
- if (options.transition) {
2505
- setTimeout(function () {
2506
- addClass(image, CLASS_TRANSITION);
2507
- addClass(list, CLASS_TRANSITION);
2508
- }, 0);
2509
- }
2510
- });
2793
+ _this0.renderImage();
2511
2794
  });
2512
2795
  }
2513
2796
  return this;
@@ -2523,7 +2806,7 @@ var methods = {
2523
2806
  }
2524
2807
  tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
2525
2808
  if (!this.tooltipping) {
2526
- if (options.transition) {
2809
+ if (isTransitionEnabled(options, 'tooltip')) {
2527
2810
  if (this.fading) {
2528
2811
  dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
2529
2812
  }
@@ -2543,7 +2826,7 @@ var methods = {
2543
2826
  clearTimeout(this.tooltipping);
2544
2827
  }
2545
2828
  this.tooltipping = setTimeout(function () {
2546
- if (options.transition) {
2829
+ if (isTransitionEnabled(options, 'tooltip')) {
2547
2830
  addListener(tooltipBox, EVENT_TRANSITION_END, function () {
2548
2831
  removeClass(tooltipBox, CLASS_SHOW);
2549
2832
  removeClass(tooltipBox, CLASS_FADE);
@@ -2585,12 +2868,19 @@ var methods = {
2585
2868
  }
2586
2869
  return this;
2587
2870
  },
2588
- // Update viewer when images changed
2589
- update: function update() {
2871
+ /**
2872
+ * Update the viewer when images or options changed.
2873
+ * @param {Object} [updateOptions] - The options to update.
2874
+ * @returns {Viewer} this
2875
+ */
2876
+ update: function update(updateOptions) {
2590
2877
  var _this10 = this;
2591
2878
  var element = this.element,
2592
2879
  options = this.options,
2593
2880
  isImg = this.isImg;
2881
+ if (isPlainObject(updateOptions)) {
2882
+ assign(options, updateOptions);
2883
+ }
2594
2884
 
2595
2885
  // Destroy viewer if the target image was deleted
2596
2886
  if (isImg && !element.parentNode) {
@@ -2613,18 +2903,19 @@ var methods = {
2613
2903
  this.length = images.length;
2614
2904
  if (this.ready) {
2615
2905
  var changedIndexes = [];
2616
- forEach(this.items, function (item, i) {
2906
+ forEach(this.items, function (item) {
2617
2907
  var img = item.querySelector('img');
2618
- var image = images[i];
2908
+ var index = Number(getData(item, 'index'));
2909
+ var image = images[index];
2619
2910
  if (image && img) {
2620
2911
  if (image.src !== img.src
2621
2912
 
2622
2913
  // Title changed (#408)
2623
2914
  || image.alt !== img.alt) {
2624
- changedIndexes.push(i);
2915
+ changedIndexes.push(index);
2625
2916
  }
2626
2917
  } else {
2627
- changedIndexes.push(i);
2918
+ changedIndexes.push(index);
2628
2919
  }
2629
2920
  });
2630
2921
  setStyle(this.list, {
@@ -2639,7 +2930,7 @@ var methods = {
2639
2930
  this.viewed = false;
2640
2931
  this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
2641
2932
  } else {
2642
- var activeItem = this.items[this.index];
2933
+ var activeItem = this.getItem(this.index);
2643
2934
 
2644
2935
  // Reactivate the current viewing item after reset the list.
2645
2936
  addClass(activeItem, CLASS_ACTIVE);
@@ -2703,7 +2994,8 @@ var methods = {
2703
2994
  }
2704
2995
  }
2705
2996
  if (!options.inline) {
2706
- removeListener(element, EVENT_CLICK, this.onStart);
2997
+ removeListener(element, EVENT_CLICK, this.onElementClick);
2998
+ removeListener(element, EVENT_KEY_DOWN, this.onElementKeyDown);
2707
2999
  }
2708
3000
  element[NAMESPACE] = undefined;
2709
3001
  return this;
@@ -2790,6 +3082,12 @@ var others = {
2790
3082
  var element = this.element,
2791
3083
  options = this.options,
2792
3084
  viewer = this.viewer;
3085
+ var activeElement = viewer.ownerDocument.activeElement;
3086
+
3087
+ // Avoid keeping focus inside the dialog before setting `aria-hidden`.
3088
+ if (activeElement && viewer.contains(activeElement) && isFunction(activeElement.blur)) {
3089
+ activeElement.blur();
3090
+ }
2793
3091
  if (options.focus) {
2794
3092
  this.clearEnforceFocus();
2795
3093
  }
@@ -2818,7 +3116,7 @@ var others = {
2818
3116
  }
2819
3117
  },
2820
3118
  requestFullscreen: function requestFullscreen(options) {
2821
- var document = this.element.ownerDocument;
3119
+ var document = this.ownerDocument;
2822
3120
  if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2823
3121
  var documentElement = document.documentElement;
2824
3122
 
@@ -2840,7 +3138,7 @@ var others = {
2840
3138
  }
2841
3139
  },
2842
3140
  exitFullscreen: function exitFullscreen() {
2843
- var document = this.element.ownerDocument;
3141
+ var document = this.ownerDocument;
2844
3142
  if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2845
3143
  // Document.exitFullscreen()
2846
3144
  if (document.exitFullscreen) {
@@ -2876,7 +3174,38 @@ var others = {
2876
3174
 
2877
3175
  // Zoom the current image
2878
3176
  case ACTION_ZOOM:
2879
- this.zoom(getMaxZoomRatio(pointers), false, null, event);
3177
+ if (options.zoomable && options.zoomOnTouch) {
3178
+ var zoomRatio = getMaxZoomRatio(pointers);
3179
+ if (zoomRatio !== 0) {
3180
+ this.zoom(zoomRatio, false, null, event);
3181
+ }
3182
+ }
3183
+ break;
3184
+
3185
+ // Rotate the current image
3186
+ case ACTION_ROTATE:
3187
+ if (options.rotatable && options.rotateOnTouch) {
3188
+ var rotateDegree = getMaxRotateDegree(pointers);
3189
+ if (rotateDegree !== 0) {
3190
+ this.rotate(rotateDegree, event);
3191
+ }
3192
+ }
3193
+ break;
3194
+
3195
+ // Transform the current image
3196
+ case ACTION_TRANSFORM:
3197
+ if (options.zoomable && options.zoomOnTouch) {
3198
+ var _zoomRatio = getMaxZoomRatio(pointers);
3199
+ if (_zoomRatio !== 0) {
3200
+ this.zoom(_zoomRatio, false, null, event);
3201
+ }
3202
+ }
3203
+ if (options.rotatable && options.rotateOnTouch) {
3204
+ var _rotateDegree = getMaxRotateDegree(pointers);
3205
+ if (_rotateDegree !== 0) {
3206
+ this.rotate(_rotateDegree, event);
3207
+ }
3208
+ }
2880
3209
  break;
2881
3210
  case ACTION_SWITCH:
2882
3211
  {
@@ -2918,16 +3247,17 @@ var getUniqueID = function (id) {
2918
3247
  var Viewer = /*#__PURE__*/function () {
2919
3248
  /**
2920
3249
  * Create a new Viewer.
2921
- * @param {Element} element - The target element for viewing.
3250
+ * @param {Element} element - The target image, or a container of images, to view.
2922
3251
  * @param {Object} [options={}] - The configuration options.
2923
3252
  */
2924
3253
  function Viewer(element) {
2925
3254
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2926
3255
  _classCallCheck(this, Viewer);
2927
- if (!element || element.nodeType !== 1) {
3256
+ if (!element || element.nodeType !== 1 && element.nodeType !== 11) {
2928
3257
  throw new Error('The first argument is required and must be an element.');
2929
3258
  }
2930
3259
  this.element = element;
3260
+ this.ownerDocument = element.ownerDocument || element.host.ownerDocument;
2931
3261
  this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
2932
3262
  this.action = false;
2933
3263
  this.fading = false;
@@ -2989,7 +3319,7 @@ var Viewer = /*#__PURE__*/function () {
2989
3319
  this.initBody();
2990
3320
 
2991
3321
  // Override `transition` option if it is not supported
2992
- if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
3322
+ if (isUndefined(this.ownerDocument.createElement(NAMESPACE).style.transition)) {
2993
3323
  options.transition = false;
2994
3324
  }
2995
3325
  if (options.inline) {
@@ -3043,12 +3373,22 @@ var Viewer = /*#__PURE__*/function () {
3043
3373
  }
3044
3374
  });
3045
3375
  } else {
3046
- addListener(element, EVENT_CLICK, this.onStart = function (_ref) {
3376
+ addListener(element, EVENT_CLICK, this.onElementClick = function (_ref) {
3047
3377
  var target = _ref.target;
3048
3378
  if (target.localName === 'img' && (!isFunction(options.filter) || options.filter.call(_this, target))) {
3049
3379
  _this.view(_this.images.indexOf(target));
3050
3380
  }
3051
3381
  });
3382
+ addListener(element, EVENT_KEY_DOWN, this.onElementKeyDown = function (event) {
3383
+ var target = event.target,
3384
+ key = event.key;
3385
+ if (target.localName === 'img' && (key === 'Enter' || key === ' ')) {
3386
+ event.preventDefault();
3387
+ if (!isFunction(options.filter) || options.filter.call(_this, target)) {
3388
+ _this.view(_this.images.indexOf(target));
3389
+ }
3390
+ }
3391
+ });
3052
3392
  }
3053
3393
  }
3054
3394
  }, {
@@ -3066,6 +3406,7 @@ var Viewer = /*#__PURE__*/function () {
3066
3406
  var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
3067
3407
  var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
3068
3408
  var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
3409
+ var navigation = viewer.querySelector(".".concat(NAMESPACE, "-navigation"));
3069
3410
  var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
3070
3411
  var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
3071
3412
  this.parent = parent;
@@ -3073,19 +3414,52 @@ var Viewer = /*#__PURE__*/function () {
3073
3414
  this.title = title;
3074
3415
  this.toolbar = toolbar;
3075
3416
  this.navbar = navbar;
3417
+ this.navigation = navigation;
3076
3418
  this.button = button;
3077
3419
  this.canvas = canvas;
3078
3420
  this.footer = viewer.querySelector(".".concat(NAMESPACE, "-footer"));
3421
+ this.magnifier = viewer.querySelector(".".concat(NAMESPACE, "-magnifier"));
3422
+ this.magnifierImage = viewer.querySelector(".".concat(NAMESPACE, "-magnifier-image"));
3079
3423
  this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
3080
3424
  this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
3081
3425
  this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
3082
3426
  viewer.id = "".concat(NAMESPACE).concat(this.id);
3083
3427
  title.id = "".concat(NAMESPACE, "Title").concat(this.id);
3084
3428
  addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
3085
- addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
3429
+ var navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
3430
+ var navbarShow = options.navbar;
3431
+ var navbarSize = !isUndefined(navbarOptions.size) ? navbarOptions.size : options.navbar;
3432
+ if (isPlainObject(options.navbar)) {
3433
+ navbarShow = !isUndefined(navbarOptions.show) ? navbarOptions.show : true;
3434
+ }
3435
+ addClass(navbar, !navbarShow ? CLASS_HIDE : getResponsiveClass(navbarShow));
3436
+ if (['small', 'medium', 'large'].indexOf(navbarSize) !== -1) {
3437
+ addClass(navbar, "".concat(NAMESPACE, "-").concat(navbarSize));
3438
+ }
3439
+ if (isPlainObject(options.navigation)) {
3440
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3441
+ var name = getData(item, DATA_ACTION);
3442
+ var value = options.navigation[name];
3443
+ var deep = isPlainObject(value);
3444
+ var show = deep && !isUndefined(value.show) ? value.show : value;
3445
+ var size = deep && !isUndefined(value.size) ? value.size : value;
3446
+ toggleClass(item, CLASS_HIDE, !show);
3447
+ if (isNumber(show)) {
3448
+ addClass(item, getResponsiveClass(show));
3449
+ }
3450
+ if (['small', 'large'].indexOf(size) !== -1) {
3451
+ addClass(item, "".concat(NAMESPACE, "-").concat(size));
3452
+ }
3453
+ });
3454
+ } else {
3455
+ addClass(navigation, !options.navigation ? CLASS_HIDE : getResponsiveClass(options.navigation));
3456
+ }
3086
3457
  toggleClass(button, CLASS_HIDE, !options.button);
3087
3458
  if (options.keyboard) {
3088
3459
  button.setAttribute('tabindex', 0);
3460
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3461
+ item.setAttribute('tabindex', 0);
3462
+ });
3089
3463
  }
3090
3464
  if (options.backdrop) {
3091
3465
  addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
@@ -3171,7 +3545,7 @@ var Viewer = /*#__PURE__*/function () {
3171
3545
  });
3172
3546
  var container = options.container;
3173
3547
  if (isString(container)) {
3174
- container = element.ownerDocument.querySelector(container);
3548
+ container = this.ownerDocument.querySelector(container);
3175
3549
  }
3176
3550
  if (!container) {
3177
3551
  container = this.body;
@@ -3199,14 +3573,15 @@ var Viewer = /*#__PURE__*/function () {
3199
3573
  }
3200
3574
 
3201
3575
  /**
3202
- * Get the no conflict viewer class.
3203
- * @returns {Viewer} The viewer class.
3576
+ * Create a new Viewer instance.
3577
+ * @param {Element} element - The target image, or a container of images, to view.
3578
+ * @param {Object} [options={}] - The configuration options.
3579
+ * @returns {Viewer} A new Viewer instance.
3204
3580
  */
3205
3581
  }], [{
3206
- key: "noConflict",
3207
- value: function noConflict() {
3208
- window.Viewer = AnotherViewer;
3209
- return Viewer;
3582
+ key: "create",
3583
+ value: function create(element, options) {
3584
+ return new Viewer(element, options);
3210
3585
  }
3211
3586
 
3212
3587
  /**
@@ -3218,6 +3593,17 @@ var Viewer = /*#__PURE__*/function () {
3218
3593
  value: function setDefaults(options) {
3219
3594
  assign(DEFAULTS, isPlainObject(options) && options);
3220
3595
  }
3596
+
3597
+ /**
3598
+ * Get the no conflict viewer class.
3599
+ * @returns {Viewer} The viewer class.
3600
+ */
3601
+ }, {
3602
+ key: "noConflict",
3603
+ value: function noConflict() {
3604
+ window.Viewer = AnotherViewer;
3605
+ return Viewer;
3606
+ }
3221
3607
  }]);
3222
3608
  }();
3223
3609
  assign(Viewer.prototype, render, events, handlers, methods, others);