viewerjs 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/viewer.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * Viewer.js v1.12.0
2
+ * Viewer.js v1.14.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-12T12:12:29.730Z
9
9
  */
10
10
 
11
11
  (function (global, factory) {
@@ -95,9 +95,14 @@
95
95
  button: true,
96
96
  /**
97
97
  * Show the navbar.
98
- * @type {boolean | number}
98
+ * @type {boolean | number | string | Object}
99
99
  */
100
100
  navbar: true,
101
+ /**
102
+ * Show the navigation buttons.
103
+ * @type {boolean | number | Object}
104
+ */
105
+ navigation: false,
101
106
  /**
102
107
  * Specify the visibility and the content of the title.
103
108
  * @type {boolean | number | Function | Array}
@@ -149,6 +154,11 @@
149
154
  * @type {boolean}
150
155
  */
151
156
  inline: false,
157
+ /**
158
+ * Enable to automatically play the images when playing.
159
+ * @type {boolean}
160
+ */
161
+ autoplay: true,
152
162
  /**
153
163
  * The amount of time to delay between automatically cycling an image when playing.
154
164
  * @type {number}
@@ -174,6 +184,11 @@
174
184
  * @type {boolean}
175
185
  */
176
186
  loop: true,
187
+ /**
188
+ * Enable to preload the next or previous image before viewing it.
189
+ * @type {boolean}
190
+ */
191
+ preload: true,
177
192
  /**
178
193
  * Min width of the viewer in inline mode.
179
194
  * @type {number}
@@ -189,11 +204,26 @@
189
204
  * @type {boolean}
190
205
  */
191
206
  movable: true,
207
+ /**
208
+ * Show a magnifier when hovering over the image in fullscreen mode.
209
+ * @type {boolean | Object}
210
+ */
211
+ magnifier: false,
192
212
  /**
193
213
  * Enable to rotate the image.
194
214
  * @type {boolean}
195
215
  */
196
216
  rotatable: true,
217
+ /**
218
+ * Enable to rotate the current image by gesture.
219
+ * @type {boolean}
220
+ */
221
+ rotateOnGesture: true,
222
+ /**
223
+ * Enable to rotate the current image by dragging on the touch screen.
224
+ * @type {boolean}
225
+ */
226
+ rotateOnTouch: true,
197
227
  /**
198
228
  * Enable to scale the image.
199
229
  * @type {boolean}
@@ -210,15 +240,33 @@
210
240
  */
211
241
  zoomOnTouch: true,
212
242
  /**
213
- * Enable to zoom the image by wheeling mouse.
243
+ * Enable to zoom the current image by gesture.
214
244
  * @type {boolean}
215
245
  */
246
+ zoomOnGesture: true,
247
+ /**
248
+ * Enable to zoom the image by wheeling mouse.
249
+ * Set to a modifier key name (`ctrl`, `shift`, `alt`, `meta`), or a
250
+ * combination joined with `+` (e.g. `ctrl+shift`), to only zoom when
251
+ * the given modifier key(s) are held down while wheeling.
252
+ * @type {boolean | string}
253
+ */
216
254
  zoomOnWheel: true,
217
255
  /**
218
256
  * Enable to slide to the next or previous image by swiping on the touch screen.
219
257
  * @type {boolean}
220
258
  */
221
259
  slideOnTouch: true,
260
+ /**
261
+ * Enable to slide to the next or previous image by wheeling mouse.
262
+ * Set to a modifier key name (`ctrl`, `shift`, `alt`, `meta`), or a
263
+ * combination joined with `+` (e.g. `ctrl+shift`), to only slide when
264
+ * the given modifier key(s) are held down while wheeling.
265
+ * Takes effect over the navbar, and also over the rest of the viewer
266
+ * when `zoomOnWheel` doesn't take effect for the wheel event.
267
+ * @type {boolean | string}
268
+ */
269
+ slideOnWheel: true,
222
270
  /**
223
271
  * Indicate if toggle the image size between its natural size
224
272
  * and initial size when double click on the image or not.
@@ -232,7 +280,7 @@
232
280
  tooltip: true,
233
281
  /**
234
282
  * Enable CSS3 Transition for some special elements.
235
- * @type {boolean}
283
+ * @type {boolean | Object}
236
284
  */
237
285
  transition: true,
238
286
  /**
@@ -252,12 +300,12 @@
252
300
  zoomRatio: 0.1,
253
301
  /**
254
302
  * Define the min ratio of the image when zoom out.
255
- * @type {number}
303
+ * @type {number | Function}
256
304
  */
257
305
  minZoomRatio: 0.01,
258
306
  /**
259
307
  * Define the max ratio of the image when zoom in.
260
- * @type {number}
308
+ * @type {number | Function}
261
309
  */
262
310
  maxZoomRatio: 100,
263
311
  /**
@@ -285,10 +333,11 @@
285
333
  zoom: null,
286
334
  zoomed: null,
287
335
  play: null,
336
+ playing: null,
288
337
  stop: null
289
338
  };
290
339
 
291
- var TEMPLATE = '<div class="viewer-container" tabindex="-1" touch-action="none">' + '<div class="viewer-canvas"></div>' + '<div class="viewer-footer">' + '<div class="viewer-title"></div>' + '<div class="viewer-toolbar"></div>' + '<div class="viewer-navbar">' + '<ul class="viewer-list" role="navigation"></ul>' + '</div>' + '</div>' + '<div class="viewer-tooltip" role="alert" aria-hidden="true"></div>' + '<div class="viewer-button" data-viewer-action="mix" role="button"></div>' + '<div class="viewer-player"></div>' + '</div>';
340
+ 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" aria-hidden="true">' + '<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" aria-hidden="true">' + '<div class="viewer-title" aria-hidden="true"></div>' + '<div class="viewer-toolbar" aria-hidden="true"></div>' + '<div class="viewer-navbar" aria-hidden="true">' + '<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" aria-hidden="true"></div>' + '<div class="viewer-player" aria-hidden="true"></div>' + '</div>';
292
341
 
293
342
  var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
294
343
  var WINDOW = IS_BROWSER ? window : {};
@@ -298,7 +347,9 @@
298
347
 
299
348
  // Actions
300
349
  var ACTION_MOVE = 'move';
350
+ var ACTION_ROTATE = 'rotate';
301
351
  var ACTION_SWITCH = 'switch';
352
+ var ACTION_TRANSFORM = 'transform';
302
353
  var ACTION_ZOOM = 'zoom';
303
354
 
304
355
  // Classes
@@ -332,11 +383,14 @@
332
383
  var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';
333
384
  var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';
334
385
  var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;
386
+ var EVENT_POINTER_ENTER = HAS_POINTER_EVENT ? 'pointerenter' : 'mouseenter';
387
+ var EVENT_POINTER_LEAVE = HAS_POINTER_EVENT ? 'pointerleave' : 'mouseleave';
335
388
  var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;
336
389
  var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;
337
390
  var EVENT_RESIZE = 'resize';
338
391
  var EVENT_TRANSITION_END = 'transitionend';
339
392
  var EVENT_WHEEL = 'wheel';
393
+ var EVENT_GESTURE = 'gesturestart gesturechange gestureend';
340
394
 
341
395
  // Custom events
342
396
  var EVENT_READY = 'ready';
@@ -355,6 +409,7 @@
355
409
  var EVENT_ZOOM = 'zoom';
356
410
  var EVENT_ZOOMED = 'zoomed';
357
411
  var EVENT_PLAY = 'play';
412
+ var EVENT_PLAYING = 'playing';
358
413
  var EVENT_STOP = 'stop';
359
414
 
360
415
  // Data keys
@@ -374,6 +429,30 @@
374
429
  function isString(value) {
375
430
  return typeof value === 'string';
376
431
  }
432
+ var MODIFIER_KEY_PROPERTIES = {
433
+ ctrl: 'ctrlKey',
434
+ shift: 'shiftKey',
435
+ alt: 'altKey',
436
+ meta: 'metaKey'
437
+ };
438
+
439
+ /**
440
+ * Check if a wheel option (`zoomOnWheel` or `slideOnWheel`) takes effect for the given wheel event.
441
+ * @param {boolean | string} option - The option value.
442
+ * @param {WheelEvent} event - The wheel event.
443
+ * @returns {boolean} Returns `true` if the option takes effect for the event, else `false`.
444
+ */
445
+ function isWheelActionEnabled(option, event) {
446
+ if (!option) {
447
+ return false;
448
+ }
449
+ if (option === true) {
450
+ return true;
451
+ }
452
+ return isString(option) && option.split('+').every(function (key) {
453
+ return event[MODIFIER_KEY_PROPERTIES[key.trim().toLowerCase()]];
454
+ });
455
+ }
377
456
 
378
457
  /**
379
458
  * Check if the given value is not a number.
@@ -460,6 +539,32 @@
460
539
  return data;
461
540
  }
462
541
 
542
+ /**
543
+ * Inherit attributes from the original image.
544
+ * @param {Element} image - The target image.
545
+ * @param {Element} originalImage - The original image.
546
+ * @param {Array} inheritedAttributes - The attributes to inherit.
547
+ */
548
+ function inheritAttributes(image, originalImage, inheritedAttributes) {
549
+ forEach(inheritedAttributes, function (name) {
550
+ var value = originalImage.getAttribute(name);
551
+ if (value !== null) {
552
+ image.setAttribute(name, value);
553
+ }
554
+ });
555
+ }
556
+
557
+ /**
558
+ * Check if transition is enabled for the given action.
559
+ * @param {Object} options - The viewer options.
560
+ * @param {string} action - The transition action.
561
+ * @returns {boolean} Returns `true` if transition is enabled.
562
+ */
563
+ function isTransitionEnabled(options, action) {
564
+ var transition = options.transition;
565
+ return transition && transition[action] !== false;
566
+ }
567
+
463
568
  /**
464
569
  * Extend the given object.
465
570
  * @param {*} obj - The object to be extended.
@@ -789,10 +894,8 @@
789
894
  if (isNumber(rotate) && rotate !== 0) {
790
895
  values.push("rotate(".concat(rotate, "deg)"));
791
896
  }
792
- if (isNumber(scaleX) && scaleX !== 1) {
897
+ if (isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1)) {
793
898
  values.push("scaleX(".concat(scaleX, ")"));
794
- }
795
- if (isNumber(scaleY) && scaleY !== 1) {
796
899
  values.push("scaleY(".concat(scaleY, ")"));
797
900
  }
798
901
  var transform = values.length ? values.join(' ') : 'none';
@@ -838,12 +941,7 @@
838
941
  body.removeChild(newImage);
839
942
  }
840
943
  };
841
- forEach(options.inheritedAttributes, function (name) {
842
- var value = image.getAttribute(name);
843
- if (value !== null) {
844
- newImage.setAttribute(name, value);
845
- }
846
- });
944
+ inheritAttributes(newImage, image, options.inheritedAttributes);
847
945
  newImage.src = image.src;
848
946
 
849
947
  // iOS Safari will convert the image automatically
@@ -900,6 +998,34 @@
900
998
  return ratios[0];
901
999
  }
902
1000
 
1001
+ /**
1002
+ * Get the max rotation degree of a group of pointers.
1003
+ * @param {Object} pointers - The target pointers.
1004
+ * @returns {number} The result degree.
1005
+ */
1006
+ function getMaxRotateDegree(pointers) {
1007
+ var pointers2 = _objectSpread2({}, pointers);
1008
+ var degrees = [];
1009
+ forEach(pointers, function (pointer, pointerId) {
1010
+ delete pointers2[pointerId];
1011
+ forEach(pointers2, function (pointer2) {
1012
+ var start = Math.atan2(pointer2.startY - pointer.startY, pointer2.startX - pointer.startX);
1013
+ var end = Math.atan2(pointer2.endY - pointer.endY, pointer2.endX - pointer.endX);
1014
+ var radians = end - start;
1015
+ if (radians > Math.PI) {
1016
+ radians -= Math.PI * 2;
1017
+ } else if (radians < -Math.PI) {
1018
+ radians += Math.PI * 2;
1019
+ }
1020
+ degrees.push(radians * 180 / Math.PI);
1021
+ });
1022
+ });
1023
+ degrees.sort(function (a, b) {
1024
+ return Math.abs(b) - Math.abs(a);
1025
+ });
1026
+ return degrees[0] || 0;
1027
+ }
1028
+
903
1029
  /**
904
1030
  * Get a pointer from an event object.
905
1031
  * @param {Object} event - The target event object.
@@ -952,7 +1078,7 @@
952
1078
  this.renderViewer();
953
1079
  },
954
1080
  initBody: function initBody() {
955
- var ownerDocument = this.element.ownerDocument;
1081
+ var ownerDocument = this.ownerDocument;
956
1082
  var body = ownerDocument.body || ownerDocument.documentElement;
957
1083
  this.body = body;
958
1084
  this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
@@ -988,26 +1114,37 @@
988
1114
  },
989
1115
  initList: function initList() {
990
1116
  var _this = this;
1117
+ var viewIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.index;
991
1118
  var element = this.element,
992
1119
  options = this.options,
993
1120
  list = this.list;
994
1121
  var items = [];
1122
+ var navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
1123
+ var probe = document.createElement('li');
1124
+ if (!this.containerData) {
1125
+ this.initContainer();
1126
+ }
1127
+ list.appendChild(probe);
1128
+ var itemWidth = probe.offsetWidth + parseInt(window.getComputedStyle(probe).marginLeft, 10);
1129
+ list.removeChild(probe);
1130
+ var visibleItemCount = isNumber(navbarOptions.visibleItemCount) ? Math.floor(navbarOptions.visibleItemCount) : Math.floor(this.containerData.width / itemWidth);
1131
+ visibleItemCount = Math.min(visibleItemCount, this.length);
1132
+ var start = visibleItemCount > 0 ? Math.min(Math.max(0, viewIndex - Math.floor(visibleItemCount / 2)), Math.max(0, this.length - visibleItemCount)) : 0;
1133
+ var end = visibleItemCount > 0 ? Math.min(this.length, start + visibleItemCount) : this.length;
995
1134
 
996
1135
  // initList may be called in this.update, so should keep idempotent
997
1136
  list.innerHTML = '';
998
1137
  forEach(this.images, function (image, index) {
1138
+ if (visibleItemCount > 0 && (index < start || index >= end)) {
1139
+ return;
1140
+ }
999
1141
  var src = image.src;
1000
1142
  var alt = image.alt || getImageNameFromURL(src);
1001
1143
  var url = _this.getImageURL(image);
1002
1144
  if (src || url) {
1003
1145
  var item = document.createElement('li');
1004
1146
  var img = document.createElement('img');
1005
- forEach(options.inheritedAttributes, function (name) {
1006
- var value = image.getAttribute(name);
1007
- if (value !== null) {
1008
- img.setAttribute(name, value);
1009
- }
1010
- });
1147
+ inheritAttributes(img, image, options.inheritedAttributes);
1011
1148
  if (options.navbar) {
1012
1149
  img.src = src || url;
1013
1150
  }
@@ -1025,6 +1162,13 @@
1025
1162
  }
1026
1163
  });
1027
1164
  this.items = items;
1165
+ if (this.viewed) {
1166
+ var activeItem = this.getItem(this.index);
1167
+ if (activeItem) {
1168
+ addClass(activeItem, CLASS_ACTIVE);
1169
+ activeItem.setAttribute('aria-selected', true);
1170
+ }
1171
+ }
1028
1172
  forEach(items, function (item) {
1029
1173
  var image = item.firstElementChild;
1030
1174
  var onLoad;
@@ -1051,7 +1195,7 @@
1051
1195
  once: true
1052
1196
  });
1053
1197
  });
1054
- if (options.transition) {
1198
+ if (isTransitionEnabled(options, 'view')) {
1055
1199
  addListener(element, EVENT_VIEWED, function () {
1056
1200
  addClass(list, CLASS_TRANSITION);
1057
1201
  }, {
@@ -1059,9 +1203,20 @@
1059
1203
  });
1060
1204
  }
1061
1205
  },
1206
+ getItem: function getItem(index) {
1207
+ var item;
1208
+ forEach(this.items, function (candidate) {
1209
+ if (Number(getData(candidate, 'index')) === index) {
1210
+ item = candidate;
1211
+ return false;
1212
+ }
1213
+ return true;
1214
+ });
1215
+ return item;
1216
+ },
1062
1217
  renderList: function renderList() {
1063
1218
  var index = this.index;
1064
- var item = this.items[index];
1219
+ var item = this.getItem(index);
1065
1220
  if (!item) {
1066
1221
  return;
1067
1222
  }
@@ -1072,9 +1227,9 @@
1072
1227
 
1073
1228
  // Place the active item in the center of the screen
1074
1229
  setStyle(this.list, assign({
1075
- width: outerWidth * this.length - gutter
1230
+ width: outerWidth * this.items.length - gutter
1076
1231
  }, getTransforms({
1077
- translateX: (this.viewerData.width - offsetWidth) / 2 - outerWidth * index
1232
+ translateX: (this.viewerData.width - offsetWidth) / 2 - item.offsetLeft
1078
1233
  })));
1079
1234
  },
1080
1235
  resetList: function resetList() {
@@ -1160,8 +1315,23 @@
1160
1315
  marginLeft: imageData.x,
1161
1316
  marginTop: imageData.y
1162
1317
  }, getTransforms(imageData)));
1318
+ if (this.magnifierPoint) {
1319
+ this.renderMagnifier();
1320
+ }
1163
1321
  if (done) {
1164
- if ((this.viewing || this.moving || this.rotating || this.scaling || this.zooming) && this.options.transition && hasClass(image, CLASS_TRANSITION)) {
1322
+ var action = false;
1323
+ if (this.viewing) {
1324
+ action = 'view';
1325
+ } else if (this.moving) {
1326
+ action = 'move';
1327
+ } else if (this.rotating) {
1328
+ action = 'rotate';
1329
+ } else if (this.scaling) {
1330
+ action = 'scale';
1331
+ } else if (this.zooming) {
1332
+ action = 'zoom';
1333
+ }
1334
+ if (action && isTransitionEnabled(this.options, action) && hasClass(image, CLASS_TRANSITION)) {
1165
1335
  var onTransitionEnd = function onTransitionEnd() {
1166
1336
  _this3.imageRendering = false;
1167
1337
  done();
@@ -1197,20 +1367,28 @@
1197
1367
  var options = this.options,
1198
1368
  viewer = this.viewer,
1199
1369
  canvas = this.canvas;
1200
- var document = this.element.ownerDocument;
1370
+ var document = this.ownerDocument;
1201
1371
  addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
1202
1372
  addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
1373
+ addListener(viewer, EVENT_POINTER_ENTER, this.onMagnifyEnter = this.magnify.bind(this));
1374
+ addListener(viewer, EVENT_POINTER_MOVE, this.onMagnify = this.magnify.bind(this));
1375
+ addListener(viewer, EVENT_POINTER_LEAVE, this.onMagnifierLeave = this.hideMagnifier.bind(this));
1203
1376
  addListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
1204
1377
  addListener(document, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
1205
1378
  addListener(document, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
1206
1379
  addListener(document, EVENT_KEY_DOWN, this.onKeyDown = this.keydown.bind(this));
1207
1380
  addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));
1208
- if (options.zoomable && options.zoomOnWheel) {
1381
+ if (options.zoomable && options.zoomOnWheel || options.slideOnWheel) {
1209
1382
  addListener(viewer, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {
1210
1383
  passive: false,
1211
1384
  capture: true
1212
1385
  });
1213
1386
  }
1387
+ if (options.zoomable && options.zoomOnGesture || options.rotatable && options.rotateOnGesture) {
1388
+ addListener(viewer, EVENT_GESTURE, this.onGesture = this.gesture.bind(this), {
1389
+ passive: false
1390
+ });
1391
+ }
1214
1392
  if (options.toggleOnDblclick) {
1215
1393
  addListener(canvas, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
1216
1394
  }
@@ -1219,20 +1397,28 @@
1219
1397
  var options = this.options,
1220
1398
  viewer = this.viewer,
1221
1399
  canvas = this.canvas;
1222
- var document = this.element.ownerDocument;
1400
+ var document = this.ownerDocument;
1223
1401
  removeListener(viewer, EVENT_CLICK, this.onClick);
1224
1402
  removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
1403
+ removeListener(viewer, EVENT_POINTER_ENTER, this.onMagnifyEnter);
1404
+ removeListener(viewer, EVENT_POINTER_MOVE, this.onMagnify);
1405
+ removeListener(viewer, EVENT_POINTER_LEAVE, this.onMagnifierLeave);
1225
1406
  removeListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown);
1226
1407
  removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove);
1227
1408
  removeListener(document, EVENT_POINTER_UP, this.onPointerUp);
1228
1409
  removeListener(document, EVENT_KEY_DOWN, this.onKeyDown);
1229
1410
  removeListener(window, EVENT_RESIZE, this.onResize);
1230
- if (options.zoomable && options.zoomOnWheel) {
1411
+ if (options.zoomable && options.zoomOnWheel || options.slideOnWheel) {
1231
1412
  removeListener(viewer, EVENT_WHEEL, this.onWheel, {
1232
1413
  passive: false,
1233
1414
  capture: true
1234
1415
  });
1235
1416
  }
1417
+ if (options.zoomable && options.zoomOnGesture || options.rotatable && options.rotateOnGesture) {
1418
+ removeListener(viewer, EVENT_GESTURE, this.onGesture, {
1419
+ passive: false
1420
+ });
1421
+ }
1236
1422
  if (options.toggleOnDblclick) {
1237
1423
  removeListener(canvas, EVENT_DBLCLICK, this.onDblclick);
1238
1424
  }
@@ -1254,6 +1440,7 @@
1254
1440
  if (IS_TOUCH_DEVICE && event.isTrusted && target === this.canvas) {
1255
1441
  clearTimeout(this.clickCanvasTimeout);
1256
1442
  }
1443
+ this.actionEvent = event;
1257
1444
  switch (action) {
1258
1445
  case 'mix':
1259
1446
  if (this.played) {
@@ -1324,7 +1511,8 @@
1324
1511
  }
1325
1512
 
1326
1513
  // XXX: No pageX/Y properties in custom event, fallback to the original event.
1327
- this.toggle(event.isTrusted ? event : event.detail && event.detail.originalEvent);
1514
+ this.actionEvent = event.isTrusted ? event : event.detail && event.detail.originalEvent;
1515
+ this.toggle();
1328
1516
  }
1329
1517
  },
1330
1518
  load: function load() {
@@ -1345,10 +1533,13 @@
1345
1533
  image.style.cssText = 'height:0;' + "margin-left:".concat(viewerData.width / 2, "px;") + "margin-top:".concat(viewerData.height / 2, "px;") + 'max-width:none!important;' + 'position:relative;' + 'width:0;';
1346
1534
  this.initImage(function () {
1347
1535
  toggleClass(image, CLASS_MOVE, options.movable);
1348
- toggleClass(image, CLASS_TRANSITION, options.transition);
1536
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'view'));
1349
1537
  _this.renderImage(function () {
1350
1538
  _this.viewed = true;
1351
1539
  _this.viewing = false;
1540
+ setTimeout(function () {
1541
+ toggleClass(image, CLASS_TRANSITION, options.transition);
1542
+ }, 300);
1352
1543
  if (isFunction(options.viewed)) {
1353
1544
  addListener(element, EVENT_VIEWED, options.viewed, {
1354
1545
  once: true
@@ -1357,10 +1548,12 @@
1357
1548
  dispatchEvent(element, EVENT_VIEWED, {
1358
1549
  originalImage: _this.images[index],
1359
1550
  index: index,
1360
- image: image
1551
+ image: image,
1552
+ originalEvent: _this.viewOriginalEvent || null
1361
1553
  }, {
1362
1554
  cancelable: false
1363
1555
  });
1556
+ _this.viewOriginalEvent = null;
1364
1557
  });
1365
1558
  });
1366
1559
  },
@@ -1400,6 +1593,7 @@
1400
1593
  return;
1401
1594
  }
1402
1595
  var keyCode = event.keyCode || event.which || event.charCode;
1596
+ this.actionEvent = event;
1403
1597
  switch (keyCode) {
1404
1598
  // Enter
1405
1599
  case 13:
@@ -1487,6 +1681,88 @@
1487
1681
  event.preventDefault();
1488
1682
  }
1489
1683
  },
1684
+ magnify: function magnify(event) {
1685
+ if (event.changedTouches || event.pointerType && event.pointerType !== 'mouse') {
1686
+ this.hideMagnifier();
1687
+ return;
1688
+ }
1689
+ this.magnifierPoint = {
1690
+ clientX: event.clientX,
1691
+ clientY: event.clientY
1692
+ };
1693
+ this.renderMagnifier();
1694
+ },
1695
+ renderMagnifier: function renderMagnifier() {
1696
+ var options = this.options,
1697
+ imageData = this.imageData,
1698
+ magnifier = this.magnifier,
1699
+ magnifierImage = this.magnifierImage,
1700
+ viewer = this.viewer;
1701
+ var config = isPlainObject(options.magnifier) ? options.magnifier : {};
1702
+ var point = this.magnifierPoint;
1703
+ if (!options.magnifier || !this.fulled || !this.viewed || !magnifier || !magnifierImage || !point) {
1704
+ this.hideMagnifier();
1705
+ return;
1706
+ }
1707
+ var size = Math.max(1, Number(config.size) || 100);
1708
+ var zoomRatio = Math.max(1, Number(config.zoomRatio) || 2);
1709
+ var opacity = Number(config.opacity);
1710
+ var rect = viewer.getBoundingClientRect();
1711
+ var x = point.clientX - rect.left;
1712
+ var y = point.clientY - rect.top;
1713
+ var imageURL = this.image.currentSrc || this.image.src;
1714
+ var imageRect = this.image.getBoundingClientRect();
1715
+ if (point.clientX < imageRect.left || point.clientX > imageRect.right || point.clientY < imageRect.top || point.clientY > imageRect.bottom) {
1716
+ this.hideMagnifier();
1717
+ return;
1718
+ }
1719
+ var scaleX = isNumber(imageData.scaleX) ? imageData.scaleX : 1;
1720
+ var scaleY = isNumber(imageData.scaleY) ? imageData.scaleY : 1;
1721
+ var rotate = (imageData.rotate || 0) * Math.PI / 180;
1722
+ var cos = Math.cos(rotate);
1723
+ var sin = Math.sin(rotate);
1724
+ var matrixA = cos * scaleX;
1725
+ var matrixB = sin * scaleX;
1726
+ var matrixC = -sin * scaleY;
1727
+ var matrixD = cos * scaleY;
1728
+ var determinant = scaleX * scaleY;
1729
+ if (determinant === 0) {
1730
+ this.hideMagnifier();
1731
+ return;
1732
+ }
1733
+ var centerX = imageData.x + imageData.width / 2;
1734
+ var centerY = imageData.y + imageData.height / 2;
1735
+ var localX = (matrixD * (x - centerX) - matrixC * (y - centerY)) / determinant;
1736
+ var localY = (-matrixB * (x - centerX) + matrixA * (y - centerY)) / determinant;
1737
+ var sourceX = localX + imageData.width / 2;
1738
+ var sourceY = localY + imageData.height / 2;
1739
+ var magnifiedWidth = imageData.width * zoomRatio;
1740
+ var magnifiedHeight = imageData.height * zoomRatio;
1741
+ var transformedX = matrixA * (sourceX * zoomRatio - magnifiedWidth / 2) + matrixC * (sourceY * zoomRatio - magnifiedHeight / 2);
1742
+ var transformedY = matrixB * (sourceX * zoomRatio - magnifiedWidth / 2) + matrixD * (sourceY * zoomRatio - magnifiedHeight / 2);
1743
+ this.magnifierSourcePoint = {
1744
+ x: sourceX,
1745
+ y: sourceY
1746
+ };
1747
+ magnifier.style.width = "".concat(size, "px");
1748
+ magnifier.style.height = "".concat(size, "px");
1749
+ magnifier.style.opacity = "".concat(Math.max(0, Math.min(1, isNumber(opacity) ? opacity : 1)));
1750
+ magnifierImage.src = imageURL;
1751
+ magnifierImage.style.width = "".concat(magnifiedWidth, "px");
1752
+ magnifierImage.style.height = "".concat(magnifiedHeight, "px");
1753
+ magnifierImage.style.left = "".concat(size / 2 - magnifiedWidth / 2 - transformedX, "px");
1754
+ magnifierImage.style.top = "".concat(size / 2 - magnifiedHeight / 2 - transformedY, "px");
1755
+ magnifierImage.style.transform = "rotate(".concat(imageData.rotate || 0, "deg) scaleX(").concat(scaleX, ") scaleY(").concat(scaleY, ")");
1756
+ magnifier.removeAttribute('aria-hidden');
1757
+ addClass(magnifier, 'viewer-show');
1758
+ },
1759
+ hideMagnifier: function hideMagnifier() {
1760
+ if (this.magnifier) {
1761
+ removeClass(this.magnifier, 'viewer-show');
1762
+ this.magnifier.setAttribute('aria-hidden', true);
1763
+ this.magnifierPoint = null;
1764
+ }
1765
+ },
1490
1766
  pointerdown: function pointerdown(event) {
1491
1767
  var options = this.options,
1492
1768
  pointers = this.pointers;
@@ -1515,12 +1791,14 @@
1515
1791
  pointers[event.pointerId || 0] = getPointer(event);
1516
1792
  }
1517
1793
  var action = options.movable ? ACTION_MOVE : false;
1518
- if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) {
1519
- action = ACTION_ZOOM;
1794
+ if ((options.zoomable && options.zoomOnTouch || options.rotatable && options.rotateOnTouch) && Object.keys(pointers).length > 1) {
1795
+ // action = ACTION_ZOOM;
1796
+ // action = ACTION_ROTATE;
1797
+ action = ACTION_TRANSFORM;
1520
1798
  } else if (options.slideOnTouch && (event.pointerType === 'touch' || event.type === 'touchstart') && this.isSwitchable()) {
1521
1799
  action = ACTION_SWITCH;
1522
1800
  }
1523
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1801
+ if (action === ACTION_MOVE || action === ACTION_ZOOM || action === ACTION_ROTATE || action === ACTION_TRANSFORM) {
1524
1802
  removeClass(this.image, CLASS_TRANSITION);
1525
1803
  }
1526
1804
  this.action = action;
@@ -1560,13 +1838,14 @@
1560
1838
  return;
1561
1839
  }
1562
1840
  event.preventDefault();
1563
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1564
- addClass(this.image, CLASS_TRANSITION);
1841
+ if (action === ACTION_MOVE || action === ACTION_ZOOM || action === ACTION_ROTATE || action === ACTION_TRANSFORM) {
1842
+ var transition = action === ACTION_TRANSFORM ? isTransitionEnabled(options, ACTION_ZOOM) || isTransitionEnabled(options, ACTION_ROTATE) : isTransitionEnabled(options, action);
1843
+ toggleClass(this.image, CLASS_TRANSITION, transition);
1565
1844
  }
1566
1845
  this.action = false;
1567
1846
 
1568
1847
  // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
1569
- if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && pointer && Date.now() - pointer.timeStamp < 500) {
1848
+ if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && action !== ACTION_TRANSFORM && pointer && Date.now() - pointer.timeStamp < 500) {
1570
1849
  clearTimeout(this.clickCanvasTimeout);
1571
1850
  clearTimeout(this.doubleClickImageTimeout);
1572
1851
  if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
@@ -1615,6 +1894,10 @@
1615
1894
  this.initContainer();
1616
1895
  this.initViewer();
1617
1896
  this.renderViewer();
1897
+ var navbarOptions = isPlainObject(this.options.navbar) ? this.options.navbar : {};
1898
+ if (isUndefined(navbarOptions.visibleItemCount)) {
1899
+ this.initList(this.index);
1900
+ }
1618
1901
  this.renderList();
1619
1902
  if (this.viewed) {
1620
1903
  this.initImage(function () {
@@ -1636,12 +1919,17 @@
1636
1919
  },
1637
1920
  wheel: function wheel(event) {
1638
1921
  var _this4 = this;
1922
+ var options = this.options,
1923
+ navbar = this.navbar;
1639
1924
  if (!this.viewed) {
1640
1925
  return;
1641
1926
  }
1642
1927
  event.preventDefault();
1928
+ if (this.gesturing) {
1929
+ return;
1930
+ }
1643
1931
 
1644
- // Limit wheel speed to prevent zoom too fast
1932
+ // Limit wheel speed to prevent zoom or slide too fast
1645
1933
  if (this.wheeling) {
1646
1934
  return;
1647
1935
  }
@@ -1649,7 +1937,6 @@
1649
1937
  setTimeout(function () {
1650
1938
  _this4.wheeling = false;
1651
1939
  }, 50);
1652
- var ratio = Number(this.options.zoomRatio) || 0.1;
1653
1940
  var delta = 1;
1654
1941
  if (event.deltaY) {
1655
1942
  delta = event.deltaY > 0 ? 1 : -1;
@@ -1658,7 +1945,63 @@
1658
1945
  } else if (event.detail) {
1659
1946
  delta = event.detail > 0 ? 1 : -1;
1660
1947
  }
1661
- this.zoom(-delta * ratio, true, null, event);
1948
+
1949
+ // Wheeling over the navbar never zooms, only slides
1950
+ var overNavbar = navbar && navbar.contains(event.target);
1951
+ var zoomable = !overNavbar && options.zoomable && isWheelActionEnabled(options.zoomOnWheel, event);
1952
+ if (zoomable) {
1953
+ var ratio = Number(options.zoomRatio) || 0.1;
1954
+ this.actionEvent = event;
1955
+ this.zoom(-delta * ratio, true);
1956
+ return;
1957
+ }
1958
+ if (isWheelActionEnabled(options.slideOnWheel, event)) {
1959
+ this.actionEvent = event;
1960
+ if (delta > 0) {
1961
+ this.next(options.loop);
1962
+ } else if (delta < 0) {
1963
+ this.prev(options.loop);
1964
+ }
1965
+ }
1966
+ },
1967
+ gesture: function gesture(event) {
1968
+ var options = this.options;
1969
+ if (!this.viewed || !options.zoomOnGesture && !options.rotateOnGesture) {
1970
+ return;
1971
+ }
1972
+ event.preventDefault();
1973
+ switch (event.type) {
1974
+ case 'gesturestart':
1975
+ this.gesturing = true;
1976
+ this.gestureScale = event.scale || 1;
1977
+ this.gestureRotation = event.rotation || 0;
1978
+ break;
1979
+ case 'gesturechange':
1980
+ {
1981
+ var scale = event.scale || 1;
1982
+ var ratio = scale / (this.gestureScale || 1);
1983
+ var rotation = Number(event.rotation);
1984
+ var degree = rotation - (this.gestureRotation || 0);
1985
+ this.gestureScale = scale;
1986
+ if (options.zoomable && options.zoomOnGesture && ratio !== 1) {
1987
+ this.actionEvent = event;
1988
+ this.zoom(ratio >= 1 ? ratio - 1 : 1 - 1 / ratio);
1989
+ }
1990
+ if (options.rotatable && options.rotateOnGesture && isNumber(rotation)) {
1991
+ this.gestureRotation = rotation;
1992
+ if (degree !== 0) {
1993
+ this.actionEvent = event;
1994
+ this.rotate(degree);
1995
+ }
1996
+ }
1997
+ break;
1998
+ }
1999
+ case 'gestureend':
2000
+ this.gesturing = false;
2001
+ this.gestureScale = 1;
2002
+ this.gestureRotation = 0;
2003
+ break;
2004
+ }
1662
2005
  }
1663
2006
  };
1664
2007
 
@@ -1681,12 +2024,17 @@
1681
2024
  }
1682
2025
  return this;
1683
2026
  }
2027
+ var originalEvent = this.actionEvent || this.showOriginalEvent || this.viewOriginalEvent || null;
2028
+ this.actionEvent = null;
2029
+ this.showOriginalEvent = originalEvent;
1684
2030
  if (isFunction(options.show)) {
1685
2031
  addListener(element, EVENT_SHOW, options.show, {
1686
2032
  once: true
1687
2033
  });
1688
2034
  }
1689
- if (dispatchEvent(element, EVENT_SHOW) === false || !this.ready) {
2035
+ if (dispatchEvent(element, EVENT_SHOW, {
2036
+ originalEvent: originalEvent
2037
+ }) === false || !this.ready) {
1690
2038
  return this;
1691
2039
  }
1692
2040
  if (this.hiding) {
@@ -1700,7 +2048,7 @@
1700
2048
  viewer.setAttribute('aria-labelledby', this.title.id);
1701
2049
  viewer.setAttribute('aria-modal', true);
1702
2050
  viewer.removeAttribute('aria-hidden');
1703
- if (options.transition && !immediate) {
2051
+ if (isTransitionEnabled(options, 'show') && !immediate) {
1704
2052
  var shown = this.shown.bind(this);
1705
2053
  this.transitioning = {
1706
2054
  abort: function abort() {
@@ -1735,12 +2083,17 @@
1735
2083
  if (options.inline || this.hiding || !(this.isShown || this.showing)) {
1736
2084
  return this;
1737
2085
  }
2086
+ var originalEvent = this.actionEvent || this.hideOriginalEvent || null;
2087
+ this.actionEvent = null;
2088
+ this.hideOriginalEvent = originalEvent;
1738
2089
  if (isFunction(options.hide)) {
1739
2090
  addListener(element, EVENT_HIDE, options.hide, {
1740
2091
  once: true
1741
2092
  });
1742
2093
  }
1743
- if (dispatchEvent(element, EVENT_HIDE) === false) {
2094
+ if (dispatchEvent(element, EVENT_HIDE, {
2095
+ originalEvent: originalEvent
2096
+ }) === false || this.destroyed) {
1744
2097
  return this;
1745
2098
  }
1746
2099
  if (this.showing) {
@@ -1758,7 +2111,7 @@
1758
2111
  removeClass(viewer, CLASS_IN);
1759
2112
  _this.hidden();
1760
2113
  };
1761
- if (options.transition && !immediate) {
2114
+ if (isTransitionEnabled(options, 'hide') && !immediate) {
1762
2115
  var _onViewerTransitionEnd = function onViewerTransitionEnd(event) {
1763
2116
  // Ignore all propagating `transitionend` events (#275).
1764
2117
  if (event && event.target === viewer) {
@@ -1791,7 +2144,7 @@
1791
2144
  addListener(image, EVENT_TRANSITION_END, onImageTransitionEnd, {
1792
2145
  once: true
1793
2146
  });
1794
- this.zoomTo(0, false, null, null, true);
2147
+ this.zoomTo(0, false, null, true);
1795
2148
  } else {
1796
2149
  onImageTransitionEnd();
1797
2150
  }
@@ -1808,10 +2161,14 @@
1808
2161
  view: function view() {
1809
2162
  var _this2 = this;
1810
2163
  var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.initialViewIndex;
2164
+ var previousIndex = this.index;
1811
2165
  index = Number(index) || 0;
1812
- if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) {
2166
+ if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === previousIndex) {
1813
2167
  return this;
1814
2168
  }
2169
+ var originalEvent = this.actionEvent || this.viewOriginalEvent || null;
2170
+ this.actionEvent = null;
2171
+ this.viewOriginalEvent = originalEvent;
1815
2172
  if (!this.isShown) {
1816
2173
  this.index = index;
1817
2174
  return this.show();
@@ -1823,17 +2180,16 @@
1823
2180
  options = this.options,
1824
2181
  title = this.title,
1825
2182
  canvas = this.canvas;
1826
- var item = this.items[index];
2183
+ var item = this.getItem(index);
2184
+ if (!item) {
2185
+ this.initList(index);
2186
+ item = this.getItem(index);
2187
+ }
1827
2188
  var img = item.querySelector('img');
1828
2189
  var url = getData(img, 'originalUrl');
1829
2190
  var alt = img.getAttribute('alt');
1830
2191
  var image = document.createElement('img');
1831
- forEach(options.inheritedAttributes, function (name) {
1832
- var value = img.getAttribute(name);
1833
- if (value !== null) {
1834
- image.setAttribute(name, value);
1835
- }
1836
- });
2192
+ inheritAttributes(image, img, options.inheritedAttributes);
1837
2193
  image.src = url;
1838
2194
  image.alt = alt;
1839
2195
  if (isFunction(options.view)) {
@@ -1844,11 +2200,13 @@
1844
2200
  if (dispatchEvent(element, EVENT_VIEW, {
1845
2201
  originalImage: this.images[index],
1846
2202
  index: index,
1847
- image: image
2203
+ image: image,
2204
+ originalEvent: originalEvent
1848
2205
  }) === false || !this.isShown || this.hiding || this.played) {
1849
2206
  return this;
1850
2207
  }
1851
- var activeItem = this.items[this.index];
2208
+ this.hideMagnifier();
2209
+ var activeItem = this.getItem(previousIndex);
1852
2210
  if (activeItem) {
1853
2211
  removeClass(activeItem, CLASS_ACTIVE);
1854
2212
  activeItem.removeAttribute('aria-selected');
@@ -1880,63 +2238,95 @@
1880
2238
  var imageData = _this2.imageData;
1881
2239
  var render = Array.isArray(options.title) ? options.title[1] : options.title;
1882
2240
  title.innerHTML = escapeHTMLEntities(isFunction(render) ? render.call(_this2, image, imageData) : "".concat(alt, " (").concat(imageData.naturalWidth, " \xD7 ").concat(imageData.naturalHeight, ")"));
1883
- };
1884
- var onLoad;
1885
- var onError;
1886
- addListener(element, EVENT_VIEWED, onViewed, {
1887
- once: true
1888
- });
1889
- this.viewing = {
1890
- abort: function abort() {
1891
- removeListener(element, EVENT_VIEWED, onViewed);
1892
- if (image.complete) {
1893
- if (_this2.imageRendering) {
1894
- _this2.imageRendering.abort();
1895
- } else if (_this2.imageInitializing) {
1896
- _this2.imageInitializing.abort();
1897
- }
1898
- } else {
1899
- // Cancel download to save bandwidth.
1900
- image.src = '';
1901
- removeListener(image, EVENT_LOAD, onLoad);
1902
- if (_this2.timeout) {
1903
- clearTimeout(_this2.timeout);
2241
+
2242
+ // Preload the image in the direction of navigation
2243
+ if (options.preload) {
2244
+ var direction = index < previousIndex ? -1 : 1;
2245
+ var nextIndex = index + direction;
2246
+ if (nextIndex < 0 || nextIndex >= _this2.length) {
2247
+ if (options.loop) {
2248
+ nextIndex = direction > 0 ? 0 : _this2.length - 1;
2249
+ } else {
2250
+ nextIndex = -1;
1904
2251
  }
1905
2252
  }
2253
+ if (nextIndex !== index) {
2254
+ var nextImage = _this2.images[nextIndex];
2255
+ var preloadedImage = document.createElement('img');
2256
+ inheritAttributes(preloadedImage, nextImage, options.inheritedAttributes);
2257
+ preloadedImage.src = _this2.getImageURL(nextImage) || nextImage.src;
2258
+ }
1906
2259
  }
1907
2260
  };
1908
- if (image.complete) {
1909
- this.load();
1910
- } else {
1911
- addListener(image, EVENT_LOAD, onLoad = function onLoad() {
1912
- removeListener(image, EVENT_ERROR, onError);
1913
- _this2.load();
1914
- }, {
1915
- once: true
1916
- });
1917
- addListener(image, EVENT_ERROR, onError = function onError() {
2261
+ addListener(element, EVENT_VIEWED, onViewed, {
2262
+ once: true
2263
+ });
2264
+ var _loadImage = function loadImage() {
2265
+ var isFallback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
2266
+ var onLoad;
2267
+ var onError;
2268
+ var clean = function clean() {
1918
2269
  removeListener(image, EVENT_LOAD, onLoad);
2270
+ removeListener(image, EVENT_ERROR, onError);
1919
2271
  if (_this2.timeout) {
1920
2272
  clearTimeout(_this2.timeout);
1921
2273
  _this2.timeout = false;
1922
2274
  }
1923
- removeClass(image, CLASS_INVISIBLE);
1924
- if (options.loading) {
1925
- removeClass(_this2.canvas, CLASS_LOADING);
2275
+ };
2276
+ _this2.viewing = {
2277
+ abort: function abort() {
2278
+ removeListener(element, EVENT_VIEWED, onViewed);
2279
+ if (image.complete) {
2280
+ if (_this2.imageRendering) {
2281
+ _this2.imageRendering.abort();
2282
+ } else if (_this2.imageInitializing) {
2283
+ _this2.imageInitializing.abort();
2284
+ }
2285
+ } else {
2286
+ // Cancel download to save bandwidth.
2287
+ image.src = '';
2288
+ clean();
2289
+ }
2290
+ }
2291
+ };
2292
+ if (image.complete) {
2293
+ _this2.load();
2294
+ } else {
2295
+ addListener(image, EVENT_LOAD, onLoad = function onLoad() {
2296
+ clean();
2297
+ _this2.load();
2298
+ }, {
2299
+ once: true
2300
+ });
2301
+ addListener(image, EVENT_ERROR, onError = function onError() {
2302
+ clean();
2303
+ if (!isFallback) {
2304
+ var fallbackSrc = img.src;
2305
+ if (fallbackSrc && fallbackSrc !== image.src) {
2306
+ image.src = fallbackSrc;
2307
+ _loadImage(true);
2308
+ return;
2309
+ }
2310
+ }
2311
+ removeClass(image, CLASS_INVISIBLE);
2312
+ if (options.loading) {
2313
+ removeClass(_this2.canvas, CLASS_LOADING);
2314
+ }
2315
+ }, {
2316
+ once: true
2317
+ });
2318
+ if (_this2.timeout) {
2319
+ clearTimeout(_this2.timeout);
1926
2320
  }
1927
- }, {
1928
- once: true
1929
- });
1930
- if (this.timeout) {
1931
- clearTimeout(this.timeout);
1932
- }
1933
2321
 
1934
- // Make the image visible if it fails to load within 1s
1935
- this.timeout = setTimeout(function () {
1936
- removeClass(image, CLASS_INVISIBLE);
1937
- _this2.timeout = false;
1938
- }, 1000);
1939
- }
2322
+ // Make the image visible if it fails to load within 1s
2323
+ _this2.timeout = setTimeout(function () {
2324
+ removeClass(image, CLASS_INVISIBLE);
2325
+ _this2.timeout = false;
2326
+ }, 1000);
2327
+ }
2328
+ };
2329
+ _loadImage();
1940
2330
  return this;
1941
2331
  },
1942
2332
  /**
@@ -1986,13 +2376,11 @@
1986
2376
  * Move the image to an absolute point.
1987
2377
  * @param {number} x - The new position in the horizontal direction.
1988
2378
  * @param {number} [y=x] - The new position in the vertical direction.
1989
- * @param {Event} [_originalEvent=null] - The original event if any.
1990
2379
  * @returns {Viewer} this
1991
2380
  */
1992
2381
  moveTo: function moveTo(x) {
1993
2382
  var _this3 = this;
1994
2383
  var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
1995
- var _originalEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
1996
2384
  var element = this.element,
1997
2385
  options = this.options,
1998
2386
  imageData = this.imageData;
@@ -2013,6 +2401,8 @@
2013
2401
  y = oldY;
2014
2402
  }
2015
2403
  if (changed) {
2404
+ var originalEvent = this.actionEvent || null;
2405
+ this.actionEvent = null;
2016
2406
  if (isFunction(options.move)) {
2017
2407
  addListener(element, EVENT_MOVE, options.move, {
2018
2408
  once: true
@@ -2023,7 +2413,7 @@
2023
2413
  y: y,
2024
2414
  oldX: oldX,
2025
2415
  oldY: oldY,
2026
- originalEvent: _originalEvent
2416
+ originalEvent: originalEvent
2027
2417
  }) === false) {
2028
2418
  return this;
2029
2419
  }
@@ -2031,6 +2421,7 @@
2031
2421
  imageData.y = y;
2032
2422
  imageData.left = x;
2033
2423
  imageData.top = y;
2424
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'move'));
2034
2425
  this.moving = true;
2035
2426
  this.renderImage(function () {
2036
2427
  _this3.moving = false;
@@ -2044,7 +2435,7 @@
2044
2435
  y: y,
2045
2436
  oldX: oldX,
2046
2437
  oldY: oldY,
2047
- originalEvent: _originalEvent
2438
+ originalEvent: originalEvent
2048
2439
  }, {
2049
2440
  cancelable: false
2050
2441
  });
@@ -2075,6 +2466,8 @@
2075
2466
  degree = Number(degree);
2076
2467
  if (isNumber(degree) && this.viewed && !this.played && options.rotatable) {
2077
2468
  var oldDegree = imageData.rotate;
2469
+ var originalEvent = this.actionEvent || null;
2470
+ this.actionEvent = null;
2078
2471
  if (isFunction(options.rotate)) {
2079
2472
  addListener(element, EVENT_ROTATE, options.rotate, {
2080
2473
  once: true
@@ -2082,11 +2475,13 @@
2082
2475
  }
2083
2476
  if (dispatchEvent(element, EVENT_ROTATE, {
2084
2477
  degree: degree,
2085
- oldDegree: oldDegree
2478
+ oldDegree: oldDegree,
2479
+ originalEvent: originalEvent
2086
2480
  }) === false) {
2087
2481
  return this;
2088
2482
  }
2089
2483
  imageData.rotate = degree;
2484
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'rotate'));
2090
2485
  this.rotating = true;
2091
2486
  this.renderImage(function () {
2092
2487
  _this4.rotating = false;
@@ -2097,7 +2492,8 @@
2097
2492
  }
2098
2493
  dispatchEvent(element, EVENT_ROTATED, {
2099
2494
  degree: degree,
2100
- oldDegree: oldDegree
2495
+ oldDegree: oldDegree,
2496
+ originalEvent: originalEvent
2101
2497
  }, {
2102
2498
  cancelable: false
2103
2499
  });
@@ -2152,6 +2548,8 @@
2152
2548
  scaleY = oldScaleY;
2153
2549
  }
2154
2550
  if (changed) {
2551
+ var originalEvent = this.actionEvent || null;
2552
+ this.actionEvent = null;
2155
2553
  if (isFunction(options.scale)) {
2156
2554
  addListener(element, EVENT_SCALE, options.scale, {
2157
2555
  once: true
@@ -2161,12 +2559,14 @@
2161
2559
  scaleX: scaleX,
2162
2560
  scaleY: scaleY,
2163
2561
  oldScaleX: oldScaleX,
2164
- oldScaleY: oldScaleY
2562
+ oldScaleY: oldScaleY,
2563
+ originalEvent: originalEvent
2165
2564
  }) === false) {
2166
2565
  return this;
2167
2566
  }
2168
2567
  imageData.scaleX = scaleX;
2169
2568
  imageData.scaleY = scaleY;
2569
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'scale'));
2170
2570
  this.scaling = true;
2171
2571
  this.renderImage(function () {
2172
2572
  _this5.scaling = false;
@@ -2179,7 +2579,8 @@
2179
2579
  scaleX: scaleX,
2180
2580
  scaleY: scaleY,
2181
2581
  oldScaleX: oldScaleX,
2182
- oldScaleY: oldScaleY
2582
+ oldScaleY: oldScaleY,
2583
+ originalEvent: originalEvent
2183
2584
  }, {
2184
2585
  cancelable: false
2185
2586
  });
@@ -2193,13 +2594,11 @@
2193
2594
  * @param {number} ratio - The target ratio.
2194
2595
  * @param {boolean} [showTooltip=false] - Indicates whether to show the tooltip.
2195
2596
  * @param {Object} [pivot] - The pivot point coordinate for zooming.
2196
- * @param {Event} [_originalEvent=null] - The original event if any.
2197
2597
  * @returns {Viewer} this
2198
2598
  */
2199
2599
  zoom: function zoom(ratio) {
2200
2600
  var showTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2201
2601
  var pivot = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
2202
- var _originalEvent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
2203
2602
  var imageData = this.imageData;
2204
2603
  ratio = Number(ratio);
2205
2604
  if (ratio < 0) {
@@ -2207,7 +2606,7 @@
2207
2606
  } else {
2208
2607
  ratio = 1 + ratio;
2209
2608
  }
2210
- this.zoomTo(imageData.width * ratio / imageData.naturalWidth, showTooltip, pivot, _originalEvent);
2609
+ this.zoomTo(imageData.width * ratio / imageData.naturalWidth, showTooltip, pivot);
2211
2610
  return this;
2212
2611
  },
2213
2612
  /**
@@ -2215,16 +2614,14 @@
2215
2614
  * @param {number} ratio - The target ratio.
2216
2615
  * @param {boolean} [showTooltip] - Indicates whether to show the tooltip.
2217
2616
  * @param {Object} [pivot] - The pivot point coordinate for zooming.
2218
- * @param {Event} [_originalEvent=null] - The original event if any.
2219
- * @param {Event} [_zoomable=false] - Indicates if the current zoom is available or not.
2617
+ * @param {boolean} [_zoomable=false] - Indicates if the current zoom is available or not.
2220
2618
  * @returns {Viewer} this
2221
2619
  */
2222
2620
  zoomTo: function zoomTo(ratio) {
2223
2621
  var _this6 = this;
2224
2622
  var showTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2225
2623
  var pivot = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
2226
- var _originalEvent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
2227
- var _zoomable = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
2624
+ var _zoomable = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
2228
2625
  var element = this.element,
2229
2626
  options = this.options,
2230
2627
  pointers = this.pointers,
@@ -2238,12 +2635,14 @@
2238
2635
  ratio = Math.max(0, ratio);
2239
2636
  if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
2240
2637
  if (!_zoomable) {
2241
- var minZoomRatio = Math.max(0.01, options.minZoomRatio);
2242
- var maxZoomRatio = Math.min(100, options.maxZoomRatio);
2638
+ var minZoomRatio = Math.max(0.01, isFunction(options.minZoomRatio) ? options.minZoomRatio.call(this, this.image, imageData) : options.minZoomRatio);
2639
+ var maxZoomRatio = Math.min(100, isFunction(options.maxZoomRatio) ? options.maxZoomRatio.call(this, this.image, imageData) : options.maxZoomRatio);
2243
2640
  ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
2244
2641
  }
2245
- if (_originalEvent) {
2246
- switch (_originalEvent.type) {
2642
+ var originalEvent = this.actionEvent || null;
2643
+ this.actionEvent = null;
2644
+ if (originalEvent && originalEvent.type !== EVENT_CLICK) {
2645
+ switch (originalEvent.type) {
2247
2646
  case 'wheel':
2248
2647
  if (options.zoomRatio >= 0.055 && ratio > 0.95 && ratio < 1.05) {
2249
2648
  ratio = 1;
@@ -2271,16 +2670,16 @@
2271
2670
  if (dispatchEvent(element, EVENT_ZOOM, {
2272
2671
  ratio: ratio,
2273
2672
  oldRatio: oldRatio,
2274
- originalEvent: _originalEvent
2673
+ originalEvent: originalEvent
2275
2674
  }) === false) {
2276
2675
  return this;
2277
2676
  }
2278
2677
  this.zooming = true;
2279
- if (_originalEvent) {
2678
+ if (originalEvent && originalEvent.type !== EVENT_CLICK) {
2280
2679
  var offset = getOffset(this.viewer);
2281
2680
  var center = pointers && Object.keys(pointers).length > 0 ? getPointersCenter(pointers) : {
2282
- pageX: _originalEvent.pageX,
2283
- pageY: _originalEvent.pageY
2681
+ pageX: originalEvent.pageX,
2682
+ pageY: originalEvent.pageY
2284
2683
  };
2285
2684
 
2286
2685
  // Zoom from the triggering point of the event
@@ -2300,6 +2699,7 @@
2300
2699
  imageData.height = newHeight;
2301
2700
  imageData.oldRatio = oldRatio;
2302
2701
  imageData.ratio = ratio;
2702
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'zoom'));
2303
2703
  this.renderImage(function () {
2304
2704
  _this6.zooming = false;
2305
2705
  if (isFunction(options.zoomed)) {
@@ -2310,7 +2710,7 @@
2310
2710
  dispatchEvent(element, EVENT_ZOOMED, {
2311
2711
  ratio: ratio,
2312
2712
  oldRatio: oldRatio,
2313
- originalEvent: _originalEvent
2713
+ originalEvent: originalEvent
2314
2714
  }, {
2315
2715
  cancelable: false
2316
2716
  });
@@ -2334,12 +2734,16 @@
2334
2734
  }
2335
2735
  var element = this.element,
2336
2736
  options = this.options;
2737
+ var originalEvent = this.actionEvent || null;
2738
+ this.actionEvent = null;
2337
2739
  if (isFunction(options.play)) {
2338
2740
  addListener(element, EVENT_PLAY, options.play, {
2339
2741
  once: true
2340
2742
  });
2341
2743
  }
2342
- if (dispatchEvent(element, EVENT_PLAY) === false) {
2744
+ if (dispatchEvent(element, EVENT_PLAY, {
2745
+ originalEvent: originalEvent
2746
+ }) === false) {
2343
2747
  return this;
2344
2748
  }
2345
2749
  var player = this.player;
@@ -2353,16 +2757,16 @@
2353
2757
  this.requestFullscreen(fullscreen);
2354
2758
  }
2355
2759
  addClass(player, CLASS_SHOW);
2356
- forEach(this.items, function (item, i) {
2357
- var img = item.querySelector('img');
2760
+ player.removeAttribute('aria-hidden');
2761
+ forEach(this.images, function (originalImage, i) {
2358
2762
  var image = document.createElement('img');
2359
- image.src = getData(img, 'originalUrl');
2360
- image.alt = img.getAttribute('alt');
2361
- image.referrerPolicy = img.referrerPolicy;
2763
+ image.src = _this7.getImageURL(originalImage) || originalImage.src;
2764
+ image.alt = originalImage.alt || '';
2765
+ image.referrerPolicy = originalImage.referrerPolicy;
2362
2766
  total += 1;
2363
2767
  addClass(image, CLASS_FADE);
2364
- toggleClass(image, CLASS_TRANSITION, options.transition);
2365
- if (hasClass(item, CLASS_ACTIVE)) {
2768
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'play'));
2769
+ if (i === _this7.index) {
2366
2770
  addClass(image, CLASS_IN);
2367
2771
  index = i;
2368
2772
  }
@@ -2375,31 +2779,68 @@
2375
2779
  if (isNumber(options.interval) && options.interval > 0) {
2376
2780
  var _prev = function prev() {
2377
2781
  clearTimeout(_this7.playing.timeout);
2782
+ var currentOriginalEvent = _this7.actionEvent || null;
2783
+ _this7.actionEvent = null;
2784
+ var prevIndex = index - 1;
2785
+ prevIndex = prevIndex >= 0 ? prevIndex : total - 1;
2786
+ if (isFunction(options.playing)) {
2787
+ addListener(element, EVENT_PLAYING, options.playing, {
2788
+ once: true
2789
+ });
2790
+ }
2791
+ if (dispatchEvent(element, EVENT_PLAYING, {
2792
+ originalImage: _this7.images[prevIndex],
2793
+ index: prevIndex,
2794
+ image: list[prevIndex],
2795
+ originalEvent: currentOriginalEvent
2796
+ }) === false) {
2797
+ _this7.playing.timeout = options.autoplay ? setTimeout(_prev, options.interval) : null;
2798
+ return;
2799
+ }
2378
2800
  removeClass(list[index], CLASS_IN);
2379
- index -= 1;
2380
- index = index >= 0 ? index : total - 1;
2801
+ index = prevIndex;
2381
2802
  addClass(list[index], CLASS_IN);
2382
- _this7.playing.timeout = setTimeout(_prev, options.interval);
2803
+ _this7.playing.timeout = options.autoplay ? setTimeout(_prev, options.interval) : null;
2383
2804
  };
2384
2805
  var _next = function next() {
2385
2806
  clearTimeout(_this7.playing.timeout);
2807
+ var currentOriginalEvent = _this7.actionEvent || null;
2808
+ _this7.actionEvent = null;
2809
+ var nextIndex = index + 1;
2810
+ nextIndex = nextIndex < total ? nextIndex : 0;
2811
+ if (isFunction(options.playing)) {
2812
+ addListener(element, EVENT_PLAYING, options.playing, {
2813
+ once: true
2814
+ });
2815
+ }
2816
+ if (dispatchEvent(element, EVENT_PLAYING, {
2817
+ originalImage: _this7.images[nextIndex],
2818
+ index: nextIndex,
2819
+ image: list[nextIndex],
2820
+ originalEvent: currentOriginalEvent
2821
+ }) === false) {
2822
+ _this7.playing.timeout = options.autoplay ? setTimeout(_next, options.interval) : null;
2823
+ return;
2824
+ }
2386
2825
  removeClass(list[index], CLASS_IN);
2387
- index += 1;
2388
- index = index < total ? index : 0;
2826
+ index = nextIndex;
2389
2827
  addClass(list[index], CLASS_IN);
2390
- _this7.playing.timeout = setTimeout(_next, options.interval);
2828
+ _this7.playing.timeout = options.autoplay ? setTimeout(_next, options.interval) : null;
2391
2829
  };
2392
2830
  if (total > 1) {
2393
2831
  this.playing = {
2394
2832
  prev: _prev,
2395
2833
  next: _next,
2396
- timeout: setTimeout(_next, options.interval)
2834
+ timeout: options.autoplay ? setTimeout(_next, options.interval) : null
2397
2835
  };
2398
2836
  }
2399
2837
  }
2400
2838
  return this;
2401
2839
  },
2402
- // Stop play
2840
+ /**
2841
+ * Stop play
2842
+ * @returns {Viewer} this
2843
+ */
2403
2844
  stop: function stop() {
2404
2845
  var _this8 = this;
2405
2846
  if (!this.played) {
@@ -2407,12 +2848,16 @@
2407
2848
  }
2408
2849
  var element = this.element,
2409
2850
  options = this.options;
2851
+ var originalEvent = this.actionEvent || null;
2852
+ this.actionEvent = null;
2410
2853
  if (isFunction(options.stop)) {
2411
2854
  addListener(element, EVENT_STOP, options.stop, {
2412
2855
  once: true
2413
2856
  });
2414
2857
  }
2415
- if (dispatchEvent(element, EVENT_STOP) === false) {
2858
+ if (dispatchEvent(element, EVENT_STOP, {
2859
+ originalEvent: originalEvent
2860
+ }) === false) {
2416
2861
  return this;
2417
2862
  }
2418
2863
  var player = this.player;
@@ -2423,6 +2868,7 @@
2423
2868
  removeListener(image, EVENT_LOAD, _this8.onLoadWhenPlay);
2424
2869
  });
2425
2870
  removeClass(player, CLASS_SHOW);
2871
+ player.setAttribute('aria-hidden', true);
2426
2872
  player.innerHTML = '';
2427
2873
  this.exitFullscreen();
2428
2874
  return this;
@@ -2440,11 +2886,9 @@
2440
2886
  this.fulled = true;
2441
2887
  this.open();
2442
2888
  addClass(this.button, CLASS_FULLSCREEN_EXIT);
2443
- if (options.transition) {
2444
- removeClass(list, CLASS_TRANSITION);
2445
- if (this.viewed) {
2446
- removeClass(image, CLASS_TRANSITION);
2447
- }
2889
+ removeClass(list, CLASS_TRANSITION);
2890
+ if (this.viewed) {
2891
+ removeClass(image, CLASS_TRANSITION);
2448
2892
  }
2449
2893
  addClass(viewer, CLASS_FIXED);
2450
2894
  viewer.setAttribute('role', 'dialog');
@@ -2462,14 +2906,7 @@
2462
2906
  this.renderList();
2463
2907
  if (this.viewed) {
2464
2908
  this.initImage(function () {
2465
- _this9.renderImage(function () {
2466
- if (options.transition) {
2467
- setTimeout(function () {
2468
- addClass(image, CLASS_TRANSITION);
2469
- addClass(list, CLASS_TRANSITION);
2470
- }, 0);
2471
- }
2472
- });
2909
+ _this9.renderImage();
2473
2910
  });
2474
2911
  }
2475
2912
  return this;
@@ -2487,11 +2924,9 @@
2487
2924
  this.fulled = false;
2488
2925
  this.close();
2489
2926
  removeClass(this.button, CLASS_FULLSCREEN_EXIT);
2490
- if (options.transition) {
2491
- removeClass(list, CLASS_TRANSITION);
2492
- if (this.viewed) {
2493
- removeClass(image, CLASS_TRANSITION);
2494
- }
2927
+ removeClass(list, CLASS_TRANSITION);
2928
+ if (this.viewed) {
2929
+ removeClass(image, CLASS_TRANSITION);
2495
2930
  }
2496
2931
  if (options.focus) {
2497
2932
  this.clearEnforceFocus();
@@ -2508,14 +2943,7 @@
2508
2943
  this.renderList();
2509
2944
  if (this.viewed) {
2510
2945
  this.initImage(function () {
2511
- _this0.renderImage(function () {
2512
- if (options.transition) {
2513
- setTimeout(function () {
2514
- addClass(image, CLASS_TRANSITION);
2515
- addClass(list, CLASS_TRANSITION);
2516
- }, 0);
2517
- }
2518
- });
2946
+ _this0.renderImage();
2519
2947
  });
2520
2948
  }
2521
2949
  return this;
@@ -2531,7 +2959,7 @@
2531
2959
  }
2532
2960
  tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
2533
2961
  if (!this.tooltipping) {
2534
- if (options.transition) {
2962
+ if (isTransitionEnabled(options, 'tooltip')) {
2535
2963
  if (this.fading) {
2536
2964
  dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
2537
2965
  }
@@ -2551,7 +2979,7 @@
2551
2979
  clearTimeout(this.tooltipping);
2552
2980
  }
2553
2981
  this.tooltipping = setTimeout(function () {
2554
- if (options.transition) {
2982
+ if (isTransitionEnabled(options, 'tooltip')) {
2555
2983
  addListener(tooltipBox, EVENT_TRANSITION_END, function () {
2556
2984
  removeClass(tooltipBox, CLASS_SHOW);
2557
2985
  removeClass(tooltipBox, CLASS_FADE);
@@ -2573,15 +3001,13 @@
2573
3001
  },
2574
3002
  /**
2575
3003
  * Toggle the image size between its current size and natural size
2576
- * @param {Event} [_originalEvent=null] - The original event if any.
2577
3004
  * @returns {Viewer} this
2578
3005
  */
2579
3006
  toggle: function toggle() {
2580
- var _originalEvent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
2581
3007
  if (this.imageData.ratio === 1) {
2582
- this.zoomTo(this.imageData.oldRatio, true, null, _originalEvent);
3008
+ this.zoomTo(this.imageData.oldRatio, true);
2583
3009
  } else {
2584
- this.zoomTo(1, true, null, _originalEvent);
3010
+ this.zoomTo(1, true);
2585
3011
  }
2586
3012
  return this;
2587
3013
  },
@@ -2593,12 +3019,19 @@
2593
3019
  }
2594
3020
  return this;
2595
3021
  },
2596
- // Update viewer when images changed
2597
- update: function update() {
3022
+ /**
3023
+ * Update the viewer when images or options changed.
3024
+ * @param {Object} [updateOptions] - The options to update.
3025
+ * @returns {Viewer} this
3026
+ */
3027
+ update: function update(updateOptions) {
2598
3028
  var _this10 = this;
2599
3029
  var element = this.element,
2600
3030
  options = this.options,
2601
3031
  isImg = this.isImg;
3032
+ if (isPlainObject(updateOptions)) {
3033
+ assign(options, updateOptions);
3034
+ }
2602
3035
 
2603
3036
  // Destroy viewer if the target image was deleted
2604
3037
  if (isImg && !element.parentNode) {
@@ -2621,18 +3054,19 @@
2621
3054
  this.length = images.length;
2622
3055
  if (this.ready) {
2623
3056
  var changedIndexes = [];
2624
- forEach(this.items, function (item, i) {
3057
+ forEach(this.items, function (item) {
2625
3058
  var img = item.querySelector('img');
2626
- var image = images[i];
3059
+ var index = Number(getData(item, 'index'));
3060
+ var image = images[index];
2627
3061
  if (image && img) {
2628
3062
  if (image.src !== img.src
2629
3063
 
2630
3064
  // Title changed (#408)
2631
3065
  || image.alt !== img.alt) {
2632
- changedIndexes.push(i);
3066
+ changedIndexes.push(index);
2633
3067
  }
2634
3068
  } else {
2635
- changedIndexes.push(i);
3069
+ changedIndexes.push(index);
2636
3070
  }
2637
3071
  });
2638
3072
  setStyle(this.list, {
@@ -2647,7 +3081,7 @@
2647
3081
  this.viewed = false;
2648
3082
  this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
2649
3083
  } else {
2650
- var activeItem = this.items[this.index];
3084
+ var activeItem = this.getItem(this.index);
2651
3085
 
2652
3086
  // Reactivate the current viewing item after reset the list.
2653
3087
  addClass(activeItem, CLASS_ACTIVE);
@@ -2788,9 +3222,12 @@
2788
3222
  once: true
2789
3223
  });
2790
3224
  }
2791
- if (dispatchEvent(element, EVENT_SHOWN) === false) {
3225
+ if (dispatchEvent(element, EVENT_SHOWN, {
3226
+ originalEvent: this.showOriginalEvent || null
3227
+ }) === false) {
2792
3228
  return;
2793
3229
  }
3230
+ this.showOriginalEvent = null;
2794
3231
  if (this.ready && this.isShown && !this.hiding) {
2795
3232
  this.view(this.index);
2796
3233
  }
@@ -2827,13 +3264,16 @@
2827
3264
  once: true
2828
3265
  });
2829
3266
  }
2830
- dispatchEvent(element, EVENT_HIDDEN, null, {
3267
+ dispatchEvent(element, EVENT_HIDDEN, {
3268
+ originalEvent: this.hideOriginalEvent || null
3269
+ }, {
2831
3270
  cancelable: false
2832
3271
  });
3272
+ this.hideOriginalEvent = null;
2833
3273
  }
2834
3274
  },
2835
3275
  requestFullscreen: function requestFullscreen(options) {
2836
- var document = this.element.ownerDocument;
3276
+ var document = this.ownerDocument;
2837
3277
  if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2838
3278
  var documentElement = document.documentElement;
2839
3279
 
@@ -2855,7 +3295,7 @@
2855
3295
  }
2856
3296
  },
2857
3297
  exitFullscreen: function exitFullscreen() {
2858
- var document = this.element.ownerDocument;
3298
+ var document = this.ownerDocument;
2859
3299
  if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2860
3300
  // Document.exitFullscreen()
2861
3301
  if (document.exitFullscreen) {
@@ -2885,13 +3325,49 @@
2885
3325
  case ACTION_MOVE:
2886
3326
  if (offsetX !== 0 || offsetY !== 0) {
2887
3327
  this.pointerMoved = true;
2888
- this.move(offsetX, offsetY, event);
3328
+ this.actionEvent = event;
3329
+ this.move(offsetX, offsetY);
2889
3330
  }
2890
3331
  break;
2891
3332
 
2892
3333
  // Zoom the current image
2893
3334
  case ACTION_ZOOM:
2894
- this.zoom(getMaxZoomRatio(pointers), false, null, event);
3335
+ if (options.zoomable && options.zoomOnTouch) {
3336
+ var zoomRatio = getMaxZoomRatio(pointers);
3337
+ if (zoomRatio !== 0) {
3338
+ this.actionEvent = event;
3339
+ this.zoom(zoomRatio);
3340
+ }
3341
+ }
3342
+ break;
3343
+
3344
+ // Rotate the current image
3345
+ case ACTION_ROTATE:
3346
+ if (options.rotatable && options.rotateOnTouch) {
3347
+ var rotateDegree = getMaxRotateDegree(pointers);
3348
+ if (rotateDegree !== 0) {
3349
+ this.actionEvent = event;
3350
+ this.rotate(rotateDegree);
3351
+ }
3352
+ }
3353
+ break;
3354
+
3355
+ // Transform the current image
3356
+ case ACTION_TRANSFORM:
3357
+ if (options.zoomable && options.zoomOnTouch) {
3358
+ var _zoomRatio = getMaxZoomRatio(pointers);
3359
+ if (_zoomRatio !== 0) {
3360
+ this.actionEvent = event;
3361
+ this.zoom(_zoomRatio);
3362
+ }
3363
+ }
3364
+ if (options.rotatable && options.rotateOnTouch) {
3365
+ var _rotateDegree = getMaxRotateDegree(pointers);
3366
+ if (_rotateDegree !== 0) {
3367
+ this.actionEvent = event;
3368
+ this.rotate(_rotateDegree);
3369
+ }
3370
+ }
2895
3371
  break;
2896
3372
  case ACTION_SWITCH:
2897
3373
  {
@@ -2900,6 +3376,7 @@
2900
3376
  if (absoluteOffsetX > 1 && absoluteOffsetX > Math.abs(offsetY)) {
2901
3377
  // Empty `pointers` as `touchend` event will not be fired after swiped in iOS browsers.
2902
3378
  this.pointers = {};
3379
+ this.actionEvent = event;
2903
3380
  if (offsetX > 1) {
2904
3381
  this.prev(options.loop);
2905
3382
  } else if (offsetX < -1) {
@@ -2933,18 +3410,20 @@
2933
3410
  var Viewer = /*#__PURE__*/function () {
2934
3411
  /**
2935
3412
  * Create a new Viewer.
2936
- * @param {Element} element - The target element for viewing.
3413
+ * @param {Element} element - The target image, or a container of images, to view.
2937
3414
  * @param {Object} [options={}] - The configuration options.
2938
3415
  */
2939
3416
  function Viewer(element) {
2940
3417
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2941
3418
  _classCallCheck(this, Viewer);
2942
- if (!element || element.nodeType !== 1) {
3419
+ if (!element || element.nodeType !== 1 && element.nodeType !== 11) {
2943
3420
  throw new Error('The first argument is required and must be an element.');
2944
3421
  }
2945
3422
  this.element = element;
3423
+ this.ownerDocument = element.ownerDocument || element.host.ownerDocument;
2946
3424
  this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
2947
3425
  this.action = false;
3426
+ this.actionEvent = null;
2948
3427
  this.fading = false;
2949
3428
  this.fulled = false;
2950
3429
  this.hiding = false;
@@ -3004,7 +3483,7 @@
3004
3483
  this.initBody();
3005
3484
 
3006
3485
  // Override `transition` option if it is not supported
3007
- if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
3486
+ if (isUndefined(this.ownerDocument.createElement(NAMESPACE).style.transition)) {
3008
3487
  options.transition = false;
3009
3488
  }
3010
3489
  if (options.inline) {
@@ -3058,9 +3537,10 @@
3058
3537
  }
3059
3538
  });
3060
3539
  } else {
3061
- addListener(element, EVENT_CLICK, this.onElementClick = function (_ref) {
3062
- var target = _ref.target;
3540
+ addListener(element, EVENT_CLICK, this.onElementClick = function (event) {
3541
+ var target = event.target;
3063
3542
  if (target.localName === 'img' && (!isFunction(options.filter) || options.filter.call(_this, target))) {
3543
+ _this.actionEvent = event;
3064
3544
  _this.view(_this.images.indexOf(target));
3065
3545
  }
3066
3546
  });
@@ -3070,6 +3550,7 @@
3070
3550
  if (target.localName === 'img' && (key === 'Enter' || key === ' ')) {
3071
3551
  event.preventDefault();
3072
3552
  if (!isFunction(options.filter) || options.filter.call(_this, target)) {
3553
+ _this.actionEvent = event;
3073
3554
  _this.view(_this.images.indexOf(target));
3074
3555
  }
3075
3556
  }
@@ -3091,6 +3572,7 @@
3091
3572
  var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
3092
3573
  var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
3093
3574
  var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
3575
+ var navigation = viewer.querySelector(".".concat(NAMESPACE, "-navigation"));
3094
3576
  var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
3095
3577
  var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
3096
3578
  this.parent = parent;
@@ -3098,19 +3580,64 @@
3098
3580
  this.title = title;
3099
3581
  this.toolbar = toolbar;
3100
3582
  this.navbar = navbar;
3583
+ this.navigation = navigation;
3101
3584
  this.button = button;
3102
3585
  this.canvas = canvas;
3103
3586
  this.footer = viewer.querySelector(".".concat(NAMESPACE, "-footer"));
3587
+ this.magnifier = viewer.querySelector(".".concat(NAMESPACE, "-magnifier"));
3588
+ this.magnifierImage = viewer.querySelector(".".concat(NAMESPACE, "-magnifier-image"));
3104
3589
  this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
3105
3590
  this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
3106
3591
  this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
3107
3592
  viewer.id = "".concat(NAMESPACE).concat(this.id);
3108
3593
  title.id = "".concat(NAMESPACE, "Title").concat(this.id);
3109
3594
  addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
3110
- addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
3595
+ if (options.title) {
3596
+ title.removeAttribute('aria-hidden');
3597
+ }
3598
+ var navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
3599
+ var navbarShow = options.navbar;
3600
+ var navbarSize = !isUndefined(navbarOptions.size) ? navbarOptions.size : options.navbar;
3601
+ if (isPlainObject(options.navbar)) {
3602
+ navbarShow = !isUndefined(navbarOptions.show) ? navbarOptions.show : true;
3603
+ }
3604
+ addClass(navbar, !navbarShow ? CLASS_HIDE : getResponsiveClass(navbarShow));
3605
+ if (navbarShow) {
3606
+ navbar.removeAttribute('aria-hidden');
3607
+ }
3608
+ if (['small', 'medium', 'large'].indexOf(navbarSize) !== -1) {
3609
+ addClass(navbar, "".concat(NAMESPACE, "-").concat(navbarSize));
3610
+ }
3611
+ if (isPlainObject(options.navigation)) {
3612
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3613
+ var name = getData(item, DATA_ACTION);
3614
+ var value = options.navigation[name];
3615
+ var deep = isPlainObject(value);
3616
+ var show = deep && !isUndefined(value.show) ? value.show : value;
3617
+ var size = deep && !isUndefined(value.size) ? value.size : value;
3618
+ toggleClass(item, CLASS_HIDE, !show);
3619
+ if (isNumber(show)) {
3620
+ addClass(item, getResponsiveClass(show));
3621
+ }
3622
+ if (['small', 'large'].indexOf(size) !== -1) {
3623
+ addClass(item, "".concat(NAMESPACE, "-").concat(size));
3624
+ }
3625
+ });
3626
+ } else {
3627
+ addClass(navigation, !options.navigation ? CLASS_HIDE : getResponsiveClass(options.navigation));
3628
+ }
3629
+ if (options.navigation) {
3630
+ navigation.removeAttribute('aria-hidden');
3631
+ }
3111
3632
  toggleClass(button, CLASS_HIDE, !options.button);
3633
+ if (options.button) {
3634
+ button.removeAttribute('aria-hidden');
3635
+ }
3112
3636
  if (options.keyboard) {
3113
3637
  button.setAttribute('tabindex', 0);
3638
+ forEach(navigation.querySelectorAll('[role="button"]'), function (item) {
3639
+ item.setAttribute('tabindex', 0);
3640
+ });
3114
3641
  }
3115
3642
  if (options.backdrop) {
3116
3643
  addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
@@ -3133,6 +3660,7 @@
3133
3660
  if (!custom) {
3134
3661
  addClass(toolbar, getResponsiveClass(options.toolbar));
3135
3662
  }
3663
+ toolbar.removeAttribute('aria-hidden');
3136
3664
  forEach(custom ? options.toolbar : BUTTONS, function (value, index) {
3137
3665
  var deep = custom && isPlainObject(value);
3138
3666
  var name = custom ? hyphenate(index) : value;
@@ -3168,6 +3696,11 @@
3168
3696
  } else {
3169
3697
  addClass(toolbar, CLASS_HIDE);
3170
3698
  }
3699
+ if (options.title || navbarShow || options.toolbar) {
3700
+ this.footer.removeAttribute('aria-hidden');
3701
+ } else {
3702
+ addClass(this.footer, CLASS_HIDE);
3703
+ }
3171
3704
  if (!options.rotatable) {
3172
3705
  var rotates = toolbar.querySelectorAll('li[class*="rotate"]');
3173
3706
  addClass(rotates, CLASS_INVISIBLE);
@@ -3196,7 +3729,7 @@
3196
3729
  });
3197
3730
  var container = options.container;
3198
3731
  if (isString(container)) {
3199
- container = element.ownerDocument.querySelector(container);
3732
+ container = this.ownerDocument.querySelector(container);
3200
3733
  }
3201
3734
  if (!container) {
3202
3735
  container = this.body;
@@ -3224,14 +3757,15 @@
3224
3757
  }
3225
3758
 
3226
3759
  /**
3227
- * Get the no conflict viewer class.
3228
- * @returns {Viewer} The viewer class.
3760
+ * Create a new Viewer instance.
3761
+ * @param {Element} element - The target image, or a container of images, to view.
3762
+ * @param {Object} [options={}] - The configuration options.
3763
+ * @returns {Viewer} A new Viewer instance.
3229
3764
  */
3230
3765
  }], [{
3231
- key: "noConflict",
3232
- value: function noConflict() {
3233
- window.Viewer = AnotherViewer;
3234
- return Viewer;
3766
+ key: "create",
3767
+ value: function create(element, options) {
3768
+ return new Viewer(element, options);
3235
3769
  }
3236
3770
 
3237
3771
  /**
@@ -3243,6 +3777,17 @@
3243
3777
  value: function setDefaults(options) {
3244
3778
  assign(DEFAULTS, isPlainObject(options) && options);
3245
3779
  }
3780
+
3781
+ /**
3782
+ * Get the no conflict viewer class.
3783
+ * @returns {Viewer} The viewer class.
3784
+ */
3785
+ }, {
3786
+ key: "noConflict",
3787
+ value: function noConflict() {
3788
+ window.Viewer = AnotherViewer;
3789
+ return Viewer;
3790
+ }
3246
3791
  }]);
3247
3792
  }();
3248
3793
  assign(Viewer.prototype, render, events, handlers, methods, others);