to be direct child of [data-gallery]\n\n lightboxHTML = '';\n lightboxHTML += '
';\n galleryNav = createGalleryNavigation(element);\n if(galleryNav){\n lightboxHTML += createGalleryNavigation(element);\n }\n\n new_content = lightboxHTML;\n new_class.push('modal--lightbox');\n\n\n // if html video\n }else if(isVideoUrl(target)){\n\n // get current elements index value\n galleryCurrent = Array.from(element.parentNode.children).indexOf(element); \n\n const extension = target.split('.').pop().toLowerCase();\n\n lightboxHTML = '';\n lightboxHTML += '';\n galleryNav = createGalleryNavigation(element);\n if(galleryNav){\n lightboxHTML += createGalleryNavigation(element);\n }\n\n new_content = lightboxHTML;\n new_class.push('modal--lightbox');\n\n\n // if instagram\n } else if(target.includes('instagram')){\n\n const url = target;\n const parts = url.split(\"/\");\n const lastPart = parts[parts.length - 2];\n const embed = buildInstagramEmbed(lastPart);\n\n new_content = embed;\n new_class = ['modal--lightbox', 'modal--instagram'];\n\n\n // if iframe\n }else if(isVideoEmbedUrl(target)){\n\n embed_url = convertToEmbedUrl(target);\n new_content = '';\n new_class.push('modal--lightbox');\n\n\n // another page/url\n } else {\n\n const xhr = new XMLHttpRequest();\n\n // Append the 'ajax=true' query string parameter to the URL\n const ajaxUrl = target + (target.indexOf('?') === -1 ? '?' : '&') + 'ajax=true';\n\n // Load the content of the link's href attribute using AJAX\n xhr.open('GET', ajaxUrl);\n xhr.onload = function() {\n if (xhr.status === 200) {\n let new_content = injectClose(xhr.responseText);\n }\n };\n xhr.send();\n\n }\n\n\n // get caption\n if(element.dataset.modalcaption){\n new_content += '' + element.dataset.modalcaption + '
';\n }\n\n\n return {new_content,new_class};\n\n}\n\n\n/* open modal [click event]\n---------------------------------------------------------------------------------------------------- */\n\ndocument.addEventListener('click', function (e){\n // Check if the clicked element matches the modal link selector\n //const modalElement = e.target.matches('[data-modal]') ? e.target : e.target.closest('[data-modal]');\n const modalElement = Array.from(modalLinks).find(link => link.contains(e.target));\n if(modalElement){\n e.preventDefault();\n openModal(modalElement);\n }\n});\n\n\n/* Close the modal when clicked outside of it\n---------------------------------------------------------------------------------------------------- */\n\nif(modal){\n modal.addEventListener('click', function(e){\n if (e.target === modal) {\n modal.close();\n }\n });\n}\n\n\n/* Remove modal content when closed\n---------------------------------------------------------------------------------------------------- */\n\nmodal.addEventListener('close', function() {\n modalContent.innerHTML = '';\n modalContent.classList = [];\n});\n\n\n/* Close button\n---------------------------------------------------------------------------------------------------- */\n\nfunction bindCloseLink(){\n const closeModal = document.querySelector('#modal-box__close');\n closeModal.addEventListener('click', function() {\n modal.close();\n });\n}\n\n\n/* Inject close button into response code\n---------------------------------------------------------------------------------------------------- */\n\nfunction injectClose(source){\n if (source.includes('[close]')) {\n return source.replace('[close]', '');\n } else {\n return source;\n }\n}\n\n\n\n/* Build lightbox gallery\n---------------------------------------------------------------------------------------------------- */\n\nfunction createGalleryNavigation(element){\n\n if(element.closest('[data-gallery]')){\n\n galleryNodes = element.closest('[data-gallery]').querySelectorAll('a:not(:empty)');\n // Filter nodes based on child elements\n galleryNodes = Array.from(galleryNodes).filter(node => {\n return node.querySelector('img') || node.querySelector('picture');\n });\n\n if(galleryNodes.length > 1){\n modal.removeEventListener('click', handleLightboxNav);\n modal.addEventListener('click', handleLightboxNav);\n\n let prev_link = 'Previous';\n let next_link = 'Next';\n\n return prev_link + next_link;\n }\n\n return '';\n }\n\n}\n\nfunction handleLightboxNav(ev){\n if(ev.target.closest('.modal--lightbox__nav')){\n\n ev.stopPropagation();\n\n const navElement = ev.target.closest('.modal--lightbox__nav');\n const direction = navElement.dataset.dir\n\n let toShowIndex = galleryCurrent;\n toShowIndex += (direction === 'next') ? 1 : -1;\n toShowIndex = (toShowIndex + galleryNodes.length) % galleryNodes.length;\n\n const modalElement = Array.from(modalLinks).find(link => link.contains(galleryNodes[toShowIndex]));\n if(modalElement){\n openModal(modalElement);\n }else{\n modal.close();\n }\n\n }\n}\n\n\n\n/* Check if url is image\n---------------------------------------------------------------------------------------------------- */\n\nfunction isImageUrl(url){\n const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'];\n const extension = url.split('.').pop().toLowerCase();\n return imageExtensions.includes(extension);\n}\n\n\n/* Check if url is video\n---------------------------------------------------------------------------------------------------- */\n\nfunction isVideoEmbedUrl(url) {\n const videoDomains = ['youtube.com', 'vimeo.com', 'dailymotion.com', /* Add more as needed */];\n const urlObject = new URL(url);\n const domain = urlObject.hostname;\n return videoDomains.some(videoDomain => domain.includes(videoDomain));\n}\n\nfunction isVideoUrl(url) {\n const supportedFormats = ['mp4', 'webm', 'ogg', 'mov']; // Add more formats if needed\n const extension = url.split('.').pop().toLowerCase();\n return supportedFormats.includes(extension);\n}\n\n\n/* Convert video url to something suitable to embed\n---------------------------------------------------------------------------------------------------- */\n\nfunction convertToEmbedUrl(originalUrl) {\n const urlObject = new URL(originalUrl);\n const hostname = urlObject.hostname;\n let embedUrl = '';\n\n if(hostname.includes('youtube.com')) {\n const videoId = urlObject.searchParams.get('v');\n embedUrl = videoId ? `https://www.youtube.com/embed/${videoId}?autoplay=1` : null;\n }else if(hostname.includes('vimeo.com')){\n const videoId = urlObject.pathname.split('/').pop();\n embedUrl = videoId ? `https://player.vimeo.com/video/${videoId}?autoplay=1` : null;\n }else if(hostname.includes('dailymotion.com')){\n const videoId = urlObject.pathname.split('/').pop();\n embedUrl = videoId ? `https://www.dailymotion.com/embed/video/${videoId}?autoplay=1` : null;\n }\n\n return embedUrl;\n}\n\n\n/* Load Instagram embed script\n---------------------------------------------------------------------------------------------------- */\n\nfunction loadInstagramEmbedScript(){\n if(!window.instagramEmbedScriptLoaded) {\n var script = document.createElement('script');\n script.async = true;\n script.src = '//www.instagram.com/embed.js';\n document.body.appendChild(script);\n window.instagramEmbedScriptLoaded = true; \n }\n window.instgrm.Embeds.process();\n}\n\n\nfunction buildInstagramEmbed(id){\n\n return false;\n \n}","/*----------------------------------------------------\r\n\r\n\tnav.js\r\n\r\n----------------------------------------------------*/\r\n\r\n// define the breakpoint\r\nconst breakpoint = 979;\r\nconst mediaQuery = window.matchMedia(`(max-width: ${breakpoint}px)`);\r\n\r\n// select the navigation element\r\nconst nav = document.getElementById('nav');\r\nconst nav_button = nav.nextElementSibling;\r\n\r\n\r\n// function to hide the popover\r\nconst hidePopover = () => {\r\n if(typeof nav.hidePopover === 'function'){\r\n\r\n nav.hidePopover();\r\n nav_button.setAttribute('aria-expanded','false');\r\n\r\n // only require inert on mobile view\r\n if(mediaQuery.matches){\r\n nav.setAttribute('inert','');\r\n }else{\r\n nav.removeAttribute('inert');\r\n }\r\n\r\n }\r\n};\r\n\r\n\r\n// popover state changes\r\nconst handlePopoverToggle = () => {\r\n if(nav.matches(':popover-open')){\r\n // popover is open\r\n nav.removeAttribute('inert');\r\n nav_button.setAttribute('aria-expanded', 'true');\r\n }else{\r\n // popover is closed\r\n hidePopover();\r\n }\r\n};\r\n\r\n\r\n// onload\r\nif(mediaQuery.matches){\r\n hidePopover();\r\n}\r\n\r\n\r\n// event listeners\r\nmediaQuery.addEventListener('change', hidePopover);\r\nnav.addEventListener('toggle', handlePopoverToggle);","/*----------------------------------------------------\r\n\r\n\tScarousel.js\r\n\t// scroll based carousel\r\n\r\n----------------------------------------------------*/\r\n\r\nfunction initScarousel(sc_container){\r\n\r\n\tlet autoplay_interval, item_width, nav;\r\n\tconst base_interval = 7000;\r\n\tconst scroll_interval = 750;\r\n\r\n\tconsole.log('init: scarousel');\r\n\r\n\t// get settings\r\n\tconst settings_attr = sc_container.dataset.scarousel;\r\n\tconst settings_array = settings_attr.split(';').map(setting => setting.trim());\r\n\tconst settings = {};\r\n\tsettings_array.forEach(setting => {\r\n\t\tif(setting !== \"\"){\r\n\t\t\tconst [key, value] = setting.split(':').map(part => part.trim());\r\n\t\t\tsettings[key] = value === 'true' ? true : value === 'false' ? false : isNaN(value) ? value : parseFloat(value) || value;\r\n\t\t}\r\n\t});\r\n\r\n\r\n\tlet first_child_style = getComputedStyle(sc_container.firstElementChild);\r\n\titem_width = first_child_style.minWidth === '100%' ? sc_container.firstElementChild.clientWidth : parseInt(first_child_style.width);\r\n \r\n // gap\r\n var gap = Number.isInteger(parseInt(getComputedStyle(sc_container).gap)) ? parseInt(getComputedStyle(sc_container).gap) : 0;\r\n\r\n\r\n\t// add navigation\r\n\tif(typeof settings.navigation !== 'undefined' && settings.navigation !== null){\r\n\t\tswitch(settings.navigation){\r\n\r\n\t\t\t// prev / next navigation\r\n\t\t\tcase 'prevnext':\r\n\t\t\t\tbuildPrevNextNavigation();\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t// prev / next navigation\r\n\t\t\tcase 'paging':\r\n\t\t\t\tbuildPagingNavigation();\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\t// no nothing\r\n\t\t\t\tbreak;\r\n\r\n\t\t}\r\n\t}\r\n\r\n\t// add autoplay\r\n\tconst initiateAutoplay = () => {\r\n\t\t// only autoplay after video has loaded, as requires duration of video\r\n\t\t// queryselector will get the first video in the list only\r\n\t\tconst video = sc_container.querySelector('video');\r\n\t\tif(video){\r\n\t\t\t\thasVideoLoaded(video, autoplayCarousel);\r\n\t\t}else{\r\n\t\t\t\tautoplayCarousel();\r\n\t\t}\r\n\r\n\t\t// cancel autoplay on touch\r\n\t\tsc_container.addEventListener(\"touchstart\", cancelAutoPlay);\r\n\r\n\t};\r\n\r\n\tif(settings.autoplay === true){\r\n\t\tinitiateAutoplay();\r\n\t}\r\n\r\n\r\n\t// scroll listener\r\n\tsc_container.addEventListener('scroll', () => {\r\n updateCurrentPaging(); // call your function on scroll\r\n\t});\r\n\r\n\r\n\r\n\r\n\r\n\t// animation\r\n\t// =========================================================================================================================\r\n\r\n\tfunction slideCarousel(next_slide = false){\r\n \r\n\t\t// redefine width and gap in case of resize\r\n\t\tlet first_child_style = getComputedStyle(sc_container.firstElementChild);\r\n\t\titem_width = first_child_style.minWidth === '100%' ? sc_container.firstElementChild.clientWidth : parseInt(first_child_style.width);\r\n\t\tvar gap = Number.isInteger(parseInt(getComputedStyle(sc_container).gap)) ? parseInt(getComputedStyle(sc_container).gap) : 0;\r\n\r\n\t\t// find out the current slide in view\r\n\t\tvar current_index = Math.floor(sc_container.scrollLeft / (item_width + gap));\r\n \r\n // adjust for items smaller than width of container\r\n var current_index_left = ((current_index * gap) + (current_index * item_width));\r\n\r\n if((sc_container.scrollLeft - 10) > current_index_left){ // use -10 to adjust for rounding\r\n current_index += 1;\r\n }\r\n\r\n // if sliding to specific number\r\n\t\tif(Number.isInteger(next_slide)){\r\n\t\t\tvar next_index = next_slide;\r\n\r\n // if next\r\n\t\t}else if(next_slide === true){\r\n\t\t\tvar next_index = current_index + 1;\r\n if(next_index >= sc_container.children.length){\r\n next_index = 0; // if no more items, go to first\r\n }\r\n\r\n // if previous\r\n\t\t}else{\r\n var next_index = current_index - 1;\r\n if(next_index < 0){\r\n next_index = sc_container.children.length - 1; // if no more items, go to last, adjusting for 0 index\r\n }\r\n\t\t}\r\n \r\n // calculate amount to scroll container to\r\n to_scroll_amount = (item_width * next_index);\r\n \r\n // need to get this programatically\r\n to_scroll_amount += gap * next_index; // add gap\r\n\r\n\r\n // if container at full scroll (adjusting for items smaller than container)\r\n if(next_slide === true){\r\n var width_of_all_items = (item_width * sc_container.children.length) + (gap * (sc_container.children.length - 1));\r\n // if scrolling again would match or exceed all items && item isn't 100% width\r\n //if((to_scroll_amount + item_width) >= width_of_all_items && item_width != sc_container.offsetWidth){\r\n\t\t\tif((sc_container.scrollLeft + sc_container.offsetWidth) >= width_of_all_items && item_width != sc_container.offsetWidth){\r\n to_scroll_amount = 0;\r\n }\r\n }\r\n\r\n // reduce by ten to avoid sub-pixels breaking the scroll\r\n to_scroll_amount -= 10;\r\n\r\n\t\t// update slider_container.scrollLeft\r\n\t\tsc_container.scrollLeft = to_scroll_amount;\r\n\r\n\t\t// update current\r\n\t\tupdateCurrentPaging();\r\n\r\n\t}\r\n\r\n\r\n\r\n\r\n\t// autoplay\r\n\t// =========================================================================================================================\r\n\r\n\t// play\r\n\tfunction autoplayCarousel(){\r\n\r\n\t\t// find current slide (after scroll)\r\n\t\tconst current_index = Math.floor(sc_container.scrollLeft / item_width);\r\n const current_slide = sc_container.children[current_index];\r\n const video = current_slide.querySelector('video');\r\n\r\n\r\n\t\t// get interval, minus scroll delay\r\n\t\tconst calculateInterval = () => {\r\n\t\t\treturn video ? getVideoLength(video) - scroll_interval : base_interval - scroll_interval;\r\n\t\t};\r\n\r\n\r\n\t\tconst restartAutoplay = () => {\r\n\t\t\t// move to next slide\r\n\t\t\tslideCarousel(true);\r\n\t\t\t// delay restarting autoplay so current scroll has time to complete\r\n\t\t\tsetTimeout(autoplayCarousel, scroll_interval);\r\n\t\t};\r\n\r\n\t\t// set autoplay\r\n\t\tautoplay_interval = setTimeout(restartAutoplay, calculateInterval());\r\n\r\n\t}\r\n\r\n\t// stop\r\n\tfunction cancelAutoPlay(){\r\n\t\tclearTimeout(autoplay_interval); \r\n\t}\r\n\r\n\r\n\r\n\r\n\t// video\r\n\t// =========================================================================================================================\r\n\r\n\t// duration\r\n\tfunction getVideoLength(video){\r\n\t\treturn video.duration * 1000; // convert to milliseconds\r\n\t}\r\n\r\n\t// has loaded\r\n\tfunction hasVideoLoaded(video, callback){\r\n\r\n\t\t// if video already loaded\r\n\t\tif(video.readyState == 4){\r\n\t\t\tif(typeof callback === 'function'){\r\n\t\t\t\tcallback();\r\n\t\t\t}\r\n\r\n\t\t// else listen to onloadedmetadata event\r\n\t\t}else{\r\n\t\t\tvideo.onloadedmetadata = function(){\r\n\t\t\t\tif(typeof callback === 'function'){\r\n\t\t\t\t\tcallback();\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t}\r\n\r\n\t}\r\n\r\n\r\n\r\n\r\n\t// prev / next navigation\r\n\t// =========================================================================================================================\r\n\r\n\t// build\r\n\tfunction buildPrevNextNavigation(){\r\n\r\n\t\tconst existing_nav = sc_container.querySelector('.scarousel__nav');\r\n\r\n if(!existing_nav){\r\n const nav_to_add = document.createElement(\"nav\");\r\n nav_to_add.classList.add('scarousel__nav', 'scarousel__nav--prevnext');\r\n nav_to_add.innerHTML = '