@tomasvn/glightbox 0.0.0-stage → 3.3.3
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/.editorconfig +16 -0
- package/.eslintrc.json +94 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- package/.github/stale.yml +21 -0
- package/.github/workflows/greetings.yml +13 -0
- package/.lando.yml +10 -0
- package/.prettierignore +1 -0
- package/.prettierrc.json +31 -0
- package/CHANGELOG.md +247 -0
- package/CONTRIBUTING.md +29 -0
- package/LICENSE.md +21 -0
- package/README.md +710 -2
- package/bower.json +29 -0
- package/development/jscompiler.js +84 -0
- package/development/notifications.js +15 -0
- package/development/package.js +116 -0
- package/development/postcss.js +63 -0
- package/development/watcher.js +105 -0
- package/dist/css/glightbox.css +939 -0
- package/dist/css/glightbox.min.css +1 -0
- package/dist/js/glightbox.js +3087 -0
- package/dist/js/glightbox.min.js +1 -0
- package/index.d.ts +570 -0
- package/package.json +55 -4
- package/src/js/.jshintrc +6 -0
- package/src/js/core/drag.js +219 -0
- package/src/js/core/keyboard-navigation.js +98 -0
- package/src/js/core/slide-parser.js +231 -0
- package/src/js/core/slide.js +245 -0
- package/src/js/core/touch-events.js +378 -0
- package/src/js/core/touch-navigation.js +287 -0
- package/src/js/core/zoom.js +156 -0
- package/src/js/glightbox.js +1341 -0
- package/src/js/slides/iframe.js +24 -0
- package/src/js/slides/image.js +56 -0
- package/src/js/slides/inline.js +71 -0
- package/src/js/slides/video.js +105 -0
- package/src/js/utils/helpers.js +541 -0
- package/src/postcss/glightbox.css +723 -0
|
@@ -0,0 +1,1341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLightbox
|
|
3
|
+
* Awesome pure javascript lightbox
|
|
4
|
+
* made by https://www.biati.digital
|
|
5
|
+
* Github: https://github.com/biati-digital/glightbox
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import keyboardNavigation from './core/keyboard-navigation.js';
|
|
9
|
+
import Slide from './core/slide.js';
|
|
10
|
+
import touchNavigation from './core/touch-navigation.js';
|
|
11
|
+
import * as _ from './utils/helpers.js';
|
|
12
|
+
|
|
13
|
+
const version = '3.3.1';
|
|
14
|
+
const isMobile = _.isMobile();
|
|
15
|
+
const isTouch = _.isTouch();
|
|
16
|
+
const html = document.getElementsByTagName('html')[0];
|
|
17
|
+
|
|
18
|
+
const defaults = {
|
|
19
|
+
selector: '.glightbox',
|
|
20
|
+
elements: null,
|
|
21
|
+
skin: 'clean',
|
|
22
|
+
theme: 'clean',
|
|
23
|
+
closeButton: true,
|
|
24
|
+
startAt: null,
|
|
25
|
+
autoplayVideos: true,
|
|
26
|
+
autofocusVideos: true,
|
|
27
|
+
descPosition: 'bottom',
|
|
28
|
+
width: '900px',
|
|
29
|
+
height: '506px',
|
|
30
|
+
videosWidth: '960px',
|
|
31
|
+
beforeSlideChange: null,
|
|
32
|
+
afterSlideChange: null,
|
|
33
|
+
beforeSlideLoad: null,
|
|
34
|
+
afterSlideLoad: null,
|
|
35
|
+
slideInserted: null,
|
|
36
|
+
slideRemoved: null,
|
|
37
|
+
slideExtraAttributes: null,
|
|
38
|
+
onOpen: null,
|
|
39
|
+
onClose: null,
|
|
40
|
+
loop: false,
|
|
41
|
+
zoomable: true,
|
|
42
|
+
draggable: true,
|
|
43
|
+
dragAutoSnap: false,
|
|
44
|
+
dragToleranceX: 40,
|
|
45
|
+
dragToleranceY: 65,
|
|
46
|
+
preload: true,
|
|
47
|
+
oneSlidePerOpen: false,
|
|
48
|
+
touchNavigation: true,
|
|
49
|
+
touchFollowAxis: true,
|
|
50
|
+
keyboardNavigation: true,
|
|
51
|
+
closeOnOutsideClick: true,
|
|
52
|
+
plugins: false,
|
|
53
|
+
plyr: {
|
|
54
|
+
css: 'https://cdn.plyr.io/3.6.12/plyr.css',
|
|
55
|
+
js: 'https://cdn.plyr.io/3.6.12/plyr.js',
|
|
56
|
+
config: {
|
|
57
|
+
ratio: '16:9', // or '4:3'
|
|
58
|
+
fullscreen: { enabled: true, iosNative: true },
|
|
59
|
+
youtube: {
|
|
60
|
+
noCookie: true,
|
|
61
|
+
rel: 0,
|
|
62
|
+
showinfo: 0,
|
|
63
|
+
iv_load_policy: 3 // eslint-disable-line camelcase
|
|
64
|
+
},
|
|
65
|
+
vimeo: {
|
|
66
|
+
byline: false,
|
|
67
|
+
portrait: false,
|
|
68
|
+
title: false,
|
|
69
|
+
transparent: false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
openEffect: 'zoom', // fade, zoom, none
|
|
74
|
+
closeEffect: 'zoom', // fade, zoom, none
|
|
75
|
+
slideEffect: 'slide', // fade, slide, zoom, none
|
|
76
|
+
moreText: 'See more',
|
|
77
|
+
moreLength: 60,
|
|
78
|
+
cssEfects: {
|
|
79
|
+
fade: { in: 'fadeIn', out: 'fadeOut' },
|
|
80
|
+
zoom: { in: 'zoomIn', out: 'zoomOut' },
|
|
81
|
+
slide: { in: 'slideInRight', out: 'slideOutLeft' },
|
|
82
|
+
slideBack: { in: 'slideInLeft', out: 'slideOutRight' },
|
|
83
|
+
none: { in: 'none', out: 'none' }
|
|
84
|
+
},
|
|
85
|
+
svg: {
|
|
86
|
+
close: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" xml:space="preserve"><g><g><path d="M505.943,6.058c-8.077-8.077-21.172-8.077-29.249,0L6.058,476.693c-8.077,8.077-8.077,21.172,0,29.249C10.096,509.982,15.39,512,20.683,512c5.293,0,10.586-2.019,14.625-6.059L505.943,35.306C514.019,27.23,514.019,14.135,505.943,6.058z"/></g></g><g><g><path d="M505.942,476.694L35.306,6.059c-8.076-8.077-21.172-8.077-29.248,0c-8.077,8.076-8.077,21.171,0,29.248l470.636,470.636c4.038,4.039,9.332,6.058,14.625,6.058c5.293,0,10.587-2.019,14.624-6.057C514.018,497.866,514.018,484.771,505.942,476.694z"/></g></g></svg>',
|
|
87
|
+
next: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 477.175 477.175" xml:space="preserve"> <g><path d="M360.731,229.075l-225.1-225.1c-5.3-5.3-13.8-5.3-19.1,0s-5.3,13.8,0,19.1l215.5,215.5l-215.5,215.5c-5.3,5.3-5.3,13.8,0,19.1c2.6,2.6,6.1,4,9.5,4c3.4,0,6.9-1.3,9.5-4l225.1-225.1C365.931,242.875,365.931,234.275,360.731,229.075z"/></g></svg>',
|
|
88
|
+
prev: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 477.175 477.175" xml:space="preserve"><g><path d="M145.188,238.575l215.5-215.5c5.3-5.3,5.3-13.8,0-19.1s-13.8-5.3-19.1,0l-225.1,225.1c-5.3,5.3-5.3,13.8,0,19.1l225.1,225c2.6,2.6,6.1,4,9.5,4s6.9-1.3,9.5-4c5.3-5.3,5.3-13.8,0-19.1L145.188,238.575z"/></g></svg>'
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// You can pass your own slide structure
|
|
93
|
+
// just make sure that add the same classes so they are populated
|
|
94
|
+
// title class = gslide-title
|
|
95
|
+
// desc class = gslide-desc
|
|
96
|
+
// prev arrow class = gnext
|
|
97
|
+
// next arrow id = gprev
|
|
98
|
+
// close id = gclose
|
|
99
|
+
defaults.slideHTML = `<div class="gslide">
|
|
100
|
+
<div class="gslide-inner-content">
|
|
101
|
+
<div class="ginner-container">
|
|
102
|
+
<div class="gslide-media">
|
|
103
|
+
</div>
|
|
104
|
+
<div class="gslide-description">
|
|
105
|
+
<div class="gdesc-inner">
|
|
106
|
+
<h4 class="gslide-title"></h4>
|
|
107
|
+
<div class="gslide-desc"></div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>`;
|
|
113
|
+
|
|
114
|
+
defaults.lightboxHTML = `<div id="glightbox-body" class="glightbox-container" tabindex="-1" role="dialog" aria-hidden="false">
|
|
115
|
+
<div class="gloader visible"></div>
|
|
116
|
+
<div class="goverlay"></div>
|
|
117
|
+
<div class="gcontainer">
|
|
118
|
+
<div id="glightbox-slider" class="gslider"></div>
|
|
119
|
+
<button class="gclose gbtn" aria-label="Close" data-taborder="3">{closeSVG}</button>
|
|
120
|
+
<button class="gprev gbtn" aria-label="Previous" data-taborder="2">{prevSVG}</button>
|
|
121
|
+
<button class="gnext gbtn" aria-label="Next" data-taborder="1">{nextSVG}</button>
|
|
122
|
+
</div>
|
|
123
|
+
</div>`;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* GLightbox Class
|
|
127
|
+
* Class and public methods
|
|
128
|
+
*/
|
|
129
|
+
class GlightboxInit {
|
|
130
|
+
constructor(options = {}) {
|
|
131
|
+
this.customOptions = options;
|
|
132
|
+
this.settings = _.extend(defaults, options);
|
|
133
|
+
this.effectsClasses = this.getAnimationClasses();
|
|
134
|
+
this.videoPlayers = {};
|
|
135
|
+
this.apiEvents = [];
|
|
136
|
+
this.fullElementsList = false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
init() {
|
|
140
|
+
const selector = this.getSelector();
|
|
141
|
+
|
|
142
|
+
if (selector) {
|
|
143
|
+
this.baseEvents = _.addEvent('click', {
|
|
144
|
+
onElement: selector,
|
|
145
|
+
withCallback: (e, target) => {
|
|
146
|
+
e.preventDefault();
|
|
147
|
+
this.open(target);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.elements = this.getElements();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
open(element = null, startAt = null) {
|
|
156
|
+
if (this.elements.length === 0) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.activeSlide = null;
|
|
161
|
+
this.prevActiveSlideIndex = null;
|
|
162
|
+
this.prevActiveSlide = null;
|
|
163
|
+
let index = _.isNumber(startAt) ? startAt : this.settings.startAt;
|
|
164
|
+
|
|
165
|
+
if (_.isNode(element)) {
|
|
166
|
+
const gallery = element.getAttribute('data-gallery');
|
|
167
|
+
if (gallery) {
|
|
168
|
+
this.fullElementsList = this.elements;
|
|
169
|
+
this.elements = this.getGalleryElements(this.elements, gallery);
|
|
170
|
+
}
|
|
171
|
+
if (_.isNil(index)) {
|
|
172
|
+
// get the index of the element
|
|
173
|
+
index = this.getElementIndex(element);
|
|
174
|
+
if (index < 0) {
|
|
175
|
+
index = 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!_.isNumber(index)) {
|
|
181
|
+
index = 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this.build();
|
|
185
|
+
|
|
186
|
+
_.animateElement(this.overlay, this.settings.openEffect === 'none' ? 'none' : this.settings.cssEfects.fade.in);
|
|
187
|
+
|
|
188
|
+
const body = document.body;
|
|
189
|
+
|
|
190
|
+
const scrollBar = window.innerWidth - document.documentElement.clientWidth;
|
|
191
|
+
if (scrollBar > 0) {
|
|
192
|
+
var styleSheet = document.createElement('style');
|
|
193
|
+
styleSheet.type = 'text/css';
|
|
194
|
+
styleSheet.className = 'gcss-styles';
|
|
195
|
+
styleSheet.innerText = `.gscrollbar-fixer {margin-right: ${scrollBar}px}`;
|
|
196
|
+
document.head.appendChild(styleSheet);
|
|
197
|
+
_.addClass(body, 'gscrollbar-fixer');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_.addClass(body, 'glightbox-open');
|
|
201
|
+
_.addClass(html, 'glightbox-open');
|
|
202
|
+
if (isMobile) {
|
|
203
|
+
_.addClass(document.body, 'glightbox-mobile');
|
|
204
|
+
this.settings.slideEffect = 'slide';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
this.showSlide(index, true);
|
|
208
|
+
|
|
209
|
+
if (this.elements.length === 1) {
|
|
210
|
+
_.addClass(this.prevButton, 'glightbox-button-hidden');
|
|
211
|
+
_.addClass(this.nextButton, 'glightbox-button-hidden');
|
|
212
|
+
} else {
|
|
213
|
+
_.removeClass(this.prevButton, 'glightbox-button-hidden');
|
|
214
|
+
_.removeClass(this.nextButton, 'glightbox-button-hidden');
|
|
215
|
+
}
|
|
216
|
+
this.lightboxOpen = true;
|
|
217
|
+
|
|
218
|
+
this.trigger('open');
|
|
219
|
+
|
|
220
|
+
// settings.onOpen is deprecated and will be removed in a future update
|
|
221
|
+
if (_.isFunction(this.settings.onOpen)) {
|
|
222
|
+
this.settings.onOpen();
|
|
223
|
+
}
|
|
224
|
+
if (isTouch && this.settings.touchNavigation) {
|
|
225
|
+
touchNavigation(this);
|
|
226
|
+
}
|
|
227
|
+
if (this.settings.keyboardNavigation) {
|
|
228
|
+
keyboardNavigation(this);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Open at specific index
|
|
234
|
+
* @param {int} index
|
|
235
|
+
*/
|
|
236
|
+
openAt(index = 0) {
|
|
237
|
+
this.open(null, index);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Set Slide
|
|
242
|
+
*/
|
|
243
|
+
showSlide(index = 0, first = false) {
|
|
244
|
+
_.show(this.loader);
|
|
245
|
+
this.index = parseInt(index);
|
|
246
|
+
|
|
247
|
+
let current = this.slidesContainer.querySelector('.current');
|
|
248
|
+
if (current) {
|
|
249
|
+
_.removeClass(current, 'current');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// hide prev slide
|
|
253
|
+
this.slideAnimateOut();
|
|
254
|
+
|
|
255
|
+
let slideNode = this.slidesContainer.querySelectorAll('.gslide')[index];
|
|
256
|
+
|
|
257
|
+
// Check if slide's content is alreay loaded
|
|
258
|
+
if (_.hasClass(slideNode, 'loaded')) {
|
|
259
|
+
this.slideAnimateIn(slideNode, first);
|
|
260
|
+
_.hide(this.loader);
|
|
261
|
+
} else {
|
|
262
|
+
// If not loaded add the slide content
|
|
263
|
+
_.show(this.loader);
|
|
264
|
+
|
|
265
|
+
const slide = this.elements[index];
|
|
266
|
+
const slideData = {
|
|
267
|
+
index: this.index,
|
|
268
|
+
slide: slideNode, //this will be removed in the future
|
|
269
|
+
slideNode: slideNode,
|
|
270
|
+
slideConfig: slide.slideConfig,
|
|
271
|
+
slideIndex: this.index,
|
|
272
|
+
trigger: slide.node,
|
|
273
|
+
player: null
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
this.trigger('slide_before_load', slideData);
|
|
277
|
+
|
|
278
|
+
slide.instance.setContent(slideNode, () => {
|
|
279
|
+
_.hide(this.loader);
|
|
280
|
+
this.resize();
|
|
281
|
+
this.slideAnimateIn(slideNode, first);
|
|
282
|
+
this.trigger('slide_after_load', slideData);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.slideDescription = slideNode.querySelector('.gslide-description');
|
|
287
|
+
this.slideDescriptionContained = this.slideDescription && _.hasClass(this.slideDescription.parentNode, 'gslide-media');
|
|
288
|
+
|
|
289
|
+
// Preload subsequent slides
|
|
290
|
+
if (this.settings.preload) {
|
|
291
|
+
this.preloadSlide(index + 1);
|
|
292
|
+
this.preloadSlide(index - 1);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Handle navigation arrows
|
|
296
|
+
this.updateNavigationClasses();
|
|
297
|
+
|
|
298
|
+
this.activeSlide = slideNode;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Preload slides
|
|
303
|
+
* @param {Int} index slide index
|
|
304
|
+
* @return {null}
|
|
305
|
+
*/
|
|
306
|
+
preloadSlide(index) {
|
|
307
|
+
// Verify slide index, it can not be lower than 0
|
|
308
|
+
// and it can not be greater than the total elements
|
|
309
|
+
if (index < 0 || index > this.elements.length - 1) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (_.isNil(this.elements[index])) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
let slideNode = this.slidesContainer.querySelectorAll('.gslide')[index];
|
|
318
|
+
if (_.hasClass(slideNode, 'loaded')) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const slide = this.elements[index];
|
|
323
|
+
const type = slide.type;
|
|
324
|
+
const slideData = {
|
|
325
|
+
index: index,
|
|
326
|
+
slide: slideNode, //this will be removed in the future
|
|
327
|
+
slideNode: slideNode,
|
|
328
|
+
slideConfig: slide.slideConfig,
|
|
329
|
+
slideIndex: index,
|
|
330
|
+
trigger: slide.node,
|
|
331
|
+
player: null
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
this.trigger('slide_before_load', slideData);
|
|
335
|
+
|
|
336
|
+
if (type === 'video' || type === 'external') {
|
|
337
|
+
setTimeout(() => {
|
|
338
|
+
slide.instance.setContent(slideNode, () => {
|
|
339
|
+
this.trigger('slide_after_load', slideData);
|
|
340
|
+
});
|
|
341
|
+
}, 200);
|
|
342
|
+
} else {
|
|
343
|
+
slide.instance.setContent(slideNode, () => {
|
|
344
|
+
this.trigger('slide_after_load', slideData);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Load previous slide
|
|
351
|
+
* calls goToslide
|
|
352
|
+
*/
|
|
353
|
+
prevSlide() {
|
|
354
|
+
this.goToSlide(this.index - 1);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Load next slide
|
|
359
|
+
* calls goToslide
|
|
360
|
+
*/
|
|
361
|
+
nextSlide() {
|
|
362
|
+
this.goToSlide(this.index + 1);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Go to sldei
|
|
367
|
+
* calls set slide
|
|
368
|
+
* @param {Int} - index
|
|
369
|
+
*/
|
|
370
|
+
goToSlide(index = false) {
|
|
371
|
+
this.prevActiveSlide = this.activeSlide;
|
|
372
|
+
this.prevActiveSlideIndex = this.index;
|
|
373
|
+
|
|
374
|
+
if (!this.loop() && (index < 0 || index > this.elements.length - 1)) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
if (index < 0) {
|
|
378
|
+
index = this.elements.length - 1;
|
|
379
|
+
} else if (index >= this.elements.length) {
|
|
380
|
+
index = 0;
|
|
381
|
+
}
|
|
382
|
+
this.showSlide(index);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Insert slide
|
|
387
|
+
*
|
|
388
|
+
* @param { object } data
|
|
389
|
+
* @param { numeric } position
|
|
390
|
+
*/
|
|
391
|
+
insertSlide(config = {}, index = -1) {
|
|
392
|
+
// Append at the end
|
|
393
|
+
if (index < 0) {
|
|
394
|
+
index = this.elements.length;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const slide = new Slide(config, this, index);
|
|
398
|
+
const data = slide.getConfig();
|
|
399
|
+
const slideInfo = _.extend({}, data);
|
|
400
|
+
const newSlide = slide.create();
|
|
401
|
+
const totalSlides = this.elements.length - 1;
|
|
402
|
+
|
|
403
|
+
slideInfo.index = index;
|
|
404
|
+
slideInfo.node = false;
|
|
405
|
+
slideInfo.instance = slide;
|
|
406
|
+
slideInfo.slideConfig = data;
|
|
407
|
+
this.elements.splice(index, 0, slideInfo);
|
|
408
|
+
|
|
409
|
+
let addedSlideNode = null;
|
|
410
|
+
let addedSlidePlayer = null;
|
|
411
|
+
|
|
412
|
+
if (this.slidesContainer) {
|
|
413
|
+
// Append at the end
|
|
414
|
+
if (index > totalSlides) {
|
|
415
|
+
this.slidesContainer.appendChild(newSlide);
|
|
416
|
+
} else {
|
|
417
|
+
// A current slide must exist in the position specified
|
|
418
|
+
// we need tp get that slide and insder the new slide before
|
|
419
|
+
let existingSlide = this.slidesContainer.querySelectorAll('.gslide')[index];
|
|
420
|
+
this.slidesContainer.insertBefore(newSlide, existingSlide);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if ((this.settings.preload && this.index == 0 && index == 0) || this.index - 1 == index || this.index + 1 == index) {
|
|
424
|
+
this.preloadSlide(index);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (this.index === 0 && index === 0) {
|
|
428
|
+
this.index = 1;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
this.updateNavigationClasses();
|
|
432
|
+
|
|
433
|
+
addedSlideNode = this.slidesContainer.querySelectorAll('.gslide')[index];
|
|
434
|
+
addedSlidePlayer = this.getSlidePlayerInstance(index);
|
|
435
|
+
slideInfo.slideNode = addedSlideNode;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
this.trigger('slide_inserted', {
|
|
439
|
+
index: index,
|
|
440
|
+
slide: addedSlideNode,
|
|
441
|
+
slideNode: addedSlideNode,
|
|
442
|
+
slideConfig: data,
|
|
443
|
+
slideIndex: index,
|
|
444
|
+
trigger: null,
|
|
445
|
+
player: addedSlidePlayer
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Deprecated and will be removed in a future update
|
|
449
|
+
if (_.isFunction(this.settings.slideInserted)) {
|
|
450
|
+
this.settings.slideInserted({
|
|
451
|
+
index: index,
|
|
452
|
+
slide: addedSlideNode,
|
|
453
|
+
player: addedSlidePlayer
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Remove slide
|
|
460
|
+
*
|
|
461
|
+
* @param { numeric } position
|
|
462
|
+
*/
|
|
463
|
+
removeSlide(index = -1) {
|
|
464
|
+
if (index < 0 || index > this.elements.length - 1) {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const slide = this.slidesContainer && this.slidesContainer.querySelectorAll('.gslide')[index];
|
|
469
|
+
|
|
470
|
+
if (slide) {
|
|
471
|
+
if (this.getActiveSlideIndex() == index) {
|
|
472
|
+
if (index == this.elements.length - 1) {
|
|
473
|
+
this.prevSlide();
|
|
474
|
+
} else {
|
|
475
|
+
this.nextSlide();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
slide.parentNode.removeChild(slide);
|
|
479
|
+
}
|
|
480
|
+
this.elements.splice(index, 1);
|
|
481
|
+
|
|
482
|
+
this.trigger('slide_removed', index);
|
|
483
|
+
|
|
484
|
+
// Deprecated and will be removed in a future update
|
|
485
|
+
if (_.isFunction(this.settings.slideRemoved)) {
|
|
486
|
+
this.settings.slideRemoved(index);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Slide In
|
|
492
|
+
* @return {null}
|
|
493
|
+
*/
|
|
494
|
+
slideAnimateIn(slide, first) {
|
|
495
|
+
let slideMedia = slide.querySelector('.gslide-media');
|
|
496
|
+
let slideDesc = slide.querySelector('.gslide-description');
|
|
497
|
+
let prevData = {
|
|
498
|
+
index: this.prevActiveSlideIndex,
|
|
499
|
+
slide: this.prevActiveSlide, //this will be removed in the future
|
|
500
|
+
slideNode: this.prevActiveSlide,
|
|
501
|
+
slideIndex: this.prevActiveSlide,
|
|
502
|
+
slideConfig: _.isNil(this.prevActiveSlideIndex) ? null : this.elements[this.prevActiveSlideIndex].slideConfig,
|
|
503
|
+
trigger: _.isNil(this.prevActiveSlideIndex) ? null : this.elements[this.prevActiveSlideIndex].node,
|
|
504
|
+
player: this.getSlidePlayerInstance(this.prevActiveSlideIndex)
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
let nextData = {
|
|
508
|
+
index: this.index,
|
|
509
|
+
slide: this.activeSlide, //this will be removed in the future
|
|
510
|
+
slideNode: this.activeSlide,
|
|
511
|
+
slideConfig: this.elements[this.index].slideConfig,
|
|
512
|
+
slideIndex: this.index,
|
|
513
|
+
trigger: this.elements[this.index].node,
|
|
514
|
+
player: this.getSlidePlayerInstance(this.index)
|
|
515
|
+
};
|
|
516
|
+
if (slideMedia.offsetWidth > 0 && slideDesc) {
|
|
517
|
+
_.hide(slideDesc);
|
|
518
|
+
slideDesc.style.display = '';
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
_.removeClass(slide, this.effectsClasses);
|
|
522
|
+
|
|
523
|
+
if (first) {
|
|
524
|
+
_.animateElement(slide, this.settings.cssEfects[this.settings.openEffect].in, () => {
|
|
525
|
+
if (this.settings.autoplayVideos) {
|
|
526
|
+
this.slidePlayerPlay(slide);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
this.trigger('slide_changed', {
|
|
530
|
+
prev: prevData,
|
|
531
|
+
current: nextData
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// settings.afterSlideChange is deprecated and will be removed in a future update
|
|
535
|
+
if (_.isFunction(this.settings.afterSlideChange)) {
|
|
536
|
+
this.settings.afterSlideChange.apply(this, [prevData, nextData]);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
} else {
|
|
540
|
+
let effectName = this.settings.slideEffect;
|
|
541
|
+
let animIn = effectName !== 'none' ? this.settings.cssEfects[effectName].in : effectName;
|
|
542
|
+
if (this.prevActiveSlideIndex > this.index) {
|
|
543
|
+
if (this.settings.slideEffect == 'slide') {
|
|
544
|
+
animIn = this.settings.cssEfects.slideBack.in;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
_.animateElement(slide, animIn, () => {
|
|
548
|
+
if (this.settings.autoplayVideos) {
|
|
549
|
+
this.slidePlayerPlay(slide);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
this.trigger('slide_changed', {
|
|
553
|
+
prev: prevData,
|
|
554
|
+
current: nextData
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// settings.afterSlideChange is deprecated and will be removed in a future update
|
|
558
|
+
if (_.isFunction(this.settings.afterSlideChange)) {
|
|
559
|
+
this.settings.afterSlideChange.apply(this, [prevData, nextData]);
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
setTimeout(() => {
|
|
565
|
+
this.resize(slide);
|
|
566
|
+
}, 100);
|
|
567
|
+
_.addClass(slide, 'current');
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Slide out
|
|
572
|
+
*/
|
|
573
|
+
slideAnimateOut() {
|
|
574
|
+
if (!this.prevActiveSlide) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
let prevSlide = this.prevActiveSlide;
|
|
579
|
+
_.removeClass(prevSlide, this.effectsClasses);
|
|
580
|
+
_.addClass(prevSlide, 'prev');
|
|
581
|
+
|
|
582
|
+
let animation = this.settings.slideEffect;
|
|
583
|
+
let animOut = animation !== 'none' ? this.settings.cssEfects[animation].out : animation;
|
|
584
|
+
|
|
585
|
+
this.slidePlayerPause(prevSlide);
|
|
586
|
+
|
|
587
|
+
this.trigger('slide_before_change', {
|
|
588
|
+
prev: {
|
|
589
|
+
index: this.prevActiveSlideIndex, //this will be removed in the future
|
|
590
|
+
slide: this.prevActiveSlide, //this will be removed in the future
|
|
591
|
+
slideNode: this.prevActiveSlide,
|
|
592
|
+
slideIndex: this.prevActiveSlideIndex,
|
|
593
|
+
slideConfig: _.isNil(this.prevActiveSlideIndex) ? null : this.elements[this.prevActiveSlideIndex].slideConfig,
|
|
594
|
+
trigger: _.isNil(this.prevActiveSlideIndex) ? null : this.elements[this.prevActiveSlideIndex].node,
|
|
595
|
+
player: this.getSlidePlayerInstance(this.prevActiveSlideIndex)
|
|
596
|
+
},
|
|
597
|
+
current: {
|
|
598
|
+
index: this.index, //this will be removed in the future
|
|
599
|
+
slide: this.activeSlide, //this will be removed in the future
|
|
600
|
+
slideNode: this.activeSlide,
|
|
601
|
+
slideIndex: this.index,
|
|
602
|
+
slideConfig: this.elements[this.index].slideConfig,
|
|
603
|
+
trigger: this.elements[this.index].node,
|
|
604
|
+
player: this.getSlidePlayerInstance(this.index)
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
// settings.beforeSlideChange is deprecated and will be removed in a future update
|
|
609
|
+
if (_.isFunction(this.settings.beforeSlideChange)) {
|
|
610
|
+
this.settings.beforeSlideChange.apply(this, [
|
|
611
|
+
{
|
|
612
|
+
index: this.prevActiveSlideIndex,
|
|
613
|
+
slide: this.prevActiveSlide,
|
|
614
|
+
player: this.getSlidePlayerInstance(this.prevActiveSlideIndex)
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
index: this.index,
|
|
618
|
+
slide: this.activeSlide,
|
|
619
|
+
player: this.getSlidePlayerInstance(this.index)
|
|
620
|
+
}
|
|
621
|
+
]);
|
|
622
|
+
}
|
|
623
|
+
if (this.prevActiveSlideIndex > this.index && this.settings.slideEffect == 'slide') {
|
|
624
|
+
// going back
|
|
625
|
+
animOut = this.settings.cssEfects.slideBack.out;
|
|
626
|
+
}
|
|
627
|
+
_.animateElement(prevSlide, animOut, () => {
|
|
628
|
+
let container = prevSlide.querySelector('.ginner-container');
|
|
629
|
+
let media = prevSlide.querySelector('.gslide-media');
|
|
630
|
+
let desc = prevSlide.querySelector('.gslide-description');
|
|
631
|
+
|
|
632
|
+
container.style.transform = '';
|
|
633
|
+
media.style.transform = '';
|
|
634
|
+
_.removeClass(media, 'greset');
|
|
635
|
+
media.style.opacity = '';
|
|
636
|
+
if (desc) {
|
|
637
|
+
desc.style.opacity = '';
|
|
638
|
+
}
|
|
639
|
+
_.removeClass(prevSlide, 'prev');
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Get all defined players
|
|
645
|
+
*/
|
|
646
|
+
getAllPlayers() {
|
|
647
|
+
return this.videoPlayers;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Get player at index
|
|
652
|
+
*
|
|
653
|
+
* @param index
|
|
654
|
+
* @return bool|object
|
|
655
|
+
*/
|
|
656
|
+
getSlidePlayerInstance(index) {
|
|
657
|
+
const id = 'gvideo' + index;
|
|
658
|
+
const videoPlayers = this.getAllPlayers();
|
|
659
|
+
|
|
660
|
+
if (_.has(videoPlayers, id) && videoPlayers[id]) {
|
|
661
|
+
return videoPlayers[id];
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Stop video at specified
|
|
669
|
+
* node or index
|
|
670
|
+
*
|
|
671
|
+
* @param slide node or index
|
|
672
|
+
* @return void
|
|
673
|
+
*/
|
|
674
|
+
stopSlideVideo(slide) {
|
|
675
|
+
if (_.isNode(slide)) {
|
|
676
|
+
let node = slide.querySelector('.gvideo-wrapper');
|
|
677
|
+
if (node) {
|
|
678
|
+
slide = node.getAttribute('data-index');
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
console.log('stopSlideVideo is deprecated, use slidePlayerPause');
|
|
682
|
+
const player = this.getSlidePlayerInstance(slide);
|
|
683
|
+
if (player && player.playing) {
|
|
684
|
+
player.pause();
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Stop player at specified index
|
|
690
|
+
*
|
|
691
|
+
* @param slide node or index
|
|
692
|
+
* @return void
|
|
693
|
+
*/
|
|
694
|
+
slidePlayerPause(slide) {
|
|
695
|
+
if (_.isNode(slide)) {
|
|
696
|
+
let node = slide.querySelector('.gvideo-wrapper');
|
|
697
|
+
if (node) {
|
|
698
|
+
slide = node.getAttribute('data-index');
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const player = this.getSlidePlayerInstance(slide);
|
|
702
|
+
if (player && player.playing) {
|
|
703
|
+
player.pause();
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Play video at specified
|
|
709
|
+
* node or index
|
|
710
|
+
*
|
|
711
|
+
* @param slide node or index
|
|
712
|
+
* @return void
|
|
713
|
+
*/
|
|
714
|
+
playSlideVideo(slide) {
|
|
715
|
+
if (_.isNode(slide)) {
|
|
716
|
+
let node = slide.querySelector('.gvideo-wrapper');
|
|
717
|
+
if (node) {
|
|
718
|
+
slide = node.getAttribute('data-index');
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
console.log('playSlideVideo is deprecated, use slidePlayerPlay');
|
|
722
|
+
const player = this.getSlidePlayerInstance(slide);
|
|
723
|
+
if (player && !player.playing) {
|
|
724
|
+
player.play();
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Play media player at specified
|
|
730
|
+
* node or index
|
|
731
|
+
*
|
|
732
|
+
* @param slide node or index
|
|
733
|
+
* @return void
|
|
734
|
+
*/
|
|
735
|
+
slidePlayerPlay(slide) {
|
|
736
|
+
// Do not autoplay on mobile
|
|
737
|
+
// plyr does not handle well the errors
|
|
738
|
+
// and the player becomes unplayable
|
|
739
|
+
if (isMobile && !this.settings.plyr.config?.muted) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (_.isNode(slide)) {
|
|
744
|
+
let node = slide.querySelector('.gvideo-wrapper');
|
|
745
|
+
if (node) {
|
|
746
|
+
slide = node.getAttribute('data-index');
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const player = this.getSlidePlayerInstance(slide);
|
|
751
|
+
|
|
752
|
+
if (player && !player.playing) {
|
|
753
|
+
player.play();
|
|
754
|
+
if (this.settings.autofocusVideos) {
|
|
755
|
+
player.elements.container.focus();
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Set the entire elements
|
|
762
|
+
* in the gallery, it replaces all
|
|
763
|
+
* the existing elements
|
|
764
|
+
* with the specified list
|
|
765
|
+
*
|
|
766
|
+
* @param {array} elements
|
|
767
|
+
*/
|
|
768
|
+
setElements(elements) {
|
|
769
|
+
this.settings.elements = false;
|
|
770
|
+
|
|
771
|
+
const newElements = [];
|
|
772
|
+
|
|
773
|
+
if (elements && elements.length) {
|
|
774
|
+
_.each(elements, (el, i) => {
|
|
775
|
+
const slide = new Slide(el, this, i);
|
|
776
|
+
const data = slide.getConfig();
|
|
777
|
+
const slideInfo = _.extend({}, data);
|
|
778
|
+
|
|
779
|
+
slideInfo.slideConfig = data;
|
|
780
|
+
slideInfo.instance = slide;
|
|
781
|
+
slideInfo.index = i;
|
|
782
|
+
newElements.push(slideInfo);
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
this.elements = newElements;
|
|
787
|
+
|
|
788
|
+
if (this.lightboxOpen) {
|
|
789
|
+
this.slidesContainer.innerHTML = '';
|
|
790
|
+
|
|
791
|
+
if (this.elements.length) {
|
|
792
|
+
_.each(this.elements, () => {
|
|
793
|
+
let slide = _.createHTML(this.settings.slideHTML);
|
|
794
|
+
this.slidesContainer.appendChild(slide);
|
|
795
|
+
});
|
|
796
|
+
this.showSlide(0, true);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Return the index
|
|
803
|
+
* of the specified node,
|
|
804
|
+
* this node is for example an image, link, etc.
|
|
805
|
+
* that when clicked it opens the lightbox
|
|
806
|
+
* its position in the elements array can change
|
|
807
|
+
* when using insertSlide or removeSlide so we
|
|
808
|
+
* need to find it in the elements list
|
|
809
|
+
*
|
|
810
|
+
* @param {node} node
|
|
811
|
+
* @return bool|int
|
|
812
|
+
*/
|
|
813
|
+
getElementIndex(node) {
|
|
814
|
+
let index = false;
|
|
815
|
+
_.each(this.elements, (el, i) => {
|
|
816
|
+
if (_.has(el, 'node') && el.node == node) {
|
|
817
|
+
index = i;
|
|
818
|
+
return true; // exit loop
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
return index;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Get elements
|
|
827
|
+
* returns an array containing all
|
|
828
|
+
* the elements that must be displayed in the
|
|
829
|
+
* lightbox
|
|
830
|
+
*
|
|
831
|
+
* @return { array }
|
|
832
|
+
*/
|
|
833
|
+
getElements() {
|
|
834
|
+
let list = [];
|
|
835
|
+
this.elements = this.elements ? this.elements : [];
|
|
836
|
+
|
|
837
|
+
if (!_.isNil(this.settings.elements) && _.isArray(this.settings.elements) && this.settings.elements.length) {
|
|
838
|
+
_.each(this.settings.elements, (el, i) => {
|
|
839
|
+
const slide = new Slide(el, this, i);
|
|
840
|
+
const elData = slide.getConfig();
|
|
841
|
+
const slideInfo = _.extend({}, elData);
|
|
842
|
+
|
|
843
|
+
slideInfo.node = false;
|
|
844
|
+
slideInfo.index = i;
|
|
845
|
+
slideInfo.instance = slide;
|
|
846
|
+
slideInfo.slideConfig = elData;
|
|
847
|
+
list.push(slideInfo);
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
let nodes = false;
|
|
852
|
+
let selector = this.getSelector();
|
|
853
|
+
|
|
854
|
+
if (selector) {
|
|
855
|
+
nodes = document.querySelectorAll(this.getSelector());
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (!nodes) {
|
|
859
|
+
return list;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
_.each(nodes, (el, i) => {
|
|
863
|
+
const slide = new Slide(el, this, i);
|
|
864
|
+
const elData = slide.getConfig();
|
|
865
|
+
const slideInfo = _.extend({}, elData);
|
|
866
|
+
|
|
867
|
+
slideInfo.node = el;
|
|
868
|
+
slideInfo.index = i;
|
|
869
|
+
slideInfo.instance = slide;
|
|
870
|
+
slideInfo.slideConfig = elData;
|
|
871
|
+
slideInfo.gallery = el.getAttribute('data-gallery');
|
|
872
|
+
list.push(slideInfo);
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
return list;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Return only the elements
|
|
880
|
+
* from a specific gallery
|
|
881
|
+
*
|
|
882
|
+
* @return array
|
|
883
|
+
*/
|
|
884
|
+
getGalleryElements(list, gallery) {
|
|
885
|
+
return list.filter((el) => {
|
|
886
|
+
return el.gallery == gallery;
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Get selector
|
|
892
|
+
*/
|
|
893
|
+
getSelector() {
|
|
894
|
+
if (this.settings.elements) {
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
if (this.settings.selector && this.settings.selector.substring(0, 5) == 'data-') {
|
|
898
|
+
return `*[${this.settings.selector}]`;
|
|
899
|
+
}
|
|
900
|
+
return this.settings.selector;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Get the active slide
|
|
905
|
+
*/
|
|
906
|
+
getActiveSlide() {
|
|
907
|
+
return this.slidesContainer.querySelectorAll('.gslide')[this.index];
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Get the active index
|
|
912
|
+
*/
|
|
913
|
+
getActiveSlideIndex() {
|
|
914
|
+
return this.index;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Get the defined
|
|
919
|
+
* effects as string
|
|
920
|
+
*/
|
|
921
|
+
getAnimationClasses() {
|
|
922
|
+
let effects = [];
|
|
923
|
+
for (let key in this.settings.cssEfects) {
|
|
924
|
+
if (this.settings.cssEfects.hasOwnProperty(key)) {
|
|
925
|
+
let effect = this.settings.cssEfects[key];
|
|
926
|
+
effects.push(`g${effect.in}`);
|
|
927
|
+
effects.push(`g${effect.out}`);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return effects.join(' ');
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Build the structure
|
|
935
|
+
* @return {null}
|
|
936
|
+
*/
|
|
937
|
+
build() {
|
|
938
|
+
if (this.built) {
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// TODO: :scope is not supported on IE or first Edge. so we'll
|
|
943
|
+
// update this when IE support is removed to use newer code
|
|
944
|
+
//const children = document.body.querySelectorAll(':scope > *');
|
|
945
|
+
const children = document.body.childNodes;
|
|
946
|
+
const bodyChildElms = [];
|
|
947
|
+
_.each(children, (el) => {
|
|
948
|
+
if (el.parentNode == document.body && el.nodeName.charAt(0) !== '#' && el.hasAttribute && !el.hasAttribute('aria-hidden')) {
|
|
949
|
+
bodyChildElms.push(el);
|
|
950
|
+
el.setAttribute('aria-hidden', 'true');
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
const nextSVG = _.has(this.settings.svg, 'next') ? this.settings.svg.next : '';
|
|
955
|
+
const prevSVG = _.has(this.settings.svg, 'prev') ? this.settings.svg.prev : '';
|
|
956
|
+
const closeSVG = _.has(this.settings.svg, 'close') ? this.settings.svg.close : '';
|
|
957
|
+
|
|
958
|
+
let lightboxHTML = this.settings.lightboxHTML;
|
|
959
|
+
lightboxHTML = lightboxHTML.replace(/{nextSVG}/g, nextSVG);
|
|
960
|
+
lightboxHTML = lightboxHTML.replace(/{prevSVG}/g, prevSVG);
|
|
961
|
+
lightboxHTML = lightboxHTML.replace(/{closeSVG}/g, closeSVG);
|
|
962
|
+
|
|
963
|
+
lightboxHTML = _.createHTML(lightboxHTML);
|
|
964
|
+
document.body.appendChild(lightboxHTML);
|
|
965
|
+
|
|
966
|
+
const modal = document.getElementById('glightbox-body');
|
|
967
|
+
this.modal = modal;
|
|
968
|
+
let closeButton = modal.querySelector('.gclose');
|
|
969
|
+
this.prevButton = modal.querySelector('.gprev');
|
|
970
|
+
this.nextButton = modal.querySelector('.gnext');
|
|
971
|
+
this.overlay = modal.querySelector('.goverlay');
|
|
972
|
+
this.loader = modal.querySelector('.gloader');
|
|
973
|
+
this.slidesContainer = document.getElementById('glightbox-slider');
|
|
974
|
+
this.bodyHiddenChildElms = bodyChildElms;
|
|
975
|
+
this.events = {};
|
|
976
|
+
|
|
977
|
+
_.addClass(this.modal, 'glightbox-' + this.settings.skin);
|
|
978
|
+
|
|
979
|
+
if (this.settings.closeButton && closeButton) {
|
|
980
|
+
this.events['close'] = _.addEvent('click', {
|
|
981
|
+
onElement: closeButton,
|
|
982
|
+
withCallback: (e, target) => {
|
|
983
|
+
e.preventDefault();
|
|
984
|
+
this.close();
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
if (closeButton && !this.settings.closeButton) {
|
|
989
|
+
closeButton.parentNode.removeChild(closeButton);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (this.nextButton) {
|
|
993
|
+
this.events['next'] = _.addEvent('click', {
|
|
994
|
+
onElement: this.nextButton,
|
|
995
|
+
withCallback: (e, target) => {
|
|
996
|
+
e.preventDefault();
|
|
997
|
+
this.nextSlide();
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
if (this.prevButton) {
|
|
1003
|
+
this.events['prev'] = _.addEvent('click', {
|
|
1004
|
+
onElement: this.prevButton,
|
|
1005
|
+
withCallback: (e, target) => {
|
|
1006
|
+
e.preventDefault();
|
|
1007
|
+
this.prevSlide();
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
if (this.settings.closeOnOutsideClick) {
|
|
1012
|
+
this.events['outClose'] = _.addEvent('click', {
|
|
1013
|
+
onElement: modal,
|
|
1014
|
+
withCallback: (e, target) => {
|
|
1015
|
+
if (!this.preventOutsideClick && !_.hasClass(document.body, 'glightbox-mobile') && !_.closest(e.target, '.ginner-container')) {
|
|
1016
|
+
if (!_.closest(e.target, '.gbtn') && !_.hasClass(e.target, 'gnext') && !_.hasClass(e.target, 'gprev')) {
|
|
1017
|
+
this.close();
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
_.each(this.elements, (slide, i) => {
|
|
1025
|
+
this.slidesContainer.appendChild(slide.instance.create());
|
|
1026
|
+
slide.slideNode = this.slidesContainer.querySelectorAll('.gslide')[i];
|
|
1027
|
+
});
|
|
1028
|
+
if (isTouch) {
|
|
1029
|
+
_.addClass(document.body, 'glightbox-touch');
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
this.events['resize'] = _.addEvent('resize', {
|
|
1033
|
+
onElement: window,
|
|
1034
|
+
withCallback: () => {
|
|
1035
|
+
this.resize();
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
this.built = true;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Handle resize
|
|
1044
|
+
* Create only to handle
|
|
1045
|
+
* when the height of the screen
|
|
1046
|
+
* is lower than the slide content
|
|
1047
|
+
* this helps to resize videos vertically
|
|
1048
|
+
* and images with description
|
|
1049
|
+
*/
|
|
1050
|
+
resize(slide = null) {
|
|
1051
|
+
slide = !slide ? this.activeSlide : slide;
|
|
1052
|
+
|
|
1053
|
+
if (!slide || _.hasClass(slide, 'zoomed')) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
const winSize = _.windowSize();
|
|
1058
|
+
const video = slide.querySelector('.gvideo-wrapper');
|
|
1059
|
+
const image = slide.querySelector('.gslide-image');
|
|
1060
|
+
const description = this.slideDescription;
|
|
1061
|
+
|
|
1062
|
+
let winWidth = winSize.width;
|
|
1063
|
+
let winHeight = winSize.height;
|
|
1064
|
+
|
|
1065
|
+
if (winWidth <= 768) {
|
|
1066
|
+
_.addClass(document.body, 'glightbox-mobile');
|
|
1067
|
+
} else {
|
|
1068
|
+
_.removeClass(document.body, 'glightbox-mobile');
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (!video && !image) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
let descriptionResize = false;
|
|
1076
|
+
if (description && (_.hasClass(description, 'description-bottom') || _.hasClass(description, 'description-top')) && !_.hasClass(description, 'gabsolute')) {
|
|
1077
|
+
descriptionResize = true;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if (image) {
|
|
1081
|
+
if (winWidth <= 768) {
|
|
1082
|
+
let imgNode = image.querySelector('img');
|
|
1083
|
+
//imgNode.setAttribute('style', '');
|
|
1084
|
+
} else if (descriptionResize) {
|
|
1085
|
+
let descHeight = description.offsetHeight;
|
|
1086
|
+
let imgNode = image.querySelector('img');
|
|
1087
|
+
|
|
1088
|
+
// if a slide height is set via data-height, we want to use that
|
|
1089
|
+
// if not, we fall back to 100vh
|
|
1090
|
+
const slideTriggerNode = this.elements[this.index]?.node;
|
|
1091
|
+
let maxHeightValue = '100vh';
|
|
1092
|
+
if (slideTriggerNode) {
|
|
1093
|
+
maxHeightValue = slideTriggerNode.getAttribute('data-height') ?? maxHeightValue;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
imgNode.setAttribute('style', `max-height: calc(${maxHeightValue} - ${descHeight}px)`);
|
|
1097
|
+
description.setAttribute('style', `max-width: ${imgNode.offsetWidth}px;`);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (video) {
|
|
1102
|
+
let ratio = _.has(this.settings.plyr.config, 'ratio') ? this.settings.plyr.config.ratio : '';
|
|
1103
|
+
|
|
1104
|
+
if (!ratio) {
|
|
1105
|
+
// If no ratio passed, calculate it using the video width and height
|
|
1106
|
+
// generated by Plyr
|
|
1107
|
+
const containerWidth = video.clientWidth;
|
|
1108
|
+
const containerHeight = video.clientHeight;
|
|
1109
|
+
const divisor = containerWidth / containerHeight;
|
|
1110
|
+
ratio = `${containerWidth / divisor}:${containerHeight / divisor}`;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
let videoRatio = ratio.split(':');
|
|
1114
|
+
let videoWidth = this.settings.videosWidth;
|
|
1115
|
+
let maxWidth = this.settings.videosWidth;
|
|
1116
|
+
|
|
1117
|
+
if (_.isNumber(videoWidth) || videoWidth.indexOf('px') !== -1) {
|
|
1118
|
+
maxWidth = parseInt(videoWidth);
|
|
1119
|
+
} else {
|
|
1120
|
+
// If video size is vw, vh or % convert it to pixels,
|
|
1121
|
+
// fallback to the current video size
|
|
1122
|
+
if (videoWidth.indexOf('vw') !== -1) {
|
|
1123
|
+
maxWidth = (winWidth * parseInt(videoWidth)) / 100;
|
|
1124
|
+
} else if (videoWidth.indexOf('vh') !== -1) {
|
|
1125
|
+
maxWidth = (winHeight * parseInt(videoWidth)) / 100;
|
|
1126
|
+
} else if (videoWidth.indexOf('%') !== -1) {
|
|
1127
|
+
maxWidth = (winWidth * parseInt(videoWidth)) / 100;
|
|
1128
|
+
} else {
|
|
1129
|
+
maxWidth = parseInt(video.clientWidth);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
let maxHeight = maxWidth / (parseInt(videoRatio[0]) / parseInt(videoRatio[1]));
|
|
1134
|
+
maxHeight = Math.floor(maxHeight);
|
|
1135
|
+
|
|
1136
|
+
if (descriptionResize) {
|
|
1137
|
+
winHeight = winHeight - description.offsetHeight;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
if (maxWidth > winWidth || maxHeight > winHeight || (winHeight < maxHeight && winWidth > maxWidth)) {
|
|
1141
|
+
let vwidth = video.offsetWidth;
|
|
1142
|
+
let vheight = video.offsetHeight;
|
|
1143
|
+
let ratio = winHeight / vheight;
|
|
1144
|
+
let vsize = { width: vwidth * ratio, height: vheight * ratio };
|
|
1145
|
+
video.parentNode.setAttribute('style', `max-width: ${vsize.width}px`);
|
|
1146
|
+
|
|
1147
|
+
if (descriptionResize) {
|
|
1148
|
+
description.setAttribute('style', `max-width: ${vsize.width}px;`);
|
|
1149
|
+
}
|
|
1150
|
+
} else {
|
|
1151
|
+
video.parentNode.style.maxWidth = `${videoWidth}`;
|
|
1152
|
+
if (descriptionResize) {
|
|
1153
|
+
description.setAttribute('style', `max-width: ${videoWidth};`);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Reload Lightbox
|
|
1161
|
+
* reload and apply events to nodes
|
|
1162
|
+
*/
|
|
1163
|
+
reload() {
|
|
1164
|
+
this.init();
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Update navigation classes on slide change
|
|
1169
|
+
*/
|
|
1170
|
+
updateNavigationClasses() {
|
|
1171
|
+
const loop = this.loop();
|
|
1172
|
+
// Handle navigation arrows
|
|
1173
|
+
_.removeClass(this.nextButton, 'disabled');
|
|
1174
|
+
_.removeClass(this.prevButton, 'disabled');
|
|
1175
|
+
|
|
1176
|
+
if (this.index == 0 && this.elements.length - 1 == 0) {
|
|
1177
|
+
_.addClass(this.prevButton, 'disabled');
|
|
1178
|
+
_.addClass(this.nextButton, 'disabled');
|
|
1179
|
+
} else if (this.index === 0 && !loop) {
|
|
1180
|
+
_.addClass(this.prevButton, 'disabled');
|
|
1181
|
+
} else if (this.index === this.elements.length - 1 && !loop) {
|
|
1182
|
+
_.addClass(this.nextButton, 'disabled');
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Handle loop config
|
|
1188
|
+
*/
|
|
1189
|
+
loop() {
|
|
1190
|
+
let loop = _.has(this.settings, 'loopAtEnd') ? this.settings.loopAtEnd : null;
|
|
1191
|
+
loop = _.has(this.settings, 'loop') ? this.settings.loop : loop;
|
|
1192
|
+
|
|
1193
|
+
return loop;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Close Lightbox
|
|
1198
|
+
* closes the lightbox and removes the slides
|
|
1199
|
+
* and some classes
|
|
1200
|
+
*/
|
|
1201
|
+
close() {
|
|
1202
|
+
if (!this.lightboxOpen) {
|
|
1203
|
+
if (this.events) {
|
|
1204
|
+
for (let key in this.events) {
|
|
1205
|
+
if (this.events.hasOwnProperty(key)) {
|
|
1206
|
+
this.events[key].destroy();
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
this.events = null;
|
|
1210
|
+
}
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
if (this.closing) {
|
|
1215
|
+
return false;
|
|
1216
|
+
}
|
|
1217
|
+
this.closing = true;
|
|
1218
|
+
this.slidePlayerPause(this.activeSlide);
|
|
1219
|
+
|
|
1220
|
+
if (this.fullElementsList) {
|
|
1221
|
+
this.elements = this.fullElementsList;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
if (this.bodyHiddenChildElms.length) {
|
|
1225
|
+
_.each(this.bodyHiddenChildElms, (el) => {
|
|
1226
|
+
el.removeAttribute('aria-hidden');
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
_.addClass(this.modal, 'glightbox-closing');
|
|
1231
|
+
_.animateElement(this.overlay, this.settings.openEffect == 'none' ? 'none' : this.settings.cssEfects.fade.out);
|
|
1232
|
+
_.animateElement(this.activeSlide, this.settings.cssEfects[this.settings.closeEffect].out, () => {
|
|
1233
|
+
this.activeSlide = null;
|
|
1234
|
+
this.prevActiveSlideIndex = null;
|
|
1235
|
+
this.prevActiveSlide = null;
|
|
1236
|
+
this.built = false;
|
|
1237
|
+
|
|
1238
|
+
if (this.events) {
|
|
1239
|
+
for (let key in this.events) {
|
|
1240
|
+
if (this.events.hasOwnProperty(key)) {
|
|
1241
|
+
this.events[key].destroy();
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
this.events = null;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
const body = document.body;
|
|
1248
|
+
_.removeClass(html, 'glightbox-open');
|
|
1249
|
+
_.removeClass(body, 'glightbox-open touching gdesc-open glightbox-touch glightbox-mobile gscrollbar-fixer');
|
|
1250
|
+
this.modal.parentNode.removeChild(this.modal);
|
|
1251
|
+
|
|
1252
|
+
this.trigger('close');
|
|
1253
|
+
|
|
1254
|
+
// settings.onClose is deprecated and will be removed in a future update
|
|
1255
|
+
if (_.isFunction(this.settings.onClose)) {
|
|
1256
|
+
this.settings.onClose();
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
const styles = document.querySelector('.gcss-styles');
|
|
1260
|
+
if (styles) {
|
|
1261
|
+
styles.parentNode.removeChild(styles);
|
|
1262
|
+
}
|
|
1263
|
+
this.lightboxOpen = false;
|
|
1264
|
+
this.closing = null;
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
/**
|
|
1269
|
+
* Destroy lightbox
|
|
1270
|
+
* and all events
|
|
1271
|
+
*/
|
|
1272
|
+
destroy() {
|
|
1273
|
+
this.close();
|
|
1274
|
+
this.clearAllEvents();
|
|
1275
|
+
|
|
1276
|
+
if (this.baseEvents) {
|
|
1277
|
+
this.baseEvents.destroy();
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Set event
|
|
1283
|
+
*/
|
|
1284
|
+
on(evt, callback, once = false) {
|
|
1285
|
+
if (!evt || !_.isFunction(callback)) {
|
|
1286
|
+
throw new TypeError('Event name and callback must be defined');
|
|
1287
|
+
}
|
|
1288
|
+
this.apiEvents.push({ evt, once, callback });
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
/**
|
|
1292
|
+
* Set event
|
|
1293
|
+
*/
|
|
1294
|
+
once(evt, callback) {
|
|
1295
|
+
this.on(evt, callback, true);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/**
|
|
1299
|
+
* Triggers an specific event
|
|
1300
|
+
* with data
|
|
1301
|
+
*
|
|
1302
|
+
* @param string eventName
|
|
1303
|
+
*/
|
|
1304
|
+
trigger(eventName, data = null) {
|
|
1305
|
+
const onceTriggered = [];
|
|
1306
|
+
_.each(this.apiEvents, (event, i) => {
|
|
1307
|
+
const { evt, once, callback } = event;
|
|
1308
|
+
if (evt == eventName) {
|
|
1309
|
+
callback(data);
|
|
1310
|
+
if (once) {
|
|
1311
|
+
onceTriggered.push(i);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
if (onceTriggered.length) {
|
|
1316
|
+
_.each(onceTriggered, (i) => this.apiEvents.splice(i, 1));
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* Removes all events
|
|
1322
|
+
* set using the API
|
|
1323
|
+
*/
|
|
1324
|
+
clearAllEvents() {
|
|
1325
|
+
this.apiEvents.splice(0, this.apiEvents.length);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* Get Version
|
|
1330
|
+
*/
|
|
1331
|
+
version() {
|
|
1332
|
+
return version;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
export default function (options = {}) {
|
|
1337
|
+
const instance = new GlightboxInit(options);
|
|
1338
|
+
instance.init();
|
|
1339
|
+
|
|
1340
|
+
return instance;
|
|
1341
|
+
}
|