viewerjs 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -106,6 +106,34 @@ export function forEach(data, callback) {
106
106
  return data;
107
107
  }
108
108
 
109
+ /**
110
+ * Inherit attributes from the original image.
111
+ * @param {Element} image - The target image.
112
+ * @param {Element} originalImage - The original image.
113
+ * @param {Array} inheritedAttributes - The attributes to inherit.
114
+ */
115
+ export function inheritAttributes(image, originalImage, inheritedAttributes) {
116
+ forEach(inheritedAttributes, (name) => {
117
+ const value = originalImage.getAttribute(name);
118
+
119
+ if (value !== null) {
120
+ image.setAttribute(name, value);
121
+ }
122
+ });
123
+ }
124
+
125
+ /**
126
+ * Check if transition is enabled for the given action.
127
+ * @param {Object} options - The viewer options.
128
+ * @param {string} action - The transition action.
129
+ * @returns {boolean} Returns `true` if transition is enabled.
130
+ */
131
+ export function isTransitionEnabled(options, action) {
132
+ const { transition } = options;
133
+
134
+ return transition && transition[action] !== false;
135
+ }
136
+
109
137
  /**
110
138
  * Extend the given object.
111
139
  * @param {*} obj - The object to be extended.
@@ -495,11 +523,8 @@ export function getTransforms({
495
523
  values.push(`rotate(${rotate}deg)`);
496
524
  }
497
525
 
498
- if (isNumber(scaleX) && scaleX !== 1) {
526
+ if (isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1)) {
499
527
  values.push(`scaleX(${scaleX})`);
500
- }
501
-
502
- if (isNumber(scaleY) && scaleY !== 1) {
503
528
  values.push(`scaleY(${scaleY})`);
504
529
  }
505
530
 
@@ -552,14 +577,7 @@ export function getImageNaturalSizes(image, options, callback) {
552
577
  }
553
578
  };
554
579
 
555
- forEach(options.inheritedAttributes, (name) => {
556
- const value = image.getAttribute(name);
557
-
558
- if (value !== null) {
559
- newImage.setAttribute(name, value);
560
- }
561
- });
562
-
580
+ inheritAttributes(newImage, image, options.inheritedAttributes);
563
581
  newImage.src = image.src;
564
582
 
565
583
  // iOS Safari will convert the image automatically
@@ -633,6 +651,44 @@ export function getMaxZoomRatio(pointers) {
633
651
  return ratios[0];
634
652
  }
635
653
 
654
+ /**
655
+ * Get the max rotation degree of a group of pointers.
656
+ * @param {Object} pointers - The target pointers.
657
+ * @returns {number} The result degree.
658
+ */
659
+ export function getMaxRotateDegree(pointers) {
660
+ const pointers2 = { ...pointers };
661
+ const degrees = [];
662
+
663
+ forEach(pointers, (pointer, pointerId) => {
664
+ delete pointers2[pointerId];
665
+
666
+ forEach(pointers2, (pointer2) => {
667
+ const start = Math.atan2(
668
+ pointer2.startY - pointer.startY,
669
+ pointer2.startX - pointer.startX,
670
+ );
671
+ const end = Math.atan2(
672
+ pointer2.endY - pointer.endY,
673
+ pointer2.endX - pointer.endX,
674
+ );
675
+ let radians = end - start;
676
+
677
+ if (radians > Math.PI) {
678
+ radians -= Math.PI * 2;
679
+ } else if (radians < -Math.PI) {
680
+ radians += Math.PI * 2;
681
+ }
682
+
683
+ degrees.push((radians * 180) / Math.PI);
684
+ });
685
+ });
686
+
687
+ degrees.sort((a, b) => Math.abs(b) - Math.abs(a));
688
+
689
+ return degrees[0] || 0;
690
+ }
691
+
636
692
  /**
637
693
  * Get a pointer from an event object.
638
694
  * @param {Object} event - The target event object.
package/src/js/viewer.js CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  assign,
30
30
  dispatchEvent,
31
31
  forEach,
32
+ getData,
32
33
  getResponsiveClass,
33
34
  hyphenate,
34
35
  isFunction,
@@ -51,15 +52,16 @@ const getUniqueID = ((id) => (() => {
51
52
  class Viewer {
52
53
  /**
53
54
  * Create a new Viewer.
54
- * @param {Element} element - The target element for viewing.
55
+ * @param {Element} element - The target image, or a container of images, to view.
55
56
  * @param {Object} [options={}] - The configuration options.
56
57
  */
57
58
  constructor(element, options = {}) {
58
- if (!element || element.nodeType !== 1) {
59
+ if (!element || (element.nodeType !== 1 && element.nodeType !== 11)) {
59
60
  throw new Error('The first argument is required and must be an element.');
60
61
  }
61
62
 
62
63
  this.element = element;
64
+ this.ownerDocument = element.ownerDocument || element.host.ownerDocument;
63
65
  this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
64
66
  this.action = false;
65
67
  this.fading = false;
@@ -123,7 +125,7 @@ class Viewer {
123
125
  this.initBody();
124
126
 
125
127
  // Override `transition` option if it is not supported
126
- if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
128
+ if (isUndefined(this.ownerDocument.createElement(NAMESPACE).style.transition)) {
127
129
  options.transition = false;
128
130
  }
129
131
 
@@ -217,6 +219,7 @@ class Viewer {
217
219
  const title = viewer.querySelector(`.${NAMESPACE}-title`);
218
220
  const toolbar = viewer.querySelector(`.${NAMESPACE}-toolbar`);
219
221
  const navbar = viewer.querySelector(`.${NAMESPACE}-navbar`);
222
+ const navigation = viewer.querySelector(`.${NAMESPACE}-navigation`);
220
223
  const button = viewer.querySelector(`.${NAMESPACE}-button`);
221
224
  const canvas = viewer.querySelector(`.${NAMESPACE}-canvas`);
222
225
 
@@ -225,9 +228,12 @@ class Viewer {
225
228
  this.title = title;
226
229
  this.toolbar = toolbar;
227
230
  this.navbar = navbar;
231
+ this.navigation = navigation;
228
232
  this.button = button;
229
233
  this.canvas = canvas;
230
234
  this.footer = viewer.querySelector(`.${NAMESPACE}-footer`);
235
+ this.magnifier = viewer.querySelector(`.${NAMESPACE}-magnifier`);
236
+ this.magnifierImage = viewer.querySelector(`.${NAMESPACE}-magnifier-image`);
231
237
  this.tooltipBox = viewer.querySelector(`.${NAMESPACE}-tooltip`);
232
238
  this.player = viewer.querySelector(`.${NAMESPACE}-player`);
233
239
  this.list = viewer.querySelector(`.${NAMESPACE}-list`);
@@ -237,11 +243,54 @@ class Viewer {
237
243
  addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title)
238
244
  ? options.title[0]
239
245
  : options.title));
240
- addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
246
+ const navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
247
+ let navbarShow = options.navbar;
248
+ const navbarSize = !isUndefined(navbarOptions.size) ? navbarOptions.size : options.navbar;
249
+
250
+ if (isPlainObject(options.navbar)) {
251
+ navbarShow = !isUndefined(navbarOptions.show) ? navbarOptions.show : true;
252
+ }
253
+
254
+ addClass(navbar, !navbarShow ? CLASS_HIDE : getResponsiveClass(navbarShow));
255
+
256
+ if (['small', 'medium', 'large'].indexOf(navbarSize) !== -1) {
257
+ addClass(navbar, `${NAMESPACE}-${navbarSize}`);
258
+ }
259
+
260
+ if (isPlainObject(options.navigation)) {
261
+ forEach(navigation.querySelectorAll('[role="button"]'), (item) => {
262
+ const name = getData(item, DATA_ACTION);
263
+ const value = options.navigation[name];
264
+ const deep = isPlainObject(value);
265
+ const show = deep && !isUndefined(value.show) ? value.show : value;
266
+ const size = deep && !isUndefined(value.size)
267
+ ? value.size
268
+ : value;
269
+
270
+ toggleClass(item, CLASS_HIDE, !show);
271
+
272
+ if (isNumber(show)) {
273
+ addClass(item, getResponsiveClass(show));
274
+ }
275
+
276
+ if (['small', 'large'].indexOf(size) !== -1) {
277
+ addClass(item, `${NAMESPACE}-${size}`);
278
+ }
279
+ });
280
+ } else {
281
+ addClass(
282
+ navigation,
283
+ !options.navigation ? CLASS_HIDE : getResponsiveClass(options.navigation),
284
+ );
285
+ }
286
+
241
287
  toggleClass(button, CLASS_HIDE, !options.button);
242
288
 
243
289
  if (options.keyboard) {
244
290
  button.setAttribute('tabindex', 0);
291
+ forEach(navigation.querySelectorAll('[role="button"]'), (item) => {
292
+ item.setAttribute('tabindex', 0);
293
+ });
245
294
  }
246
295
 
247
296
  if (options.backdrop) {
@@ -356,7 +405,7 @@ class Viewer {
356
405
  let { container } = options;
357
406
 
358
407
  if (isString(container)) {
359
- container = element.ownerDocument.querySelector(container);
408
+ container = this.ownerDocument.querySelector(container);
360
409
  }
361
410
 
362
411
  if (!container) {
@@ -391,12 +440,13 @@ class Viewer {
391
440
  }
392
441
 
393
442
  /**
394
- * Get the no conflict viewer class.
395
- * @returns {Viewer} The viewer class.
443
+ * Create a new Viewer instance.
444
+ * @param {Element} element - The target image, or a container of images, to view.
445
+ * @param {Object} [options={}] - The configuration options.
446
+ * @returns {Viewer} A new Viewer instance.
396
447
  */
397
- static noConflict() {
398
- window.Viewer = AnotherViewer;
399
- return Viewer;
448
+ static create(element, options) {
449
+ return new Viewer(element, options);
400
450
  }
401
451
 
402
452
  /**
@@ -406,6 +456,15 @@ class Viewer {
406
456
  static setDefaults(options) {
407
457
  assign(DEFAULTS, isPlainObject(options) && options);
408
458
  }
459
+
460
+ /**
461
+ * Get the no conflict viewer class.
462
+ * @returns {Viewer} The viewer class.
463
+ */
464
+ static noConflict() {
465
+ window.Viewer = AnotherViewer;
466
+ return Viewer;
467
+ }
409
468
  }
410
469
 
411
470
  assign(Viewer.prototype, render, events, handlers, methods, others);
package/types/index.d.ts CHANGED
@@ -5,6 +5,7 @@ declare namespace Viewer {
5
5
  export type Filter = (this: Viewer, image: HTMLImageElement) => boolean;
6
6
  export type TitleRenderer = (this: Viewer, image: HTMLImageElement, imageData: Record<string, any>) => string;
7
7
  export type ImageURLResolver = (this: Viewer, image: HTMLImageElement) => string;
8
+ export type ZoomRatio = number | ((this: Viewer, image: HTMLImageElement, imageData: Record<string, any>) => number);
8
9
  export type ToolbarButtonClick = (this: Viewer, event: Event) => void;
9
10
  export type ToolbarOption = boolean | Visibility | ToolbarButtonSize | ToolbarButtonOptions | undefined;
10
11
 
@@ -29,6 +30,44 @@ declare namespace Viewer {
29
30
  [x: string]: ToolbarOption;
30
31
  }
31
32
 
33
+ export interface NavigationButtonOptions {
34
+ show?: boolean | Visibility;
35
+ size?: ToolbarButtonSize;
36
+ }
37
+
38
+ export type NavigationOption = boolean | Visibility | NavigationButtonOptions | undefined;
39
+
40
+ export interface NavbarOptions {
41
+ visibleItemCount?: number;
42
+ show?: boolean | Visibility;
43
+ size?: ToolbarButtonSize;
44
+ }
45
+
46
+ export type NavbarOption = boolean | Visibility | ToolbarButtonSize | NavbarOptions | undefined;
47
+
48
+ export interface NavigationOptions {
49
+ next?: NavigationOption;
50
+ prev?: NavigationOption;
51
+ }
52
+
53
+ export interface TransitionOptions {
54
+ hide?: boolean;
55
+ move?: boolean;
56
+ play?: boolean;
57
+ rotate?: boolean;
58
+ scale?: boolean;
59
+ show?: boolean;
60
+ tooltip?: boolean;
61
+ view?: boolean;
62
+ zoom?: boolean;
63
+ }
64
+
65
+ export interface MagnifierOptions {
66
+ size?: number;
67
+ zoomRatio?: number;
68
+ opacity?: number;
69
+ }
70
+
32
71
  export interface Pivot {
33
72
  x: number;
34
73
  y: number;
@@ -90,21 +129,27 @@ declare namespace Viewer {
90
129
  initialCoverage?: number;
91
130
  initialViewIndex?: number;
92
131
  inline?: boolean;
132
+ autoplay?: boolean;
93
133
  interval?: number;
94
134
  keyboard?: boolean;
95
135
  loading?: boolean;
96
136
  loop?: boolean;
97
- maxZoomRatio?: number;
137
+ magnifier?: boolean | MagnifierOptions;
138
+ maxZoomRatio?: ZoomRatio;
98
139
  minHeight?: number;
99
140
  minWidth?: number;
100
- minZoomRatio?: number;
141
+ minZoomRatio?: ZoomRatio;
101
142
  movable?: boolean;
102
143
  move?: EventHandler<MoveEvent>;
103
144
  moved?: EventHandler<MovedEvent>;
104
- navbar?: boolean | Visibility;
145
+ navbar?: NavbarOption;
146
+ navigation?: boolean | Visibility | NavigationOptions;
105
147
  play?: EventHandler;
148
+ preload?: boolean;
106
149
  ready?: EventHandler;
107
150
  rotatable?: boolean;
151
+ rotateOnGesture?: boolean;
152
+ rotateOnTouch?: boolean;
108
153
  rotate?: EventHandler<RotateEvent>;
109
154
  rotated?: EventHandler<RotatedEvent>;
110
155
  scalable?: boolean;
@@ -118,13 +163,14 @@ declare namespace Viewer {
118
163
  toggleOnDblclick?: boolean;
119
164
  toolbar?: boolean | Visibility | ToolbarOptions;
120
165
  tooltip?: boolean;
121
- transition?: boolean;
166
+ transition?: boolean | TransitionOptions;
122
167
  url?: string | ImageURLResolver;
123
168
  view?: EventHandler;
124
169
  viewed?: EventHandler;
125
170
  zIndex?: number;
126
171
  zIndexInline?: number;
127
172
  zoom?: EventHandler<ZoomEvent>;
173
+ zoomOnGesture?: boolean;
128
174
  zoomOnTouch?: boolean;
129
175
  zoomOnWheel?: boolean;
130
176
  zoomRatio?: number;
@@ -134,7 +180,7 @@ declare namespace Viewer {
134
180
  }
135
181
 
136
182
  declare class Viewer {
137
- constructor(element: HTMLElement, options?: Viewer.Options);
183
+ constructor(element: HTMLElement | ShadowRoot, options?: Viewer.Options);
138
184
  destroy(): Viewer;
139
185
  exit(): Viewer;
140
186
  full(): Viewer;
@@ -154,12 +200,13 @@ declare class Viewer {
154
200
  stop(): Viewer;
155
201
  toggle(): Viewer;
156
202
  tooltip(): Viewer;
157
- update(): Viewer;
203
+ update(options?: Viewer.Options): Viewer;
158
204
  view(index?: number): Viewer;
159
205
  zoom(ratio: number, hasTooltip?: boolean, pivot?: Viewer.Pivot): Viewer;
160
206
  zoomTo(ratio: number, hasTooltip?: boolean, pivot?: Viewer.Pivot): Viewer;
161
- static noConflict(): Viewer;
207
+ static create(element: HTMLElement | ShadowRoot, options?: Viewer.Options): Viewer;
162
208
  static setDefaults(options: Viewer.Options): void;
209
+ static noConflict(): Viewer;
163
210
  }
164
211
 
165
212
  declare module 'viewerjs' {