@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.
Files changed (41) hide show
  1. package/.editorconfig +16 -0
  2. package/.eslintrc.json +94 -0
  3. package/.github/FUNDING.yml +3 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  6. package/.github/stale.yml +21 -0
  7. package/.github/workflows/greetings.yml +13 -0
  8. package/.lando.yml +10 -0
  9. package/.prettierignore +1 -0
  10. package/.prettierrc.json +31 -0
  11. package/CHANGELOG.md +247 -0
  12. package/CONTRIBUTING.md +29 -0
  13. package/LICENSE.md +21 -0
  14. package/README.md +710 -2
  15. package/bower.json +29 -0
  16. package/development/jscompiler.js +84 -0
  17. package/development/notifications.js +15 -0
  18. package/development/package.js +116 -0
  19. package/development/postcss.js +63 -0
  20. package/development/watcher.js +105 -0
  21. package/dist/css/glightbox.css +939 -0
  22. package/dist/css/glightbox.min.css +1 -0
  23. package/dist/js/glightbox.js +3087 -0
  24. package/dist/js/glightbox.min.js +1 -0
  25. package/index.d.ts +570 -0
  26. package/package.json +55 -4
  27. package/src/js/.jshintrc +6 -0
  28. package/src/js/core/drag.js +219 -0
  29. package/src/js/core/keyboard-navigation.js +98 -0
  30. package/src/js/core/slide-parser.js +231 -0
  31. package/src/js/core/slide.js +245 -0
  32. package/src/js/core/touch-events.js +378 -0
  33. package/src/js/core/touch-navigation.js +287 -0
  34. package/src/js/core/zoom.js +156 -0
  35. package/src/js/glightbox.js +1341 -0
  36. package/src/js/slides/iframe.js +24 -0
  37. package/src/js/slides/image.js +56 -0
  38. package/src/js/slides/inline.js +71 -0
  39. package/src/js/slides/video.js +105 -0
  40. package/src/js/utils/helpers.js +541 -0
  41. 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
+ }