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/README.md +261 -39
- package/dist/viewer.common.js +750 -205
- package/dist/viewer.css +111 -2
- package/dist/viewer.esm.js +750 -205
- package/dist/viewer.js +750 -205
- package/dist/viewer.min.css +3 -3
- package/dist/viewer.min.js +3 -3
- package/package.json +6 -7
- package/src/css/viewer.css +107 -1
- package/src/css/viewer.scss +107 -1
- package/src/js/constants.js +6 -0
- package/src/js/defaults.js +62 -5
- package/src/js/events.js +31 -4
- package/src/js/handlers.js +232 -10
- package/src/js/methods.js +280 -138
- package/src/js/others.js +60 -6
- package/src/js/render.js +87 -15
- package/src/js/template.js +13 -6
- package/src/js/utilities.js +95 -12
- package/src/js/viewer.js +99 -11
- package/types/index.d.ts +116 -16
package/src/js/others.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ACTION_MOVE,
|
|
3
|
+
ACTION_ROTATE,
|
|
3
4
|
ACTION_SWITCH,
|
|
5
|
+
ACTION_TRANSFORM,
|
|
4
6
|
ACTION_ZOOM,
|
|
5
7
|
CLASS_HIDE,
|
|
6
8
|
CLASS_OPEN,
|
|
@@ -13,6 +15,7 @@ import {
|
|
|
13
15
|
addListener,
|
|
14
16
|
dispatchEvent,
|
|
15
17
|
forEach,
|
|
18
|
+
getMaxRotateDegree,
|
|
16
19
|
getMaxZoomRatio,
|
|
17
20
|
isFunction,
|
|
18
21
|
isPlainObject,
|
|
@@ -106,10 +109,14 @@ export default {
|
|
|
106
109
|
});
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
if (dispatchEvent(element, EVENT_SHOWN
|
|
112
|
+
if (dispatchEvent(element, EVENT_SHOWN, {
|
|
113
|
+
originalEvent: this.showOriginalEvent || null,
|
|
114
|
+
}) === false) {
|
|
110
115
|
return;
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
this.showOriginalEvent = null;
|
|
119
|
+
|
|
113
120
|
if (this.ready && this.isShown && !this.hiding) {
|
|
114
121
|
this.view(this.index);
|
|
115
122
|
}
|
|
@@ -149,14 +156,18 @@ export default {
|
|
|
149
156
|
});
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
dispatchEvent(element, EVENT_HIDDEN,
|
|
159
|
+
dispatchEvent(element, EVENT_HIDDEN, {
|
|
160
|
+
originalEvent: this.hideOriginalEvent || null,
|
|
161
|
+
}, {
|
|
153
162
|
cancelable: false,
|
|
154
163
|
});
|
|
164
|
+
|
|
165
|
+
this.hideOriginalEvent = null;
|
|
155
166
|
}
|
|
156
167
|
},
|
|
157
168
|
|
|
158
169
|
requestFullscreen(options) {
|
|
159
|
-
const document = this
|
|
170
|
+
const { ownerDocument: document } = this;
|
|
160
171
|
|
|
161
172
|
if (this.fulled && !(
|
|
162
173
|
document.fullscreenElement
|
|
@@ -185,7 +196,7 @@ export default {
|
|
|
185
196
|
},
|
|
186
197
|
|
|
187
198
|
exitFullscreen() {
|
|
188
|
-
const document = this
|
|
199
|
+
const { ownerDocument: document } = this;
|
|
189
200
|
|
|
190
201
|
if (this.fulled && (
|
|
191
202
|
document.fullscreenElement
|
|
@@ -223,13 +234,54 @@ export default {
|
|
|
223
234
|
case ACTION_MOVE:
|
|
224
235
|
if (offsetX !== 0 || offsetY !== 0) {
|
|
225
236
|
this.pointerMoved = true;
|
|
226
|
-
this.
|
|
237
|
+
this.actionEvent = event;
|
|
238
|
+
this.move(offsetX, offsetY);
|
|
227
239
|
}
|
|
228
240
|
break;
|
|
229
241
|
|
|
230
242
|
// Zoom the current image
|
|
231
243
|
case ACTION_ZOOM:
|
|
232
|
-
|
|
244
|
+
if (options.zoomable && options.zoomOnTouch) {
|
|
245
|
+
const zoomRatio = getMaxZoomRatio(pointers);
|
|
246
|
+
|
|
247
|
+
if (zoomRatio !== 0) {
|
|
248
|
+
this.actionEvent = event;
|
|
249
|
+
this.zoom(zoomRatio);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
|
|
254
|
+
// Rotate the current image
|
|
255
|
+
case ACTION_ROTATE:
|
|
256
|
+
if (options.rotatable && options.rotateOnTouch) {
|
|
257
|
+
const rotateDegree = getMaxRotateDegree(pointers);
|
|
258
|
+
|
|
259
|
+
if (rotateDegree !== 0) {
|
|
260
|
+
this.actionEvent = event;
|
|
261
|
+
this.rotate(rotateDegree);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
|
|
266
|
+
// Transform the current image
|
|
267
|
+
case ACTION_TRANSFORM:
|
|
268
|
+
if (options.zoomable && options.zoomOnTouch) {
|
|
269
|
+
const zoomRatio = getMaxZoomRatio(pointers);
|
|
270
|
+
|
|
271
|
+
if (zoomRatio !== 0) {
|
|
272
|
+
this.actionEvent = event;
|
|
273
|
+
this.zoom(zoomRatio);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (options.rotatable && options.rotateOnTouch) {
|
|
278
|
+
const rotateDegree = getMaxRotateDegree(pointers);
|
|
279
|
+
|
|
280
|
+
if (rotateDegree !== 0) {
|
|
281
|
+
this.actionEvent = event;
|
|
282
|
+
this.rotate(rotateDegree);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
233
285
|
break;
|
|
234
286
|
|
|
235
287
|
case ACTION_SWITCH: {
|
|
@@ -241,6 +293,8 @@ export default {
|
|
|
241
293
|
// Empty `pointers` as `touchend` event will not be fired after swiped in iOS browsers.
|
|
242
294
|
this.pointers = {};
|
|
243
295
|
|
|
296
|
+
this.actionEvent = event;
|
|
297
|
+
|
|
244
298
|
if (offsetX > 1) {
|
|
245
299
|
this.prev(options.loop);
|
|
246
300
|
} else if (offsetX < -1) {
|
package/src/js/render.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CLASS_ACTIVE,
|
|
2
3
|
CLASS_LOADING,
|
|
3
4
|
CLASS_TRANSITION,
|
|
4
5
|
EVENT_ERROR,
|
|
@@ -11,11 +12,15 @@ import {
|
|
|
11
12
|
addListener,
|
|
12
13
|
assign,
|
|
13
14
|
forEach,
|
|
15
|
+
getData,
|
|
14
16
|
getImageNameFromURL,
|
|
15
17
|
getImageNaturalSizes,
|
|
16
18
|
getTransforms,
|
|
17
19
|
hasClass,
|
|
20
|
+
inheritAttributes,
|
|
18
21
|
isNumber,
|
|
22
|
+
isPlainObject,
|
|
23
|
+
isTransitionEnabled,
|
|
19
24
|
removeClass,
|
|
20
25
|
removeListener,
|
|
21
26
|
setData,
|
|
@@ -31,7 +36,7 @@ export default {
|
|
|
31
36
|
},
|
|
32
37
|
|
|
33
38
|
initBody() {
|
|
34
|
-
const { ownerDocument } = this
|
|
39
|
+
const { ownerDocument } = this;
|
|
35
40
|
const body = ownerDocument.body || ownerDocument.documentElement;
|
|
36
41
|
|
|
37
42
|
this.body = body;
|
|
@@ -73,14 +78,45 @@ export default {
|
|
|
73
78
|
}
|
|
74
79
|
},
|
|
75
80
|
|
|
76
|
-
initList() {
|
|
81
|
+
initList(viewIndex = this.index) {
|
|
77
82
|
const { element, options, list } = this;
|
|
78
83
|
const items = [];
|
|
84
|
+
const navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
|
|
85
|
+
const probe = document.createElement('li');
|
|
86
|
+
|
|
87
|
+
if (!this.containerData) {
|
|
88
|
+
this.initContainer();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
list.appendChild(probe);
|
|
92
|
+
const itemWidth = probe.offsetWidth + parseInt(window.getComputedStyle(probe).marginLeft, 10);
|
|
93
|
+
|
|
94
|
+
list.removeChild(probe);
|
|
95
|
+
|
|
96
|
+
let visibleItemCount = isNumber(navbarOptions.visibleItemCount)
|
|
97
|
+
? Math.floor(navbarOptions.visibleItemCount)
|
|
98
|
+
: Math.floor(this.containerData.width / itemWidth);
|
|
99
|
+
|
|
100
|
+
visibleItemCount = Math.min(visibleItemCount, this.length);
|
|
101
|
+
|
|
102
|
+
const start = visibleItemCount > 0
|
|
103
|
+
? Math.min(
|
|
104
|
+
Math.max(0, viewIndex - Math.floor(visibleItemCount / 2)),
|
|
105
|
+
Math.max(0, this.length - visibleItemCount),
|
|
106
|
+
)
|
|
107
|
+
: 0;
|
|
108
|
+
const end = visibleItemCount > 0
|
|
109
|
+
? Math.min(this.length, start + visibleItemCount)
|
|
110
|
+
: this.length;
|
|
79
111
|
|
|
80
112
|
// initList may be called in this.update, so should keep idempotent
|
|
81
113
|
list.innerHTML = '';
|
|
82
114
|
|
|
83
115
|
forEach(this.images, (image, index) => {
|
|
116
|
+
if (visibleItemCount > 0 && (index < start || index >= end)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
84
120
|
const { src } = image;
|
|
85
121
|
const alt = image.alt || getImageNameFromURL(src);
|
|
86
122
|
const url = this.getImageURL(image);
|
|
@@ -89,13 +125,7 @@ export default {
|
|
|
89
125
|
const item = document.createElement('li');
|
|
90
126
|
const img = document.createElement('img');
|
|
91
127
|
|
|
92
|
-
|
|
93
|
-
const value = image.getAttribute(name);
|
|
94
|
-
|
|
95
|
-
if (value !== null) {
|
|
96
|
-
img.setAttribute(name, value);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
128
|
+
inheritAttributes(img, image, options.inheritedAttributes);
|
|
99
129
|
|
|
100
130
|
if (options.navbar) {
|
|
101
131
|
img.src = src || url;
|
|
@@ -119,6 +149,15 @@ export default {
|
|
|
119
149
|
|
|
120
150
|
this.items = items;
|
|
121
151
|
|
|
152
|
+
if (this.viewed) {
|
|
153
|
+
const activeItem = this.getItem(this.index);
|
|
154
|
+
|
|
155
|
+
if (activeItem) {
|
|
156
|
+
addClass(activeItem, CLASS_ACTIVE);
|
|
157
|
+
activeItem.setAttribute('aria-selected', true);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
122
161
|
forEach(items, (item) => {
|
|
123
162
|
const image = item.firstElementChild;
|
|
124
163
|
let onLoad;
|
|
@@ -152,7 +191,7 @@ export default {
|
|
|
152
191
|
});
|
|
153
192
|
});
|
|
154
193
|
|
|
155
|
-
if (options
|
|
194
|
+
if (isTransitionEnabled(options, 'view')) {
|
|
156
195
|
addListener(element, EVENT_VIEWED, () => {
|
|
157
196
|
addClass(list, CLASS_TRANSITION);
|
|
158
197
|
}, {
|
|
@@ -161,9 +200,24 @@ export default {
|
|
|
161
200
|
}
|
|
162
201
|
},
|
|
163
202
|
|
|
203
|
+
getItem(index) {
|
|
204
|
+
let item;
|
|
205
|
+
|
|
206
|
+
forEach(this.items, (candidate) => {
|
|
207
|
+
if (Number(getData(candidate, 'index')) === index) {
|
|
208
|
+
item = candidate;
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return true;
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return item;
|
|
216
|
+
},
|
|
217
|
+
|
|
164
218
|
renderList() {
|
|
165
219
|
const { index } = this;
|
|
166
|
-
const item = this.
|
|
220
|
+
const item = this.getItem(index);
|
|
167
221
|
|
|
168
222
|
if (!item) {
|
|
169
223
|
return;
|
|
@@ -176,9 +230,9 @@ export default {
|
|
|
176
230
|
|
|
177
231
|
// Place the active item in the center of the screen
|
|
178
232
|
setStyle(this.list, assign({
|
|
179
|
-
width: outerWidth * this.length - gutter,
|
|
233
|
+
width: outerWidth * this.items.length - gutter,
|
|
180
234
|
}, getTransforms({
|
|
181
|
-
translateX: ((this.viewerData.width - offsetWidth) / 2) -
|
|
235
|
+
translateX: ((this.viewerData.width - offsetWidth) / 2) - item.offsetLeft,
|
|
182
236
|
})));
|
|
183
237
|
},
|
|
184
238
|
|
|
@@ -277,9 +331,27 @@ export default {
|
|
|
277
331
|
marginTop: imageData.y,
|
|
278
332
|
}, getTransforms(imageData)));
|
|
279
333
|
|
|
334
|
+
if (this.magnifierPoint) {
|
|
335
|
+
this.renderMagnifier();
|
|
336
|
+
}
|
|
337
|
+
|
|
280
338
|
if (done) {
|
|
281
|
-
|
|
282
|
-
|
|
339
|
+
let action = false;
|
|
340
|
+
|
|
341
|
+
if (this.viewing) {
|
|
342
|
+
action = 'view';
|
|
343
|
+
} else if (this.moving) {
|
|
344
|
+
action = 'move';
|
|
345
|
+
} else if (this.rotating) {
|
|
346
|
+
action = 'rotate';
|
|
347
|
+
} else if (this.scaling) {
|
|
348
|
+
action = 'scale';
|
|
349
|
+
} else if (this.zooming) {
|
|
350
|
+
action = 'zoom';
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (action
|
|
354
|
+
&& isTransitionEnabled(this.options, action)
|
|
283
355
|
&& hasClass(image, CLASS_TRANSITION)) {
|
|
284
356
|
const onTransitionEnd = () => {
|
|
285
357
|
this.imageRendering = false;
|
package/src/js/template.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
export default (
|
|
2
2
|
'<div class="viewer-container" tabindex="-1" touch-action="none">'
|
|
3
3
|
+ '<div class="viewer-canvas"></div>'
|
|
4
|
-
+ '<div class="viewer-
|
|
5
|
-
+ '<
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
+ '<div class="viewer-magnifier" aria-hidden="true">'
|
|
5
|
+
+ '<img class="viewer-magnifier-image" alt="">'
|
|
6
|
+
+ '</div>'
|
|
7
|
+
+ '<div class="viewer-navigation" aria-hidden="true">'
|
|
8
|
+
+ '<div class="viewer-prev" data-viewer-action="prev" role="button" aria-label="Previous"></div>'
|
|
9
|
+
+ '<div class="viewer-next" data-viewer-action="next" role="button" aria-label="Next"></div>'
|
|
10
|
+
+ '</div>'
|
|
11
|
+
+ '<div class="viewer-footer" aria-hidden="true">'
|
|
12
|
+
+ '<div class="viewer-title" aria-hidden="true"></div>'
|
|
13
|
+
+ '<div class="viewer-toolbar" aria-hidden="true"></div>'
|
|
14
|
+
+ '<div class="viewer-navbar" aria-hidden="true">'
|
|
8
15
|
+ '<ul class="viewer-list" role="navigation"></ul>'
|
|
9
16
|
+ '</div>'
|
|
10
17
|
+ '</div>'
|
|
11
18
|
+ '<div class="viewer-tooltip" role="alert" aria-hidden="true"></div>'
|
|
12
|
-
+ '<div class="viewer-button" data-viewer-action="mix" role="button"></div>'
|
|
13
|
-
+ '<div class="viewer-player"></div>'
|
|
19
|
+
+ '<div class="viewer-button" data-viewer-action="mix" role="button" aria-hidden="true"></div>'
|
|
20
|
+
+ '<div class="viewer-player" aria-hidden="true"></div>'
|
|
14
21
|
+ '</div>'
|
|
15
22
|
);
|
package/src/js/utilities.js
CHANGED
|
@@ -16,6 +16,33 @@ export function isString(value) {
|
|
|
16
16
|
return typeof value === 'string';
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
const MODIFIER_KEY_PROPERTIES = {
|
|
20
|
+
ctrl: 'ctrlKey',
|
|
21
|
+
shift: 'shiftKey',
|
|
22
|
+
alt: 'altKey',
|
|
23
|
+
meta: 'metaKey',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if a wheel option (`zoomOnWheel` or `slideOnWheel`) takes effect for the given wheel event.
|
|
28
|
+
* @param {boolean | string} option - The option value.
|
|
29
|
+
* @param {WheelEvent} event - The wheel event.
|
|
30
|
+
* @returns {boolean} Returns `true` if the option takes effect for the event, else `false`.
|
|
31
|
+
*/
|
|
32
|
+
export function isWheelActionEnabled(option, event) {
|
|
33
|
+
if (!option) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (option === true) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return isString(option) && option
|
|
42
|
+
.split('+')
|
|
43
|
+
.every((key) => event[MODIFIER_KEY_PROPERTIES[key.trim().toLowerCase()]]);
|
|
44
|
+
}
|
|
45
|
+
|
|
19
46
|
/**
|
|
20
47
|
* Check if the given value is not a number.
|
|
21
48
|
*/
|
|
@@ -106,6 +133,34 @@ export function forEach(data, callback) {
|
|
|
106
133
|
return data;
|
|
107
134
|
}
|
|
108
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Inherit attributes from the original image.
|
|
138
|
+
* @param {Element} image - The target image.
|
|
139
|
+
* @param {Element} originalImage - The original image.
|
|
140
|
+
* @param {Array} inheritedAttributes - The attributes to inherit.
|
|
141
|
+
*/
|
|
142
|
+
export function inheritAttributes(image, originalImage, inheritedAttributes) {
|
|
143
|
+
forEach(inheritedAttributes, (name) => {
|
|
144
|
+
const value = originalImage.getAttribute(name);
|
|
145
|
+
|
|
146
|
+
if (value !== null) {
|
|
147
|
+
image.setAttribute(name, value);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check if transition is enabled for the given action.
|
|
154
|
+
* @param {Object} options - The viewer options.
|
|
155
|
+
* @param {string} action - The transition action.
|
|
156
|
+
* @returns {boolean} Returns `true` if transition is enabled.
|
|
157
|
+
*/
|
|
158
|
+
export function isTransitionEnabled(options, action) {
|
|
159
|
+
const { transition } = options;
|
|
160
|
+
|
|
161
|
+
return transition && transition[action] !== false;
|
|
162
|
+
}
|
|
163
|
+
|
|
109
164
|
/**
|
|
110
165
|
* Extend the given object.
|
|
111
166
|
* @param {*} obj - The object to be extended.
|
|
@@ -495,11 +550,8 @@ export function getTransforms({
|
|
|
495
550
|
values.push(`rotate(${rotate}deg)`);
|
|
496
551
|
}
|
|
497
552
|
|
|
498
|
-
if (isNumber(scaleX) && scaleX !== 1) {
|
|
553
|
+
if (isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1)) {
|
|
499
554
|
values.push(`scaleX(${scaleX})`);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
if (isNumber(scaleY) && scaleY !== 1) {
|
|
503
555
|
values.push(`scaleY(${scaleY})`);
|
|
504
556
|
}
|
|
505
557
|
|
|
@@ -552,14 +604,7 @@ export function getImageNaturalSizes(image, options, callback) {
|
|
|
552
604
|
}
|
|
553
605
|
};
|
|
554
606
|
|
|
555
|
-
|
|
556
|
-
const value = image.getAttribute(name);
|
|
557
|
-
|
|
558
|
-
if (value !== null) {
|
|
559
|
-
newImage.setAttribute(name, value);
|
|
560
|
-
}
|
|
561
|
-
});
|
|
562
|
-
|
|
607
|
+
inheritAttributes(newImage, image, options.inheritedAttributes);
|
|
563
608
|
newImage.src = image.src;
|
|
564
609
|
|
|
565
610
|
// iOS Safari will convert the image automatically
|
|
@@ -633,6 +678,44 @@ export function getMaxZoomRatio(pointers) {
|
|
|
633
678
|
return ratios[0];
|
|
634
679
|
}
|
|
635
680
|
|
|
681
|
+
/**
|
|
682
|
+
* Get the max rotation degree of a group of pointers.
|
|
683
|
+
* @param {Object} pointers - The target pointers.
|
|
684
|
+
* @returns {number} The result degree.
|
|
685
|
+
*/
|
|
686
|
+
export function getMaxRotateDegree(pointers) {
|
|
687
|
+
const pointers2 = { ...pointers };
|
|
688
|
+
const degrees = [];
|
|
689
|
+
|
|
690
|
+
forEach(pointers, (pointer, pointerId) => {
|
|
691
|
+
delete pointers2[pointerId];
|
|
692
|
+
|
|
693
|
+
forEach(pointers2, (pointer2) => {
|
|
694
|
+
const start = Math.atan2(
|
|
695
|
+
pointer2.startY - pointer.startY,
|
|
696
|
+
pointer2.startX - pointer.startX,
|
|
697
|
+
);
|
|
698
|
+
const end = Math.atan2(
|
|
699
|
+
pointer2.endY - pointer.endY,
|
|
700
|
+
pointer2.endX - pointer.endX,
|
|
701
|
+
);
|
|
702
|
+
let radians = end - start;
|
|
703
|
+
|
|
704
|
+
if (radians > Math.PI) {
|
|
705
|
+
radians -= Math.PI * 2;
|
|
706
|
+
} else if (radians < -Math.PI) {
|
|
707
|
+
radians += Math.PI * 2;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
degrees.push((radians * 180) / Math.PI);
|
|
711
|
+
});
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
degrees.sort((a, b) => Math.abs(b) - Math.abs(a));
|
|
715
|
+
|
|
716
|
+
return degrees[0] || 0;
|
|
717
|
+
}
|
|
718
|
+
|
|
636
719
|
/**
|
|
637
720
|
* Get a pointer from an event object.
|
|
638
721
|
* @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,17 +52,19 @@ const getUniqueID = ((id) => (() => {
|
|
|
51
52
|
class Viewer {
|
|
52
53
|
/**
|
|
53
54
|
* Create a new Viewer.
|
|
54
|
-
* @param {Element} element - The target
|
|
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;
|
|
67
|
+
this.actionEvent = null;
|
|
65
68
|
this.fading = false;
|
|
66
69
|
this.fulled = false;
|
|
67
70
|
this.hiding = false;
|
|
@@ -123,7 +126,7 @@ class Viewer {
|
|
|
123
126
|
this.initBody();
|
|
124
127
|
|
|
125
128
|
// Override `transition` option if it is not supported
|
|
126
|
-
if (isUndefined(
|
|
129
|
+
if (isUndefined(this.ownerDocument.createElement(NAMESPACE).style.transition)) {
|
|
127
130
|
options.transition = false;
|
|
128
131
|
}
|
|
129
132
|
|
|
@@ -183,8 +186,11 @@ class Viewer {
|
|
|
183
186
|
}
|
|
184
187
|
});
|
|
185
188
|
} else {
|
|
186
|
-
addListener(element, EVENT_CLICK, (this.onElementClick = (
|
|
189
|
+
addListener(element, EVENT_CLICK, (this.onElementClick = (event) => {
|
|
190
|
+
const { target } = event;
|
|
191
|
+
|
|
187
192
|
if (target.localName === 'img' && (!isFunction(options.filter) || options.filter.call(this, target))) {
|
|
193
|
+
this.actionEvent = event;
|
|
188
194
|
this.view(this.images.indexOf(target));
|
|
189
195
|
}
|
|
190
196
|
}));
|
|
@@ -195,6 +201,7 @@ class Viewer {
|
|
|
195
201
|
event.preventDefault();
|
|
196
202
|
|
|
197
203
|
if (!isFunction(options.filter) || options.filter.call(this, target)) {
|
|
204
|
+
this.actionEvent = event;
|
|
198
205
|
this.view(this.images.indexOf(target));
|
|
199
206
|
}
|
|
200
207
|
}
|
|
@@ -217,6 +224,7 @@ class Viewer {
|
|
|
217
224
|
const title = viewer.querySelector(`.${NAMESPACE}-title`);
|
|
218
225
|
const toolbar = viewer.querySelector(`.${NAMESPACE}-toolbar`);
|
|
219
226
|
const navbar = viewer.querySelector(`.${NAMESPACE}-navbar`);
|
|
227
|
+
const navigation = viewer.querySelector(`.${NAMESPACE}-navigation`);
|
|
220
228
|
const button = viewer.querySelector(`.${NAMESPACE}-button`);
|
|
221
229
|
const canvas = viewer.querySelector(`.${NAMESPACE}-canvas`);
|
|
222
230
|
|
|
@@ -225,9 +233,12 @@ class Viewer {
|
|
|
225
233
|
this.title = title;
|
|
226
234
|
this.toolbar = toolbar;
|
|
227
235
|
this.navbar = navbar;
|
|
236
|
+
this.navigation = navigation;
|
|
228
237
|
this.button = button;
|
|
229
238
|
this.canvas = canvas;
|
|
230
239
|
this.footer = viewer.querySelector(`.${NAMESPACE}-footer`);
|
|
240
|
+
this.magnifier = viewer.querySelector(`.${NAMESPACE}-magnifier`);
|
|
241
|
+
this.magnifierImage = viewer.querySelector(`.${NAMESPACE}-magnifier-image`);
|
|
231
242
|
this.tooltipBox = viewer.querySelector(`.${NAMESPACE}-tooltip`);
|
|
232
243
|
this.player = viewer.querySelector(`.${NAMESPACE}-player`);
|
|
233
244
|
this.list = viewer.querySelector(`.${NAMESPACE}-list`);
|
|
@@ -237,11 +248,70 @@ class Viewer {
|
|
|
237
248
|
addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title)
|
|
238
249
|
? options.title[0]
|
|
239
250
|
: options.title));
|
|
240
|
-
|
|
251
|
+
|
|
252
|
+
if (options.title) {
|
|
253
|
+
title.removeAttribute('aria-hidden');
|
|
254
|
+
}
|
|
255
|
+
const navbarOptions = isPlainObject(options.navbar) ? options.navbar : {};
|
|
256
|
+
let navbarShow = options.navbar;
|
|
257
|
+
const navbarSize = !isUndefined(navbarOptions.size) ? navbarOptions.size : options.navbar;
|
|
258
|
+
|
|
259
|
+
if (isPlainObject(options.navbar)) {
|
|
260
|
+
navbarShow = !isUndefined(navbarOptions.show) ? navbarOptions.show : true;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
addClass(navbar, !navbarShow ? CLASS_HIDE : getResponsiveClass(navbarShow));
|
|
264
|
+
|
|
265
|
+
if (navbarShow) {
|
|
266
|
+
navbar.removeAttribute('aria-hidden');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (['small', 'medium', 'large'].indexOf(navbarSize) !== -1) {
|
|
270
|
+
addClass(navbar, `${NAMESPACE}-${navbarSize}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (isPlainObject(options.navigation)) {
|
|
274
|
+
forEach(navigation.querySelectorAll('[role="button"]'), (item) => {
|
|
275
|
+
const name = getData(item, DATA_ACTION);
|
|
276
|
+
const value = options.navigation[name];
|
|
277
|
+
const deep = isPlainObject(value);
|
|
278
|
+
const show = deep && !isUndefined(value.show) ? value.show : value;
|
|
279
|
+
const size = deep && !isUndefined(value.size)
|
|
280
|
+
? value.size
|
|
281
|
+
: value;
|
|
282
|
+
|
|
283
|
+
toggleClass(item, CLASS_HIDE, !show);
|
|
284
|
+
|
|
285
|
+
if (isNumber(show)) {
|
|
286
|
+
addClass(item, getResponsiveClass(show));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (['small', 'large'].indexOf(size) !== -1) {
|
|
290
|
+
addClass(item, `${NAMESPACE}-${size}`);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
} else {
|
|
294
|
+
addClass(
|
|
295
|
+
navigation,
|
|
296
|
+
!options.navigation ? CLASS_HIDE : getResponsiveClass(options.navigation),
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (options.navigation) {
|
|
301
|
+
navigation.removeAttribute('aria-hidden');
|
|
302
|
+
}
|
|
303
|
+
|
|
241
304
|
toggleClass(button, CLASS_HIDE, !options.button);
|
|
242
305
|
|
|
306
|
+
if (options.button) {
|
|
307
|
+
button.removeAttribute('aria-hidden');
|
|
308
|
+
}
|
|
309
|
+
|
|
243
310
|
if (options.keyboard) {
|
|
244
311
|
button.setAttribute('tabindex', 0);
|
|
312
|
+
forEach(navigation.querySelectorAll('[role="button"]'), (item) => {
|
|
313
|
+
item.setAttribute('tabindex', 0);
|
|
314
|
+
});
|
|
245
315
|
}
|
|
246
316
|
|
|
247
317
|
if (options.backdrop) {
|
|
@@ -270,6 +340,8 @@ class Viewer {
|
|
|
270
340
|
addClass(toolbar, getResponsiveClass(options.toolbar));
|
|
271
341
|
}
|
|
272
342
|
|
|
343
|
+
toolbar.removeAttribute('aria-hidden');
|
|
344
|
+
|
|
273
345
|
forEach(custom ? options.toolbar : BUTTONS, (value, index) => {
|
|
274
346
|
const deep = custom && isPlainObject(value);
|
|
275
347
|
const name = custom ? hyphenate(index) : value;
|
|
@@ -321,6 +393,12 @@ class Viewer {
|
|
|
321
393
|
addClass(toolbar, CLASS_HIDE);
|
|
322
394
|
}
|
|
323
395
|
|
|
396
|
+
if (options.title || navbarShow || options.toolbar) {
|
|
397
|
+
this.footer.removeAttribute('aria-hidden');
|
|
398
|
+
} else {
|
|
399
|
+
addClass(this.footer, CLASS_HIDE);
|
|
400
|
+
}
|
|
401
|
+
|
|
324
402
|
if (!options.rotatable) {
|
|
325
403
|
const rotates = toolbar.querySelectorAll('li[class*="rotate"]');
|
|
326
404
|
|
|
@@ -356,7 +434,7 @@ class Viewer {
|
|
|
356
434
|
let { container } = options;
|
|
357
435
|
|
|
358
436
|
if (isString(container)) {
|
|
359
|
-
container =
|
|
437
|
+
container = this.ownerDocument.querySelector(container);
|
|
360
438
|
}
|
|
361
439
|
|
|
362
440
|
if (!container) {
|
|
@@ -391,12 +469,13 @@ class Viewer {
|
|
|
391
469
|
}
|
|
392
470
|
|
|
393
471
|
/**
|
|
394
|
-
*
|
|
395
|
-
* @
|
|
472
|
+
* Create a new Viewer instance.
|
|
473
|
+
* @param {Element} element - The target image, or a container of images, to view.
|
|
474
|
+
* @param {Object} [options={}] - The configuration options.
|
|
475
|
+
* @returns {Viewer} A new Viewer instance.
|
|
396
476
|
*/
|
|
397
|
-
static
|
|
398
|
-
|
|
399
|
-
return Viewer;
|
|
477
|
+
static create(element, options) {
|
|
478
|
+
return new Viewer(element, options);
|
|
400
479
|
}
|
|
401
480
|
|
|
402
481
|
/**
|
|
@@ -406,6 +485,15 @@ class Viewer {
|
|
|
406
485
|
static setDefaults(options) {
|
|
407
486
|
assign(DEFAULTS, isPlainObject(options) && options);
|
|
408
487
|
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get the no conflict viewer class.
|
|
491
|
+
* @returns {Viewer} The viewer class.
|
|
492
|
+
*/
|
|
493
|
+
static noConflict() {
|
|
494
|
+
window.Viewer = AnotherViewer;
|
|
495
|
+
return Viewer;
|
|
496
|
+
}
|
|
409
497
|
}
|
|
410
498
|
|
|
411
499
|
assign(Viewer.prototype, render, events, handlers, methods, others);
|