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/src/js/methods.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  EVENT_MOVE,
18
18
  EVENT_MOVED,
19
19
  EVENT_PLAY,
20
+ EVENT_PLAYING,
20
21
  EVENT_ROTATE,
21
22
  EVENT_ROTATED,
22
23
  EVENT_SCALE,
@@ -41,9 +42,11 @@ import {
41
42
  getOffset,
42
43
  getPointersCenter,
43
44
  hasClass,
45
+ inheritAttributes,
44
46
  isFunction,
45
47
  isNumber,
46
48
  isPlainObject,
49
+ isTransitionEnabled,
47
50
  isUndefined,
48
51
  removeClass,
49
52
  removeListener,
@@ -73,13 +76,23 @@ export default {
73
76
  return this;
74
77
  }
75
78
 
79
+ const originalEvent = this.actionEvent
80
+ || this.showOriginalEvent
81
+ || this.viewOriginalEvent
82
+ || null;
83
+
84
+ this.actionEvent = null;
85
+ this.showOriginalEvent = originalEvent;
86
+
76
87
  if (isFunction(options.show)) {
77
88
  addListener(element, EVENT_SHOW, options.show, {
78
89
  once: true,
79
90
  });
80
91
  }
81
92
 
82
- if (dispatchEvent(element, EVENT_SHOW) === false || !this.ready) {
93
+ if (dispatchEvent(element, EVENT_SHOW, {
94
+ originalEvent,
95
+ }) === false || !this.ready) {
83
96
  return this;
84
97
  }
85
98
 
@@ -98,7 +111,7 @@ export default {
98
111
  viewer.setAttribute('aria-modal', true);
99
112
  viewer.removeAttribute('aria-hidden');
100
113
 
101
- if (options.transition && !immediate) {
114
+ if (isTransitionEnabled(options, 'show') && !immediate) {
102
115
  const shown = this.shown.bind(this);
103
116
 
104
117
  this.transitioning = {
@@ -136,13 +149,20 @@ export default {
136
149
  return this;
137
150
  }
138
151
 
152
+ const originalEvent = this.actionEvent || this.hideOriginalEvent || null;
153
+
154
+ this.actionEvent = null;
155
+ this.hideOriginalEvent = originalEvent;
156
+
139
157
  if (isFunction(options.hide)) {
140
158
  addListener(element, EVENT_HIDE, options.hide, {
141
159
  once: true,
142
160
  });
143
161
  }
144
162
 
145
- if (dispatchEvent(element, EVENT_HIDE) === false) {
163
+ if (dispatchEvent(element, EVENT_HIDE, {
164
+ originalEvent,
165
+ }) === false || this.destroyed) {
146
166
  return this;
147
167
  }
148
168
 
@@ -164,7 +184,7 @@ export default {
164
184
  this.hidden();
165
185
  };
166
186
 
167
- if (options.transition && !immediate) {
187
+ if (isTransitionEnabled(options, 'hide') && !immediate) {
168
188
  const onViewerTransitionEnd = (event) => {
169
189
  // Ignore all propagating `transitionend` events (#275).
170
190
  if (event && event.target === viewer) {
@@ -198,7 +218,7 @@ export default {
198
218
  addListener(image, EVENT_TRANSITION_END, onImageTransitionEnd, {
199
219
  once: true,
200
220
  });
201
- this.zoomTo(0, false, null, null, true);
221
+ this.zoomTo(0, false, null, true);
202
222
  } else {
203
223
  onImageTransitionEnd();
204
224
  }
@@ -215,13 +235,20 @@ export default {
215
235
  * @returns {Viewer} this
216
236
  */
217
237
  view(index = this.options.initialViewIndex) {
238
+ const previousIndex = this.index;
239
+
218
240
  index = Number(index) || 0;
219
241
 
220
242
  if (this.hiding || this.played || index < 0 || index >= this.length
221
- || (this.viewed && index === this.index)) {
243
+ || (this.viewed && index === previousIndex)) {
222
244
  return this;
223
245
  }
224
246
 
247
+ const originalEvent = this.actionEvent || this.viewOriginalEvent || null;
248
+
249
+ this.actionEvent = null;
250
+ this.viewOriginalEvent = originalEvent;
251
+
225
252
  if (!this.isShown) {
226
253
  this.index = index;
227
254
  return this.show();
@@ -237,20 +264,19 @@ export default {
237
264
  title,
238
265
  canvas,
239
266
  } = this;
240
- const item = this.items[index];
267
+ let item = this.getItem(index);
268
+
269
+ if (!item) {
270
+ this.initList(index);
271
+ item = this.getItem(index);
272
+ }
273
+
241
274
  const img = item.querySelector('img');
242
275
  const url = getData(img, 'originalUrl');
243
276
  const alt = img.getAttribute('alt');
244
277
  const image = document.createElement('img');
245
278
 
246
- forEach(options.inheritedAttributes, (name) => {
247
- const value = img.getAttribute(name);
248
-
249
- if (value !== null) {
250
- image.setAttribute(name, value);
251
- }
252
- });
253
-
279
+ inheritAttributes(image, img, options.inheritedAttributes);
254
280
  image.src = url;
255
281
  image.alt = alt;
256
282
 
@@ -264,11 +290,14 @@ export default {
264
290
  originalImage: this.images[index],
265
291
  index,
266
292
  image,
293
+ originalEvent,
267
294
  }) === false || !this.isShown || this.hiding || this.played) {
268
295
  return this;
269
296
  }
270
297
 
271
- const activeItem = this.items[this.index];
298
+ this.hideMagnifier();
299
+
300
+ const activeItem = this.getItem(previousIndex);
272
301
 
273
302
  if (activeItem) {
274
303
  removeClass(activeItem, CLASS_ACTIVE);
@@ -309,72 +338,110 @@ export default {
309
338
  title.innerHTML = escapeHTMLEntities(isFunction(render)
310
339
  ? render.call(this, image, imageData)
311
340
  : `${alt} (${imageData.naturalWidth} × ${imageData.naturalHeight})`);
312
- };
313
- let onLoad;
314
- let onError;
315
341
 
316
- addListener(element, EVENT_VIEWED, onViewed, {
317
- once: true,
318
- });
319
-
320
- this.viewing = {
321
- abort: () => {
322
- removeListener(element, EVENT_VIEWED, onViewed);
342
+ // Preload the image in the direction of navigation
343
+ if (options.preload) {
344
+ const direction = index < previousIndex ? -1 : 1;
345
+ let nextIndex = index + direction;
323
346
 
324
- if (image.complete) {
325
- if (this.imageRendering) {
326
- this.imageRendering.abort();
327
- } else if (this.imageInitializing) {
328
- this.imageInitializing.abort();
347
+ if (nextIndex < 0 || nextIndex >= this.length) {
348
+ if (options.loop) {
349
+ nextIndex = direction > 0 ? 0 : this.length - 1;
350
+ } else {
351
+ nextIndex = -1;
329
352
  }
330
- } else {
331
- // Cancel download to save bandwidth.
332
- image.src = '';
333
- removeListener(image, EVENT_LOAD, onLoad);
353
+ }
334
354
 
335
- if (this.timeout) {
336
- clearTimeout(this.timeout);
337
- }
355
+ if (nextIndex !== index) {
356
+ const nextImage = this.images[nextIndex];
357
+ const preloadedImage = document.createElement('img');
358
+
359
+ inheritAttributes(preloadedImage, nextImage, options.inheritedAttributes);
360
+ preloadedImage.src = this.getImageURL(nextImage) || nextImage.src;
338
361
  }
339
- },
362
+ }
340
363
  };
341
364
 
342
- if (image.complete) {
343
- this.load();
344
- } else {
345
- addListener(image, EVENT_LOAD, onLoad = () => {
346
- removeListener(image, EVENT_ERROR, onError);
347
- this.load();
348
- }, {
349
- once: true,
350
- });
351
- addListener(image, EVENT_ERROR, onError = () => {
365
+ addListener(element, EVENT_VIEWED, onViewed, {
366
+ once: true,
367
+ });
368
+
369
+ const loadImage = (isFallback = false) => {
370
+ let onLoad;
371
+ let onError;
372
+
373
+ const clean = () => {
352
374
  removeListener(image, EVENT_LOAD, onLoad);
375
+ removeListener(image, EVENT_ERROR, onError);
353
376
 
354
377
  if (this.timeout) {
355
378
  clearTimeout(this.timeout);
356
379
  this.timeout = false;
357
380
  }
381
+ };
382
+
383
+ this.viewing = {
384
+ abort: () => {
385
+ removeListener(element, EVENT_VIEWED, onViewed);
386
+
387
+ if (image.complete) {
388
+ if (this.imageRendering) {
389
+ this.imageRendering.abort();
390
+ } else if (this.imageInitializing) {
391
+ this.imageInitializing.abort();
392
+ }
393
+ } else {
394
+ // Cancel download to save bandwidth.
395
+ image.src = '';
396
+ clean();
397
+ }
398
+ },
399
+ };
400
+
401
+ if (image.complete) {
402
+ this.load();
403
+ } else {
404
+ addListener(image, EVENT_LOAD, onLoad = () => {
405
+ clean();
406
+ this.load();
407
+ }, {
408
+ once: true,
409
+ });
410
+ addListener(image, EVENT_ERROR, onError = () => {
411
+ clean();
358
412
 
359
- removeClass(image, CLASS_INVISIBLE);
413
+ if (!isFallback) {
414
+ const fallbackSrc = img.src;
360
415
 
361
- if (options.loading) {
362
- removeClass(this.canvas, CLASS_LOADING);
416
+ if (fallbackSrc && fallbackSrc !== image.src) {
417
+ image.src = fallbackSrc;
418
+ loadImage(true);
419
+ return;
420
+ }
421
+ }
422
+
423
+ removeClass(image, CLASS_INVISIBLE);
424
+
425
+ if (options.loading) {
426
+ removeClass(this.canvas, CLASS_LOADING);
427
+ }
428
+ }, {
429
+ once: true,
430
+ });
431
+
432
+ if (this.timeout) {
433
+ clearTimeout(this.timeout);
363
434
  }
364
- }, {
365
- once: true,
366
- });
367
435
 
368
- if (this.timeout) {
369
- clearTimeout(this.timeout);
436
+ // Make the image visible if it fails to load within 1s
437
+ this.timeout = setTimeout(() => {
438
+ removeClass(image, CLASS_INVISIBLE);
439
+ this.timeout = false;
440
+ }, 1000);
370
441
  }
442
+ };
371
443
 
372
- // Make the image visible if it fails to load within 1s
373
- this.timeout = setTimeout(() => {
374
- removeClass(image, CLASS_INVISIBLE);
375
- this.timeout = false;
376
- }, 1000);
377
- }
444
+ loadImage();
378
445
 
379
446
  return this;
380
447
  },
@@ -435,10 +502,9 @@ export default {
435
502
  * Move the image to an absolute point.
436
503
  * @param {number} x - The new position in the horizontal direction.
437
504
  * @param {number} [y=x] - The new position in the vertical direction.
438
- * @param {Event} [_originalEvent=null] - The original event if any.
439
505
  * @returns {Viewer} this
440
506
  */
441
- moveTo(x, y = x, _originalEvent = null) {
507
+ moveTo(x, y = x) {
442
508
  const { element, options, imageData } = this;
443
509
 
444
510
  x = Number(x);
@@ -462,6 +528,10 @@ export default {
462
528
  }
463
529
 
464
530
  if (changed) {
531
+ const originalEvent = this.actionEvent || null;
532
+
533
+ this.actionEvent = null;
534
+
465
535
  if (isFunction(options.move)) {
466
536
  addListener(element, EVENT_MOVE, options.move, {
467
537
  once: true,
@@ -473,7 +543,7 @@ export default {
473
543
  y,
474
544
  oldX,
475
545
  oldY,
476
- originalEvent: _originalEvent,
546
+ originalEvent,
477
547
  }) === false) {
478
548
  return this;
479
549
  }
@@ -482,6 +552,7 @@ export default {
482
552
  imageData.y = y;
483
553
  imageData.left = x;
484
554
  imageData.top = y;
555
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'move'));
485
556
  this.moving = true;
486
557
  this.renderImage(() => {
487
558
  this.moving = false;
@@ -497,7 +568,7 @@ export default {
497
568
  y,
498
569
  oldX,
499
570
  oldY,
500
- originalEvent: _originalEvent,
571
+ originalEvent,
501
572
  }, {
502
573
  cancelable: false,
503
574
  });
@@ -531,6 +602,9 @@ export default {
531
602
 
532
603
  if (isNumber(degree) && this.viewed && !this.played && options.rotatable) {
533
604
  const oldDegree = imageData.rotate;
605
+ const originalEvent = this.actionEvent || null;
606
+
607
+ this.actionEvent = null;
534
608
 
535
609
  if (isFunction(options.rotate)) {
536
610
  addListener(element, EVENT_ROTATE, options.rotate, {
@@ -541,11 +615,13 @@ export default {
541
615
  if (dispatchEvent(element, EVENT_ROTATE, {
542
616
  degree,
543
617
  oldDegree,
618
+ originalEvent,
544
619
  }) === false) {
545
620
  return this;
546
621
  }
547
622
 
548
623
  imageData.rotate = degree;
624
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'rotate'));
549
625
  this.rotating = true;
550
626
  this.renderImage(() => {
551
627
  this.rotating = false;
@@ -559,6 +635,7 @@ export default {
559
635
  dispatchEvent(element, EVENT_ROTATED, {
560
636
  degree,
561
637
  oldDegree,
638
+ originalEvent,
562
639
  }, {
563
640
  cancelable: false,
564
641
  });
@@ -620,6 +697,10 @@ export default {
620
697
  }
621
698
 
622
699
  if (changed) {
700
+ const originalEvent = this.actionEvent || null;
701
+
702
+ this.actionEvent = null;
703
+
623
704
  if (isFunction(options.scale)) {
624
705
  addListener(element, EVENT_SCALE, options.scale, {
625
706
  once: true,
@@ -631,12 +712,14 @@ export default {
631
712
  scaleY,
632
713
  oldScaleX,
633
714
  oldScaleY,
715
+ originalEvent,
634
716
  }) === false) {
635
717
  return this;
636
718
  }
637
719
 
638
720
  imageData.scaleX = scaleX;
639
721
  imageData.scaleY = scaleY;
722
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'scale'));
640
723
  this.scaling = true;
641
724
  this.renderImage(() => {
642
725
  this.scaling = false;
@@ -652,6 +735,7 @@ export default {
652
735
  scaleY,
653
736
  oldScaleX,
654
737
  oldScaleY,
738
+ originalEvent,
655
739
  }, {
656
740
  cancelable: false,
657
741
  });
@@ -667,10 +751,9 @@ export default {
667
751
  * @param {number} ratio - The target ratio.
668
752
  * @param {boolean} [showTooltip=false] - Indicates whether to show the tooltip.
669
753
  * @param {Object} [pivot] - The pivot point coordinate for zooming.
670
- * @param {Event} [_originalEvent=null] - The original event if any.
671
754
  * @returns {Viewer} this
672
755
  */
673
- zoom(ratio, showTooltip = false, pivot = null, _originalEvent = null) {
756
+ zoom(ratio, showTooltip = false, pivot = null) {
674
757
  const { imageData } = this;
675
758
 
676
759
  ratio = Number(ratio);
@@ -685,7 +768,6 @@ export default {
685
768
  (imageData.width * ratio) / imageData.naturalWidth,
686
769
  showTooltip,
687
770
  pivot,
688
- _originalEvent,
689
771
  );
690
772
 
691
773
  return this;
@@ -696,11 +778,10 @@ export default {
696
778
  * @param {number} ratio - The target ratio.
697
779
  * @param {boolean} [showTooltip] - Indicates whether to show the tooltip.
698
780
  * @param {Object} [pivot] - The pivot point coordinate for zooming.
699
- * @param {Event} [_originalEvent=null] - The original event if any.
700
- * @param {Event} [_zoomable=false] - Indicates if the current zoom is available or not.
781
+ * @param {boolean} [_zoomable=false] - Indicates if the current zoom is available or not.
701
782
  * @returns {Viewer} this
702
783
  */
703
- zoomTo(ratio, showTooltip = false, pivot = null, _originalEvent = null, _zoomable = false) {
784
+ zoomTo(ratio, showTooltip = false, pivot = null, _zoomable = false) {
704
785
  const {
705
786
  element,
706
787
  options,
@@ -720,14 +801,22 @@ export default {
720
801
 
721
802
  if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
722
803
  if (!_zoomable) {
723
- const minZoomRatio = Math.max(0.01, options.minZoomRatio);
724
- const maxZoomRatio = Math.min(100, options.maxZoomRatio);
804
+ const minZoomRatio = Math.max(0.01, isFunction(options.minZoomRatio)
805
+ ? options.minZoomRatio.call(this, this.image, imageData)
806
+ : options.minZoomRatio);
807
+ const maxZoomRatio = Math.min(100, isFunction(options.maxZoomRatio)
808
+ ? options.maxZoomRatio.call(this, this.image, imageData)
809
+ : options.maxZoomRatio);
725
810
 
726
811
  ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
727
812
  }
728
813
 
729
- if (_originalEvent) {
730
- switch (_originalEvent.type) {
814
+ const originalEvent = this.actionEvent || null;
815
+
816
+ this.actionEvent = null;
817
+
818
+ if (originalEvent && originalEvent.type !== EVENT_CLICK) {
819
+ switch (originalEvent.type) {
731
820
  case 'wheel':
732
821
  if (options.zoomRatio >= 0.055 && ratio > 0.95 && ratio < 1.05) {
733
822
  ratio = 1;
@@ -761,20 +850,20 @@ export default {
761
850
  if (dispatchEvent(element, EVENT_ZOOM, {
762
851
  ratio,
763
852
  oldRatio,
764
- originalEvent: _originalEvent,
853
+ originalEvent,
765
854
  }) === false) {
766
855
  return this;
767
856
  }
768
857
 
769
858
  this.zooming = true;
770
859
 
771
- if (_originalEvent) {
860
+ if (originalEvent && originalEvent.type !== EVENT_CLICK) {
772
861
  const offset = getOffset(this.viewer);
773
862
  const center = pointers && Object.keys(pointers).length > 0
774
863
  ? getPointersCenter(pointers)
775
864
  : {
776
- pageX: _originalEvent.pageX,
777
- pageY: _originalEvent.pageY,
865
+ pageX: originalEvent.pageX,
866
+ pageY: originalEvent.pageY,
778
867
  };
779
868
 
780
869
  // Zoom from the triggering point of the event
@@ -795,6 +884,7 @@ export default {
795
884
  imageData.height = newHeight;
796
885
  imageData.oldRatio = oldRatio;
797
886
  imageData.ratio = ratio;
887
+ toggleClass(this.image, CLASS_TRANSITION, isTransitionEnabled(options, 'zoom'));
798
888
  this.renderImage(() => {
799
889
  this.zooming = false;
800
890
 
@@ -807,7 +897,7 @@ export default {
807
897
  dispatchEvent(element, EVENT_ZOOMED, {
808
898
  ratio,
809
899
  oldRatio,
810
- originalEvent: _originalEvent,
900
+ originalEvent,
811
901
  }, {
812
902
  cancelable: false,
813
903
  });
@@ -832,6 +922,9 @@ export default {
832
922
  }
833
923
 
834
924
  const { element, options } = this;
925
+ const originalEvent = this.actionEvent || null;
926
+
927
+ this.actionEvent = null;
835
928
 
836
929
  if (isFunction(options.play)) {
837
930
  addListener(element, EVENT_PLAY, options.play, {
@@ -839,7 +932,9 @@ export default {
839
932
  });
840
933
  }
841
934
 
842
- if (dispatchEvent(element, EVENT_PLAY) === false) {
935
+ if (dispatchEvent(element, EVENT_PLAY, {
936
+ originalEvent,
937
+ }) === false) {
843
938
  return this;
844
939
  }
845
940
 
@@ -857,18 +952,18 @@ export default {
857
952
  }
858
953
 
859
954
  addClass(player, CLASS_SHOW);
860
- forEach(this.items, (item, i) => {
861
- const img = item.querySelector('img');
955
+ player.removeAttribute('aria-hidden');
956
+ forEach(this.images, (originalImage, i) => {
862
957
  const image = document.createElement('img');
863
958
 
864
- image.src = getData(img, 'originalUrl');
865
- image.alt = img.getAttribute('alt');
866
- image.referrerPolicy = img.referrerPolicy;
959
+ image.src = this.getImageURL(originalImage) || originalImage.src;
960
+ image.alt = originalImage.alt || '';
961
+ image.referrerPolicy = originalImage.referrerPolicy;
867
962
  total += 1;
868
963
  addClass(image, CLASS_FADE);
869
- toggleClass(image, CLASS_TRANSITION, options.transition);
964
+ toggleClass(image, CLASS_TRANSITION, isTransitionEnabled(options, 'play'));
870
965
 
871
- if (hasClass(item, CLASS_ACTIVE)) {
966
+ if (i === this.index) {
872
967
  addClass(image, CLASS_IN);
873
968
  index = i;
874
969
  }
@@ -883,26 +978,74 @@ export default {
883
978
  if (isNumber(options.interval) && options.interval > 0) {
884
979
  const prev = () => {
885
980
  clearTimeout(this.playing.timeout);
981
+
982
+ const currentOriginalEvent = this.actionEvent || null;
983
+
984
+ this.actionEvent = null;
985
+
986
+ let prevIndex = index - 1;
987
+
988
+ prevIndex = prevIndex >= 0 ? prevIndex : total - 1;
989
+
990
+ if (isFunction(options.playing)) {
991
+ addListener(element, EVENT_PLAYING, options.playing, {
992
+ once: true,
993
+ });
994
+ }
995
+
996
+ if (dispatchEvent(element, EVENT_PLAYING, {
997
+ originalImage: this.images[prevIndex],
998
+ index: prevIndex,
999
+ image: list[prevIndex],
1000
+ originalEvent: currentOriginalEvent,
1001
+ }) === false) {
1002
+ this.playing.timeout = options.autoplay ? setTimeout(prev, options.interval) : null;
1003
+ return;
1004
+ }
1005
+
886
1006
  removeClass(list[index], CLASS_IN);
887
- index -= 1;
888
- index = index >= 0 ? index : total - 1;
1007
+ index = prevIndex;
889
1008
  addClass(list[index], CLASS_IN);
890
- this.playing.timeout = setTimeout(prev, options.interval);
1009
+ this.playing.timeout = options.autoplay ? setTimeout(prev, options.interval) : null;
891
1010
  };
892
1011
  const next = () => {
893
1012
  clearTimeout(this.playing.timeout);
1013
+
1014
+ const currentOriginalEvent = this.actionEvent || null;
1015
+
1016
+ this.actionEvent = null;
1017
+
1018
+ let nextIndex = index + 1;
1019
+
1020
+ nextIndex = nextIndex < total ? nextIndex : 0;
1021
+
1022
+ if (isFunction(options.playing)) {
1023
+ addListener(element, EVENT_PLAYING, options.playing, {
1024
+ once: true,
1025
+ });
1026
+ }
1027
+
1028
+ if (dispatchEvent(element, EVENT_PLAYING, {
1029
+ originalImage: this.images[nextIndex],
1030
+ index: nextIndex,
1031
+ image: list[nextIndex],
1032
+ originalEvent: currentOriginalEvent,
1033
+ }) === false) {
1034
+ this.playing.timeout = options.autoplay ? setTimeout(next, options.interval) : null;
1035
+ return;
1036
+ }
1037
+
894
1038
  removeClass(list[index], CLASS_IN);
895
- index += 1;
896
- index = index < total ? index : 0;
1039
+ index = nextIndex;
897
1040
  addClass(list[index], CLASS_IN);
898
- this.playing.timeout = setTimeout(next, options.interval);
1041
+ this.playing.timeout = options.autoplay ? setTimeout(next, options.interval) : null;
899
1042
  };
900
1043
 
901
1044
  if (total > 1) {
902
1045
  this.playing = {
903
1046
  prev,
904
1047
  next,
905
- timeout: setTimeout(next, options.interval),
1048
+ timeout: options.autoplay ? setTimeout(next, options.interval) : null,
906
1049
  };
907
1050
  }
908
1051
  }
@@ -910,13 +1053,19 @@ export default {
910
1053
  return this;
911
1054
  },
912
1055
 
913
- // Stop play
1056
+ /**
1057
+ * Stop play
1058
+ * @returns {Viewer} this
1059
+ */
914
1060
  stop() {
915
1061
  if (!this.played) {
916
1062
  return this;
917
1063
  }
918
1064
 
919
1065
  const { element, options } = this;
1066
+ const originalEvent = this.actionEvent || null;
1067
+
1068
+ this.actionEvent = null;
920
1069
 
921
1070
  if (isFunction(options.stop)) {
922
1071
  addListener(element, EVENT_STOP, options.stop, {
@@ -924,7 +1073,9 @@ export default {
924
1073
  });
925
1074
  }
926
1075
 
927
- if (dispatchEvent(element, EVENT_STOP) === false) {
1076
+ if (dispatchEvent(element, EVENT_STOP, {
1077
+ originalEvent,
1078
+ }) === false) {
928
1079
  return this;
929
1080
  }
930
1081
 
@@ -937,6 +1088,7 @@ export default {
937
1088
  removeListener(image, EVENT_LOAD, this.onLoadWhenPlay);
938
1089
  });
939
1090
  removeClass(player, CLASS_SHOW);
1091
+ player.setAttribute('aria-hidden', true);
940
1092
  player.innerHTML = '';
941
1093
  this.exitFullscreen();
942
1094
 
@@ -960,12 +1112,10 @@ export default {
960
1112
  this.open();
961
1113
  addClass(this.button, CLASS_FULLSCREEN_EXIT);
962
1114
 
963
- if (options.transition) {
964
- removeClass(list, CLASS_TRANSITION);
1115
+ removeClass(list, CLASS_TRANSITION);
965
1116
 
966
- if (this.viewed) {
967
- removeClass(image, CLASS_TRANSITION);
968
- }
1117
+ if (this.viewed) {
1118
+ removeClass(image, CLASS_TRANSITION);
969
1119
  }
970
1120
 
971
1121
  addClass(viewer, CLASS_FIXED);
@@ -987,14 +1137,7 @@ export default {
987
1137
 
988
1138
  if (this.viewed) {
989
1139
  this.initImage(() => {
990
- this.renderImage(() => {
991
- if (options.transition) {
992
- setTimeout(() => {
993
- addClass(image, CLASS_TRANSITION);
994
- addClass(list, CLASS_TRANSITION);
995
- }, 0);
996
- }
997
- });
1140
+ this.renderImage();
998
1141
  });
999
1142
  }
1000
1143
 
@@ -1018,12 +1161,10 @@ export default {
1018
1161
  this.close();
1019
1162
  removeClass(this.button, CLASS_FULLSCREEN_EXIT);
1020
1163
 
1021
- if (options.transition) {
1022
- removeClass(list, CLASS_TRANSITION);
1164
+ removeClass(list, CLASS_TRANSITION);
1023
1165
 
1024
- if (this.viewed) {
1025
- removeClass(image, CLASS_TRANSITION);
1026
- }
1166
+ if (this.viewed) {
1167
+ removeClass(image, CLASS_TRANSITION);
1027
1168
  }
1028
1169
 
1029
1170
  if (options.focus) {
@@ -1044,14 +1185,7 @@ export default {
1044
1185
 
1045
1186
  if (this.viewed) {
1046
1187
  this.initImage(() => {
1047
- this.renderImage(() => {
1048
- if (options.transition) {
1049
- setTimeout(() => {
1050
- addClass(image, CLASS_TRANSITION);
1051
- addClass(list, CLASS_TRANSITION);
1052
- }, 0);
1053
- }
1054
- });
1188
+ this.renderImage();
1055
1189
  });
1056
1190
  }
1057
1191
 
@@ -1069,7 +1203,7 @@ export default {
1069
1203
  tooltipBox.textContent = `${Math.round(imageData.ratio * 100)}%`;
1070
1204
 
1071
1205
  if (!this.tooltipping) {
1072
- if (options.transition) {
1206
+ if (isTransitionEnabled(options, 'tooltip')) {
1073
1207
  if (this.fading) {
1074
1208
  dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
1075
1209
  }
@@ -1091,7 +1225,7 @@ export default {
1091
1225
  }
1092
1226
 
1093
1227
  this.tooltipping = setTimeout(() => {
1094
- if (options.transition) {
1228
+ if (isTransitionEnabled(options, 'tooltip')) {
1095
1229
  addListener(tooltipBox, EVENT_TRANSITION_END, () => {
1096
1230
  removeClass(tooltipBox, CLASS_SHOW);
1097
1231
  removeClass(tooltipBox, CLASS_FADE);
@@ -1117,14 +1251,13 @@ export default {
1117
1251
 
1118
1252
  /**
1119
1253
  * Toggle the image size between its current size and natural size
1120
- * @param {Event} [_originalEvent=null] - The original event if any.
1121
1254
  * @returns {Viewer} this
1122
1255
  */
1123
- toggle(_originalEvent = null) {
1256
+ toggle() {
1124
1257
  if (this.imageData.ratio === 1) {
1125
- this.zoomTo(this.imageData.oldRatio, true, null, _originalEvent);
1258
+ this.zoomTo(this.imageData.oldRatio, true);
1126
1259
  } else {
1127
- this.zoomTo(1, true, null, _originalEvent);
1260
+ this.zoomTo(1, true);
1128
1261
  }
1129
1262
 
1130
1263
  return this;
@@ -1140,10 +1273,18 @@ export default {
1140
1273
  return this;
1141
1274
  },
1142
1275
 
1143
- // Update viewer when images changed
1144
- update() {
1276
+ /**
1277
+ * Update the viewer when images or options changed.
1278
+ * @param {Object} [updateOptions] - The options to update.
1279
+ * @returns {Viewer} this
1280
+ */
1281
+ update(updateOptions) {
1145
1282
  const { element, options, isImg } = this;
1146
1283
 
1284
+ if (isPlainObject(updateOptions)) {
1285
+ assign(options, updateOptions);
1286
+ }
1287
+
1147
1288
  // Destroy viewer if the target image was deleted
1148
1289
  if (isImg && !element.parentNode) {
1149
1290
  return this.destroy();
@@ -1171,9 +1312,10 @@ export default {
1171
1312
  if (this.ready) {
1172
1313
  const changedIndexes = [];
1173
1314
 
1174
- forEach(this.items, (item, i) => {
1315
+ forEach(this.items, (item) => {
1175
1316
  const img = item.querySelector('img');
1176
- const image = images[i];
1317
+ const index = Number(getData(item, 'index'));
1318
+ const image = images[index];
1177
1319
 
1178
1320
  if (image && img) {
1179
1321
  if (
@@ -1182,10 +1324,10 @@ export default {
1182
1324
  // Title changed (#408)
1183
1325
  || image.alt !== img.alt
1184
1326
  ) {
1185
- changedIndexes.push(i);
1327
+ changedIndexes.push(index);
1186
1328
  }
1187
1329
  } else {
1188
- changedIndexes.push(i);
1330
+ changedIndexes.push(index);
1189
1331
  }
1190
1332
  });
1191
1333
 
@@ -1204,7 +1346,7 @@ export default {
1204
1346
  this.viewed = false;
1205
1347
  this.view(Math.max(Math.min(this.index - changedIndex, this.length - 1), 0));
1206
1348
  } else {
1207
- const activeItem = this.items[this.index];
1349
+ const activeItem = this.getItem(this.index);
1208
1350
 
1209
1351
  // Reactivate the current viewing item after reset the list.
1210
1352
  addClass(activeItem, CLASS_ACTIVE);