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