PluginProbe
Photonic Gallery & Lightbox for Flickr, SmugMug & Others / 3.37
Photonic Gallery & Lightbox for Flickr, SmugMug & Others v3.37
3.37 3.36 3.35 3.34 3.33 2.19 2.20 2.21 2.22 2.23 2.24 2.25 2.26 2.27 2.28 2.29 2.30 2.31 2.32 2.33 2.34 2.40 2.41 2.42 2.43 All 142 releases
photonic / include / ext / venobox / venobox.js

venobox.js in Photonic Gallery & Lightbox for Flickr, SmugMug & Others 3.37, at include/ext/venobox/venobox.js

1,014 lines 30.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 typeof define === 'function' && define.amd ? define(factory) :
4 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.VenoBox = factory());
5 }(this, (function () { 'use strict';
6
7 /**
8 * VenoBox 2.0.4
9 * Copyright 2013-2021 Nicola Franchini
10 * @license: https://github.com/nicolafranchini/VenoBox/blob/master/LICENSE
11 */
12 let backdrop, blocknum, blockshare, blocktitle, core, container, content, current_item, current_index, diffX, diffY, endY, elPreloader, elPreloaderInner;
13 let gallIndex, images, infinigall, items, navigationDisabled, newcontent, numeratio, nextok, prevok, overlay;
14 let set_maxWidth, set_overlayColor, set_ratio, set_autoplay, set_href, set_customclass, startY, thenext, theprev, thisborder, thisgall, title, throttle;
15
16 const svgOpen = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">';
17 const svgClose = '</svg>';
18 const downloadIcon = svgOpen + '<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>' + svgClose;
19 const shareIcon = svgOpen + '<path fill-rule="evenodd" d="M3.5 6a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5h9a.5.5 0 0 0 .5-.5v-8a.5.5 0 0 0-.5-.5h-2a.5.5 0 0 1 0-1h2A1.5 1.5 0 0 1 14 6.5v8a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 14.5v-8A1.5 1.5 0 0 1 3.5 5h2a.5.5 0 0 1 0 1h-2z"/><path fill-rule="evenodd" d="M7.646.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 1.707V10.5a.5.5 0 0 1-1 0V1.707L5.354 3.854a.5.5 0 1 1-.708-.708l3-3z"/>' + svgClose;
20 const linkIcon = svgOpen + '<path fill-rule="evenodd" d="M10.854 7.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 9.793l2.646-2.647a.5.5 0 0 1 .708 0z"/><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/><path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>' + svgClose;
21 const imagesHolder = document.createElement('div');
22
23 let startX = 0;
24 let endX = 0;
25 let diff = 0;
26 let threshold = 50;
27 let startouch = false;
28 let imgLoader = new Image();
29
30 const spinners = {
31 'bounce': ['sk-bounce', 'sk-bounce-dot', 2],
32 'chase': ['sk-chase', 'sk-chase-dot', 6],
33 'circle': ['sk-circle', 'sk-circle-dot', 12],
34 'circle-fade': ['sk-circle-fade', 'sk-circle-fade-dot', 12],
35 'flow': ['sk-flow', 'sk-flow-dot', 3],
36 'fold': ['sk-fold', 'sk-fold-cube', 4],
37 'grid': ['sk-grid', 'sk-grid-cube', 9],
38 'plane': ['sk-plane', '', 0],
39 'pulse': ['sk-pulse', '', 5],
40 'swing': ['sk-swing', 'sk-swing-dot', 2],
41 'wander': ['sk-wander', 'sk-wander-cube', 3],
42 'wave': ['sk-wave', 'sk-wave-rec', 5]
43 };
44
45 // Default settings
46 const defaults = {
47 selector: '.venobox',
48 autoplay : false,
49 bgcolor: '#fff',
50 border: '0',
51 customClass: false,
52 infinigall: false,
53 maxWidth: '100%',
54 navigation: true,
55 navKeyboard: true,
56 navTouch: true,
57 navSpeed: 300,
58 numeration: false,
59 overlayClose: true,
60 overlayColor: 'rgba(23,23,23,0.95)',
61 popup: false,
62 ratio: '16x9', // '1x1' | '4x3' | '16x9' | '21x9'
63 share: false,
64 shareStyle: 'pill', // 'bar' | 'block' | 'pill' | 'transparent'
65 spinner: 'bounce', // 'plane' | 'chase' | 'bounce' | 'wave' | 'pulse' | 'flow' | 'swing' | 'circle' | 'circle-fade' | 'grid' | 'fold' | 'wander'
66 spinColor : '#d2d2d2',
67 titleattr: 'title',
68 titlePosition: 'top', // 'top' || 'bottom'
69 titleStyle: 'bar', // 'bar' | 'block' | 'pill' | 'transparent'
70 toolsBackground: '#1C1C1C', // 'transparent'
71 toolsColor: '#d2d2d2',
72 onPreOpen: function(){ return true; }, // Return the selected object - set return false to prevent opening
73 onPostOpen: function(){}, // Return: current_item, gallIndex, thenext, theprev
74 onPreClose: function(){ return true; }, // Return: current_item, gallIndex, thenext, theprev - set return false to prevent closing
75 onNavComplete: function(){}, // Return: current_item, gallIndex, thenext, theprev
76 onContentLoaded: function(){}, // Return: newcontent
77 onInit: function(){}, // Return: plugin obj
78 jQuerySelectors: false,
79 };
80
81 /**
82 * Generate spinner html
83 * @param {Array} spinarray Selected spinner
84 */
85 function createspinner(spinarray){
86 if (!spinarray) {
87 return 'Loading...';
88 }
89 let spinner = '<div class="sk-center ' + spinarray[0] + '">';
90 let i = 0;
91 for (i = 0; i < spinarray[2]; i++) {
92 spinner += '<div class="' + spinarray[1] + '"></div>';
93 }
94 spinner += '</div>';
95 return spinner;
96 }
97
98 /**
99 * A simple forEach() implementation for Arrays, Objects and NodeLists
100 * @param {Array|Object|NodeList} collection Collection of items to iterate
101 * @param {Function} callback Callback function for each iteration
102 * @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
103 */
104 function forEach(collection, callback, scope) {
105 if (Object.prototype.toString.call(collection) === '[object Object]') {
106 let prop;
107 for (prop in collection) {
108 if (Object.prototype.hasOwnProperty.call(collection, prop)) {
109 callback.call(scope, collection[prop], prop, collection);
110 }
111 }
112 } else {
113 let i = 0;
114 let len = collection.length;
115 for (i = 0; i < len; i++) {
116 callback.call(scope, collection[i], i, collection);
117 }
118 }
119 }
120
121 /**
122 * Merge defaults with user options
123 * @param {Object} defaults Default settings
124 * @param {Object} options User options
125 * @returns {Object} Merged values of defaults and options
126 */
127 function extend( defaults, options ) {
128 let extended = {};
129
130 forEach(defaults, function (value, prop) {
131 extended[prop] = defaults[prop];
132 });
133
134 forEach(options, function (value, prop) {
135 extended[prop] = options[prop];
136 });
137 return extended;
138 }
139
140 /**
141 * Linear animation timing
142 */
143 function timingLinear(timeFraction){
144 return timeFraction;
145 }
146
147 /**
148 * Animate with callback
149 * https://javascript.info/js-animation
150 */
151 function animate({timing, draw, duration}) {
152 let start = performance.now();
153 requestAnimationFrame(function animate(time) {
154 // timeFraction goes from 0 to 1
155 let timeFraction = (time - start) / duration;
156 if (timeFraction > 1) {
157 timeFraction = 1;
158 }
159 // calculate the current animation state
160 let progress = timing(timeFraction);
161 draw(progress); // draw it
162 if (timeFraction < 1) {
163 requestAnimationFrame(animate);
164 }
165 });
166 }
167
168 /**
169 * Parse Youtube or Vimeo videos and get host & ID
170 */
171 function parseVideo(url) {
172 let type, match, vid;
173 let regYt = /(https?:\/\/)?((www\.)?(youtube(-nocookie)?|youtube.googleapis)\.com.*(v\/|v=|vi=|vi\/|e\/|embed\/|user\/.*\/u\/\d+\/)|youtu\.be\/)([_0-9a-z-]+)/i;
174 match = url.match(regYt);
175 if (match && match[7]) {
176 type = 'youtube';
177 vid = match[7];
178 } else {
179 let regVim = /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/;
180 match = url.match(regVim);
181 if (match && match[5]) {
182 type = 'vimeo';
183 vid = match[5];
184 }
185 }
186 return {
187 type: type,
188 id: vid
189 };
190 }
191
192 /**
193 * Get additional url parameters
194 */
195 function getUrlParameter(url) {
196 let result = '';
197 let sPageURL = decodeURIComponent(url);
198 let firstsplit = sPageURL.split('?');
199
200 if (firstsplit[1] !== undefined) {
201 let sURLVariables = firstsplit[1].split('&');
202 let sParameterName;
203 let i;
204 for (i = 0; i < sURLVariables.length; i++) {
205 sParameterName = sURLVariables[i].split('=');
206 result = result + '&'+ sParameterName[0]+'='+ sParameterName[1];
207 }
208 }
209 return encodeURI(result);
210 }
211
212 /**
213 * Get all images from string
214 */
215 function getImages(string) {
216 imagesHolder.innerHTML = string;
217 return imagesHolder.querySelectorAll('img');
218 }
219
220 /**
221 * Update item settings.
222 */
223 function updateVars(obj){
224 if (!obj) {
225 return false;
226 }
227 navigationDisabled = true;
228 current_item = obj;
229 nextok = false;
230 prevok = false;
231 set_maxWidth = obj.getAttribute("data-maxwidth") || obj.settings.maxWidth;
232 set_overlayColor = obj.getAttribute("data-overlay") || obj.settings.overlayColor;
233 set_ratio = obj.getAttribute("data-ratio") || obj.settings.ratio;
234 set_autoplay = obj.getAttribute("data-autoplay") || obj.settings.autoplay;
235 set_href = obj.getAttribute("data-href") || obj.getAttribute('href');
236 set_customclass = obj.getAttribute("data-customclass") || obj.settings.customClass;
237 title = obj.getAttribute(obj.settings.titleattr) || '';
238 thisborder = obj.getAttribute("data-border") || obj.settings.border;
239 }
240
241 /**
242 * Close modal.
243 */
244 function close() {
245 if (!current_item || !document.body.classList.contains('vbox-open')) {
246 return false;
247 }
248 if (current_item.settings.onPreClose && typeof current_item.settings.onPreClose === 'function') {
249 current_item.settings.onPreClose(current_item, gallIndex, thenext, theprev);
250 if (current_item.settings.onPreClose === false) {
251 return false;
252 }
253 }
254 document.body.removeEventListener('keydown', keyboardHandler);
255 document.body.classList.remove('vbox-open');
256
257 current_item.focus();
258 animate({
259 duration: 200,
260 timing: timingLinear,
261 draw: function(progress) {
262 overlay.style.opacity = 1 - progress;
263 if (progress === 1){
264 overlay.remove();
265 }
266 }
267 });
268 }
269
270 /**
271 * Navigate gallery.
272 */
273 function next() {
274 navigateGall(thenext);
275 }
276 function prev() {
277 navigateGall(theprev);
278 }
279
280 /**
281 * Keyboard navigation.
282 */
283 function keyboardHandler(e) {
284 if (e.keyCode === 27) { // esc
285 close();
286 }
287 if (!throttle) {
288 if (e.keyCode == 37 && prevok === true) { // <
289 navigateGall(theprev);
290 }
291 if (e.keyCode == 39 && nextok === true) { // >
292 navigateGall(thenext);
293 }
294 /* prevent keyboard processing until timer completed */
295 throttle = setTimeout(() => {
296 throttle = null;
297 }, 100);
298 }
299 }
300
301 /**
302 * Append and fade-in new content
303 */
304 function contentLoaded(){
305
306 navigationDisabled = false;
307
308 content.style.opacity = 0;
309
310 content.innerHTML = newcontent;
311
312 let vboxChild = content.querySelector(":first-child");
313
314 vboxChild.classList.add('vbox-child');
315
316 vboxChild.style.backgroundColor = current_item.settings.bgcolor;
317 vboxChild.style.maxWidth = set_maxWidth;
318 vboxChild.style.transform = 'scale(0.9)';
319 vboxChild.style.transition = 'transform 200ms';
320
321 // Fix weird drag
322 let childImageLock = content.querySelector('.vbox-child img');
323
324 if (childImageLock) {
325 childImageLock.addEventListener('dragstart', function(e) {
326 e.preventDefault();
327 });
328 }
329
330 // reset content scroll
331 container.scrollTo(0, 0);
332 vboxChild.style.transform = 'scale(1)';
333
334 overlay.style.setProperty('--vbox-padding', thisborder);
335
336 // Reset custom classes.
337 forEach(overlay.classList, function(obj){
338 if (obj !== 'vbox-overlay') {
339 overlay.classList.remove(obj);
340 }
341 });
342
343 // Set custom class.
344 if (set_customclass){
345 overlay.classList.add(set_customclass);
346 }
347
348 animate({
349 duration: 200,
350 timing: timingLinear,
351 draw: function(progress) {
352 content.style.opacity = progress;
353 if (progress === 1){
354 elPreloader.classList.add('vbox-hidden');
355 }
356 }
357 });
358 if (current_item.settings.onContentLoaded && typeof current_item.settings.onContentLoaded === 'function') {
359 current_item.settings.onContentLoaded(newcontent);
360 }
361 }
362
363 /**
364 * Check animation state
365 * @param {string} state 'loading' | 'animated'
366 */
367 function checkState(state) {
368 if (!content.classList.contains('vbox-' + state)) {
369 contentLoaded();
370 }
371 }
372
373 /**
374 * Load iFrame
375 */
376 function loadIframe(dest, ratio){
377 content.classList.add("vbox-loading");
378 newcontent = '<div class="venoratio venoratio-' + ratio + '"><iframe src="' + dest + '"></iframe></div>';
379 content.classList.remove("vbox-loading");
380 checkState('animated');
381 }
382
383 /**
384 * Load videos
385 */
386 function loadVid(dest, ratio, autoplay){
387
388 content.classList.add("vbox-loading");
389
390 let stringAutoplay;
391 // check if it's a video file - thanks to @alexxandar
392 if (dest.search(/.+\.mp4|og[gv]|webm/) !== -1) {
393 stringAutoplay = autoplay ? " autoplay" : "";
394 newcontent = '<div class="venoratio venoratio-' + ratio + '"><video src="' + dest + '"' + stringAutoplay + ' controls>Your browser does not support the video tag.</video></div>';
395 } else {
396 let player;
397 let videoObj = parseVideo(dest);
398
399 // set rel=0 to hide related videos at the end of YT + optional autoplay
400 stringAutoplay = autoplay ? "?rel=0&autoplay=1" : "?rel=0";
401 let queryvars = stringAutoplay + getUrlParameter(dest);
402
403 if (videoObj.type == 'vimeo') {
404 player = 'https://player.vimeo.com/video/';
405 } else if (videoObj.type == 'youtube') {
406 player = 'https://www.youtube.com/embed/';
407 }
408 newcontent = '<div class="venoratio venoratio-' + ratio + '"><iframe webkitallowfullscreen mozallowfullscreen allowfullscreen allow="autoplay" frameborder="0" src="'+player+videoObj.id+queryvars+'"></iframe></div>';
409 }
410
411 content.classList.remove("vbox-loading");
412 checkState('animated');
413 }
414
415 /**
416 * Load inline content
417 */
418 function loadInline(dest){
419 let inlineContent = document.querySelector(dest);
420 if (inlineContent) {
421 content.classList.add("vbox-loading");
422 newcontent = '<div class="vbox-inline">' + inlineContent.innerHTML + '</div>';
423 content.classList.remove("vbox-loading");
424 checkState('animated');
425 }
426 }
427
428 /**
429 * Preload images from ajax call
430 */
431 function loadAjaxImages(){
432 images = getImages(newcontent);
433 if (images.length) {
434 let imgCounter = 0;
435 forEach(images, function(getimg){
436 let srcimg = getimg.src;
437 imgLoader = new Image();
438 imgLoader.onload = function(){
439 imgCounter++;
440 if ( imgCounter == images.length ) {
441 content.classList.remove("vbox-loading");
442 checkState('animated');
443 }
444 };
445 imgLoader.onerror = function(){
446 imgCounter++;
447 if ( imgCounter == images.length ) {
448 content.classList.remove("vbox-loading");
449 checkState('animated');
450 }
451 };
452 imgLoader.src = srcimg;
453 });
454 } else {
455 content.classList.remove("vbox-loading");
456 checkState('animated');
457 }
458 }
459
460 /**
461 * Load Ajax
462 */
463 function loadAjax(dest){
464 content.classList.add("vbox-loading");
465 let xhr = new XMLHttpRequest();
466 xhr.open("GET", dest, true);
467 xhr.onload = function() {
468 newcontent = '<div class="vbox-inline">'+ xhr.response +'</div>';
469 loadAjaxImages();
470 };
471 xhr.onerror = function() {
472 newcontent = '<div class="vbox-inline"></div>';
473 content.classList.remove("vbox-loading");
474 checkState('animated');
475 };
476 xhr.send();
477 }
478
479 /**
480 * Preload image
481 */
482 function loadImage(dest){
483
484 imgLoader.onload = function(){
485 // image has been loaded
486 newcontent = '<div class="vbox-child"><img src="' + dest + '"></div>';
487 content.classList.remove('vbox-loading');
488 checkState('animated');
489 };
490 imgLoader.src = dest;
491 }
492
493 /**
494 * Start Drag
495 */
496 function dragStart(e) {
497 if (!navigationDisabled) {
498 let speed = (current_item.settings.navSpeed * 0.84);
499 content.style.transition = 'margin '+ speed + 'ms ease-out, opacity '+ speed + 'ms ease-out';
500 startY = endY = e.pageY;
501 startX = endX = e.pageX;
502 startouch = true;
503 }
504 }
505
506 /**
507 * End Drag
508 */
509 function dragEnd(e) {
510 if (startouch) {
511 startouch = false;
512 let subject = current_item;
513 let change = false;
514 diff = endX - startX;
515
516 if (diff < 0 && nextok) {
517 subject = thenext;
518 change = true;
519 }
520 if (diff > 0 && prevok) {
521 subject = theprev;
522 change = true;
523 }
524
525 if (Math.abs(diff) >= threshold && change) {
526 navigateGall(subject);
527 } else {
528 content.style.marginLeft = 0;
529 content.style.opacity = 1;
530 }
531 }
532 }
533
534 /**
535 * Drag items
536 */
537 function drag(e) {
538 if (startouch && !navigationDisabled) {
539 endX = e.pageX;
540 endY = e.pageY;
541 diffX = endX - startX;
542 diffY = endY - startY;
543
544 let absdiffX = Math.abs(diffX);
545 let absdiffY = Math.abs(diffY);
546
547 if ((absdiffX > absdiffY) && (absdiffX <= 180)) {
548 let diffopac = (1 - absdiffX / 180) * 1.5;
549 e.preventDefault();
550 content.style.marginLeft = diffX + 'px';
551 content.style.opacity = diffopac;
552 }
553 }
554 }
555
556
557 function setShareButtons(href){
558 // Navigator share
559 if (navigator.canShare) {
560 const shareData = {
561 url: href
562 };
563 blockshare.insertAdjacentHTML('beforeend', '<div class="vbox-link-btn vbox-share-mobile">'+shareIcon+'</div>');
564 const mobileShareBtn = blockshare.querySelector('.vbox-share-mobile');
565 mobileShareBtn.addEventListener('click', function(e){
566 e.preventDefault();
567 navigator.share(shareData);
568 });
569 }
570
571 // Download
572 blockshare.insertAdjacentHTML('beforeend', '<a target="_blank" href="'+href+'" download>'+downloadIcon+'</a>');
573
574 // Copy link
575 blockshare.insertAdjacentHTML('beforeend', '<div class="vbox-tooltip"><div class="vbox-link-btn vbox-share-copy"><span class="vbox-tooltip-text" id="myTooltip"></span>'+linkIcon+'</div ></div>');
576 const shareCopyBtn = blockshare.querySelector('.vbox-share-copy');
577 shareCopyBtn.addEventListener('click', function(e){
578 e.preventDefault();
579 let tooltip = document.getElementById("myTooltip");
580 navigator.clipboard.writeText(href).then(function() {
581 tooltip.innerHTML = '<div class="vbox-tooltip-inner">Copied</div>';
582 }, function() {
583 console.log('copy failed');
584 });
585 });
586 }
587
588 /**
589 * Check navigation
590 * @param {object} el Current item
591 */
592 function checknav(el){
593
594 if (!el) {
595 return false;
596 }
597
598 thisgall = el.dataset.gall;
599 numeratio = el.settings.numeration;
600 infinigall = el.settings.infinigall;
601 blockshare.innerHTML = '';
602
603 let vbtype = el.dataset.vbtype;
604
605 if (el.settings.share && vbtype !== 'iframe' && vbtype !== 'inline' && vbtype !== 'ajax' ) {
606 setShareButtons(el.href);
607 }
608
609 items = document.querySelectorAll('.vbox-item[data-gall="' + thisgall + '"]');
610
611 current_index = Array.prototype.indexOf.call(items, el);
612
613 if (items.length < 2) {
614 infinigall = false;
615 numeratio = false;
616 }
617
618 thenext = items[current_index + 1];
619 theprev = items[current_index - 1];
620
621 if (!thenext && infinigall) {
622 thenext = items[0];
623 }
624
625 if (!theprev && infinigall) {
626 theprev = items[items.length - 1];
627 }
628
629 // Update gallery numeration
630 if (items.length >= 1) {
631 gallIndex = current_index + 1;
632 blocknum.innerHTML = gallIndex + ' / ' + items.length;
633 } else {
634 gallIndex = 1;
635 }
636 if (numeratio) {
637 blocknum.classList.remove('vbox-hidden');
638 } else {
639 blocknum.classList.add('vbox-hidden');
640 }
641
642 // Update title
643 if (title !== '') {
644 blocktitle.classList.remove('vbox-hidden');
645 } else {
646 blocktitle.classList.add('vbox-hidden');
647 }
648 blocktitle.innerHTML = title;
649
650 // update navigation arrows
651 prevok = false;
652 nextok = false;
653
654 if (thenext || infinigall) {
655 nextok = true;
656 }
657
658 if (current_index > 0 || infinigall) {
659 prevok = true;
660 }
661
662 // activate swipe
663 if ((prevok || nextok) && el.settings.navTouch) {
664 content.classList.add('vbox-grab');
665 content.addEventListener("touchstart", dragStart, false);
666 content.addEventListener("touchend", dragEnd, false);
667 content.addEventListener("touchmove", drag, false);
668 content.addEventListener("mousedown", dragStart, false);
669 content.addEventListener("mouseup", dragEnd, false);
670 content.addEventListener("mouseout", dragEnd, false);
671 content.addEventListener("mousemove", drag, false);
672 } else {
673 content.classList.remove('vbox-grab');
674 content.removeEventListener("touchstart", dragStart, false);
675 content.removeEventListener("touchend", dragEnd, false);
676 content.removeEventListener("touchmove", drag, false);
677 content.removeEventListener("mousedown", dragStart, false);
678 content.removeEventListener("mouseup", dragEnd, false);
679 content.removeEventListener("mouseout", dragEnd, false);
680 content.removeEventListener("mousemove", drag, false);
681 }
682
683 let vbox_next = overlay.querySelector('.vbox-next');
684 let vbox_prev = overlay.querySelector('.vbox-prev');
685
686 if (prevok) {
687 vbox_prev.classList.remove('vbox-hidden');
688 } else {
689 vbox_prev.classList.add('vbox-hidden');
690 }
691
692 if (nextok) {
693 vbox_next.classList.remove('vbox-hidden');
694 } else {
695 vbox_next.classList.add('vbox-hidden');
696 }
697
698 if (!el.settings.navigation) {
699 vbox_next.classList.add('vbox-hidden');
700 vbox_prev.classList.add('vbox-hidden');
701 }
702 } // Checknav
703
704 /**
705 * Update overlay and tools style.
706 */
707 function updateOverlay(destination){
708
709 if (!destination) {
710 return false;
711 }
712
713 backdrop.style.backgroundColor = set_overlayColor;
714
715 // Custom preloader color.
716 elPreloaderInner.innerHTML = createspinner(spinners[destination.settings.spinner]);
717
718 overlay.style.setProperty('--sk-color', destination.settings.spinColor);
719
720 elPreloader.classList.remove('vbox-hidden');
721
722 blockshare.classList.remove('vbox-top', 'vbox-bottom');
723 blocktitle.classList.remove('vbox-top', 'vbox-bottom');
724
725 if (destination.settings.titlePosition == 'top') {
726 blocktitle.classList.add('vbox-top');
727 blockshare.classList.add('vbox-bottom');
728 } else {
729 blocktitle.classList.add('vbox-bottom');
730 blockshare.classList.add('vbox-top');
731 }
732
733 let titleWidth = destination.settings.titleStyle === 'bar' ? '100%' : 'auto';
734 let titleRadius = destination.settings.titleStyle === 'pill' ? '5em' : '0';
735 let shareWidth = destination.settings.shareStyle === 'bar' ? '100%' : 'auto';
736 let shareRadius = destination.settings.shareStyle === 'pill' ? '5em' : '0';
737 let titlebg = destination.settings.titleStyle === 'transparent' ? 'transparent' : destination.settings.toolsBackground;
738 let sharebg = destination.settings.shareStyle === 'transparent' ? 'transparent' : destination.settings.toolsBackground;
739
740 overlay.style.setProperty('--vbox-title-width', titleWidth);
741 overlay.style.setProperty('--vbox-title-radius', titleRadius);
742 overlay.style.setProperty('--vbox-share-width', shareWidth);
743 overlay.style.setProperty('--vbox-share-radius', shareRadius);
744 overlay.style.setProperty('--vbox-tools-color', destination.settings.toolsColor);
745 overlay.style.setProperty('--vbox-title-background', titlebg);
746 overlay.style.setProperty('--vbox-share-background', sharebg);
747 }
748
749 /**
750 * Load content
751 */
752 function loadContent(){
753 if (!current_item) {
754 return false;
755 }
756 let vbtype = current_item.dataset.vbtype;
757
758 switch (vbtype) {
759 case 'iframe':
760 loadIframe(set_href, set_ratio);
761 break;
762 case 'inline':
763 loadInline(set_href);
764 break;
765 case 'ajax':
766 loadAjax(set_href);
767 break;
768 case 'video':
769 loadVid(set_href, set_ratio, set_autoplay);
770 break;
771 default:
772 loadImage(set_href);
773 }
774 }
775
776 /**
777 * Gallery navigation.
778 */
779 function navigateGall(destination) {
780
781 if (!destination || navigationDisabled || !document.body.classList.contains('vbox-open')) {
782 return false;
783 }
784
785 updateVars(destination);
786 updateOverlay(destination);
787
788 // swipe out item
789 let speed = (current_item.settings.navSpeed * 0.84);
790 content.style.transition = 'margin '+ speed + 'ms ease-out, opacity '+ speed + 'ms ease-out';
791
792 if (destination === theprev) {
793 content.classList.add("swipe-right");
794 }
795 if (destination === thenext) {
796 content.classList.add("swipe-left");
797 }
798
799 elPreloader.classList.remove('vbox-hidden');
800
801 let startopacity = content.style.opacity;
802
803 content.classList.add("vbox-animated", "vbox-loading");
804
805 checknav(destination);
806
807 animate({
808 duration: current_item.settings.navSpeed,
809 timing: timingLinear,
810 draw: function(progress) {
811
812 content.style.opacity = startopacity - progress/startopacity;
813
814 if (progress === 1){
815 content.classList.remove("swipe-left", "swipe-right", "vbox-animated");
816 content.style.marginLeft = 0;
817 content.style.transition = '';
818
819 checkState('loading');
820
821 navigationDisabled = false;
822
823 if (current_item.settings.onNavComplete && typeof current_item.settings.onNavComplete === 'function') {
824 current_item.settings.onNavComplete(current_item, gallIndex, thenext, theprev);
825 }
826 }
827 }
828 });
829 loadContent();
830 }
831
832 /**
833 * Open item.
834 */
835 function open(obj) {
836
837 if (document.body.classList.contains('vbox-open') || !obj) {
838 return false;
839 }
840
841 if (obj.settings.onPreOpen && typeof obj.settings.onPreOpen === 'function') {
842 obj.settings.onPreOpen(obj);
843 }
844 if (!obj.settings.onPreOpen) {
845 return false;
846 }
847
848 updateVars(obj);
849
850 document.body.insertAdjacentHTML('beforeend', core);
851 document.body.classList.add('vbox-open');
852
853 overlay = document.querySelector(".vbox-overlay");
854 backdrop = overlay.querySelector(".vbox-backdrop");
855 container = overlay.querySelector(".vbox-container");
856 content = container.querySelector(".vbox-content");
857 blocknum = overlay.querySelector(".vbox-num");
858 blockshare = overlay.querySelector(".vbox-share");
859 blocktitle = overlay.querySelector(".vbox-title");
860 elPreloader = overlay.querySelector(".vbox-preloader");
861 elPreloaderInner = elPreloader.querySelector(".vbox-preloader-inner");
862
863 overlay.style.opacity = 0;
864
865 updateOverlay(obj);
866 checknav(obj);
867
868 content.classList.add("vbox-animated", "vbox-loading");
869
870 // fade in overlay
871 animate({
872 duration: 200,
873 timing: timingLinear,
874 draw: function(progress) {
875 overlay.style.opacity = progress;
876 if (progress === 1){
877 content.classList.remove('vbox-animated');
878 navigationDisabled = false;
879 checkState('loading');
880 if (current_item.settings.onPostOpen && typeof current_item.settings.onPostOpen === 'function') {
881 current_item.settings.onPostOpen(current_item, gallIndex, thenext, theprev);
882 }
883 }
884 }
885 });
886
887 loadContent();
888
889 // Keyboard actions
890 if (obj.settings.navKeyboard) {
891 document.body.addEventListener('keydown', keyboardHandler);
892
893 // Reset the throttle timer
894 document.body.addEventListener('keyup', () => {
895 if (throttle) {
896 clearTimeout(throttle);
897 throttle = null;
898 }
899 });
900 }
901
902 // Prev gallery
903 document.querySelector('.vbox-prev').addEventListener('click', function(){
904 navigateGall(theprev);
905 });
906
907 // Newxt gallery
908 document.querySelector('.vbox-next').addEventListener('click', function(){
909 navigateGall(thenext);
910 });
911
912 // Close modal.
913 overlay.addEventListener('click', function(e){
914 let closeBtn = document.querySelector('.vbox-close');
915 if (closeBtn) {
916 if (closeBtn.contains(e.target) || closeBtn === e.target || (current_item.settings.overlayClose &&
917 e.target.classList.contains('vbox-overlay') ||
918 e.target.classList.contains('vbox-content') ||
919 e.target.classList.contains('vbox-backdrop') ||
920 e.target.classList.contains('vbox-close') ||
921 e.target.classList.contains('vbox-preloader') ||
922 e.target.classList.contains('vbox-container')
923 )) {
924 close();
925 }
926 }
927 });
928 }
929
930 /**
931 * Initialize Plugin
932 */
933 function init(venobox, settings) {
934
935 if (settings.onInit && typeof settings.onInit === 'function') {
936 settings.onInit(venobox);
937 }
938
939 let selectors = settings.jQuerySelectors || document.querySelectorAll(settings.selector);
940 let navigation = '<a class="vbox-next"><span>Next</span></a><a class="vbox-prev"><span>Prev</span></a>';
941 let vbheader = '<div class="vbox-title"></div><div class="vbox-left-corner"><div class="vbox-num">0/0</div></div><div class="vbox-close"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="vbox-close-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M13.854 2.146a.5.5 0 0 1 0 .708l-11 11a.5.5 0 0 1-.708-.708l11-11a.5.5 0 0 1 .708 0Z"/><path fill-rule="evenodd" d="M2.146 2.146a.5.5 0 0 0 0 .708l11 11a.5.5 0 0 0 .708-.708l-11-11a.5.5 0 0 0-.708 0Z"/></svg></div>';
942 let vbfooter = '<div class="vbox-share"></div>';
943 let preloader = '<div class="vbox-preloader"><div class="vbox-preloader-inner"></div></div>';
944 core = '<div class="vbox-overlay"><div class="vbox-backdrop"></div>' + preloader + '<div class="vbox-container"><div class="vbox-content"></div></div>' + vbheader + navigation + vbfooter + '</div>';
945
946 /**
947 * Loop items.
948 */
949 forEach(selectors, function(obj){
950 if (obj.classList.contains("vbox-item")) {
951 return true;
952 }
953 obj.settings = settings;
954 obj.classList.add("vbox-item");
955
956 // Open Link
957 obj.addEventListener("click", function(e){
958 e.preventDefault();
959 // Remove focus from link to avoid multiple calls with enter key
960 obj.blur();
961 open(obj);
962 return false;
963 }); // Click;
964 }); // forEach
965
966 if (settings.popup) {
967 let popup = document.querySelector(settings.popup);
968 popup.settings = settings;
969 open(popup);
970 }
971 } // init
972
973 /**
974 * VenoBox constructor
975 */
976 const VenoBox = function (options) {
977 const venobox = {};
978
979 // Merge user options with defaults
980 let settings = extend( defaults, options || {} );
981
982 venobox.close = close;
983 venobox.next = next;
984 venobox.prev = prev;
985 venobox.open = open;
986 venobox.settings = settings;
987
988 init(venobox, settings);
989
990 // Public APIs
991 return venobox;
992 };
993
994 /* jQuery bridge for $().venobox() */
995 if (typeof jQuery === 'function') {
996 (function($){
997 $.fn.extend({
998 // plugin name - venobox
999 venobox: function(options) {
1000 const pluginoptions = options || {};
1001 pluginoptions.jQuerySelectors = this;
1002 // Init venobx
1003 new VenoBox({pluginoptions});
1004 } // venobox
1005 }); // extend
1006 })(jQuery);
1007 }
1008
1009 // See https://www.npmjs.com/package/venobox documentation.
1010
1011 return VenoBox;
1012
1013 })));
1014