intranet-pictures 2.0.0 → 2.1.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.
- checksums.yaml +4 -4
- data/lib/intranet/pictures/version.rb +1 -1
- data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js +1149 -571
- data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.min.js +3 -3
- data/lib/intranet/resources/www/photoswipe/photoswipe.css +7 -7
- data/lib/intranet/resources/www/photoswipe/photoswipe.esm.js +3450 -1655
- data/lib/intranet/resources/www/photoswipe/photoswipe.esm.min.js +3 -3
- metadata +10 -82
- data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js.map +0 -1
- data/lib/intranet/resources/www/photoswipe/photoswipe.esm.js.map +0 -1
@@ -1,83 +1,91 @@
|
|
1
1
|
/*!
|
2
|
-
* PhotoSwipe Lightbox 5.2
|
3
|
-
* (c)
|
2
|
+
* PhotoSwipe Lightbox 5.4.2 - https://photoswipe.com
|
3
|
+
* (c) 2023 Dmytro Semenov
|
4
4
|
*/
|
5
|
+
/** @typedef {import('../photoswipe.js').Point} Point */
|
6
|
+
|
5
7
|
/**
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
* @template {keyof HTMLElementTagNameMap} T
|
9
|
+
* @param {string} className
|
10
|
+
* @param {T} tagName
|
11
|
+
* @param {Node} [appendToEl]
|
12
|
+
* @returns {HTMLElementTagNameMap[T]}
|
13
|
+
*/
|
12
14
|
function createElement(className, tagName, appendToEl) {
|
13
|
-
const el = document.createElement(tagName
|
15
|
+
const el = document.createElement(tagName);
|
16
|
+
|
14
17
|
if (className) {
|
15
18
|
el.className = className;
|
16
19
|
}
|
20
|
+
|
17
21
|
if (appendToEl) {
|
18
22
|
appendToEl.appendChild(el);
|
19
23
|
}
|
24
|
+
|
20
25
|
return el;
|
21
26
|
}
|
22
|
-
|
23
27
|
/**
|
24
28
|
* Get transform string
|
25
29
|
*
|
26
|
-
* @param {
|
27
|
-
* @param {
|
28
|
-
* @param {
|
30
|
+
* @param {number} x
|
31
|
+
* @param {number} [y]
|
32
|
+
* @param {number} [scale]
|
33
|
+
* @returns {string}
|
29
34
|
*/
|
35
|
+
|
30
36
|
function toTransformString(x, y, scale) {
|
31
|
-
let propValue =
|
32
|
-
+ x + 'px,' + (y || 0) + 'px'
|
33
|
-
+ ',0)';
|
37
|
+
let propValue = `translate3d(${x}px,${y || 0}px,0)`;
|
34
38
|
|
35
39
|
if (scale !== undefined) {
|
36
|
-
propValue +=
|
37
|
-
+ scale + ',' + scale
|
38
|
-
+ ',1)';
|
40
|
+
propValue += ` scale3d(${scale},${scale},1)`;
|
39
41
|
}
|
40
42
|
|
41
43
|
return propValue;
|
42
44
|
}
|
43
|
-
|
44
45
|
/**
|
45
46
|
* Apply width and height CSS properties to element
|
47
|
+
*
|
48
|
+
* @param {HTMLElement} el
|
49
|
+
* @param {string | number} w
|
50
|
+
* @param {string | number} h
|
46
51
|
*/
|
52
|
+
|
47
53
|
function setWidthHeight(el, w, h) {
|
48
|
-
el.style.width =
|
49
|
-
el.style.height =
|
54
|
+
el.style.width = typeof w === 'number' ? `${w}px` : w;
|
55
|
+
el.style.height = typeof h === 'number' ? `${h}px` : h;
|
50
56
|
}
|
57
|
+
/** @typedef {LOAD_STATE[keyof LOAD_STATE]} LoadState */
|
58
|
+
|
59
|
+
/** @type {{ IDLE: 'idle'; LOADING: 'loading'; LOADED: 'loaded'; ERROR: 'error' }} */
|
51
60
|
|
52
61
|
const LOAD_STATE = {
|
53
62
|
IDLE: 'idle',
|
54
63
|
LOADING: 'loading',
|
55
64
|
LOADED: 'loaded',
|
56
|
-
ERROR: 'error'
|
65
|
+
ERROR: 'error'
|
57
66
|
};
|
58
|
-
|
59
|
-
|
60
67
|
/**
|
61
68
|
* Check if click or keydown event was dispatched
|
62
69
|
* with a special key or via mouse wheel.
|
63
70
|
*
|
64
|
-
* @param {
|
71
|
+
* @param {MouseEvent | KeyboardEvent} e
|
72
|
+
* @returns {boolean}
|
65
73
|
*/
|
74
|
+
|
66
75
|
function specialKeyUsed(e) {
|
67
|
-
|
68
|
-
return true;
|
69
|
-
}
|
76
|
+
return 'button' in e && e.button === 1 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey;
|
70
77
|
}
|
71
|
-
|
72
78
|
/**
|
73
79
|
* Parse `gallery` or `children` options.
|
74
80
|
*
|
75
|
-
* @param {
|
76
|
-
* @param {
|
77
|
-
* @param {
|
78
|
-
* @returns
|
81
|
+
* @param {import('../photoswipe.js').ElementProvider} [option]
|
82
|
+
* @param {string} [legacySelector]
|
83
|
+
* @param {HTMLElement | Document} [parent]
|
84
|
+
* @returns HTMLElement[]
|
79
85
|
*/
|
86
|
+
|
80
87
|
function getElementsFromOption(option, legacySelector, parent = document) {
|
88
|
+
/** @type {HTMLElement[]} */
|
81
89
|
let elements = [];
|
82
90
|
|
83
91
|
if (option instanceof Element) {
|
@@ -86,6 +94,7 @@ function getElementsFromOption(option, legacySelector, parent = document) {
|
|
86
94
|
elements = Array.from(option);
|
87
95
|
} else {
|
88
96
|
const selector = typeof option === 'string' ? option : legacySelector;
|
97
|
+
|
89
98
|
if (selector) {
|
90
99
|
elements = Array.from(parent.querySelectorAll(selector));
|
91
100
|
}
|
@@ -93,25 +102,253 @@ function getElementsFromOption(option, legacySelector, parent = document) {
|
|
93
102
|
|
94
103
|
return elements;
|
95
104
|
}
|
96
|
-
|
97
105
|
/**
|
98
106
|
* Check if variable is PhotoSwipe class
|
99
107
|
*
|
100
|
-
* @param {
|
101
|
-
* @returns
|
108
|
+
* @param {any} fn
|
109
|
+
* @returns {boolean}
|
102
110
|
*/
|
111
|
+
|
103
112
|
function isPswpClass(fn) {
|
104
|
-
return typeof fn === 'function'
|
105
|
-
|
106
|
-
|
113
|
+
return typeof fn === 'function' && fn.prototype && fn.prototype.goTo;
|
114
|
+
}
|
115
|
+
/**
|
116
|
+
* Check if browser is Safari
|
117
|
+
*
|
118
|
+
* @returns {boolean}
|
119
|
+
*/
|
120
|
+
|
121
|
+
function isSafari() {
|
122
|
+
return !!(navigator.vendor && navigator.vendor.match(/apple/i));
|
107
123
|
}
|
108
124
|
|
125
|
+
/** @typedef {import('../lightbox/lightbox.js').default} PhotoSwipeLightbox */
|
126
|
+
|
127
|
+
/** @typedef {import('../photoswipe.js').default} PhotoSwipe */
|
128
|
+
|
129
|
+
/** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
|
130
|
+
|
131
|
+
/** @typedef {import('../photoswipe.js').DataSource} DataSource */
|
132
|
+
|
133
|
+
/** @typedef {import('../ui/ui-element.js').UIElementData} UIElementData */
|
134
|
+
|
135
|
+
/** @typedef {import('../slide/content.js').default} ContentDefault */
|
136
|
+
|
137
|
+
/** @typedef {import('../slide/slide.js').default} Slide */
|
138
|
+
|
139
|
+
/** @typedef {import('../slide/slide.js').SlideData} SlideData */
|
140
|
+
|
141
|
+
/** @typedef {import('../slide/zoom-level.js').default} ZoomLevel */
|
142
|
+
|
143
|
+
/** @typedef {import('../slide/get-thumb-bounds.js').Bounds} Bounds */
|
144
|
+
|
145
|
+
/**
|
146
|
+
* Allow adding an arbitrary props to the Content
|
147
|
+
* https://photoswipe.com/custom-content/#using-webp-image-format
|
148
|
+
* @typedef {ContentDefault & Record<string, any>} Content
|
149
|
+
*/
|
150
|
+
|
151
|
+
/** @typedef {{ x?: number; y?: number }} Point */
|
152
|
+
|
153
|
+
/**
|
154
|
+
* @typedef {Object} PhotoSwipeEventsMap https://photoswipe.com/events/
|
155
|
+
*
|
156
|
+
*
|
157
|
+
* https://photoswipe.com/adding-ui-elements/
|
158
|
+
*
|
159
|
+
* @prop {undefined} uiRegister
|
160
|
+
* @prop {{ data: UIElementData }} uiElementCreate
|
161
|
+
*
|
162
|
+
*
|
163
|
+
* https://photoswipe.com/events/#initialization-events
|
164
|
+
*
|
165
|
+
* @prop {undefined} beforeOpen
|
166
|
+
* @prop {undefined} firstUpdate
|
167
|
+
* @prop {undefined} initialLayout
|
168
|
+
* @prop {undefined} change
|
169
|
+
* @prop {undefined} afterInit
|
170
|
+
* @prop {undefined} bindEvents
|
171
|
+
*
|
172
|
+
*
|
173
|
+
* https://photoswipe.com/events/#opening-or-closing-transition-events
|
174
|
+
*
|
175
|
+
* @prop {undefined} openingAnimationStart
|
176
|
+
* @prop {undefined} openingAnimationEnd
|
177
|
+
* @prop {undefined} closingAnimationStart
|
178
|
+
* @prop {undefined} closingAnimationEnd
|
179
|
+
*
|
180
|
+
*
|
181
|
+
* https://photoswipe.com/events/#closing-events
|
182
|
+
*
|
183
|
+
* @prop {undefined} close
|
184
|
+
* @prop {undefined} destroy
|
185
|
+
*
|
186
|
+
*
|
187
|
+
* https://photoswipe.com/events/#pointer-and-gesture-events
|
188
|
+
*
|
189
|
+
* @prop {{ originalEvent: PointerEvent }} pointerDown
|
190
|
+
* @prop {{ originalEvent: PointerEvent }} pointerMove
|
191
|
+
* @prop {{ originalEvent: PointerEvent }} pointerUp
|
192
|
+
* @prop {{ bgOpacity: number }} pinchClose can be default prevented
|
193
|
+
* @prop {{ panY: number }} verticalDrag can be default prevented
|
194
|
+
*
|
195
|
+
*
|
196
|
+
* https://photoswipe.com/events/#slide-content-events
|
197
|
+
*
|
198
|
+
* @prop {{ content: Content }} contentInit
|
199
|
+
* @prop {{ content: Content; isLazy: boolean }} contentLoad can be default prevented
|
200
|
+
* @prop {{ content: Content; isLazy: boolean }} contentLoadImage can be default prevented
|
201
|
+
* @prop {{ content: Content; slide: Slide; isError?: boolean }} loadComplete
|
202
|
+
* @prop {{ content: Content; slide: Slide }} loadError
|
203
|
+
* @prop {{ content: Content; width: number; height: number }} contentResize can be default prevented
|
204
|
+
* @prop {{ content: Content; width: number; height: number; slide: Slide }} imageSizeChange
|
205
|
+
* @prop {{ content: Content }} contentLazyLoad can be default prevented
|
206
|
+
* @prop {{ content: Content }} contentAppend can be default prevented
|
207
|
+
* @prop {{ content: Content }} contentActivate can be default prevented
|
208
|
+
* @prop {{ content: Content }} contentDeactivate can be default prevented
|
209
|
+
* @prop {{ content: Content }} contentRemove can be default prevented
|
210
|
+
* @prop {{ content: Content }} contentDestroy can be default prevented
|
211
|
+
*
|
212
|
+
*
|
213
|
+
* undocumented
|
214
|
+
*
|
215
|
+
* @prop {{ point: Point; originalEvent: PointerEvent }} imageClickAction can be default prevented
|
216
|
+
* @prop {{ point: Point; originalEvent: PointerEvent }} bgClickAction can be default prevented
|
217
|
+
* @prop {{ point: Point; originalEvent: PointerEvent }} tapAction can be default prevented
|
218
|
+
* @prop {{ point: Point; originalEvent: PointerEvent }} doubleTapAction can be default prevented
|
219
|
+
*
|
220
|
+
* @prop {{ originalEvent: KeyboardEvent }} keydown can be default prevented
|
221
|
+
* @prop {{ x: number; dragging: boolean }} moveMainScroll
|
222
|
+
* @prop {{ slide: Slide }} firstZoomPan
|
223
|
+
* @prop {{ slide: Slide | undefined, data: SlideData, index: number }} gettingData
|
224
|
+
* @prop {undefined} beforeResize
|
225
|
+
* @prop {undefined} resize
|
226
|
+
* @prop {undefined} viewportSize
|
227
|
+
* @prop {undefined} updateScrollOffset
|
228
|
+
* @prop {{ slide: Slide }} slideInit
|
229
|
+
* @prop {{ slide: Slide }} afterSetContent
|
230
|
+
* @prop {{ slide: Slide }} slideLoad
|
231
|
+
* @prop {{ slide: Slide }} appendHeavy can be default prevented
|
232
|
+
* @prop {{ slide: Slide }} appendHeavyContent
|
233
|
+
* @prop {{ slide: Slide }} slideActivate
|
234
|
+
* @prop {{ slide: Slide }} slideDeactivate
|
235
|
+
* @prop {{ slide: Slide }} slideDestroy
|
236
|
+
* @prop {{ destZoomLevel: number, centerPoint: Point | undefined, transitionDuration: number | false | undefined }} beforeZoomTo
|
237
|
+
* @prop {{ slide: Slide }} zoomPanUpdate
|
238
|
+
* @prop {{ slide: Slide }} initialZoomPan
|
239
|
+
* @prop {{ slide: Slide }} calcSlideSize
|
240
|
+
* @prop {undefined} resolutionChanged
|
241
|
+
* @prop {{ originalEvent: WheelEvent }} wheel can be default prevented
|
242
|
+
* @prop {{ content: Content }} contentAppendImage can be default prevented
|
243
|
+
* @prop {{ index: number; itemData: SlideData }} lazyLoadSlide can be default prevented
|
244
|
+
* @prop {undefined} lazyLoad
|
245
|
+
* @prop {{ slide: Slide }} calcBounds
|
246
|
+
* @prop {{ zoomLevels: ZoomLevel, slideData: SlideData }} zoomLevelsUpdate
|
247
|
+
*
|
248
|
+
*
|
249
|
+
* legacy
|
250
|
+
*
|
251
|
+
* @prop {undefined} init
|
252
|
+
* @prop {undefined} initialZoomIn
|
253
|
+
* @prop {undefined} initialZoomOut
|
254
|
+
* @prop {undefined} initialZoomInEnd
|
255
|
+
* @prop {undefined} initialZoomOutEnd
|
256
|
+
* @prop {{ dataSource: DataSource | undefined, numItems: number }} numItems
|
257
|
+
* @prop {{ itemData: SlideData; index: number }} itemData
|
258
|
+
* @prop {{ index: number, itemData: SlideData, instance: PhotoSwipe }} thumbBounds
|
259
|
+
*/
|
260
|
+
|
261
|
+
/**
|
262
|
+
* @typedef {Object} PhotoSwipeFiltersMap https://photoswipe.com/filters/
|
263
|
+
*
|
264
|
+
* @prop {(numItems: number, dataSource: DataSource | undefined) => number} numItems
|
265
|
+
* Modify the total amount of slides. Example on Data sources page.
|
266
|
+
* https://photoswipe.com/filters/#numitems
|
267
|
+
*
|
268
|
+
* @prop {(itemData: SlideData, index: number) => SlideData} itemData
|
269
|
+
* Modify slide item data. Example on Data sources page.
|
270
|
+
* https://photoswipe.com/filters/#itemdata
|
271
|
+
*
|
272
|
+
* @prop {(itemData: SlideData, element: HTMLElement, linkEl: HTMLAnchorElement) => SlideData} domItemData
|
273
|
+
* Modify item data when it's parsed from DOM element. Example on Data sources page.
|
274
|
+
* https://photoswipe.com/filters/#domitemdata
|
275
|
+
*
|
276
|
+
* @prop {(clickedIndex: number, e: MouseEvent, instance: PhotoSwipeLightbox) => number} clickedIndex
|
277
|
+
* Modify clicked gallery item index.
|
278
|
+
* https://photoswipe.com/filters/#clickedindex
|
279
|
+
*
|
280
|
+
* @prop {(placeholderSrc: string | false, content: Content) => string | false} placeholderSrc
|
281
|
+
* Modify placeholder image source.
|
282
|
+
* https://photoswipe.com/filters/#placeholdersrc
|
283
|
+
*
|
284
|
+
* @prop {(isContentLoading: boolean, content: Content) => boolean} isContentLoading
|
285
|
+
* Modify if the content is currently loading.
|
286
|
+
* https://photoswipe.com/filters/#iscontentloading
|
287
|
+
*
|
288
|
+
* @prop {(isContentZoomable: boolean, content: Content) => boolean} isContentZoomable
|
289
|
+
* Modify if the content can be zoomed.
|
290
|
+
* https://photoswipe.com/filters/#iscontentzoomable
|
291
|
+
*
|
292
|
+
* @prop {(useContentPlaceholder: boolean, content: Content) => boolean} useContentPlaceholder
|
293
|
+
* Modify if the placeholder should be used for the content.
|
294
|
+
* https://photoswipe.com/filters/#usecontentplaceholder
|
295
|
+
*
|
296
|
+
* @prop {(isKeepingPlaceholder: boolean, content: Content) => boolean} isKeepingPlaceholder
|
297
|
+
* Modify if the placeholder should be kept after the content is loaded.
|
298
|
+
* https://photoswipe.com/filters/#iskeepingplaceholder
|
299
|
+
*
|
300
|
+
*
|
301
|
+
* @prop {(contentErrorElement: HTMLElement, content: Content) => HTMLElement} contentErrorElement
|
302
|
+
* Modify an element when the content has error state (for example, if image cannot be loaded).
|
303
|
+
* https://photoswipe.com/filters/#contenterrorelement
|
304
|
+
*
|
305
|
+
* @prop {(element: HTMLElement, data: UIElementData) => HTMLElement} uiElement
|
306
|
+
* Modify a UI element that's being created.
|
307
|
+
* https://photoswipe.com/filters/#uielement
|
308
|
+
*
|
309
|
+
* @prop {(thumbnail: HTMLElement | null | undefined, itemData: SlideData, index: number) => HTMLElement} thumbEl
|
310
|
+
* Modify the thumbnail element from which opening zoom animation starts or ends.
|
311
|
+
* https://photoswipe.com/filters/#thumbel
|
312
|
+
*
|
313
|
+
* @prop {(thumbBounds: Bounds | undefined, itemData: SlideData, index: number) => Bounds} thumbBounds
|
314
|
+
* Modify the thumbnail bounds from which opening zoom animation starts or ends.
|
315
|
+
* https://photoswipe.com/filters/#thumbbounds
|
316
|
+
*
|
317
|
+
* @prop {(srcsetSizesWidth: number, content: Content) => number} srcsetSizesWidth
|
318
|
+
*
|
319
|
+
* @prop {(preventPointerEvent: boolean, event: PointerEvent, pointerType: string) => boolean} preventPointerEvent
|
320
|
+
*
|
321
|
+
*/
|
322
|
+
|
323
|
+
/**
|
324
|
+
* @template {keyof PhotoSwipeFiltersMap} T
|
325
|
+
* @typedef {{ fn: PhotoSwipeFiltersMap[T], priority: number }} Filter
|
326
|
+
*/
|
327
|
+
|
328
|
+
/**
|
329
|
+
* @template {keyof PhotoSwipeEventsMap} T
|
330
|
+
* @typedef {PhotoSwipeEventsMap[T] extends undefined ? PhotoSwipeEvent<T> : PhotoSwipeEvent<T> & PhotoSwipeEventsMap[T]} AugmentedEvent
|
331
|
+
*/
|
332
|
+
|
333
|
+
/**
|
334
|
+
* @template {keyof PhotoSwipeEventsMap} T
|
335
|
+
* @typedef {(event: AugmentedEvent<T>) => void} EventCallback
|
336
|
+
*/
|
337
|
+
|
109
338
|
/**
|
110
339
|
* Base PhotoSwipe event object
|
340
|
+
*
|
341
|
+
* @template {keyof PhotoSwipeEventsMap} T
|
111
342
|
*/
|
112
343
|
class PhotoSwipeEvent {
|
344
|
+
/**
|
345
|
+
* @param {T} type
|
346
|
+
* @param {PhotoSwipeEventsMap[T]} [details]
|
347
|
+
*/
|
113
348
|
constructor(type, details) {
|
114
349
|
this.type = type;
|
350
|
+
this.defaultPrevented = false;
|
351
|
+
|
115
352
|
if (details) {
|
116
353
|
Object.assign(this, details);
|
117
354
|
}
|
@@ -120,118 +357,180 @@ class PhotoSwipeEvent {
|
|
120
357
|
preventDefault() {
|
121
358
|
this.defaultPrevented = true;
|
122
359
|
}
|
123
|
-
}
|
124
360
|
|
361
|
+
}
|
125
362
|
/**
|
126
363
|
* PhotoSwipe base class that can listen and dispatch for events.
|
127
364
|
* Shared by PhotoSwipe Core and PhotoSwipe Lightbox, extended by base.js
|
128
365
|
*/
|
366
|
+
|
367
|
+
|
129
368
|
class Eventable {
|
130
369
|
constructor() {
|
370
|
+
/**
|
371
|
+
* @type {{ [T in keyof PhotoSwipeEventsMap]?: ((event: AugmentedEvent<T>) => void)[] }}
|
372
|
+
*/
|
131
373
|
this._listeners = {};
|
374
|
+
/**
|
375
|
+
* @type {{ [T in keyof PhotoSwipeFiltersMap]?: Filter<T>[] }}
|
376
|
+
*/
|
377
|
+
|
132
378
|
this._filters = {};
|
379
|
+
/** @type {PhotoSwipe | undefined} */
|
380
|
+
|
381
|
+
this.pswp = undefined;
|
382
|
+
/** @type {PhotoSwipeOptions | undefined} */
|
383
|
+
|
384
|
+
this.options = undefined;
|
133
385
|
}
|
386
|
+
/**
|
387
|
+
* @template {keyof PhotoSwipeFiltersMap} T
|
388
|
+
* @param {T} name
|
389
|
+
* @param {PhotoSwipeFiltersMap[T]} fn
|
390
|
+
* @param {number} priority
|
391
|
+
*/
|
392
|
+
|
134
393
|
|
135
394
|
addFilter(name, fn, priority = 100) {
|
395
|
+
var _this$_filters$name, _this$_filters$name2, _this$pswp;
|
396
|
+
|
136
397
|
if (!this._filters[name]) {
|
137
398
|
this._filters[name] = [];
|
138
399
|
}
|
139
400
|
|
140
|
-
this._filters[name].push({
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
401
|
+
(_this$_filters$name = this._filters[name]) === null || _this$_filters$name === void 0 || _this$_filters$name.push({
|
402
|
+
fn,
|
403
|
+
priority
|
404
|
+
});
|
405
|
+
(_this$_filters$name2 = this._filters[name]) === null || _this$_filters$name2 === void 0 || _this$_filters$name2.sort((f1, f2) => f1.priority - f2.priority);
|
406
|
+
(_this$pswp = this.pswp) === null || _this$pswp === void 0 || _this$pswp.addFilter(name, fn, priority);
|
146
407
|
}
|
408
|
+
/**
|
409
|
+
* @template {keyof PhotoSwipeFiltersMap} T
|
410
|
+
* @param {T} name
|
411
|
+
* @param {PhotoSwipeFiltersMap[T]} fn
|
412
|
+
*/
|
413
|
+
|
147
414
|
|
148
415
|
removeFilter(name, fn) {
|
149
416
|
if (this._filters[name]) {
|
150
|
-
|
417
|
+
// @ts-expect-error
|
418
|
+
this._filters[name] = this._filters[name].filter(filter => filter.fn !== fn);
|
151
419
|
}
|
152
420
|
|
153
421
|
if (this.pswp) {
|
154
422
|
this.pswp.removeFilter(name, fn);
|
155
423
|
}
|
156
424
|
}
|
425
|
+
/**
|
426
|
+
* @template {keyof PhotoSwipeFiltersMap} T
|
427
|
+
* @param {T} name
|
428
|
+
* @param {Parameters<PhotoSwipeFiltersMap[T]>} args
|
429
|
+
* @returns {Parameters<PhotoSwipeFiltersMap[T]>[0]}
|
430
|
+
*/
|
431
|
+
|
157
432
|
|
158
433
|
applyFilters(name, ...args) {
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
434
|
+
var _this$_filters$name3;
|
435
|
+
|
436
|
+
(_this$_filters$name3 = this._filters[name]) === null || _this$_filters$name3 === void 0 || _this$_filters$name3.forEach(filter => {
|
437
|
+
// @ts-expect-error
|
438
|
+
args[0] = filter.fn.apply(this, args);
|
439
|
+
});
|
164
440
|
return args[0];
|
165
441
|
}
|
442
|
+
/**
|
443
|
+
* @template {keyof PhotoSwipeEventsMap} T
|
444
|
+
* @param {T} name
|
445
|
+
* @param {EventCallback<T>} fn
|
446
|
+
*/
|
447
|
+
|
166
448
|
|
167
449
|
on(name, fn) {
|
450
|
+
var _this$_listeners$name, _this$pswp2;
|
451
|
+
|
168
452
|
if (!this._listeners[name]) {
|
169
453
|
this._listeners[name] = [];
|
170
454
|
}
|
171
|
-
this._listeners[name].push(fn);
|
172
455
|
|
173
|
-
// When binding events to lightbox,
|
456
|
+
(_this$_listeners$name = this._listeners[name]) === null || _this$_listeners$name === void 0 || _this$_listeners$name.push(fn); // When binding events to lightbox,
|
174
457
|
// also bind events to PhotoSwipe Core,
|
175
458
|
// if it's open.
|
176
|
-
|
177
|
-
|
178
|
-
}
|
459
|
+
|
460
|
+
(_this$pswp2 = this.pswp) === null || _this$pswp2 === void 0 || _this$pswp2.on(name, fn);
|
179
461
|
}
|
462
|
+
/**
|
463
|
+
* @template {keyof PhotoSwipeEventsMap} T
|
464
|
+
* @param {T} name
|
465
|
+
* @param {EventCallback<T>} fn
|
466
|
+
*/
|
467
|
+
|
180
468
|
|
181
469
|
off(name, fn) {
|
470
|
+
var _this$pswp3;
|
471
|
+
|
182
472
|
if (this._listeners[name]) {
|
183
|
-
|
473
|
+
// @ts-expect-error
|
474
|
+
this._listeners[name] = this._listeners[name].filter(listener => fn !== listener);
|
184
475
|
}
|
185
476
|
|
186
|
-
|
187
|
-
this.pswp.off(name, fn);
|
188
|
-
}
|
477
|
+
(_this$pswp3 = this.pswp) === null || _this$pswp3 === void 0 || _this$pswp3.off(name, fn);
|
189
478
|
}
|
479
|
+
/**
|
480
|
+
* @template {keyof PhotoSwipeEventsMap} T
|
481
|
+
* @param {T} name
|
482
|
+
* @param {PhotoSwipeEventsMap[T]} [details]
|
483
|
+
* @returns {AugmentedEvent<T>}
|
484
|
+
*/
|
485
|
+
|
190
486
|
|
191
487
|
dispatch(name, details) {
|
488
|
+
var _this$_listeners$name2;
|
489
|
+
|
192
490
|
if (this.pswp) {
|
193
491
|
return this.pswp.dispatch(name, details);
|
194
492
|
}
|
195
493
|
|
196
|
-
const event =
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
if (this._listeners[name]) {
|
203
|
-
this._listeners[name].forEach((listener) => {
|
204
|
-
listener.call(this, event);
|
205
|
-
});
|
206
|
-
}
|
207
|
-
|
494
|
+
const event =
|
495
|
+
/** @type {AugmentedEvent<T>} */
|
496
|
+
new PhotoSwipeEvent(name, details);
|
497
|
+
(_this$_listeners$name2 = this._listeners[name]) === null || _this$_listeners$name2 === void 0 || _this$_listeners$name2.forEach(listener => {
|
498
|
+
listener.call(this, event);
|
499
|
+
});
|
208
500
|
return event;
|
209
501
|
}
|
502
|
+
|
210
503
|
}
|
211
504
|
|
212
505
|
class Placeholder {
|
213
506
|
/**
|
214
|
-
* @param {
|
215
|
-
* @param {
|
507
|
+
* @param {string | false} imageSrc
|
508
|
+
* @param {HTMLElement} container
|
216
509
|
*/
|
217
510
|
constructor(imageSrc, container) {
|
218
511
|
// Create placeholder
|
219
512
|
// (stretched thumbnail or simple div behind the main image)
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
container
|
224
|
-
);
|
513
|
+
|
514
|
+
/** @type {HTMLImageElement | HTMLDivElement | null} */
|
515
|
+
this.element = createElement('pswp__img pswp__img--placeholder', imageSrc ? 'img' : 'div', container);
|
225
516
|
|
226
517
|
if (imageSrc) {
|
227
|
-
|
228
|
-
|
229
|
-
this.element
|
230
|
-
|
518
|
+
const imgEl =
|
519
|
+
/** @type {HTMLImageElement} */
|
520
|
+
this.element;
|
521
|
+
imgEl.decoding = 'async';
|
522
|
+
imgEl.alt = '';
|
523
|
+
imgEl.src = imageSrc;
|
524
|
+
imgEl.setAttribute('role', 'presentation');
|
231
525
|
}
|
232
526
|
|
233
|
-
this.element.setAttribute('aria-
|
527
|
+
this.element.setAttribute('aria-hidden', 'true');
|
234
528
|
}
|
529
|
+
/**
|
530
|
+
* @param {number} width
|
531
|
+
* @param {number} height
|
532
|
+
*/
|
533
|
+
|
235
534
|
|
236
535
|
setDisplayedSize(width, height) {
|
237
536
|
if (!this.element) {
|
@@ -251,31 +550,53 @@ class Placeholder {
|
|
251
550
|
}
|
252
551
|
|
253
552
|
destroy() {
|
254
|
-
|
553
|
+
var _this$element;
|
554
|
+
|
555
|
+
if ((_this$element = this.element) !== null && _this$element !== void 0 && _this$element.parentNode) {
|
255
556
|
this.element.remove();
|
256
557
|
}
|
558
|
+
|
257
559
|
this.element = null;
|
258
560
|
}
|
561
|
+
|
259
562
|
}
|
260
563
|
|
564
|
+
/** @typedef {import('./slide.js').default} Slide */
|
565
|
+
|
566
|
+
/** @typedef {import('./slide.js').SlideData} SlideData */
|
567
|
+
|
568
|
+
/** @typedef {import('../core/base.js').default} PhotoSwipeBase */
|
569
|
+
|
570
|
+
/** @typedef {import('../util/util.js').LoadState} LoadState */
|
571
|
+
|
261
572
|
class Content {
|
262
573
|
/**
|
263
|
-
* @param {
|
574
|
+
* @param {SlideData} itemData Slide data
|
264
575
|
* @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox instance
|
265
|
-
* @param {
|
266
|
-
* can be undefined if image was requested by something else
|
267
|
-
* (for example by lazy-loader)
|
576
|
+
* @param {number} index
|
268
577
|
*/
|
269
578
|
constructor(itemData, instance, index) {
|
270
579
|
this.instance = instance;
|
271
580
|
this.data = itemData;
|
272
581
|
this.index = index;
|
582
|
+
/** @type {HTMLImageElement | HTMLDivElement | undefined} */
|
583
|
+
|
584
|
+
this.element = undefined;
|
585
|
+
/** @type {Placeholder | undefined} */
|
273
586
|
|
587
|
+
this.placeholder = undefined;
|
588
|
+
/** @type {Slide | undefined} */
|
589
|
+
|
590
|
+
this.slide = undefined;
|
591
|
+
this.displayedImageWidth = 0;
|
592
|
+
this.displayedImageHeight = 0;
|
274
593
|
this.width = Number(this.data.w) || Number(this.data.width) || 0;
|
275
594
|
this.height = Number(this.data.h) || Number(this.data.height) || 0;
|
276
|
-
|
277
595
|
this.isAttached = false;
|
278
596
|
this.hasSlide = false;
|
597
|
+
this.isDecoding = false;
|
598
|
+
/** @type {LoadState} */
|
599
|
+
|
279
600
|
this.state = LOAD_STATE.IDLE;
|
280
601
|
|
281
602
|
if (this.data.type) {
|
@@ -286,7 +607,9 @@ class Content {
|
|
286
607
|
this.type = 'html';
|
287
608
|
}
|
288
609
|
|
289
|
-
this.instance.dispatch('contentInit', {
|
610
|
+
this.instance.dispatch('contentInit', {
|
611
|
+
content: this
|
612
|
+
});
|
290
613
|
}
|
291
614
|
|
292
615
|
removePlaceholder() {
|
@@ -295,44 +618,55 @@ class Content {
|
|
295
618
|
setTimeout(() => {
|
296
619
|
if (this.placeholder) {
|
297
620
|
this.placeholder.destroy();
|
298
|
-
this.placeholder =
|
621
|
+
this.placeholder = undefined;
|
299
622
|
}
|
300
|
-
},
|
623
|
+
}, 1000);
|
301
624
|
}
|
302
625
|
}
|
303
|
-
|
304
626
|
/**
|
305
627
|
* Preload content
|
306
628
|
*
|
307
|
-
* @param {
|
629
|
+
* @param {boolean} isLazy
|
630
|
+
* @param {boolean} [reload]
|
308
631
|
*/
|
632
|
+
|
633
|
+
|
309
634
|
load(isLazy, reload) {
|
310
|
-
if (
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
635
|
+
if (this.slide && this.usePlaceholder()) {
|
636
|
+
if (!this.placeholder) {
|
637
|
+
const placeholderSrc = this.instance.applyFilters('placeholderSrc', // use image-based placeholder only for the first slide,
|
638
|
+
// as rendering (even small stretched thumbnail) is an expensive operation
|
639
|
+
this.data.msrc && this.slide.isFirstSlide ? this.data.msrc : false, this);
|
640
|
+
this.placeholder = new Placeholder(placeholderSrc, this.slide.container);
|
641
|
+
} else {
|
642
|
+
const placeholderEl = this.placeholder.element; // Add placeholder to DOM if it was already created
|
643
|
+
|
644
|
+
if (placeholderEl && !placeholderEl.parentElement) {
|
645
|
+
this.slide.container.prepend(placeholderEl);
|
646
|
+
}
|
647
|
+
}
|
322
648
|
}
|
323
649
|
|
324
650
|
if (this.element && !reload) {
|
325
651
|
return;
|
326
652
|
}
|
327
653
|
|
328
|
-
if (this.instance.dispatch('contentLoad', {
|
654
|
+
if (this.instance.dispatch('contentLoad', {
|
655
|
+
content: this,
|
656
|
+
isLazy
|
657
|
+
}).defaultPrevented) {
|
329
658
|
return;
|
330
659
|
}
|
331
660
|
|
332
661
|
if (this.isImageContent()) {
|
333
|
-
this.
|
662
|
+
this.element = createElement('pswp__img', 'img'); // Start loading only after width is defined, as sizes might depend on it.
|
663
|
+
// Due to Safari feature, we must define sizes before srcset.
|
664
|
+
|
665
|
+
if (this.displayedImageWidth) {
|
666
|
+
this.loadImage(isLazy);
|
667
|
+
}
|
334
668
|
} else {
|
335
|
-
this.element = createElement('pswp__content');
|
669
|
+
this.element = createElement('pswp__content', 'div');
|
336
670
|
this.element.innerHTML = this.data.html || '';
|
337
671
|
}
|
338
672
|
|
@@ -340,116 +674,137 @@ class Content {
|
|
340
674
|
this.slide.updateContentSize(true);
|
341
675
|
}
|
342
676
|
}
|
343
|
-
|
344
677
|
/**
|
345
678
|
* Preload image
|
346
679
|
*
|
347
|
-
* @param {
|
680
|
+
* @param {boolean} isLazy
|
348
681
|
*/
|
682
|
+
|
683
|
+
|
349
684
|
loadImage(isLazy) {
|
350
|
-
|
685
|
+
var _this$data$src, _this$data$alt;
|
351
686
|
|
352
|
-
if (this.instance.dispatch('contentLoadImage', {
|
687
|
+
if (!this.isImageContent() || !this.element || this.instance.dispatch('contentLoadImage', {
|
688
|
+
content: this,
|
689
|
+
isLazy
|
690
|
+
}).defaultPrevented) {
|
353
691
|
return;
|
354
692
|
}
|
355
693
|
|
694
|
+
const imageElement =
|
695
|
+
/** @type HTMLImageElement */
|
696
|
+
this.element;
|
697
|
+
this.updateSrcsetSizes();
|
698
|
+
|
356
699
|
if (this.data.srcset) {
|
357
|
-
|
700
|
+
imageElement.srcset = this.data.srcset;
|
358
701
|
}
|
359
702
|
|
360
|
-
|
361
|
-
|
362
|
-
this.element.alt = this.data.alt || '';
|
363
|
-
|
703
|
+
imageElement.src = (_this$data$src = this.data.src) !== null && _this$data$src !== void 0 ? _this$data$src : '';
|
704
|
+
imageElement.alt = (_this$data$alt = this.data.alt) !== null && _this$data$alt !== void 0 ? _this$data$alt : '';
|
364
705
|
this.state = LOAD_STATE.LOADING;
|
365
706
|
|
366
|
-
if (
|
707
|
+
if (imageElement.complete) {
|
367
708
|
this.onLoaded();
|
368
709
|
} else {
|
369
|
-
|
710
|
+
imageElement.onload = () => {
|
370
711
|
this.onLoaded();
|
371
712
|
};
|
372
713
|
|
373
|
-
|
714
|
+
imageElement.onerror = () => {
|
374
715
|
this.onError();
|
375
716
|
};
|
376
717
|
}
|
377
718
|
}
|
378
|
-
|
379
719
|
/**
|
380
720
|
* Assign slide to content
|
381
721
|
*
|
382
722
|
* @param {Slide} slide
|
383
723
|
*/
|
724
|
+
|
725
|
+
|
384
726
|
setSlide(slide) {
|
385
727
|
this.slide = slide;
|
386
728
|
this.hasSlide = true;
|
387
|
-
this.instance = slide.pswp;
|
388
|
-
|
389
|
-
// todo: do we need to unset slide?
|
729
|
+
this.instance = slide.pswp; // todo: do we need to unset slide?
|
390
730
|
}
|
391
|
-
|
392
731
|
/**
|
393
732
|
* Content load success handler
|
394
733
|
*/
|
734
|
+
|
735
|
+
|
395
736
|
onLoaded() {
|
396
737
|
this.state = LOAD_STATE.LOADED;
|
397
738
|
|
398
|
-
if (this.slide) {
|
399
|
-
this.instance.dispatch('loadComplete', {
|
739
|
+
if (this.slide && this.element) {
|
740
|
+
this.instance.dispatch('loadComplete', {
|
741
|
+
slide: this.slide,
|
742
|
+
content: this
|
743
|
+
}); // if content is reloaded
|
400
744
|
|
401
|
-
|
402
|
-
if (this.slide.isActive
|
403
|
-
&& this.slide.heavyAppended
|
404
|
-
&& !this.element.parentNode) {
|
405
|
-
this.slide.container.innerHTML = '';
|
745
|
+
if (this.slide.isActive && this.slide.heavyAppended && !this.element.parentNode) {
|
406
746
|
this.append();
|
407
747
|
this.slide.updateContentSize(true);
|
408
748
|
}
|
749
|
+
|
750
|
+
if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
|
751
|
+
this.removePlaceholder();
|
752
|
+
}
|
409
753
|
}
|
410
754
|
}
|
411
|
-
|
412
755
|
/**
|
413
756
|
* Content load error handler
|
414
757
|
*/
|
758
|
+
|
759
|
+
|
415
760
|
onError() {
|
416
761
|
this.state = LOAD_STATE.ERROR;
|
417
762
|
|
418
763
|
if (this.slide) {
|
419
764
|
this.displayError();
|
420
|
-
this.instance.dispatch('loadComplete', {
|
421
|
-
|
765
|
+
this.instance.dispatch('loadComplete', {
|
766
|
+
slide: this.slide,
|
767
|
+
isError: true,
|
768
|
+
content: this
|
769
|
+
});
|
770
|
+
this.instance.dispatch('loadError', {
|
771
|
+
slide: this.slide,
|
772
|
+
content: this
|
773
|
+
});
|
422
774
|
}
|
423
775
|
}
|
424
|
-
|
425
776
|
/**
|
426
777
|
* @returns {Boolean} If the content is currently loading
|
427
778
|
*/
|
779
|
+
|
780
|
+
|
428
781
|
isLoading() {
|
429
|
-
return this.instance.applyFilters(
|
430
|
-
'isContentLoading',
|
431
|
-
this.state === LOAD_STATE.LOADING,
|
432
|
-
this
|
433
|
-
);
|
782
|
+
return this.instance.applyFilters('isContentLoading', this.state === LOAD_STATE.LOADING, this);
|
434
783
|
}
|
784
|
+
/**
|
785
|
+
* @returns {Boolean} If the content is in error state
|
786
|
+
*/
|
787
|
+
|
435
788
|
|
436
789
|
isError() {
|
437
790
|
return this.state === LOAD_STATE.ERROR;
|
438
791
|
}
|
439
|
-
|
440
792
|
/**
|
441
|
-
* @returns {
|
793
|
+
* @returns {boolean} If the content is image
|
442
794
|
*/
|
795
|
+
|
796
|
+
|
443
797
|
isImageContent() {
|
444
798
|
return this.type === 'image';
|
445
799
|
}
|
446
|
-
|
447
800
|
/**
|
448
801
|
* Update content size
|
449
802
|
*
|
450
803
|
* @param {Number} width
|
451
804
|
* @param {Number} height
|
452
805
|
*/
|
806
|
+
|
807
|
+
|
453
808
|
setDisplayedSize(width, height) {
|
454
809
|
if (!this.element) {
|
455
810
|
return;
|
@@ -459,122 +814,160 @@ class Content {
|
|
459
814
|
this.placeholder.setDisplayedSize(width, height);
|
460
815
|
}
|
461
816
|
|
462
|
-
if (this.instance.dispatch('contentResize', {
|
817
|
+
if (this.instance.dispatch('contentResize', {
|
818
|
+
content: this,
|
819
|
+
width,
|
820
|
+
height
|
821
|
+
}).defaultPrevented) {
|
463
822
|
return;
|
464
823
|
}
|
465
824
|
|
466
825
|
setWidthHeight(this.element, width, height);
|
467
826
|
|
468
827
|
if (this.isImageContent() && !this.isError()) {
|
469
|
-
const
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
image.sizes = width + 'px';
|
478
|
-
image.dataset.largestUsedSize = width;
|
828
|
+
const isInitialSizeUpdate = !this.displayedImageWidth && width;
|
829
|
+
this.displayedImageWidth = width;
|
830
|
+
this.displayedImageHeight = height;
|
831
|
+
|
832
|
+
if (isInitialSizeUpdate) {
|
833
|
+
this.loadImage(false);
|
834
|
+
} else {
|
835
|
+
this.updateSrcsetSizes();
|
479
836
|
}
|
480
837
|
|
481
838
|
if (this.slide) {
|
482
|
-
this.instance.dispatch('imageSizeChange', {
|
839
|
+
this.instance.dispatch('imageSizeChange', {
|
840
|
+
slide: this.slide,
|
841
|
+
width,
|
842
|
+
height,
|
843
|
+
content: this
|
844
|
+
});
|
483
845
|
}
|
484
846
|
}
|
485
847
|
}
|
486
|
-
|
487
848
|
/**
|
488
|
-
* @returns {
|
849
|
+
* @returns {boolean} If the content can be zoomed
|
489
850
|
*/
|
851
|
+
|
852
|
+
|
490
853
|
isZoomable() {
|
491
|
-
return this.instance.applyFilters(
|
492
|
-
'isContentZoomable',
|
493
|
-
this.isImageContent() && (this.state !== LOAD_STATE.ERROR),
|
494
|
-
this
|
495
|
-
);
|
854
|
+
return this.instance.applyFilters('isContentZoomable', this.isImageContent() && this.state !== LOAD_STATE.ERROR, this);
|
496
855
|
}
|
856
|
+
/**
|
857
|
+
* Update image srcset sizes attribute based on width and height
|
858
|
+
*/
|
859
|
+
|
860
|
+
|
861
|
+
updateSrcsetSizes() {
|
862
|
+
// Handle srcset sizes attribute.
|
863
|
+
//
|
864
|
+
// Never lower quality, if it was increased previously.
|
865
|
+
// Chrome does this automatically, Firefox and Safari do not,
|
866
|
+
// so we store largest used size in dataset.
|
867
|
+
if (!this.isImageContent() || !this.element || !this.data.srcset) {
|
868
|
+
return;
|
869
|
+
}
|
870
|
+
|
871
|
+
const image =
|
872
|
+
/** @type HTMLImageElement */
|
873
|
+
this.element;
|
874
|
+
const sizesWidth = this.instance.applyFilters('srcsetSizesWidth', this.displayedImageWidth, this);
|
497
875
|
|
876
|
+
if (!image.dataset.largestUsedSize || sizesWidth > parseInt(image.dataset.largestUsedSize, 10)) {
|
877
|
+
image.sizes = sizesWidth + 'px';
|
878
|
+
image.dataset.largestUsedSize = String(sizesWidth);
|
879
|
+
}
|
880
|
+
}
|
498
881
|
/**
|
499
|
-
* @returns {
|
882
|
+
* @returns {boolean} If content should use a placeholder (from msrc by default)
|
500
883
|
*/
|
884
|
+
|
885
|
+
|
501
886
|
usePlaceholder() {
|
502
|
-
return this.instance.applyFilters(
|
503
|
-
'useContentPlaceholder',
|
504
|
-
this.isImageContent(),
|
505
|
-
this
|
506
|
-
);
|
887
|
+
return this.instance.applyFilters('useContentPlaceholder', this.isImageContent(), this);
|
507
888
|
}
|
508
|
-
|
509
889
|
/**
|
510
890
|
* Preload content with lazy-loading param
|
511
|
-
*
|
512
|
-
* @param {Boolean} isLazy
|
513
891
|
*/
|
892
|
+
|
893
|
+
|
514
894
|
lazyLoad() {
|
515
|
-
if (this.instance.dispatch('contentLazyLoad', {
|
895
|
+
if (this.instance.dispatch('contentLazyLoad', {
|
896
|
+
content: this
|
897
|
+
}).defaultPrevented) {
|
516
898
|
return;
|
517
899
|
}
|
518
900
|
|
519
901
|
this.load(true);
|
520
902
|
}
|
521
|
-
|
522
903
|
/**
|
523
|
-
* @returns {
|
904
|
+
* @returns {boolean} If placeholder should be kept after content is loaded
|
524
905
|
*/
|
906
|
+
|
907
|
+
|
525
908
|
keepPlaceholder() {
|
526
|
-
return this.instance.applyFilters(
|
527
|
-
'isKeepingPlaceholder',
|
528
|
-
this.isLoading(),
|
529
|
-
this
|
530
|
-
);
|
909
|
+
return this.instance.applyFilters('isKeepingPlaceholder', this.isLoading(), this);
|
531
910
|
}
|
532
|
-
|
533
911
|
/**
|
534
912
|
* Destroy the content
|
535
913
|
*/
|
914
|
+
|
915
|
+
|
536
916
|
destroy() {
|
537
917
|
this.hasSlide = false;
|
538
|
-
this.slide =
|
918
|
+
this.slide = undefined;
|
539
919
|
|
540
|
-
if (this.instance.dispatch('contentDestroy', {
|
920
|
+
if (this.instance.dispatch('contentDestroy', {
|
921
|
+
content: this
|
922
|
+
}).defaultPrevented) {
|
541
923
|
return;
|
542
924
|
}
|
543
925
|
|
544
926
|
this.remove();
|
545
927
|
|
928
|
+
if (this.placeholder) {
|
929
|
+
this.placeholder.destroy();
|
930
|
+
this.placeholder = undefined;
|
931
|
+
}
|
932
|
+
|
546
933
|
if (this.isImageContent() && this.element) {
|
547
934
|
this.element.onload = null;
|
548
935
|
this.element.onerror = null;
|
549
|
-
this.element =
|
936
|
+
this.element = undefined;
|
550
937
|
}
|
551
938
|
}
|
552
|
-
|
553
939
|
/**
|
554
940
|
* Display error message
|
555
941
|
*/
|
942
|
+
|
943
|
+
|
556
944
|
displayError() {
|
557
945
|
if (this.slide) {
|
558
|
-
|
559
|
-
|
560
|
-
errorMsgEl =
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
);
|
565
|
-
this.element = createElement('pswp__content pswp__error-msg-container');
|
946
|
+
var _this$instance$option, _this$instance$option2;
|
947
|
+
|
948
|
+
let errorMsgEl = createElement('pswp__error-msg', 'div');
|
949
|
+
errorMsgEl.innerText = (_this$instance$option = (_this$instance$option2 = this.instance.options) === null || _this$instance$option2 === void 0 ? void 0 : _this$instance$option2.errorMsg) !== null && _this$instance$option !== void 0 ? _this$instance$option : '';
|
950
|
+
errorMsgEl =
|
951
|
+
/** @type {HTMLDivElement} */
|
952
|
+
this.instance.applyFilters('contentErrorElement', errorMsgEl, this);
|
953
|
+
this.element = createElement('pswp__content pswp__error-msg-container', 'div');
|
566
954
|
this.element.appendChild(errorMsgEl);
|
567
|
-
this.slide.container.
|
955
|
+
this.slide.container.innerText = '';
|
568
956
|
this.slide.container.appendChild(this.element);
|
569
957
|
this.slide.updateContentSize(true);
|
570
958
|
this.removePlaceholder();
|
571
959
|
}
|
572
960
|
}
|
573
|
-
|
574
961
|
/**
|
575
962
|
* Append the content
|
576
963
|
*/
|
964
|
+
|
965
|
+
|
577
966
|
append() {
|
967
|
+
if (this.isAttached || !this.element) {
|
968
|
+
return;
|
969
|
+
}
|
970
|
+
|
578
971
|
this.isAttached = true;
|
579
972
|
|
580
973
|
if (this.state === LOAD_STATE.ERROR) {
|
@@ -582,10 +975,14 @@ class Content {
|
|
582
975
|
return;
|
583
976
|
}
|
584
977
|
|
585
|
-
if (this.instance.dispatch('contentAppend', {
|
978
|
+
if (this.instance.dispatch('contentAppend', {
|
979
|
+
content: this
|
980
|
+
}).defaultPrevented) {
|
586
981
|
return;
|
587
982
|
}
|
588
983
|
|
984
|
+
const supportsDecode = ('decode' in this.element);
|
985
|
+
|
589
986
|
if (this.isImageContent()) {
|
590
987
|
// Use decode() on nearby slides
|
591
988
|
//
|
@@ -598,263 +995,131 @@ class Content {
|
|
598
995
|
// You might ask "why dont you just decode() and then append all images",
|
599
996
|
// that's because I want to show image before it's fully loaded,
|
600
997
|
// as browser can render parts of image while it is loading.
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
this.appendImage();
|
613
|
-
});
|
614
|
-
}).catch(() => {
|
615
|
-
this.isDecoding = false;
|
616
|
-
});
|
617
|
-
}
|
618
|
-
});
|
998
|
+
// We do not do this in Safari due to partial loading bug.
|
999
|
+
if (supportsDecode && this.slide && (!this.slide.isActive || isSafari())) {
|
1000
|
+
this.isDecoding = true; // purposefully using finally instead of then,
|
1001
|
+
// as if srcset sizes changes dynamically - it may cause decode error
|
1002
|
+
|
1003
|
+
/** @type {HTMLImageElement} */
|
1004
|
+
|
1005
|
+
this.element.decode().catch(() => {}).finally(() => {
|
1006
|
+
this.isDecoding = false;
|
1007
|
+
this.appendImage();
|
1008
|
+
});
|
619
1009
|
} else {
|
620
|
-
if (this.placeholder
|
621
|
-
&& (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR)) {
|
622
|
-
this.removePlaceholder();
|
623
|
-
}
|
624
1010
|
this.appendImage();
|
625
1011
|
}
|
626
|
-
} else if (this.
|
1012
|
+
} else if (this.slide && !this.element.parentNode) {
|
627
1013
|
this.slide.container.appendChild(this.element);
|
628
1014
|
}
|
629
1015
|
}
|
630
|
-
|
631
1016
|
/**
|
632
1017
|
* Activate the slide,
|
633
1018
|
* active slide is generally the current one,
|
634
1019
|
* meaning the user can see it.
|
635
1020
|
*/
|
1021
|
+
|
1022
|
+
|
636
1023
|
activate() {
|
637
|
-
if (this.instance.dispatch('contentActivate', {
|
1024
|
+
if (this.instance.dispatch('contentActivate', {
|
1025
|
+
content: this
|
1026
|
+
}).defaultPrevented || !this.slide) {
|
638
1027
|
return;
|
639
1028
|
}
|
640
1029
|
|
641
|
-
if (this.
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
this.load(false, true); // try to reload
|
648
|
-
}
|
1030
|
+
if (this.isImageContent() && this.isDecoding && !isSafari()) {
|
1031
|
+
// add image to slide when it becomes active,
|
1032
|
+
// even if it's not finished decoding
|
1033
|
+
this.appendImage();
|
1034
|
+
} else if (this.isError()) {
|
1035
|
+
this.load(false, true); // try to reload
|
649
1036
|
}
|
650
|
-
}
|
651
1037
|
|
1038
|
+
if (this.slide.holderElement) {
|
1039
|
+
this.slide.holderElement.setAttribute('aria-hidden', 'false');
|
1040
|
+
}
|
1041
|
+
}
|
652
1042
|
/**
|
653
1043
|
* Deactivate the content
|
654
1044
|
*/
|
655
|
-
deactivate() {
|
656
|
-
this.instance.dispatch('contentDeactivate', { content: this });
|
657
|
-
}
|
658
1045
|
|
659
1046
|
|
1047
|
+
deactivate() {
|
1048
|
+
this.instance.dispatch('contentDeactivate', {
|
1049
|
+
content: this
|
1050
|
+
});
|
1051
|
+
|
1052
|
+
if (this.slide && this.slide.holderElement) {
|
1053
|
+
this.slide.holderElement.setAttribute('aria-hidden', 'true');
|
1054
|
+
}
|
1055
|
+
}
|
660
1056
|
/**
|
661
1057
|
* Remove the content from DOM
|
662
1058
|
*/
|
1059
|
+
|
1060
|
+
|
663
1061
|
remove() {
|
664
1062
|
this.isAttached = false;
|
665
1063
|
|
666
|
-
if (this.instance.dispatch('contentRemove', {
|
1064
|
+
if (this.instance.dispatch('contentRemove', {
|
1065
|
+
content: this
|
1066
|
+
}).defaultPrevented) {
|
667
1067
|
return;
|
668
1068
|
}
|
669
1069
|
|
670
1070
|
if (this.element && this.element.parentNode) {
|
671
1071
|
this.element.remove();
|
672
1072
|
}
|
673
|
-
}
|
674
1073
|
|
1074
|
+
if (this.placeholder && this.placeholder.element) {
|
1075
|
+
this.placeholder.element.remove();
|
1076
|
+
}
|
1077
|
+
}
|
675
1078
|
/**
|
676
1079
|
* Append the image content to slide container
|
677
1080
|
*/
|
1081
|
+
|
1082
|
+
|
678
1083
|
appendImage() {
|
679
1084
|
if (!this.isAttached) {
|
680
1085
|
return;
|
681
1086
|
}
|
682
1087
|
|
683
|
-
if (this.instance.dispatch('contentAppendImage', {
|
1088
|
+
if (this.instance.dispatch('contentAppendImage', {
|
1089
|
+
content: this
|
1090
|
+
}).defaultPrevented) {
|
684
1091
|
return;
|
685
|
-
}
|
1092
|
+
} // ensure that element exists and is not already appended
|
1093
|
+
|
686
1094
|
|
687
|
-
// ensure that element exists and is not already appended
|
688
1095
|
if (this.slide && this.element && !this.element.parentNode) {
|
689
1096
|
this.slide.container.appendChild(this.element);
|
690
|
-
|
691
|
-
if (this.placeholder
|
692
|
-
&& (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR)) {
|
693
|
-
this.removePlaceholder();
|
694
|
-
}
|
695
|
-
}
|
696
|
-
}
|
697
|
-
}
|
698
|
-
|
699
|
-
/**
|
700
|
-
* PhotoSwipe base class that can retrieve data about every slide.
|
701
|
-
* Shared by PhotoSwipe Core and PhotoSwipe Lightbox
|
702
|
-
*/
|
703
|
-
|
704
|
-
|
705
|
-
class PhotoSwipeBase extends Eventable {
|
706
|
-
/**
|
707
|
-
* Get total number of slides
|
708
|
-
*/
|
709
|
-
getNumItems() {
|
710
|
-
let numItems;
|
711
|
-
const { dataSource } = this.options;
|
712
|
-
if (!dataSource) {
|
713
|
-
numItems = 0;
|
714
|
-
} else if (dataSource.length) {
|
715
|
-
// may be an array or just object with length property
|
716
|
-
numItems = dataSource.length;
|
717
|
-
} else if (dataSource.gallery) {
|
718
|
-
// query DOM elements
|
719
|
-
if (!dataSource.items) {
|
720
|
-
dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
|
721
|
-
}
|
722
|
-
|
723
|
-
if (dataSource.items) {
|
724
|
-
numItems = dataSource.items.length;
|
725
|
-
}
|
726
|
-
}
|
727
|
-
|
728
|
-
// legacy event, before filters were introduced
|
729
|
-
const event = this.dispatch('numItems', {
|
730
|
-
dataSource,
|
731
|
-
numItems
|
732
|
-
});
|
733
|
-
return this.applyFilters('numItems', event.numItems, dataSource);
|
734
|
-
}
|
735
|
-
|
736
|
-
createContentFromData(slideData, index) {
|
737
|
-
return new Content(slideData, this, index);
|
738
|
-
}
|
739
|
-
|
740
|
-
/**
|
741
|
-
* Get item data by index.
|
742
|
-
*
|
743
|
-
* "item data" should contain normalized information that PhotoSwipe needs to generate a slide.
|
744
|
-
* For example, it may contain properties like
|
745
|
-
* `src`, `srcset`, `w`, `h`, which will be used to generate a slide with image.
|
746
|
-
*
|
747
|
-
* @param {Integer} index
|
748
|
-
*/
|
749
|
-
getItemData(index) {
|
750
|
-
const { dataSource } = this.options;
|
751
|
-
let dataSourceItem;
|
752
|
-
if (Array.isArray(dataSource)) {
|
753
|
-
// Datasource is an array of elements
|
754
|
-
dataSourceItem = dataSource[index];
|
755
|
-
} else if (dataSource && dataSource.gallery) {
|
756
|
-
// dataSource has gallery property,
|
757
|
-
// thus it was created by Lightbox, based on
|
758
|
-
// gallerySelecor and childSelector options
|
759
|
-
|
760
|
-
// query DOM elements
|
761
|
-
if (!dataSource.items) {
|
762
|
-
dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
|
763
|
-
}
|
764
|
-
|
765
|
-
dataSourceItem = dataSource.items[index];
|
766
1097
|
}
|
767
1098
|
|
768
|
-
|
769
|
-
|
770
|
-
if (itemData instanceof Element) {
|
771
|
-
itemData = this._domElementToItemData(itemData);
|
772
|
-
}
|
773
|
-
|
774
|
-
// Dispatching the itemData event,
|
775
|
-
// it's a legacy verion before filters were introduced
|
776
|
-
const event = this.dispatch('itemData', {
|
777
|
-
itemData: itemData || {},
|
778
|
-
index
|
779
|
-
});
|
780
|
-
|
781
|
-
return this.applyFilters('itemData', event.itemData, index);
|
782
|
-
}
|
783
|
-
|
784
|
-
/**
|
785
|
-
* Get array of gallery DOM elements,
|
786
|
-
* based on childSelector and gallery element.
|
787
|
-
*
|
788
|
-
* @param {Element} galleryElement
|
789
|
-
*/
|
790
|
-
_getGalleryDOMElements(galleryElement) {
|
791
|
-
if (this.options.children || this.options.childSelector) {
|
792
|
-
return getElementsFromOption(
|
793
|
-
this.options.children,
|
794
|
-
this.options.childSelector,
|
795
|
-
galleryElement
|
796
|
-
) || [];
|
1099
|
+
if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
|
1100
|
+
this.removePlaceholder();
|
797
1101
|
}
|
798
|
-
|
799
|
-
return [galleryElement];
|
800
1102
|
}
|
801
1103
|
|
802
|
-
|
803
|
-
* Converts DOM element to item data object.
|
804
|
-
*
|
805
|
-
* @param {Element} element DOM element
|
806
|
-
*/
|
807
|
-
// eslint-disable-next-line class-methods-use-this
|
808
|
-
_domElementToItemData(element) {
|
809
|
-
const itemData = {
|
810
|
-
element
|
811
|
-
};
|
812
|
-
|
813
|
-
const linkEl = element.tagName === 'A' ? element : element.querySelector('a');
|
814
|
-
|
815
|
-
if (linkEl) {
|
816
|
-
// src comes from data-pswp-src attribute,
|
817
|
-
// if it's empty link href is used
|
818
|
-
itemData.src = linkEl.dataset.pswpSrc || linkEl.href;
|
819
|
-
|
820
|
-
if (linkEl.dataset.pswpSrcset) {
|
821
|
-
itemData.srcset = linkEl.dataset.pswpSrcset;
|
822
|
-
}
|
823
|
-
|
824
|
-
itemData.width = parseInt(linkEl.dataset.pswpWidth, 10);
|
825
|
-
itemData.height = parseInt(linkEl.dataset.pswpHeight, 10);
|
826
|
-
|
827
|
-
// support legacy w & h properties
|
828
|
-
itemData.w = itemData.width;
|
829
|
-
itemData.h = itemData.height;
|
830
|
-
|
831
|
-
if (linkEl.dataset.pswpType) {
|
832
|
-
itemData.type = linkEl.dataset.pswpType;
|
833
|
-
}
|
834
|
-
|
835
|
-
const thumbnailEl = element.querySelector('img');
|
1104
|
+
}
|
836
1105
|
|
837
|
-
|
838
|
-
// msrc is URL to placeholder image that's displayed before large image is loaded
|
839
|
-
// by default it's displayed only for the first slide
|
840
|
-
itemData.msrc = thumbnailEl.currentSrc || thumbnailEl.src;
|
841
|
-
itemData.alt = thumbnailEl.getAttribute('alt');
|
842
|
-
}
|
1106
|
+
/** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
|
843
1107
|
|
844
|
-
|
845
|
-
itemData.thumbCropped = true;
|
846
|
-
}
|
847
|
-
}
|
1108
|
+
/** @typedef {import('../core/base.js').default} PhotoSwipeBase */
|
848
1109
|
|
849
|
-
|
1110
|
+
/** @typedef {import('../photoswipe.js').Point} Point */
|
850
1111
|
|
851
|
-
|
852
|
-
}
|
853
|
-
}
|
1112
|
+
/** @typedef {import('../slide/slide.js').SlideData} SlideData */
|
854
1113
|
|
1114
|
+
/**
|
1115
|
+
* @param {PhotoSwipeOptions} options
|
1116
|
+
* @param {PhotoSwipeBase} pswp
|
1117
|
+
* @returns {Point}
|
1118
|
+
*/
|
855
1119
|
function getViewportSize(options, pswp) {
|
856
1120
|
if (options.getViewportSizeFn) {
|
857
1121
|
const newViewportSize = options.getViewportSizeFn(options, pswp);
|
1122
|
+
|
858
1123
|
if (newViewportSize) {
|
859
1124
|
return newViewportSize;
|
860
1125
|
}
|
@@ -862,7 +1127,6 @@ function getViewportSize(options, pswp) {
|
|
862
1127
|
|
863
1128
|
return {
|
864
1129
|
x: document.documentElement.clientWidth,
|
865
|
-
|
866
1130
|
// TODO: height on mobile is very incosistent due to toolbar
|
867
1131
|
// find a way to improve this
|
868
1132
|
//
|
@@ -870,7 +1134,6 @@ function getViewportSize(options, pswp) {
|
|
870
1134
|
y: window.innerHeight
|
871
1135
|
};
|
872
1136
|
}
|
873
|
-
|
874
1137
|
/**
|
875
1138
|
* Parses padding option.
|
876
1139
|
* Supported formats:
|
@@ -899,117 +1162,141 @@ function getViewportSize(options, pswp) {
|
|
899
1162
|
* paddingTop: 0,
|
900
1163
|
* paddingBottom: 0,
|
901
1164
|
*
|
902
|
-
* @param {
|
903
|
-
* @param {
|
904
|
-
* @param {
|
905
|
-
* @param {
|
906
|
-
* @param {
|
907
|
-
* @returns {
|
1165
|
+
* @param {'left' | 'top' | 'bottom' | 'right'} prop
|
1166
|
+
* @param {PhotoSwipeOptions} options PhotoSwipe options
|
1167
|
+
* @param {Point} viewportSize PhotoSwipe viewport size, for example: { x:800, y:600 }
|
1168
|
+
* @param {SlideData} itemData Data about the slide
|
1169
|
+
* @param {number} index Slide index
|
1170
|
+
* @returns {number}
|
908
1171
|
*/
|
1172
|
+
|
909
1173
|
function parsePaddingOption(prop, options, viewportSize, itemData, index) {
|
910
|
-
let paddingValue;
|
1174
|
+
let paddingValue = 0;
|
911
1175
|
|
912
1176
|
if (options.paddingFn) {
|
913
1177
|
paddingValue = options.paddingFn(viewportSize, itemData, index)[prop];
|
914
1178
|
} else if (options.padding) {
|
915
1179
|
paddingValue = options.padding[prop];
|
916
1180
|
} else {
|
917
|
-
const legacyPropName = 'padding' + prop[0].toUpperCase() + prop.slice(1);
|
1181
|
+
const legacyPropName = 'padding' + prop[0].toUpperCase() + prop.slice(1); // @ts-expect-error
|
1182
|
+
|
918
1183
|
if (options[legacyPropName]) {
|
1184
|
+
// @ts-expect-error
|
919
1185
|
paddingValue = options[legacyPropName];
|
920
1186
|
}
|
921
1187
|
}
|
922
1188
|
|
923
|
-
return paddingValue || 0;
|
1189
|
+
return Number(paddingValue) || 0;
|
924
1190
|
}
|
925
|
-
|
1191
|
+
/**
|
1192
|
+
* @param {PhotoSwipeOptions} options
|
1193
|
+
* @param {Point} viewportSize
|
1194
|
+
* @param {SlideData} itemData
|
1195
|
+
* @param {number} index
|
1196
|
+
* @returns {Point}
|
1197
|
+
*/
|
926
1198
|
|
927
1199
|
function getPanAreaSize(options, viewportSize, itemData, index) {
|
928
1200
|
return {
|
929
|
-
x: viewportSize.x
|
930
|
-
|
931
|
-
- parsePaddingOption('right', options, viewportSize, itemData, index),
|
932
|
-
y: viewportSize.y
|
933
|
-
- parsePaddingOption('top', options, viewportSize, itemData, index)
|
934
|
-
- parsePaddingOption('bottom', options, viewportSize, itemData, index)
|
1201
|
+
x: viewportSize.x - parsePaddingOption('left', options, viewportSize, itemData, index) - parsePaddingOption('right', options, viewportSize, itemData, index),
|
1202
|
+
y: viewportSize.y - parsePaddingOption('top', options, viewportSize, itemData, index) - parsePaddingOption('bottom', options, viewportSize, itemData, index)
|
935
1203
|
};
|
936
1204
|
}
|
937
1205
|
|
1206
|
+
const MAX_IMAGE_WIDTH = 4000;
|
1207
|
+
/** @typedef {import('../photoswipe.js').default} PhotoSwipe */
|
1208
|
+
|
1209
|
+
/** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
|
1210
|
+
|
1211
|
+
/** @typedef {import('../photoswipe.js').Point} Point */
|
1212
|
+
|
1213
|
+
/** @typedef {import('../slide/slide.js').SlideData} SlideData */
|
1214
|
+
|
1215
|
+
/** @typedef {'fit' | 'fill' | number | ((zoomLevelObject: ZoomLevel) => number)} ZoomLevelOption */
|
1216
|
+
|
938
1217
|
/**
|
939
1218
|
* Calculates zoom levels for specific slide.
|
940
1219
|
* Depends on viewport size and image size.
|
941
1220
|
*/
|
942
1221
|
|
943
|
-
const MAX_IMAGE_WIDTH = 4000;
|
944
|
-
|
945
1222
|
class ZoomLevel {
|
946
1223
|
/**
|
947
|
-
* @param {
|
948
|
-
* @param {
|
949
|
-
* @param {
|
950
|
-
* @param {PhotoSwipe
|
1224
|
+
* @param {PhotoSwipeOptions} options PhotoSwipe options
|
1225
|
+
* @param {SlideData} itemData Slide data
|
1226
|
+
* @param {number} index Slide index
|
1227
|
+
* @param {PhotoSwipe} [pswp] PhotoSwipe instance, can be undefined if not initialized yet
|
951
1228
|
*/
|
952
1229
|
constructor(options, itemData, index, pswp) {
|
953
1230
|
this.pswp = pswp;
|
954
1231
|
this.options = options;
|
955
1232
|
this.itemData = itemData;
|
956
1233
|
this.index = index;
|
1234
|
+
/** @type { Point | null } */
|
1235
|
+
|
1236
|
+
this.panAreaSize = null;
|
1237
|
+
/** @type { Point | null } */
|
1238
|
+
|
1239
|
+
this.elementSize = null;
|
1240
|
+
this.fit = 1;
|
1241
|
+
this.fill = 1;
|
1242
|
+
this.vFill = 1;
|
1243
|
+
this.initial = 1;
|
1244
|
+
this.secondary = 1;
|
1245
|
+
this.max = 1;
|
1246
|
+
this.min = 1;
|
957
1247
|
}
|
958
|
-
|
959
1248
|
/**
|
960
1249
|
* Calculate initial, secondary and maximum zoom level for the specified slide.
|
961
1250
|
*
|
962
1251
|
* It should be called when either image or viewport size changes.
|
963
1252
|
*
|
964
|
-
* @param {
|
1253
|
+
* @param {number} maxWidth
|
1254
|
+
* @param {number} maxHeight
|
1255
|
+
* @param {Point} panAreaSize
|
965
1256
|
*/
|
1257
|
+
|
1258
|
+
|
966
1259
|
update(maxWidth, maxHeight, panAreaSize) {
|
967
|
-
|
1260
|
+
/** @type {Point} */
|
1261
|
+
const elementSize = {
|
968
1262
|
x: maxWidth,
|
969
1263
|
y: maxHeight
|
970
1264
|
};
|
971
|
-
|
1265
|
+
this.elementSize = elementSize;
|
972
1266
|
this.panAreaSize = panAreaSize;
|
973
|
-
|
974
|
-
const
|
975
|
-
const vRatio = this.panAreaSize.y / this.elementSize.y;
|
976
|
-
|
1267
|
+
const hRatio = panAreaSize.x / elementSize.x;
|
1268
|
+
const vRatio = panAreaSize.y / elementSize.y;
|
977
1269
|
this.fit = Math.min(1, hRatio < vRatio ? hRatio : vRatio);
|
978
|
-
this.fill = Math.min(1, hRatio > vRatio ? hRatio : vRatio);
|
979
|
-
|
980
|
-
// zoom.vFill defines zoom level of the image
|
1270
|
+
this.fill = Math.min(1, hRatio > vRatio ? hRatio : vRatio); // zoom.vFill defines zoom level of the image
|
981
1271
|
// when it has 100% of viewport vertical space (height)
|
982
|
-
this.vFill = Math.min(1, vRatio);
|
983
1272
|
|
1273
|
+
this.vFill = Math.min(1, vRatio);
|
984
1274
|
this.initial = this._getInitial();
|
985
1275
|
this.secondary = this._getSecondary();
|
986
|
-
this.max = Math.max(
|
987
|
-
|
988
|
-
this.secondary,
|
989
|
-
this._getMax()
|
990
|
-
);
|
991
|
-
|
992
|
-
this.min = Math.min(
|
993
|
-
this.fit,
|
994
|
-
this.initial,
|
995
|
-
this.secondary
|
996
|
-
);
|
1276
|
+
this.max = Math.max(this.initial, this.secondary, this._getMax());
|
1277
|
+
this.min = Math.min(this.fit, this.initial, this.secondary);
|
997
1278
|
|
998
1279
|
if (this.pswp) {
|
999
|
-
this.pswp.dispatch('zoomLevelsUpdate', {
|
1280
|
+
this.pswp.dispatch('zoomLevelsUpdate', {
|
1281
|
+
zoomLevels: this,
|
1282
|
+
slideData: this.itemData
|
1283
|
+
});
|
1000
1284
|
}
|
1001
1285
|
}
|
1002
|
-
|
1003
1286
|
/**
|
1004
1287
|
* Parses user-defined zoom option.
|
1005
1288
|
*
|
1006
|
-
* @
|
1289
|
+
* @private
|
1290
|
+
* @param {'initial' | 'secondary' | 'max'} optionPrefix Zoom level option prefix (initial, secondary, max)
|
1291
|
+
* @returns { number | undefined }
|
1007
1292
|
*/
|
1293
|
+
|
1294
|
+
|
1008
1295
|
_parseZoomLevelOption(optionPrefix) {
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
const optionValue = this.options[
|
1296
|
+
const optionName =
|
1297
|
+
/** @type {'initialZoomLevel' | 'secondaryZoomLevel' | 'maxZoomLevel'} */
|
1298
|
+
optionPrefix + 'ZoomLevel';
|
1299
|
+
const optionValue = this.options[optionName];
|
1013
1300
|
|
1014
1301
|
if (!optionValue) {
|
1015
1302
|
return;
|
@@ -1029,59 +1316,60 @@ class ZoomLevel {
|
|
1029
1316
|
|
1030
1317
|
return Number(optionValue);
|
1031
1318
|
}
|
1032
|
-
|
1033
1319
|
/**
|
1034
1320
|
* Get zoom level to which image will be zoomed after double-tap gesture,
|
1035
1321
|
* or when user clicks on zoom icon,
|
1036
1322
|
* or mouse-click on image itself.
|
1037
1323
|
* If you return 1 image will be zoomed to its original size.
|
1038
1324
|
*
|
1039
|
-
* @
|
1325
|
+
* @private
|
1326
|
+
* @return {number}
|
1040
1327
|
*/
|
1328
|
+
|
1329
|
+
|
1041
1330
|
_getSecondary() {
|
1042
1331
|
let currZoomLevel = this._parseZoomLevelOption('secondary');
|
1043
1332
|
|
1044
1333
|
if (currZoomLevel) {
|
1045
1334
|
return currZoomLevel;
|
1046
|
-
}
|
1335
|
+
} // 3x of "fit" state, but not larger than original
|
1336
|
+
|
1047
1337
|
|
1048
|
-
// 3x of "fit" state, but not larger than original
|
1049
1338
|
currZoomLevel = Math.min(1, this.fit * 3);
|
1050
1339
|
|
1051
|
-
if (currZoomLevel * this.elementSize.x > MAX_IMAGE_WIDTH) {
|
1340
|
+
if (this.elementSize && currZoomLevel * this.elementSize.x > MAX_IMAGE_WIDTH) {
|
1052
1341
|
currZoomLevel = MAX_IMAGE_WIDTH / this.elementSize.x;
|
1053
1342
|
}
|
1054
1343
|
|
1055
1344
|
return currZoomLevel;
|
1056
1345
|
}
|
1057
|
-
|
1058
1346
|
/**
|
1059
1347
|
* Get initial image zoom level.
|
1060
1348
|
*
|
1061
|
-
* @
|
1349
|
+
* @private
|
1350
|
+
* @return {number}
|
1062
1351
|
*/
|
1352
|
+
|
1353
|
+
|
1063
1354
|
_getInitial() {
|
1064
1355
|
return this._parseZoomLevelOption('initial') || this.fit;
|
1065
1356
|
}
|
1066
|
-
|
1067
1357
|
/**
|
1068
1358
|
* Maximum zoom level when user zooms
|
1069
1359
|
* via zoom/pinch gesture,
|
1070
1360
|
* via cmd/ctrl-wheel or via trackpad.
|
1071
1361
|
*
|
1072
|
-
* @
|
1362
|
+
* @private
|
1363
|
+
* @return {number}
|
1073
1364
|
*/
|
1074
|
-
_getMax() {
|
1075
|
-
const currZoomLevel = this._parseZoomLevelOption('max');
|
1076
1365
|
|
1077
|
-
if (currZoomLevel) {
|
1078
|
-
return currZoomLevel;
|
1079
|
-
}
|
1080
1366
|
|
1367
|
+
_getMax() {
|
1081
1368
|
// max zoom level is x4 from "fit state",
|
1082
1369
|
// used for zoom gesture and ctrl/trackpad zoom
|
1083
|
-
return Math.max(1, this.fit * 4);
|
1370
|
+
return this._parseZoomLevelOption('max') || Math.max(1, this.fit * 4);
|
1084
1371
|
}
|
1372
|
+
|
1085
1373
|
}
|
1086
1374
|
|
1087
1375
|
/**
|
@@ -1089,61 +1377,283 @@ class ZoomLevel {
|
|
1089
1377
|
* This function is used both by Lightbox and PhotoSwipe core,
|
1090
1378
|
* thus it can be called before dialog is opened.
|
1091
1379
|
*
|
1092
|
-
* @param {
|
1093
|
-
* @param {PhotoSwipeBase}
|
1094
|
-
* @param {
|
1095
|
-
* @returns {
|
1380
|
+
* @param {SlideData} itemData Data about the slide
|
1381
|
+
* @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox instance
|
1382
|
+
* @param {number} index
|
1383
|
+
* @returns {Content} Image that is being decoded or false.
|
1096
1384
|
*/
|
1385
|
+
|
1097
1386
|
function lazyLoadData(itemData, instance, index) {
|
1098
|
-
// src/slide/content/content.js
|
1099
1387
|
const content = instance.createContentFromData(itemData, index);
|
1388
|
+
/** @type {ZoomLevel | undefined} */
|
1100
1389
|
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1390
|
+
let zoomLevel;
|
1391
|
+
const {
|
1392
|
+
options
|
1393
|
+
} = instance; // We need to know dimensions of the image to preload it,
|
1394
|
+
// as it might use srcset, and we need to define sizes
|
1104
1395
|
|
1105
|
-
|
1396
|
+
if (options) {
|
1397
|
+
zoomLevel = new ZoomLevel(options, itemData, -1);
|
1398
|
+
let viewportSize;
|
1106
1399
|
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1400
|
+
if (instance.pswp) {
|
1401
|
+
viewportSize = instance.pswp.viewportSize;
|
1402
|
+
} else {
|
1403
|
+
viewportSize = getViewportSize(options, instance);
|
1404
|
+
}
|
1111
1405
|
|
1112
|
-
|
1113
|
-
|
1406
|
+
const panAreaSize = getPanAreaSize(options, viewportSize, itemData, index);
|
1407
|
+
zoomLevel.update(content.width, content.height, panAreaSize);
|
1408
|
+
}
|
1114
1409
|
|
1115
1410
|
content.lazyLoad();
|
1116
|
-
|
1117
|
-
|
1118
|
-
Math.ceil(content.height * zoomLevel.initial)
|
1119
|
-
|
1411
|
+
|
1412
|
+
if (zoomLevel) {
|
1413
|
+
content.setDisplayedSize(Math.ceil(content.width * zoomLevel.initial), Math.ceil(content.height * zoomLevel.initial));
|
1414
|
+
}
|
1120
1415
|
|
1121
1416
|
return content;
|
1122
1417
|
}
|
1123
|
-
|
1124
|
-
|
1125
1418
|
/**
|
1126
1419
|
* Lazy-loads specific slide.
|
1127
1420
|
* This function is used both by Lightbox and PhotoSwipe core,
|
1128
1421
|
* thus it can be called before dialog is opened.
|
1129
1422
|
*
|
1130
|
-
* By default it loads image based on viewport size and initial zoom level.
|
1423
|
+
* By default, it loads image based on viewport size and initial zoom level.
|
1131
1424
|
*
|
1132
|
-
* @param {
|
1133
|
-
* @param {
|
1425
|
+
* @param {number} index Slide index
|
1426
|
+
* @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox eventable instance
|
1427
|
+
* @returns {Content | undefined}
|
1134
1428
|
*/
|
1429
|
+
|
1135
1430
|
function lazyLoadSlide(index, instance) {
|
1136
1431
|
const itemData = instance.getItemData(index);
|
1137
1432
|
|
1138
|
-
if (instance.dispatch('lazyLoadSlide', {
|
1433
|
+
if (instance.dispatch('lazyLoadSlide', {
|
1434
|
+
index,
|
1435
|
+
itemData
|
1436
|
+
}).defaultPrevented) {
|
1139
1437
|
return;
|
1140
1438
|
}
|
1141
1439
|
|
1142
1440
|
return lazyLoadData(itemData, instance, index);
|
1143
1441
|
}
|
1144
1442
|
|
1443
|
+
/** @typedef {import("../photoswipe.js").default} PhotoSwipe */
|
1444
|
+
|
1445
|
+
/** @typedef {import("../slide/slide.js").SlideData} SlideData */
|
1446
|
+
|
1447
|
+
/**
|
1448
|
+
* PhotoSwipe base class that can retrieve data about every slide.
|
1449
|
+
* Shared by PhotoSwipe Core and PhotoSwipe Lightbox
|
1450
|
+
*/
|
1451
|
+
|
1452
|
+
class PhotoSwipeBase extends Eventable {
|
1453
|
+
/**
|
1454
|
+
* Get total number of slides
|
1455
|
+
*
|
1456
|
+
* @returns {number}
|
1457
|
+
*/
|
1458
|
+
getNumItems() {
|
1459
|
+
var _this$options;
|
1460
|
+
|
1461
|
+
let numItems = 0;
|
1462
|
+
const dataSource = (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.dataSource;
|
1463
|
+
|
1464
|
+
if (dataSource && 'length' in dataSource) {
|
1465
|
+
// may be an array or just object with length property
|
1466
|
+
numItems = dataSource.length;
|
1467
|
+
} else if (dataSource && 'gallery' in dataSource) {
|
1468
|
+
// query DOM elements
|
1469
|
+
if (!dataSource.items) {
|
1470
|
+
dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
|
1471
|
+
}
|
1472
|
+
|
1473
|
+
if (dataSource.items) {
|
1474
|
+
numItems = dataSource.items.length;
|
1475
|
+
}
|
1476
|
+
} // legacy event, before filters were introduced
|
1477
|
+
|
1478
|
+
|
1479
|
+
const event = this.dispatch('numItems', {
|
1480
|
+
dataSource,
|
1481
|
+
numItems
|
1482
|
+
});
|
1483
|
+
return this.applyFilters('numItems', event.numItems, dataSource);
|
1484
|
+
}
|
1485
|
+
/**
|
1486
|
+
* @param {SlideData} slideData
|
1487
|
+
* @param {number} index
|
1488
|
+
* @returns {Content}
|
1489
|
+
*/
|
1490
|
+
|
1491
|
+
|
1492
|
+
createContentFromData(slideData, index) {
|
1493
|
+
return new Content(slideData, this, index);
|
1494
|
+
}
|
1495
|
+
/**
|
1496
|
+
* Get item data by index.
|
1497
|
+
*
|
1498
|
+
* "item data" should contain normalized information that PhotoSwipe needs to generate a slide.
|
1499
|
+
* For example, it may contain properties like
|
1500
|
+
* `src`, `srcset`, `w`, `h`, which will be used to generate a slide with image.
|
1501
|
+
*
|
1502
|
+
* @param {number} index
|
1503
|
+
* @returns {SlideData}
|
1504
|
+
*/
|
1505
|
+
|
1506
|
+
|
1507
|
+
getItemData(index) {
|
1508
|
+
var _this$options2;
|
1509
|
+
|
1510
|
+
const dataSource = (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.dataSource;
|
1511
|
+
/** @type {SlideData | HTMLElement} */
|
1512
|
+
|
1513
|
+
let dataSourceItem = {};
|
1514
|
+
|
1515
|
+
if (Array.isArray(dataSource)) {
|
1516
|
+
// Datasource is an array of elements
|
1517
|
+
dataSourceItem = dataSource[index];
|
1518
|
+
} else if (dataSource && 'gallery' in dataSource) {
|
1519
|
+
// dataSource has gallery property,
|
1520
|
+
// thus it was created by Lightbox, based on
|
1521
|
+
// gallery and children options
|
1522
|
+
// query DOM elements
|
1523
|
+
if (!dataSource.items) {
|
1524
|
+
dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
|
1525
|
+
}
|
1526
|
+
|
1527
|
+
dataSourceItem = dataSource.items[index];
|
1528
|
+
}
|
1529
|
+
|
1530
|
+
let itemData = dataSourceItem;
|
1531
|
+
|
1532
|
+
if (itemData instanceof Element) {
|
1533
|
+
itemData = this._domElementToItemData(itemData);
|
1534
|
+
} // Dispatching the itemData event,
|
1535
|
+
// it's a legacy verion before filters were introduced
|
1536
|
+
|
1537
|
+
|
1538
|
+
const event = this.dispatch('itemData', {
|
1539
|
+
itemData: itemData || {},
|
1540
|
+
index
|
1541
|
+
});
|
1542
|
+
return this.applyFilters('itemData', event.itemData, index);
|
1543
|
+
}
|
1544
|
+
/**
|
1545
|
+
* Get array of gallery DOM elements,
|
1546
|
+
* based on childSelector and gallery element.
|
1547
|
+
*
|
1548
|
+
* @param {HTMLElement} galleryElement
|
1549
|
+
* @returns {HTMLElement[]}
|
1550
|
+
*/
|
1551
|
+
|
1552
|
+
|
1553
|
+
_getGalleryDOMElements(galleryElement) {
|
1554
|
+
var _this$options3, _this$options4;
|
1555
|
+
|
1556
|
+
if ((_this$options3 = this.options) !== null && _this$options3 !== void 0 && _this$options3.children || (_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.childSelector) {
|
1557
|
+
return getElementsFromOption(this.options.children, this.options.childSelector, galleryElement) || [];
|
1558
|
+
}
|
1559
|
+
|
1560
|
+
return [galleryElement];
|
1561
|
+
}
|
1562
|
+
/**
|
1563
|
+
* Converts DOM element to item data object.
|
1564
|
+
*
|
1565
|
+
* @param {HTMLElement} element DOM element
|
1566
|
+
* @returns {SlideData}
|
1567
|
+
*/
|
1568
|
+
|
1569
|
+
|
1570
|
+
_domElementToItemData(element) {
|
1571
|
+
/** @type {SlideData} */
|
1572
|
+
const itemData = {
|
1573
|
+
element
|
1574
|
+
};
|
1575
|
+
const linkEl =
|
1576
|
+
/** @type {HTMLAnchorElement} */
|
1577
|
+
element.tagName === 'A' ? element : element.querySelector('a');
|
1578
|
+
|
1579
|
+
if (linkEl) {
|
1580
|
+
// src comes from data-pswp-src attribute,
|
1581
|
+
// if it's empty link href is used
|
1582
|
+
itemData.src = linkEl.dataset.pswpSrc || linkEl.href;
|
1583
|
+
|
1584
|
+
if (linkEl.dataset.pswpSrcset) {
|
1585
|
+
itemData.srcset = linkEl.dataset.pswpSrcset;
|
1586
|
+
}
|
1587
|
+
|
1588
|
+
itemData.width = linkEl.dataset.pswpWidth ? parseInt(linkEl.dataset.pswpWidth, 10) : 0;
|
1589
|
+
itemData.height = linkEl.dataset.pswpHeight ? parseInt(linkEl.dataset.pswpHeight, 10) : 0; // support legacy w & h properties
|
1590
|
+
|
1591
|
+
itemData.w = itemData.width;
|
1592
|
+
itemData.h = itemData.height;
|
1593
|
+
|
1594
|
+
if (linkEl.dataset.pswpType) {
|
1595
|
+
itemData.type = linkEl.dataset.pswpType;
|
1596
|
+
}
|
1597
|
+
|
1598
|
+
const thumbnailEl = element.querySelector('img');
|
1599
|
+
|
1600
|
+
if (thumbnailEl) {
|
1601
|
+
var _thumbnailEl$getAttri;
|
1602
|
+
|
1603
|
+
// msrc is URL to placeholder image that's displayed before large image is loaded
|
1604
|
+
// by default it's displayed only for the first slide
|
1605
|
+
itemData.msrc = thumbnailEl.currentSrc || thumbnailEl.src;
|
1606
|
+
itemData.alt = (_thumbnailEl$getAttri = thumbnailEl.getAttribute('alt')) !== null && _thumbnailEl$getAttri !== void 0 ? _thumbnailEl$getAttri : '';
|
1607
|
+
}
|
1608
|
+
|
1609
|
+
if (linkEl.dataset.pswpCropped || linkEl.dataset.cropped) {
|
1610
|
+
itemData.thumbCropped = true;
|
1611
|
+
}
|
1612
|
+
}
|
1613
|
+
|
1614
|
+
return this.applyFilters('domItemData', itemData, element, linkEl);
|
1615
|
+
}
|
1616
|
+
/**
|
1617
|
+
* Lazy-load by slide data
|
1618
|
+
*
|
1619
|
+
* @param {SlideData} itemData Data about the slide
|
1620
|
+
* @param {number} index
|
1621
|
+
* @returns {Content} Image that is being decoded or false.
|
1622
|
+
*/
|
1623
|
+
|
1624
|
+
|
1625
|
+
lazyLoadData(itemData, index) {
|
1626
|
+
return lazyLoadData(itemData, this, index);
|
1627
|
+
}
|
1628
|
+
|
1629
|
+
}
|
1630
|
+
|
1631
|
+
/**
|
1632
|
+
* @template T
|
1633
|
+
* @typedef {import('../types.js').Type<T>} Type<T>
|
1634
|
+
*/
|
1635
|
+
|
1636
|
+
/** @typedef {import('../photoswipe.js').default} PhotoSwipe */
|
1637
|
+
|
1638
|
+
/** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
|
1639
|
+
|
1640
|
+
/** @typedef {import('../photoswipe.js').DataSource} DataSource */
|
1641
|
+
|
1642
|
+
/** @typedef {import('../photoswipe.js').Point} Point */
|
1643
|
+
|
1644
|
+
/** @typedef {import('../slide/content.js').default} Content */
|
1645
|
+
|
1646
|
+
/** @typedef {import('../core/eventable.js').PhotoSwipeEventsMap} PhotoSwipeEventsMap */
|
1647
|
+
|
1648
|
+
/** @typedef {import('../core/eventable.js').PhotoSwipeFiltersMap} PhotoSwipeFiltersMap */
|
1649
|
+
|
1650
|
+
/**
|
1651
|
+
* @template {keyof PhotoSwipeEventsMap} T
|
1652
|
+
* @typedef {import('../core/eventable.js').EventCallback<T>} EventCallback<T>
|
1653
|
+
*/
|
1654
|
+
|
1145
1655
|
/**
|
1146
|
-
* PhotoSwipe
|
1656
|
+
* PhotoSwipe Lightbox
|
1147
1657
|
*
|
1148
1658
|
* - If user has unsupported browser it falls back to default browser action (just opens URL)
|
1149
1659
|
* - Binds click event to links that should open PhotoSwipe
|
@@ -1159,38 +1669,62 @@ function lazyLoadSlide(index, instance) {
|
|
1159
1669
|
*/
|
1160
1670
|
|
1161
1671
|
class PhotoSwipeLightbox extends PhotoSwipeBase {
|
1672
|
+
/**
|
1673
|
+
* @param {PhotoSwipeOptions} [options]
|
1674
|
+
*/
|
1162
1675
|
constructor(options) {
|
1163
1676
|
super();
|
1677
|
+
/** @type {PhotoSwipeOptions} */
|
1678
|
+
|
1164
1679
|
this.options = options || {};
|
1165
1680
|
this._uid = 0;
|
1166
|
-
|
1681
|
+
this.shouldOpen = false;
|
1682
|
+
/**
|
1683
|
+
* @private
|
1684
|
+
* @type {Content | undefined}
|
1685
|
+
*/
|
1167
1686
|
|
1168
|
-
|
1687
|
+
this._preloadedContent = undefined;
|
1169
1688
|
this.onThumbnailsClick = this.onThumbnailsClick.bind(this);
|
1689
|
+
}
|
1690
|
+
/**
|
1691
|
+
* Initialize lightbox, should be called only once.
|
1692
|
+
* It's not included in the main constructor, so you may bind events before it.
|
1693
|
+
*/
|
1694
|
+
|
1170
1695
|
|
1696
|
+
init() {
|
1171
1697
|
// Bind click events to each gallery
|
1172
|
-
getElementsFromOption(this.options.gallery, this.options.gallerySelector)
|
1173
|
-
.
|
1174
|
-
|
1175
|
-
});
|
1698
|
+
getElementsFromOption(this.options.gallery, this.options.gallerySelector).forEach(galleryElement => {
|
1699
|
+
galleryElement.addEventListener('click', this.onThumbnailsClick, false);
|
1700
|
+
});
|
1176
1701
|
}
|
1702
|
+
/**
|
1703
|
+
* @param {MouseEvent} e
|
1704
|
+
*/
|
1705
|
+
|
1177
1706
|
|
1178
1707
|
onThumbnailsClick(e) {
|
1179
1708
|
// Exit and allow default browser action if:
|
1180
1709
|
if (specialKeyUsed(e) // ... if clicked with a special key (ctrl/cmd...)
|
1181
|
-
|
1182
|
-
|
1710
|
+
|| window.pswp) {
|
1711
|
+
// ... if PhotoSwipe is already open
|
1183
1712
|
return;
|
1184
|
-
}
|
1185
|
-
|
1186
|
-
// If both clientX and clientY are 0 or not defined,
|
1713
|
+
} // If both clientX and clientY are 0 or not defined,
|
1187
1714
|
// the event is likely triggered by keyboard,
|
1188
1715
|
// so we do not pass the initialPoint
|
1189
1716
|
//
|
1190
1717
|
// Note that some screen readers emulate the mouse position,
|
1191
|
-
// so it's not ideal way to detect them.
|
1718
|
+
// so it's not the ideal way to detect them.
|
1192
1719
|
//
|
1193
|
-
|
1720
|
+
|
1721
|
+
/** @type {Point | null} */
|
1722
|
+
|
1723
|
+
|
1724
|
+
let initialPoint = {
|
1725
|
+
x: e.clientX,
|
1726
|
+
y: e.clientY
|
1727
|
+
};
|
1194
1728
|
|
1195
1729
|
if (!initialPoint.x && !initialPoint.y) {
|
1196
1730
|
initialPoint = null;
|
@@ -1198,8 +1732,12 @@ class PhotoSwipeLightbox extends PhotoSwipeBase {
|
|
1198
1732
|
|
1199
1733
|
let clickedIndex = this.getClickedIndex(e);
|
1200
1734
|
clickedIndex = this.applyFilters('clickedIndex', clickedIndex, e, this);
|
1735
|
+
/** @type {DataSource} */
|
1736
|
+
|
1201
1737
|
const dataSource = {
|
1202
|
-
gallery:
|
1738
|
+
gallery:
|
1739
|
+
/** @type {HTMLElement} */
|
1740
|
+
e.currentTarget
|
1203
1741
|
};
|
1204
1742
|
|
1205
1743
|
if (clickedIndex >= 0) {
|
@@ -1207,90 +1745,111 @@ class PhotoSwipeLightbox extends PhotoSwipeBase {
|
|
1207
1745
|
this.loadAndOpen(clickedIndex, dataSource, initialPoint);
|
1208
1746
|
}
|
1209
1747
|
}
|
1210
|
-
|
1211
1748
|
/**
|
1212
1749
|
* Get index of gallery item that was clicked.
|
1213
1750
|
*
|
1214
|
-
* @param {
|
1751
|
+
* @param {MouseEvent} e click event
|
1752
|
+
* @returns {number}
|
1215
1753
|
*/
|
1754
|
+
|
1755
|
+
|
1216
1756
|
getClickedIndex(e) {
|
1217
1757
|
// legacy option
|
1218
1758
|
if (this.options.getClickedIndexFn) {
|
1219
1759
|
return this.options.getClickedIndexFn.call(this, e);
|
1220
1760
|
}
|
1221
1761
|
|
1222
|
-
const clickedTarget =
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
);
|
1228
|
-
const clickedChildIndex = childElements.findIndex(
|
1229
|
-
child => child === clickedTarget || child.contains(clickedTarget)
|
1230
|
-
);
|
1762
|
+
const clickedTarget =
|
1763
|
+
/** @type {HTMLElement} */
|
1764
|
+
e.target;
|
1765
|
+
const childElements = getElementsFromOption(this.options.children, this.options.childSelector,
|
1766
|
+
/** @type {HTMLElement} */
|
1767
|
+
e.currentTarget);
|
1768
|
+
const clickedChildIndex = childElements.findIndex(child => child === clickedTarget || child.contains(clickedTarget));
|
1231
1769
|
|
1232
1770
|
if (clickedChildIndex !== -1) {
|
1233
1771
|
return clickedChildIndex;
|
1234
1772
|
} else if (this.options.children || this.options.childSelector) {
|
1235
1773
|
// click wasn't on a child element
|
1236
1774
|
return -1;
|
1237
|
-
}
|
1775
|
+
} // There is only one item (which is the gallery)
|
1776
|
+
|
1238
1777
|
|
1239
|
-
// There is only one item (which is the gallery)
|
1240
1778
|
return 0;
|
1241
1779
|
}
|
1242
|
-
|
1243
1780
|
/**
|
1244
1781
|
* Load and open PhotoSwipe
|
1245
1782
|
*
|
1246
|
-
* @param {
|
1247
|
-
* @param {
|
1248
|
-
* @param {Point|null} initialPoint
|
1783
|
+
* @param {number} index
|
1784
|
+
* @param {DataSource} [dataSource]
|
1785
|
+
* @param {Point | null} [initialPoint]
|
1786
|
+
* @returns {boolean}
|
1249
1787
|
*/
|
1788
|
+
|
1789
|
+
|
1250
1790
|
loadAndOpen(index, dataSource, initialPoint) {
|
1251
1791
|
// Check if the gallery is already open
|
1252
|
-
if (window.pswp) {
|
1792
|
+
if (window.pswp || !this.options) {
|
1253
1793
|
return false;
|
1254
|
-
}
|
1794
|
+
} // Use the first gallery element if dataSource is not provided
|
1255
1795
|
|
1256
|
-
// set initial index
|
1257
|
-
this.options.index = index;
|
1258
1796
|
|
1259
|
-
|
1260
|
-
|
1797
|
+
if (!dataSource && this.options.gallery && this.options.children) {
|
1798
|
+
const galleryElements = getElementsFromOption(this.options.gallery);
|
1799
|
+
|
1800
|
+
if (galleryElements[0]) {
|
1801
|
+
dataSource = {
|
1802
|
+
gallery: galleryElements[0]
|
1803
|
+
};
|
1804
|
+
}
|
1805
|
+
} // set initial index
|
1261
1806
|
|
1807
|
+
|
1808
|
+
this.options.index = index; // define options for PhotoSwipe constructor
|
1809
|
+
|
1810
|
+
this.options.initialPointerPos = initialPoint;
|
1262
1811
|
this.shouldOpen = true;
|
1263
1812
|
this.preload(index, dataSource);
|
1264
1813
|
return true;
|
1265
1814
|
}
|
1266
|
-
|
1267
1815
|
/**
|
1268
1816
|
* Load the main module and the slide content by index
|
1269
1817
|
*
|
1270
|
-
* @param {
|
1818
|
+
* @param {number} index
|
1819
|
+
* @param {DataSource} [dataSource]
|
1271
1820
|
*/
|
1821
|
+
|
1822
|
+
|
1272
1823
|
preload(index, dataSource) {
|
1273
|
-
const {
|
1824
|
+
const {
|
1825
|
+
options
|
1826
|
+
} = this;
|
1274
1827
|
|
1275
1828
|
if (dataSource) {
|
1276
1829
|
options.dataSource = dataSource;
|
1277
|
-
}
|
1830
|
+
} // Add the main module
|
1831
|
+
|
1832
|
+
/** @type {Promise<Type<PhotoSwipe>>[]} */
|
1278
1833
|
|
1279
|
-
// Add the main module
|
1280
|
-
const promiseArray = [];
|
1281
1834
|
|
1835
|
+
const promiseArray = [];
|
1282
1836
|
const pswpModuleType = typeof options.pswpModule;
|
1837
|
+
|
1283
1838
|
if (isPswpClass(options.pswpModule)) {
|
1284
|
-
promiseArray.push(
|
1839
|
+
promiseArray.push(Promise.resolve(
|
1840
|
+
/** @type {Type<PhotoSwipe>} */
|
1841
|
+
options.pswpModule));
|
1285
1842
|
} else if (pswpModuleType === 'string') {
|
1286
1843
|
throw new Error('pswpModule as string is no longer supported');
|
1287
1844
|
} else if (pswpModuleType === 'function') {
|
1288
|
-
promiseArray.push(
|
1845
|
+
promiseArray.push(
|
1846
|
+
/** @type {() => Promise<Type<PhotoSwipe>>} */
|
1847
|
+
options.pswpModule());
|
1289
1848
|
} else {
|
1290
1849
|
throw new Error('pswpModule is not valid');
|
1291
|
-
}
|
1850
|
+
} // Add custom-defined promise, if any
|
1851
|
+
|
1292
1852
|
|
1293
|
-
// Add custom-defined promise, if any
|
1294
1853
|
if (typeof options.openPromise === 'function') {
|
1295
1854
|
// allow developers to perform some task before opening
|
1296
1855
|
promiseArray.push(options.openPromise());
|
@@ -1298,17 +1857,24 @@ class PhotoSwipeLightbox extends PhotoSwipeBase {
|
|
1298
1857
|
|
1299
1858
|
if (options.preloadFirstSlide !== false && index >= 0) {
|
1300
1859
|
this._preloadedContent = lazyLoadSlide(index, this);
|
1301
|
-
}
|
1860
|
+
} // Wait till all promises resolve and open PhotoSwipe
|
1861
|
+
|
1302
1862
|
|
1303
|
-
// Wait till all promises resolve and open PhotoSwipe
|
1304
1863
|
const uid = ++this._uid;
|
1305
|
-
Promise.all(promiseArray).then(
|
1864
|
+
Promise.all(promiseArray).then(iterableModules => {
|
1306
1865
|
if (this.shouldOpen) {
|
1307
1866
|
const mainModule = iterableModules[0];
|
1867
|
+
|
1308
1868
|
this._openPhotoswipe(mainModule, uid);
|
1309
1869
|
}
|
1310
1870
|
});
|
1311
1871
|
}
|
1872
|
+
/**
|
1873
|
+
* @private
|
1874
|
+
* @param {Type<PhotoSwipe> | { default: Type<PhotoSwipe> }} module
|
1875
|
+
* @param {number} uid
|
1876
|
+
*/
|
1877
|
+
|
1312
1878
|
|
1313
1879
|
_openPhotoswipe(module, uid) {
|
1314
1880
|
// Cancel opening if UID doesn't match the current one
|
@@ -1320,63 +1886,75 @@ class PhotoSwipeLightbox extends PhotoSwipeBase {
|
|
1320
1886
|
return;
|
1321
1887
|
}
|
1322
1888
|
|
1323
|
-
this.shouldOpen = false;
|
1889
|
+
this.shouldOpen = false; // PhotoSwipe is already open
|
1324
1890
|
|
1325
|
-
// PhotoSwipe is already open
|
1326
1891
|
if (window.pswp) {
|
1327
1892
|
return;
|
1328
1893
|
}
|
1894
|
+
/**
|
1895
|
+
* Pass data to PhotoSwipe and open init
|
1896
|
+
*
|
1897
|
+
* @type {PhotoSwipe}
|
1898
|
+
*/
|
1899
|
+
|
1329
1900
|
|
1330
|
-
|
1331
|
-
|
1332
|
-
? new module.default(this.options) // eslint-disable-line
|
1333
|
-
: new module(this.options); // eslint-disable-line
|
1901
|
+
const pswp = typeof module === 'object' ? new module.default(this.options) // eslint-disable-line
|
1902
|
+
: new module(this.options); // eslint-disable-line
|
1334
1903
|
|
1335
1904
|
this.pswp = pswp;
|
1336
|
-
window.pswp = pswp;
|
1905
|
+
window.pswp = pswp; // map listeners from Lightbox to PhotoSwipe Core
|
1337
1906
|
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1907
|
+
/** @type {(keyof PhotoSwipeEventsMap)[]} */
|
1908
|
+
|
1909
|
+
Object.keys(this._listeners).forEach(name => {
|
1910
|
+
var _this$_listeners$name;
|
1911
|
+
|
1912
|
+
(_this$_listeners$name = this._listeners[name]) === null || _this$_listeners$name === void 0 || _this$_listeners$name.forEach(fn => {
|
1913
|
+
pswp.on(name,
|
1914
|
+
/** @type {EventCallback<typeof name>} */
|
1915
|
+
fn);
|
1342
1916
|
});
|
1343
|
-
});
|
1917
|
+
}); // same with filters
|
1344
1918
|
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1919
|
+
/** @type {(keyof PhotoSwipeFiltersMap)[]} */
|
1920
|
+
|
1921
|
+
Object.keys(this._filters).forEach(name => {
|
1922
|
+
var _this$_filters$name;
|
1923
|
+
|
1924
|
+
(_this$_filters$name = this._filters[name]) === null || _this$_filters$name === void 0 || _this$_filters$name.forEach(filter => {
|
1348
1925
|
pswp.addFilter(name, filter.fn, filter.priority);
|
1349
1926
|
});
|
1350
1927
|
});
|
1351
1928
|
|
1352
1929
|
if (this._preloadedContent) {
|
1353
1930
|
pswp.contentLoader.addToCache(this._preloadedContent);
|
1354
|
-
this._preloadedContent =
|
1931
|
+
this._preloadedContent = undefined;
|
1355
1932
|
}
|
1356
1933
|
|
1357
1934
|
pswp.on('destroy', () => {
|
1358
1935
|
// clean up public variables
|
1359
|
-
this.pswp =
|
1360
|
-
window.pswp
|
1936
|
+
this.pswp = undefined;
|
1937
|
+
delete window.pswp;
|
1361
1938
|
});
|
1362
|
-
|
1363
1939
|
pswp.init();
|
1364
1940
|
}
|
1941
|
+
/**
|
1942
|
+
* Unbinds all events, closes PhotoSwipe if it's open.
|
1943
|
+
*/
|
1944
|
+
|
1365
1945
|
|
1366
1946
|
destroy() {
|
1367
|
-
|
1368
|
-
this.pswp.destroy();
|
1369
|
-
}
|
1947
|
+
var _this$pswp;
|
1370
1948
|
|
1949
|
+
(_this$pswp = this.pswp) === null || _this$pswp === void 0 || _this$pswp.destroy();
|
1371
1950
|
this.shouldOpen = false;
|
1372
|
-
this._listeners =
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
galleryElement.removeEventListener('click', this.onThumbnailsClick, false);
|
1377
|
-
});
|
1951
|
+
this._listeners = {};
|
1952
|
+
getElementsFromOption(this.options.gallery, this.options.gallerySelector).forEach(galleryElement => {
|
1953
|
+
galleryElement.removeEventListener('click', this.onThumbnailsClick, false);
|
1954
|
+
});
|
1378
1955
|
}
|
1956
|
+
|
1379
1957
|
}
|
1380
1958
|
|
1381
|
-
export default
|
1959
|
+
export { PhotoSwipeLightbox as default };
|
1382
1960
|
//# sourceMappingURL=photoswipe-lightbox.esm.js.map
|