PageSourceSearch

https://sstl.co.uk/js/pagescripts.js?v=0ZY0xxx6giG61iNWEUN-7RU7A1fTGyyz2oA3CJjlALs

js sstl.co.uk collected 2026-10-02 05:40:58 UTC 18,655 bytes, 570 lines download raw bytes

1function setCookie(cname, cvalue, exdays) {
2    var d = new Date();
3    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
4    var expires = "expires=" + d.toUTCString();
5    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
6}
7function getCookie(cname) {
8    var name = cname + "=";
9    var decodedCookie = decodeURIComponent(document.cookie);
10    var ca = decodedCookie.split(';');
11    for (var i = 0; i < ca.length; i++) {
12        var c = ca[i];
13        while (c.charAt(0) == ' ') {
14            c = c.substring(1);
15        }
16        if (c.indexOf(name) == 0) {
17            return c.substring(name.length, c.length);
18        }
19    }
20    return "";
21}
22
23
24
25
26$(document).ready(function () {
27
28    //Replace Stats
29    $(".stat-number").each(function (i) {
30
31        var text = $(this).text();
32        // wrap characters ["+", "%", "-", "=", "#", "*", "$", "£"] in <sup><span></span></sup>
33        var newText = text.replace(/([\+\%\-\=\#\*\$\£])/g, "<span><sup>$1</sup></span>");
34
35        // wrap character ["."] in <span></span>
36        newText = newText.replace(/(\.)/g, "<span>$1</span>");
37        $(this).html(newText);
38
39    });
40
41
42
43    $('.hero-slider > div').slick({
44        dots: true,
45        arrows: false,
46        infinite: true,
47        speed: 800,
48        cssEase: 'cubic-bezier(.72,.38,.46,.96)',
49        autoplay: true,
50        autoplaySpeed: 6000,
51        pauseOnHover: false,
52        draggable: false,
53        responsive: [
54            {
55                breakpoint: 1025,
56                settings: {
57                    autoplay: false,
58                    draggable: true
59                }
60            }
61            // You can unslick at a given breakpoint now by adding:
62            // settings: "unslick"
63            // instead of a settings object
64        ]
65    });
66
67    $('.dropdown-submenu').on("click", function (e) {
68        $('.dropdown-submenu + ul').hide();
69        $(this).attr('aria-expanded', function (i, attr) {
70            return attr == 'true' ? 'false' : 'true'
71        });
72        $(this).next('ul').toggle();
73        e.stopPropagation();
74        e.preventDefault();
75    });
76    $('body').on("click", function (e) {
77        $('.dropdown-submenu').attr('aria-expanded', false);
78        $('.dropdown-submenu').next('ul').css('display', 'none');
79    });
80
81    function loadComparison(id, before, after) {
82        slider = new juxtapose.JXSlider('#' + id,
83            [
84                {
85                    src: before
86                },
87                {
88                    src: after
89                }
90            ],
91            {
92                animate: true,
93                showLabels: false,
94                showCredits: false,
95                startingPosition: "50%",
96                makeResponsive: true
97            });
98    }
99
100
101
102    // Load Comparison
103
104
105    $(".image-comparison").each(function (i) {
106        var image1 = $(this).find("img").eq(0).attr("src"),
107            image2 = $(this).find("img").eq(1).attr("src");
108        $(this).attr("id", "JXSlider" + i)
109        console.log(image1, image2);
110        loadComparison("JXSlider" + i, image1, image2);
111        $(this).find("img").remove();
112    });
113
114
115
116    // Search
117    const urlParams = new URLSearchParams(window.location.search);
118    const searchText = urlParams.get('searchtext');
119    if (searchText != null && searchText != "") {
120        $(".search-text").val(searchText);
121        $(".search-term").text(searchText);
122        $(".search-term-container").show();
123    }
124    else {
125        $(".search-term-container").hide();
126    }
127    //console.log(searchText);
128    $(".search-text").on('keyup', function (e) {
129        if (e.key === 'Enter' || e.keyCode === 13) {
130            window.location.href = "/search?searchtext=" + this.value;
131        }
132    });
133    $(".search-btn").on('click', function (e) {
134        if ($(this).closest(".search-form").length) {
135            window.location.href = "/search?searchtext=" + $(this).closest(".search-form").find(".search-text").val();
136        }
137        if ($(this).closest(".search-form-modal").length) {
138            window.location.href = "/search?searchtext=" + $(this).closest(".search-form-modal").find(".search-text").val();
139        }
140    });
141
142
143    /* Hero Animated Text */
144    $(".two-col-hero .hero-text-col .hero-heading-box h1 span").addClass("animated");
145    $(".two-col-hero .hero-text-col .hero-heading-box .btn").addClass("animated");
146    /////////////////////////////
147
148    if ($(".rellax").length) {
149        var rellax = new Rellax('.rellax');
150    }
151    if ($(".subnav ul > li.active").length) {
152        var subnavactive = $(".subnav ul > li.active");
153        var position = subnavactive.position();
154        $(".subnav ul").scrollLeft(position.left - 50);
155    }
156
157    $('.center').slick({
158        centerMode: true,
159        centerPadding: '60px',
160        slidesToShow: 1,
161        responsive: [
162            {
163                breakpoint: 768,
164                settings: {
165                    arrows: false,
166                    centerMode: true,
167                    centerPadding: '40px',
168                    slidesToShow: 3
169                }
170            },
171            {
172                breakpoint: 480,
173                settings: {
174                    arrows: false,
175                    centerMode: true,
176                    centerPadding: '40px',
177                    slidesToShow: 1
178                }
179            }
180        ]
181    });
182
183    //Cookie Banner Check
184    var AllowCookies = getCookie("AllowCookies");
185    if (AllowCookies == "") {
186        $("#cookiebanner").addClass('active');
187    }
188
189    // Cookie Banner close
190    $("#cookiebanner .close").click(function (e) {
191        e.preventDefault();
192        $(this).parent().parent().removeClass('active');
193        setCookie("AllowCookies", "true", 365);
194    });
195
196    // Ajax News load
197
198    if ($('#load-news').length) {
199
200    var preloadedNewsData = null;
201
202    // Preload news feed when the page loads
203    $(document).ready(function () {
204        $.get("/news-feed", function (data) {
205            preloadedNewsData = data;
206        });
207    });
208
209    $('#load-news').click(function (e) {
210        e.preventDefault();
211
212        function processNewsData(data) {
213            var $html = $(data);
214            var $newsContainer = $("section.news-latest .container-fluid .news-articles-container");
215            var count = 0;
216
217            // Loop through each news item in the returned HTML
218            $html.find("div[class^='col']").each(function () {
219                if (count >= 9) return false; // Stop after adding 9 items
220
221                var $newsItem = $(this),
222                    id = $newsItem.data("id");
223
224                // Add only if the item does not already exist
225                if ($newsContainer.find("[data-id='" + id + "']").length === 0) {
226                    $newsContainer.append($newsItem);
227                    count++;
228                }
229            });
230
231            // Hide button if all items are loaded
232            setTimeout(function () {
233                var totalItems = $newsContainer.children("div[class^='col']").length;
234                var feedItemCount = $html.find("div[class^='col']").length;
235                if (totalItems === feedItemCount) {
236                    $('#load-news').closest(".row").hide();
237                }
238            }, 100);
239        }
240
241        if (preloadedNewsData) {
242            // If preloaded data is ready, use it
243            processNewsData(preloadedNewsData);
244            preloadedNewsData = null; // Prevent reuse, ensuring fresh content next time
245        } else {
246            // Otherwise, fetch it normally
247            $.get("/news-feed", function (data) {
248                processNewsData(data);
249            });
250        }
251    });
252
253}
254
255
256
257    // Ajax Photo load
258    var photoPage = 2;
259    $('#load-photos').click(function (e) {
260        e.preventDefault();
261        // Hit the news feed page with the pager QS
262        $.get("/system/photo-feed/?page=" + photoPage, function (data) {
263            var $html = $(data).find('section.news-latest');
264            var $row = $(".items .row");
265            //Loop round each news item in the returned HTML
266            var $item = $html.find(".row > div[class^='col']");
267            console.log("incoming " + $item.length);
268            $item.each(function (index) {
269                var $photoItem = $(this),
270                    id = $photoItem.data("id");
271                console.log(id);
272                // Check to see if the id of the incoming doc already exists on the page
273                console.log($("section.news-latest .container-fluid .items [data-id='+id+']").length)
274                if ($("section.news-latest .container-fluid .items [data-id='" + id + "']").length < 1) {
275                    //Append item to the row
276                    $row.append($photoItem);
277                }
278                else {
279                    //Hide load button if there is nothing left to load
280                    $('#load-photos').parents(".row").hide();
281                }
282
283            });
284            console.log("hey " + $item.length);
285            if ($item.length < 9) {
286                console.log("hey if " + $item.length);
287                $('#load-photos').parents(".row").hide();
288            }
289            /* //Insert row onto the page
290             $("section.news-latest .container-fluid .items").append($row);*/
291
292            console.log("items - " + $(".items .row > div[class^='col']").length);
293            setTimeout(function () {
294                if ($(".items .row > div[class^='col']").length == $("section.news-latest").attr("data-itemcount")) {
295                    $('#load-photos').closest(".row").hide();
296                }
297            }, 100);
298        })
299        photoPage++;
300
301        if ($(".items .row > div[class^='col']").length == $("section.news-latest").attr("data-itemcount")) {
302            $('#load-photos').parents(".row").hide();
303        }
304    });
305
306
307    /*		
308  
309  // Cache selectors
310  var lastId,
311  topMenu = $(".subnav"),
312  topMenuHeight = topMenu.outerHeight()+1,
313  // All list items
314  menuItems = topMenu.find("a"),
315  // Anchors corresponding to menu items
316  scrollItems = menuItems.map(function(){
317  var item = $($(this).attr("href"));
318  if (item.length) { return item; }
319  });
320  
321  
322  
323  
324  // Bind click handler to menu items
325  // so we can get a fancy scroll animation
326  menuItems.click(function(e){
327  var href = $(this).attr("href"),
328  offsetTop = href === "#" ? 0 : $(href).offset().top-topMenuHeight+1;
329  $('html, body').stop().animate({ 
330  scrollTop: offsetTop
331  }, 850);
332  e.preventDefault();
333  });
334  
335  // Bind to scroll
336  $(window).scroll(function(){
337  // Get container scroll position
338  var fromTop = $(this).scrollTop()+topMenuHeight;
339  
340  // Get id of current scroll item
341  var cur = scrollItems.map(function(){
342  if ($(this).offset().top < fromTop)
343  return this;
344  });
345  // Get the id of the current element
346  cur = cur[cur.length-1];
347  var id = cur && cur.length ? cur[0].id : "";
348  
349  if (lastId !== id) {
350  lastId = id;
351  // Set/remove active class
352  menuItems
353  .parent().removeClass("active")
354  .end().filter("[href=#"+id+"]").parent().addClass("active");
355  }                   
356  });
357  
358  */
359
360});
361
362function validateDecimalPlaces(e) {
363var t = e.value;
364e.value = (t.indexOf(".") >= 0) ? (t.substr(0, t.indexOf(".")) + t.substr(t.indexOf("."), 3)) : t;
365}
366
367function isIE() {
368    return window.navigator.userAgent.match(/(MSIE|Trident)/);
369}
370
371//foreach polyfill
372if (window.NodeList && !NodeList.prototype.forEach) {
373    NodeList.prototype.forEach = Array.prototype.forEach;
374}
375if (window.HTMLCollection && !HTMLCollection.prototype.forEach) {
376    HTMLCollection.prototype.forEach = Array.prototype.forEach;
377}
378
379if (!Element.prototype.matches) {
380    Element.prototype.matches =
381        Element.prototype.msMatchesSelector ||
382        Element.prototype.webkitMatchesSelector;
383}
384
385//closest polyfill
386if (!Element.prototype.closest) {
387    Element.prototype.closest = function (s) {
388        var el = this;
389
390        do {
391            if (Element.prototype.matches.call(el, s)) return el;
392            el = el.parentElement || el.parentNode;
393        } while (el !== null && el.nodeType === 1);
394        return null;
395    };
396}
397
398//Array from polyfill
399if (!Array.from) {
400    Array.from = (function () {
401        var toStr = Object.prototype.toString;
402        var isCallable = function (fn) {
403            return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
404        };
405        var toInteger = function (value) {
406            var number = Number(value);
407            if (isNaN(number)) { return 0; }
408            if (number === 0 || !isFinite(number)) { return number; }
409            return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
410        };
411        var maxSafeInteger = Math.pow(2, 53) - 1;
412        var toLength = function (value) {
413            var len = toInteger(value);
414            return Math.min(Math.max(len, 0), maxSafeInteger);
415        };
416
417        // The length property of the from method is 1.
418        return function from(arrayLike/*, mapFn, thisArg */) {
419            // 1. Let C be the this value.
420            var C = this;
421
422            // 2. Let items be ToObject(arrayLike).
423            var items = Object(arrayLike);
424
425            // 3. ReturnIfAbrupt(items).
426            if (arrayLike == null) {
427                throw new TypeError("Array.from requires an array-like object - not null or undefined");
428            }
429
430            // 4. If mapfn is undefined, then let mapping be false.
431            var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
432            var T;
433            if (typeof mapFn !== 'undefined') {
434                // 5. else
435                // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
436                if (!isCallable(mapFn)) {
437                    throw new TypeError('Array.from: when provided, the second argument must be a function');
438                }
439
440                // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
441                if (arguments.length > 2) {
442                    T = arguments[2];
443                }
444            }
445
446            // 10. Let lenValue be Get(items, "length").
447            // 11. Let len be ToLength(lenValue).
448            var len = toLength(items.length);
449
450            // 13. If IsConstructor(C) is true, then
451            // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
452            // 14. a. Else, Let A be ArrayCreate(len).
453            var A = isCallable(C) ? Object(new C(len)) : new Array(len);
454
455            // 16. Let k be 0.
456            var k = 0;
457            // 17. Repeat, while k < len� (also steps a - h)
458            var kValue;
459            while (k < len) {
460                kValue = items[k];
461                if (mapFn) {
462                    A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
463                } else {
464                    A[k] = kValue;
465                }
466                k += 1;
467            }
468            // 18. Let putStatus be Put(A, "length", len, true).
469            A.length = len;
470            // 20. Return A.
471            return A;
472        };
473    }());
474}
475
476//Array find polyfill
477if (!Array.prototype.find) {
478    Array.prototype.find = function (predicate) {
479        if (this == null) {
480            throw new TypeError('Array.prototype.find called on null or undefined');
481        }
482        if (typeof predicate !== 'function') {
483            throw new TypeError('predicate must be a function');
484        }
485        var list = Object(this);
486        var length = list.length >>> 0;
487        var thisArg = arguments[1];
488        var value;
489
490        for (var i = 0; i < length; i++) {
491            value = list[i];
492            if (predicate.call(thisArg, value, i, list)) {
493                return value;
494            }
495        }
496        return undefined;
497    };
498}
499
500
501
502
503// YouTube video loader
504function loadPosterImage() {
505    // Get all of the videos
506    let videos = document.querySelectorAll('[data-youtube]:not([data-done])');
507
508    // Progressively enhance them
509    for (let video of videos) {
510
511        // Get the video ID
512        let id = new URL(video.href).searchParams.get('v');
513
514        // Add the ID to the data-youtube attribute
515        video.setAttribute('data-youtube', id);
516
517        // Add a role of button
518        video.setAttribute('role', 'button');
519
520        const videoTitle = video.querySelector(".video-title").textContent;
521
522        // Check for img.  If no image then render fallback YouTube thumbnail
523        const hasImg = video.querySelector(".vid-thumbnail");
524
525        if (!hasImg) {
526            video.innerHTML =
527                `<img alt="${videoTitle}" src="https://img.youtube.com/vi/${id}/hqdefault.jpg" loading="lazy" data-ot-ignore>
528<span class="sr-only video-title">
529${videoTitle}
530</span>`;
531        }
532
533        // Mark as done
534        video.setAttribute('data-done', '');
535
536    }
537}
538document.addEventListener('DOMContentLoaded', function () {
539    loadPosterImage();
540
541    /**
542    * Handle click events on the video thumbnails
543    * param  {Event} event The event object
544    */
545    function videoClickHandler(event) {
546
547        // Get the video link
548        let link = event.target.closest('[data-youtube]');
549        if (!link) return;
550
551        // Prevent the URL from redirecting users
552        event.preventDefault();
553
554        // Get the video ID
555        let id = link.getAttribute('data-youtube');
556
557        const videoTitle = link.querySelector(".video-title").textContent;
558
559        // Create the player
560        let player = document.createElement('div');
561        player.innerHTML = `<iframe src="https://www.youtube-nocookie.com/embed/${id}?autoplay=1&rel=0&modestbranding=1" title="${videoTitle}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`;
562
563        // Inject the player into the UI
564        link.replaceWith(player);
565
566    }
567    // Detect clicks on the video thumbnails
568    document.addEventListener('click', videoClickHandler);
569
570})

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.