viewerjs 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * Viewer.js v1.12.0
2
+ * Viewer.js v1.13.0
3
3
  * https://fengyuanchen.github.io/viewerjs
4
4
  *
5
5
  * Copyright 2015-present Chen Fengyuan
6
6
  * Released under the MIT license
7
7
  *
8
- * Date: 2026-08-22T13:00:39.652Z
8
+ * Date: 2026-09-06T10:39:44.170Z
9
9
  */
10
10
 
11
11
  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.
@@ -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,13 +1785,14 @@ 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) {
@@ -1609,6 +1841,10 @@ var handlers = {
1609
1841
  this.initContainer();
1610
1842
  this.initViewer();
1611
1843
  this.renderViewer();
1844
+ var navbarOptions = isPlainObject(this.options.navbar) ? this.options.navbar : {};
1845
+ if (isUndefined(navbarOptions.visibleItemCount)) {
1846
+ this.initList(this.index);
1847
+ }
1612
1848
  this.renderList();
1613
1849
  if (this.viewed) {
1614
1850
  this.initImage(function () {
@@ -1634,6 +1870,9 @@ var handlers = {
1634
1870
  return;
1635
1871
  }
1636
1872
  event.preventDefault();
1873
+ if (this.gesturing) {
1874
+ return;
1875
+ }
1637
1876
 
1638
1877
  // Limit wheel speed to prevent zoom too fast
1639
1878
  if (this.wheeling) {
@@ -1653,6 +1892,43 @@ var handlers = {
1653
1892
  delta = event.detail > 0 ? 1 : -1;
1654
1893
  }
1655
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
+ }
1656
1932
  }
1657
1933
  };
1658
1934
 
@@ -1694,7 +1970,7 @@ var methods = {
1694
1970
  viewer.setAttribute('aria-labelledby', this.title.id);
1695
1971
  viewer.setAttribute('aria-modal', true);
1696
1972
  viewer.removeAttribute('aria-hidden');
1697
- if (options.transition && !immediate) {
1973
+ if (isTransitionEnabled(options, 'show') && !immediate) {
1698
1974
  var shown = this.shown.bind(this);
1699
1975
  this.transitioning = {
1700
1976
  abort: function abort() {
@@ -1734,7 +2010,7 @@ var methods = {
1734
2010
  once: true
1735
2011
  });
1736
2012
  }
1737
- if (dispatchEvent(element, EVENT_HIDE) === false) {
2013
+ if (dispatchEvent(element, EVENT_HIDE) === false || this.destroyed) {
1738
2014
  return this;
1739
2015
  }
1740
2016
  if (this.showing) {
@@ -1752,7 +2028,7 @@ var methods = {
1752
2028
  removeClass(viewer, CLASS_IN);
1753
2029
  _this.hidden();
1754
2030
  };
1755
- if (options.transition && !immediate) {
2031
+ if (isTransitionEnabled(options, 'hide') && !immediate) {
1756
2032
  var _onViewerTransitionEnd = function onViewerTransitionEnd(event) {
1757
2033
  // Ignore all propagating `transitionend` events (#275).
1758
2034
  if (event && event.target === viewer) {
@@ -1802,8 +2078,9 @@ var methods = {
1802
2078
  view: function view() {
1803
2079
  var _this2 = this;
1804
2080
  var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.initialViewIndex;
2081
+ var previousIndex = this.index;
1805
2082
  index = Number(index) || 0;
1806
- 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) {
1807
2084
  return this;
1808
2085
  }
1809
2086
  if (!this.isShown) {
@@ -1817,17 +2094,16 @@ var methods = {
1817
2094
  options = this.options,
1818
2095
  title = this.title,
1819
2096
  canvas = this.canvas;
1820
- var item = this.items[index];
2097
+ var item = this.getItem(index);
2098
+ if (!item) {
2099
+ this.initList(index);
2100
+ item = this.getItem(index);
2101
+ }
1821
2102
  var img = item.querySelector('img');
1822
2103
  var url = getData(img, 'originalUrl');
1823
2104
  var alt = img.getAttribute('alt');
1824
2105
  var image = document.createElement('img');
1825
- forEach(options.inheritedAttributes, function (name) {
1826
- var value = img.getAttribute(name);
1827
- if (value !== null) {
1828
- image.setAttribute(name, value);
1829
- }
1830
- });
2106
+ inheritAttributes(image, img, options.inheritedAttributes);
1831
2107
  image.src = url;
1832
2108
  image.alt = alt;
1833
2109
  if (isFunction(options.view)) {
@@ -1842,7 +2118,8 @@ var methods = {
1842
2118
  }) === false || !this.isShown || this.hiding || this.played) {
1843
2119
  return this;
1844
2120
  }
1845
- var activeItem = this.items[this.index];
2121
+ this.hideMagnifier();
2122
+ var activeItem = this.getItem(previousIndex);
1846
2123
  if (activeItem) {
1847
2124
  removeClass(activeItem, CLASS_ACTIVE);
1848
2125
  activeItem.removeAttribute('aria-selected');
@@ -1874,6 +2151,25 @@ var methods = {
1874
2151
  var imageData = _this2.imageData;
1875
2152
  var render = Array.isArray(options.title) ? options.title[1] : options.title;
1876
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
+ }
1877
2173
  };
1878
2174
  var onLoad;
1879
2175
  var onError;
@@ -2025,6 +2321,7 @@ var methods = {
2025
2321
  imageData.y = y;
2026
2322
  imageData.left = x;
2027
2323
  imageData.top = y;
2324
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'move'));
2028
2325
  this.moving = true;
2029
2326
  this.renderImage(function () {
2030
2327
  _this3.moving = false;
@@ -2081,6 +2378,7 @@ var methods = {
2081
2378
  return this;
2082
2379
  }
2083
2380
  imageData.rotate = degree;
2381
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'rotate'));
2084
2382
  this.rotating = true;
2085
2383
  this.renderImage(function () {
2086
2384
  _this4.rotating = false;
@@ -2161,6 +2459,7 @@ var methods = {
2161
2459
  }
2162
2460
  imageData.scaleX = scaleX;
2163
2461
  imageData.scaleY = scaleY;
2462
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'scale'));
2164
2463
  this.scaling = true;
2165
2464
  this.renderImage(function () {
2166
2465
  _this5.scaling = false;
@@ -2232,8 +2531,8 @@ var methods = {
2232
2531
  ratio = Math.max(0, ratio);
2233
2532
  if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
2234
2533
  if (!_zoomable) {
2235
- var minZoomRatio = Math.max(0.01, options.minZoomRatio);
2236
- 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);
2237
2536
  ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
2238
2537
  }
2239
2538
  if (_originalEvent) {
@@ -2294,6 +2593,7 @@ var methods = {
2294
2593
  imageData.height = newHeight;
2295
2594
  imageData.oldRatio = oldRatio;
2296
2595
  imageData.ratio = ratio;
2596
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'zoom'));
2297
2597
  this.renderImage(function () {
2298
2598
  _this6.zooming = false;
2299
2599
  if (isFunction(options.zoomed)) {
@@ -2347,16 +2647,15 @@ var methods = {
2347
2647
  this.requestFullscreen(fullscreen);
2348
2648
  }
2349
2649
  addClass(player, CLASS_SHOW);
2350
- forEach(this.items, function (item, i) {
2351
- var img = item.querySelector('img');
2650
+ forEach(this.images, function (originalImage, i) {
2352
2651
  var image = document.createElement('img');
2353
- image.src = getData(img, 'originalUrl');
2354
- image.alt = img.getAttribute('alt');
2355
- image.referrerPolicy = img.referrerPolicy;
2652
+ image.src = _this7.getImageURL(originalImage) || originalImage.src;
2653
+ image.alt = originalImage.alt || '';
2654
+ image.referrerPolicy = originalImage.referrerPolicy;
2356
2655
  total += 1;
2357
2656
  addClass(image, CLASS_FADE);
2358
- toggleClass(image, CLASS_TRANSITION, options.transition);
2359
- if (hasClass(item, CLASS_ACTIVE)) {
2657
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'play'));
2658
+ if (i === _this7.index) {
2360
2659
  addClass(image, CLASS_IN);
2361
2660
  index = i;
2362
2661
  }
@@ -2373,7 +2672,7 @@ var methods = {
2373
2672
  index -= 1;
2374
2673
  index = index >= 0 ? index : total - 1;
2375
2674
  addClass(list[index], CLASS_IN);
2376
- _this7.playing.timeout = setTimeout(_prev, options.interval);
2675
+ _this7.playing.timeout = options.autoplay ? setTimeout(_prev, options.interval) : null;
2377
2676
  };
2378
2677
  var _next = function next() {
2379
2678
  clearTimeout(_this7.playing.timeout);
@@ -2381,13 +2680,13 @@ var methods = {
2381
2680
  index += 1;
2382
2681
  index = index < total ? index : 0;
2383
2682
  addClass(list[index], CLASS_IN);
2384
- _this7.playing.timeout = setTimeout(_next, options.interval);
2683
+ _this7.playing.timeout = options.autoplay ? setTimeout(_next, options.interval) : null;
2385
2684
  };
2386
2685
  if (total > 1) {
2387
2686
  this.playing = {
2388
2687
  prev: _prev,
2389
2688
  next: _next,
2390
- timeout: setTimeout(_next, options.interval)
2689
+ timeout: options.autoplay ? setTimeout(_next, options.interval) : null
2391
2690
  };
2392
2691
  }
2393
2692
  }
@@ -2434,11 +2733,9 @@ var methods = {
2434
2733
  this.fulled = true;
2435
2734
  this.open();
2436
2735
  addClass(this.button, CLASS_FULLSCREEN_EXIT);
2437
- if (options.transition) {
2438
- removeClass(list, CLASS_TRANSITION);
2439
- if (this.viewed) {
2440
- removeClass(image, CLASS_TRANSITION);
2441
- }
2736
+ removeClass(list, CLASS_TRANSITION);
2737
+ if (this.viewed) {
2738
+ removeClass(image, CLASS_TRANSITION);
2442
2739
  }
2443
2740
  addClass(viewer, CLASS_FIXED);
2444
2741
  viewer.setAttribute('role', 'dialog');
@@ -2456,14 +2753,7 @@ var methods = {
2456
2753
  this.renderList();
2457
2754
  if (this.viewed) {
2458
2755
  this.initImage(function () {
2459
- _this9.renderImage(function () {
2460
- if (options.transition) {
2461
- setTimeout(function () {
2462
- addClass(image, CLASS_TRANSITION);
2463
- addClass(list, CLASS_TRANSITION);
2464
- }, 0);
2465
- }
2466
- });
2756
+ _this9.renderImage();
2467
2757
  });
2468
2758
  }
2469
2759
  return this;
@@ -2481,11 +2771,9 @@ var methods = {
2481
2771
  this.fulled = false;
2482
2772
  this.close();
2483
2773
  removeClass(this.button, CLASS_FULLSCREEN_EXIT);
2484
- if (options.transition) {
2485
- removeClass(list, CLASS_TRANSITION);
2486
- if (this.viewed) {
2487
- removeClass(image, CLASS_TRANSITION);
2488
- }
2774
+ removeClass(list, CLASS_TRANSITION);
2775
+ if (this.viewed) {
2776
+ removeClass(image, CLASS_TRANSITION);
2489
2777
  }
2490
2778
  if (options.focus) {
2491
2779
  this.clearEnforceFocus();
@@ -2502,14 +2790,7 @@ var methods = {
2502
2790
  this.renderList();
2503
2791
  if (this.viewed) {
2504
2792
  this.initImage(function () {
2505
- _this0.renderImage(function () {
2506
- if (options.transition) {
2507
- setTimeout(function () {
2508
- addClass(image, CLASS_TRANSITION);
2509
- addClass(list, CLASS_TRANSITION);
2510
- }, 0);
2511
- }
2512
- });
2793
+ _this0.renderImage();
2513
2794
  });
2514
2795
  }
2515
2796
  return this;
@@ -2525,7 +2806,7 @@ var methods = {
2525
2806
  }
2526
2807
  tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
2527
2808
  if (!this.tooltipping) {
2528
- if (options.transition) {
2809
+ if (isTransitionEnabled(options, 'tooltip')) {
2529
2810
  if (this.fading) {
2530
2811
  dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
2531
2812
  }
@@ -2545,7 +2826,7 @@ var methods = {
2545
2826
  clearTimeout(this.tooltipping);
2546
2827
  }
2547
2828
  this.tooltipping = setTimeout(function () {
2548
- if (options.transition) {
2829
+ if (isTransitionEnabled(options, 'tooltip')) {
2549
2830
  addListener(tooltipBox, EVENT_TRANSITION_END, function () {
2550
2831
  removeClass(tooltipBox, CLASS_SHOW);
2551
2832
  removeClass(tooltipBox, CLASS_FADE);
@@ -2587,12 +2868,19 @@ var methods = {
2587
2868
  }
2588
2869
  return this;
2589
2870
  },
2590
- // Update viewer when images changed
2591
- 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) {
2592
2877
  var _this10 = this;
2593
2878
  var element = this.element,
2594
2879
  options = this.options,
2595
2880
  isImg = this.isImg;
2881
+ if (isPlainObject(updateOptions)) {
2882
+ assign(options, updateOptions);
2883
+ }
2596
2884
 
2597
2885
  // Destroy viewer if the target image was deleted
2598
2886
  if (isImg && !element.parentNode) {
@@ -2615,18 +2903,19 @@ var methods = {
2615
2903
  this.length = images.length;
2616
2904
  if (this.ready) {
2617
2905
  var changedIndexes = [];
2618
- forEach(this.items, function (item, i) {
2906
+ forEach(this.items, function (item) {
2619
2907
  var img = item.querySelector('img');
2620
- var image = images[i];
2908
+ var index = Number(getData(item, 'index'));
2909
+ var image = images[index];
2621
2910
  if (image && img) {
2622
2911
  if (image.src !== img.src
2623
2912
 
2624
2913
  // Title changed (#408)
2625
2914
  || image.alt !== img.alt) {
2626
- changedIndexes.push(i);
2915
+ changedIndexes.push(index);
2627
2916
  }
2628
2917
  } else {
2629
- changedIndexes.push(i);
2918
+ changedIndexes.push(index);
2630
2919
  }
2631
2920
  });
2632
2921
  setStyle(this.list, {
@@ -2641,7 +2930,7 @@ var methods = {
2641
2930
  this.viewed = false;
2642
2931
  this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
2643
2932
  } else {
2644
- var activeItem = this.items[this.index];
2933
+ var activeItem = this.getItem(this.index);
2645
2934
 
2646
2935
  // Reactivate the current viewing item after reset the list.
2647
2936
  addClass(activeItem, CLASS_ACTIVE);
@@ -2827,7 +3116,7 @@ var others = {
2827
3116
  }
2828
3117
  },
2829
3118
  requestFullscreen: function requestFullscreen(options) {
2830
- var document = this.element.ownerDocument;
3119
+ var document = this.ownerDocument;
2831
3120
  if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2832
3121
  var documentElement = document.documentElement;
2833
3122
 
@@ -2849,7 +3138,7 @@ var others = {
2849
3138
  }
2850
3139
  },
2851
3140
  exitFullscreen: function exitFullscreen() {
2852
- var document = this.element.ownerDocument;
3141
+ var document = this.ownerDocument;
2853
3142
  if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2854
3143
  // Document.exitFullscreen()
2855
3144
  if (document.exitFullscreen) {
@@ -2885,7 +3174,38 @@ var others = {
2885
3174
 
2886
3175
  // Zoom the current image
2887
3176
  case ACTION_ZOOM:
2888
- 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
+ }
2889
3209
  break;
2890
3210
  case ACTION_SWITCH:
2891
3211
  {
@@ -2927,16 +3247,17 @@ var getUniqueID = function (id) {
2927
3247
  var Viewer = /*#__PURE__*/function () {
2928
3248
  /**
2929
3249
  * Create a new Viewer.
2930
- * @param {Element} element - The target element for viewing.
3250
+ * @param {Element} element - The target image, or a container of images, to view.
2931
3251
  * @param {Object} [options={}] - The configuration options.
2932
3252
  */
2933
3253
  function Viewer(element) {
2934
3254
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2935
3255
  _classCallCheck(this, Viewer);
2936
- if (!element || element.nodeType !== 1) {
3256
+ if (!element || element.nodeType !== 1 && element.nodeType !== 11) {
2937
3257
  throw new Error('The first argument is required and must be an element.');
2938
3258
  }
2939
3259
  this.element = element;
3260
+ this.ownerDocument = element.ownerDocument || element.host.ownerDocument;
2940
3261
  this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
2941
3262
  this.action = false;
2942
3263
  this.fading = false;
@@ -2998,7 +3319,7 @@ var Viewer = /*#__PURE__*/function () {
2998
3319
  this.initBody();
2999
3320
 
3000
3321
  // Override `transition` option if it is not supported
3001
- if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
3322
+ if (isUndefined(this.ownerDocument.createElement(NAMESPACE).style.transition)) {
3002
3323
  options.transition = false;
3003
3324
  }
3004
3325
  if (options.inline) {
@@ -3085,6 +3406,7 @@ var Viewer = /*#__PURE__*/function () {
3085
3406
  var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
3086
3407
  var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
3087
3408
  var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
3409
+ var navigation = viewer.querySelector(".".concat(NAMESPACE, "-navigation"));
3088
3410
  var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
3089
3411
  var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
3090
3412
  this.parent = parent;
@@ -3092,19 +3414,52 @@ var Viewer = /*#__PURE__*/function () {
3092
3414
  this.title = title;
3093
3415
  this.toolbar = toolbar;
3094
3416
  this.navbar = navbar;
3417
+ this.navigation = navigation;
3095
3418
  this.button = button;
3096
3419
  this.canvas = canvas;
3097
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"));
3098
3423
  this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
3099
3424
  this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
3100
3425
  this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
3101
3426
  viewer.id = "".concat(NAMESPACE).concat(this.id);
3102
3427
  title.id = "".concat(NAMESPACE, "Title").concat(this.id);
3103
3428
  addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
3104
- 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
+ }
3105
3457
  toggleClass(button, CLASS_HIDE, !options.button);
3106
3458
  if (options.keyboard) {
3107
3459
  button.setAttribute('tabindex', 0);
3460
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3461
+ item.setAttribute('tabindex', 0);
3462
+ });
3108
3463
  }
3109
3464
  if (options.backdrop) {
3110
3465
  addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
@@ -3190,7 +3545,7 @@ var Viewer = /*#__PURE__*/function () {
3190
3545
  });
3191
3546
  var container = options.container;
3192
3547
  if (isString(container)) {
3193
- container = element.ownerDocument.querySelector(container);
3548
+ container = this.ownerDocument.querySelector(container);
3194
3549
  }
3195
3550
  if (!container) {
3196
3551
  container = this.body;
@@ -3218,14 +3573,15 @@ var Viewer = /*#__PURE__*/function () {
3218
3573
  }
3219
3574
 
3220
3575
  /**
3221
- * Get the no conflict viewer class.
3222
- * @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.
3223
3580
  */
3224
3581
  }], [{
3225
- key: "noConflict",
3226
- value: function noConflict() {
3227
- window.Viewer = AnotherViewer;
3228
- return Viewer;
3582
+ key: "create",
3583
+ value: function create(element, options) {
3584
+ return new Viewer(element, options);
3229
3585
  }
3230
3586
 
3231
3587
  /**
@@ -3237,6 +3593,17 @@ var Viewer = /*#__PURE__*/function () {
3237
3593
  value: function setDefaults(options) {
3238
3594
  assign(DEFAULTS, isPlainObject(options) && options);
3239
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
+ }
3240
3607
  }]);
3241
3608
  }();
3242
3609
  assign(Viewer.prototype, render, events, handlers, methods, others);