PageSourceSearch

https://knockhatch.com/js/scripts.js?v=3.8.9

js knockhatch.com collected 2026-10-07 00:06:43 UTC 9,981 bytes, 308 lines download raw bytes

1document.addEventListener('DOMContentLoaded', function() {
2    const slides = document.querySelectorAll('.slide');
3    const slider = document.querySelector('.slider');
4    const sliderContainer = document.querySelector('.slider-container');
5    const pagination = document.querySelector('.pagination');
6    const leftArrow = document.querySelector('.arrow.left');
7    const rightArrow = document.querySelector('.arrow.right');
8    let currentIndex = 0;
9    let inView = true; // Whether the slider is currently scrolled into view
10
11    if (!slides.length || !slider || !pagination || !leftArrow || !rightArrow) {
12        console.error('Some elements are missing in the DOM.');
13        return;
14    }
15
16    // Create pagination dots
17    slides.forEach((slide, index) => {
18        const dot = document.createElement('div');
19        dot.classList.add('dot');
20        dot.addEventListener('click', () => goToSlide(index));
21        pagination.appendChild(dot);
22    });
23
24    const dots = document.querySelectorAll('.dot');
25
26    // <mux-video> may not be upgraded yet when this classic script runs (the
27    // element definition loads as a deferred module), so guard media calls and
28    // wait for the definition before attempting playback.
29    function safePlay(video) {
30        if (typeof video.play !== 'function') {
31            if (window.customElements) {
32                customElements.whenDefined('mux-video').then(() => safePlay(video));
33            }
34            return;
35        }
36        const played = video.play();
37        if (played && played.catch) played.catch(() => {});
38    }
39
40    function safePause(video) {
41        if (typeof video.pause === 'function') video.pause();
42    }
43
44    // Play the Mux background video for a given slide (created lazily, reused after).
45    function playVideo(index) {
46        const slide = slides[index];
47        const muxId = slide.getAttribute('data-mux-id');
48        if (!muxId) return;
49
50        let video = slide.querySelector('mux-video');
51        if (!video) {
52            video = document.createElement('mux-video');
53            video.setAttribute('playback-id', muxId);
54            video.setAttribute('stream-type', 'on-demand');
55            video.setAttribute('preload', 'auto');
56            video.setAttribute('loop', ''); // Loop, since auto-progression is disabled
57            video.setAttribute('muted', '');
58            video.muted = true; // Required for autoplay
59            video.setAttribute('playsinline', ''); // Prevent fullscreen on iOS
60            // The HLS source loads asynchronously from the playback-id, so calling
61            // play() right away rejects. The autoplay attribute starts playback as
62            // soon as the source is ready; only enable it if the slide is in view.
63            if (inView) video.setAttribute('autoplay', '');
64            slide.appendChild(video);
65            return;
66        }
67
68        // Reused element: source is already loaded, so restart and play directly.
69        try { video.currentTime = 0; } catch (e) {}
70        if (inView) safePlay(video);
71    }
72
73    function stopVideos() {
74        slides.forEach(slide => {
75            const video = slide.querySelector('mux-video');
76            if (video) {
77                safePause(video);
78                try { video.currentTime = 0; } catch (e) {}
79            }
80        });
81    }
82
83    function nextSlide() {
84        currentIndex = (currentIndex + 1) % slides.length;
85        updateSlider();
86    }
87
88    function prevSlide() {
89        currentIndex = (currentIndex - 1 + slides.length) % slides.length;
90        updateSlider();
91    }
92
93    function goToSlide(index) {
94        currentIndex = index;
95        updateSlider();
96    }
97
98    function updateSlider() {
99        stopVideos(); // Stop all videos before updating the slider
100        slider.style.transform = `translateX(-${currentIndex * 100}%)`;
101        dots.forEach((dot, i) => {
102            dot.classList.toggle('active', i === currentIndex);
103        });
104        playVideo(currentIndex); // Play the video in the active slide
105    }
106
107    leftArrow.addEventListener('click', prevSlide);
108    rightArrow.addEventListener('click', nextSlide);
109
110    // Pause the slideshow/video when it's scrolled out of view, resume when back.
111    if (sliderContainer && 'IntersectionObserver' in window) {
112        const observer = new IntersectionObserver((entries) => {
113            entries.forEach(entry => {
114                inView = entry.isIntersecting;
115                const video = slides[currentIndex].querySelector('mux-video');
116                if (!video) return;
117                if (inView) {
118                    safePlay(video);
119                } else {
120                    safePause(video);
121                }
122            });
123        }, { threshold: 0.35 });
124        observer.observe(sliderContainer);
125    }
126
127    // Initialize the slider and play the first video
128    updateSlider();
129});
130
131
132
133
134/*var cookieDismiss = Cookies.get('cookieDismiss');
135if(cookieDismiss == 'true'){
136    $( ".cookie-notice" ).hide();
137}
138
139function cookieNoticeDismiss(){
140    Cookies.set('cookieDismiss', true, { expires: 365 });
141    $( ".cookie-notice" ).hide();
142}*/
143
144//window.onload = function () { $(".loader").fadeOut(); }
145
146$('.language-button').on('click', function(event) {
147    event.preventDefault();
148    $( ".language-selector" ).addClass( "active" );
149});
150
151$('.mobile-menu_toggle').on('click', function(event) {
152    event.preventDefault();
153    $( ".mobile-menu-overlay" ).toggle();
154    $( ".mobile-menu" ).toggleClass( "mobile-menu--open" );
155    //$( "body" ).toggleClass( "no-scroll" );
156    $( ".mobile-menu_toggle" ).toggleClass( "--hidden" );
157});
158
159$('.main-menu_toggle').on('click', function(event) {
160    event.preventDefault();
161    $( ".mobile-menu-overlay" ).toggle();
162    $( ".mobile-menu" ).toggleClass( "mobile-menu--open" );
163    //$( "body" ).toggleClass( "no-scroll" );
164    $( ".mobile-nav__toggle" ).toggleClass( "mobile-nav__toggle--active" );
165});
166
167$('.mobile-menu-overlay').on('click', function(event) {
168    event.preventDefault();
169    $( ".mobile-menu-overlay" ).toggle();
170    $( ".mobile-menu" ).toggleClass( "mobile-menu--open" );
171    //$( "body" ).toggleClass( "no-scroll" );
172    $( ".mobile-nav__toggle" ).toggleClass( "mobile-nav__toggle--active" );
173    $( ".mobile-menu_toggle" ).removeClass( "--hidden" );
174});
175
176$('.mobile-menu__submenu').on('click', function(event) {
177	$(this).toggleClass( "mobile-menu__submenu--open");
178});
179
180var lazyLoadInstance = new LazyLoad({
181    elements_selector: ".lazy"
182    // ... more custom settings?
183});
184
185const animateCSS = (element, animation, prefix = 'animate__') =>
186  // We create a Promise and return it
187  new Promise((resolve, reject) => {
188    const animationName = `${prefix}${animation}`;
189    const node = document.querySelector(element);
190
191    node.classList.add(`${prefix}animated`, animationName);
192
193    // When the animation ends, we clean the classes and resolve the Promise
194    function handleAnimationEnd(event) {
195      event.stopPropagation();
196      node.classList.remove(`${prefix}animated`, animationName);
197      resolve('Animation ended');
198    }
199
200    node.addEventListener('animationend', handleAnimationEnd, {once: true});
201  });
202
203function showSiteNotice(){
204	$('#info-popup-overlay').removeClass('hidden');
205	$('#info-popup-container').removeClass('hidden');
206}
207function hideSiteNotice(){
208	animateCSS('#info-popup', 'bounceOut').then((message) => {
209		$('#info-popup-overlay').addClass('hidden');
210		$('#info-popup-container').addClass('hidden');
211	});
212}
213
214$('#info-notice-dismiss').click(function(e){
215	e.preventDefault();
216	Cookies.set('notice_dismiss', 'true');
217	hideSiteNotice();
218});
219if(Cookies.get('notice_dismiss') != 'true'){
220	showSiteNotice();
221}
222/*
223function showBookingPopup(){
224	$('#booking-popup-overlay').removeClass('hidden');
225	$('#booking-popup-container').removeClass('hidden');
226}
227function hideBookingPopup(){
228	animateCSS('#booking-popup', 'bounceOut').then((message) => {
229		$('#booking-popup-overlay').addClass('hidden');
230		$('#booking-popup-container').addClass('hidden');
231	});
232}
233$('#booking-popup-dismiss').click(function(e){
234	e.preventDefault();
235	hideBookingPopup();
236});
237*/
238$('.marquee').marquee({
239    //speed in milliseconds of the marquee
240    duration: 7000,
241    //time in milliseconds before the marquee will start animating
242    delayBeforeStart: 0,
243    //'left' or 'right'
244    direction: 'left'
245});
246/*
247$('.homepage-hero').vide({
248    mp4: 'https://cdn.knockhatch.com/videos/homepageV2.mp4',
249    poster: '/assets/dragon-castle-1.jpg'
250});
251*/
252$('.review-slider').flickity({
253    // options
254    percentPosition: false,
255    wrapAround: true,
256    prevNextButtons: false,
257    pageDots: false,
258    autoPlay: true
259});
260
261//var rellax = new Rellax('.rellax');
262function processCalendar(){
263
264    var calendarEl = document.getElementById('calendar');
265    if(!calendarEl){ return; }
266
267    var validRange = {};
268    if(calendarEl.dataset.rangeStart){ validRange.start = calendarEl.dataset.rangeStart; }
269    if(calendarEl.dataset.rangeEnd){ validRange.end = calendarEl.dataset.rangeEnd; }
270
271    $.getJSON('/calendar-api.json', function(response){
272
273        var calendar = new FullCalendar.Calendar(calendarEl, {
274            plugins: [ 'dayGrid','interaction' ],
275            validRange: validRange,
276            firstDay: 1,
277            dateClick: function(info) {
278                window.location.href = "https://knockhatch.digitickets.co.uk/tickets";
279            }
280        });
281
282        calendar.render();
283
284        (response.data || []).forEach(function(item){
285            calendar.addEvent({
286                start: item.startDate,
287                end: item.endDate,
288                rendering: 'background',
289                classNames: [item.type]
290            });
291        });
292    });
293}
294
295function toggleCollapse(id){
296	event.preventDefault();
297	$('#collapse-'+id).toggleClass('collapse-item--open');
298}
299
300
301const swiper = new Swiper('.swiper', {
302    // Optional parameters
303    direction: 'horizontal',
304    loop: true,
305    autoplay: {
306        delay: 3000,
307      },
308  });

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.