PageSourceSearch

https://pvl.ph/assets/js/custom.js

js pvl.ph collected 2026-10-02 13:31:38 UTC 18,543 bytes, 679 lines download raw bytes

1(() => {
2  // Debounce
3  const debounce = (callback, wait) => {
4    let timeoutId = null;
5
6    return (...args) => {
7      window.clearTimeout(timeoutId);
8
9      timeoutId = window.setTimeout(() => {
10        callback.apply(null, args);
11      }, wait);
12    };
13  };
14
15  // Class function
16  const elementClass = {
17    add: (el, className) => {
18      let element = document.querySelector(el);
19
20      element.classList.add(className);
21    },
22
23    remove: (el, className) => {
24      let element = document.querySelector(el);
25
26      element.classList.remove(className);
27    },
28
29    toggle: (el, className) => {
30      let element = document.querySelector(el);
31
32      element.classList.toggle(className);
33    },
34  };
35
36  // Navbar toggle button
37  const navbarCollapse = document.querySelector("#navbarText");
38
39  const navbarToggle = () => {
40    elementClass.toggle(".pvl-header", "is-collapsed");
41    elementClass.toggle(".bi.bi-list", "text-white");
42  };
43
44  navbarCollapse.addEventListener("hide.bs.collapse", () => {
45    navbarToggle();
46  });
47
48  navbarCollapse.addEventListener("show.bs.collapse", () => {
49    navbarToggle();
50  });
51
52  // Latest news, Match schedule, Match recap slider
53  var swiper = new Swiper(".swiper.cards-slider", {
54    slidesPerView: "auto",
55    lazy: true,
56    spaceBetween: 25,
57    grabCursor: true,
58    breakpoints: {
59      576: {
60        slidesPerView: "auto",
61      },
62      768: {
63        slidesPerView: 2,
64      },
65      1200: {
66        slidesPerView: 3,
67      },
68    },
69    navigation: {
70      nextEl: ".swiper.cards-slider .swiper-button-next",
71      prevEl: ".swiper.cards-slider .swiper-button-prev",
72    },
73    on: {
74      init: () => {
75        let swiperSlider = document.querySelectorAll(".swiper");
76
77        if (swiperSlider) {
78          swiperSlider.forEach((item) => {
79            item.style.visibility = "visible";
80          });
81        }
82      },
83    },
84  });
85
86  // Featured videos slider
87  var swiper = new Swiper(".swiper.featured-videos", {
88    slidesPerView: "auto",
89    lazy: true,
90    spaceBetween: 25,
91    grabCursor: true,
92    breakpoints: {
93      576: {
94        slidesPerView: "auto",
95      },
96      768: {
97        slidesPerView: 2,
98      },
99    },
100    navigation: {
101      nextEl: ".swiper.featured-videos .swiper-button-next",
102      prevEl: ".swiper.featured-videos .swiper-button-prev",
103    },
104  });
105
106  // Player of the game slider
107  var swiper = new Swiper(".swiper.player-of-the-game", {
108    slidesPerView: 1,
109    effect: "fade",
110    fadeEffect: {
111      crossFade: true,
112    },
113    navigation: {
114      nextEl: ".swiper.player-of-the-game .swiper-button-next",
115      prevEl: ".swiper.player-of-the-game .swiper-button-prev",
116    },
117  });
118
119  // Brackets scroll horizontal
120  var swiper = new Swiper(".swiper.brackets", {
121    slidesPerView: "auto",
122    freeMode: true,
123    grabCursor: true,
124  });
125
126  // Player stats card slider
127  var swiper = new Swiper(".swiper.player-stats-cards", {
128    slidesPerView: "auto",
129    lazy: true,
130    spaceBetween: 30,
131    grabCursor: true,
132    navigation: {
133      nextEl: ".swiper.player-stats-cards .swiper-button-next",
134      prevEl: ".swiper.player-stats-cards .swiper-button-prev",
135    },
136  });
137
138  // All teams slider
139  var swiper = new Swiper(".swiper.all-teams", {
140    loop: true,
141    lazy: true,
142    spaceBetween: 5,
143    grabCursor: true,
144    centeredSlides: true,
145    autoplay: {
146      delay: 3000,
147    },
148    breakpoints: {
149      576: {
150        slidesPerView: 1,
151      },
152      768: {
153        slidesPerView: 3,
154      },
155      992: {
156        slidesPerView: 4,
157      },
158      1200: {
159        slidesPerView: 5,
160      },
161    },
162    //added
163    navigation: {
164          nextEl: ".swiper.all-teams .swiper-button-next",
165          prevEl: ".swiper.all-teams .swiper-button-prev",
166        },
167        on: {
168          init: () => {
169            let swiperSlider = document.querySelectorAll(".swiper");
170
171            if (swiperSlider) {
172              swiperSlider.forEach((item) => {
173                item.style.visibility = "visible";
174              });
175            }
176          },
177        },
178    //added    
179  });
180
181  // videosPage search filter if the videos are loaded all in the page
182 /*const videoFilter = {
183  match: () => {
184    let qsRegex;
185
186    let quicksearch = document.querySelector(".iso-filter-quicksearch");
187    if (quicksearch) {
188      quicksearch.addEventListener(
189        "input",
190        debounce(() => {
191          qsRegex = new RegExp(quicksearch.value.trim(), "gi");
192
193          let videoContainers = document.querySelectorAll(".videos-posts-container .col");
194          let noResultsMessage = document.getElementById("noResultsMessage");
195          let hasResults = false;
196
197          videoContainers.forEach((container) => {
198            let videoTitleElement = container.querySelector(".video-title");
199
200            if (videoTitleElement) {
201              let videoTitle = videoTitleElement.textContent;
202              let isMatch = qsRegex ? videoTitle.match(qsRegex) : true;
203
204              container.style.display = isMatch ? "block" : "none";
205
206              if (isMatch) {
207                hasResults = true;
208              }
209            }
210          });  
211          
212          if (noResultsMessage) {
213            noResultsMessage.style.display = hasResults ? "none" : "block";
214          }
215
216        }, 300)
217      );
218    }
219  },
220};
221
222videoFilter.match();*/
223
224  
225  // Isotope filtering
226  const isotopeFilter = {
227    filterButton: () => {
228      return ".iso-filter-button";
229    },
230    label: () => {
231      document.body.addEventListener("click", function (event) {
232        if (event.target.matches(isotopeFilter.filterButton())) {
233          let label = event.target.getAttribute("data-label"),
234            filterLabel = document.querySelector(".iso-filter-label");
235
236          if(filterLabel){
237            filterLabel.textContent = label;
238          }
239        } else {
240          return;
241        }
242      });
243    },
244    arrange: () => {
245      document.body.addEventListener("click", function (event) {
246        if (event.target.matches(isotopeFilter.filterButton())) {
247          let filterValue = event.target.getAttribute("data-filter");
248          
249          if(iso){
250            iso.arrange({ filter: filterValue });            
251          }
252        } else {
253          return;
254        }
255      });
256    },
257    match: () => {
258      // quick search regex
259      let qsRegex;
260      let filterSelector = "*";
261
262      // use value of search field to filter
263      let quicksearch = document.querySelector(".iso-filter-quicksearch");
264      if (quicksearch) {
265        quicksearch.addEventListener(
266          "keyup",
267          debounce(() => {
268            qsRegex = new RegExp(quicksearch.value, "gi");
269
270            let filterLabel = document.querySelector(".iso-filter-label");
271
272            if (quicksearch.value == "") {
273              filterLabel.textContent = quicksearch.getAttribute("data-default-label");
274            } else {
275              filterLabel.textContent = `Search for "${quicksearch.value}"`;
276            }
277            iso.arrange({
278              filter: (itemElem) => {
279                var search = qsRegex ? itemElem.textContent.match(qsRegex) : true;
280                var filterRes = filterSelector != "*" ? itemElem.dataset.cat.includes(filterSelector) : true;
281
282                return search && filterRes;
283              },
284            });
285          }, 100)
286        );
287      }
288    },
289  };
290 
291
292  // Teams page isotope filtering
293  const playerPool = document.querySelector(".player-pool-grid");
294
295  if (playerPool) {
296    var iso = new Isotope(playerPool, {
297      itemSelector: ".player-grid-item",
298      layoutMode: "fitRows",
299    });
300  }
301
302  const scheduleGrid = document.querySelector(".schedule-grid");
303
304  if (scheduleGrid) {
305    var iso = new Isotope(scheduleGrid, {
306      itemSelector: ".schedule-grid-item",
307      layoutMode: "fitRows",
308    });
309  }
310  
311
312  // Teams logo slider
313  var swiper = new Swiper(".swiper.teams-logo", {
314    initialSlide: 6,
315    lazy: true,
316    grabCursor: true,
317    slidesPerView: 2,
318    spaceBetween: 5, /*Modified from 30 */
319    centeredSlides: true,
320    slideToClickedSlide: true,
321    breakpoints: {
322      576: {
323        slidesPerView: 4,
324      },
325      768: {
326        slidesPerView: 4,
327      },
328      992: {
329        slidesPerView: 6,
330      },
331      1200: {
332        slidesPerView: 13, /*Modified from 10 */
333      },
334    },
335    on: {
336      init: isotopeFilter.match(),
337      init: () => {
338        let quicksearch = document.querySelector(".iso-filter-quicksearch");
339        if (quicksearch) {
340          quicksearch.addEventListener("keyup", () => {
341            let teamsLogo = document.querySelectorAll(".teams-logo img");
342
343            teamsLogo.forEach((item) => {
344              item.classList.remove("opacity-100");
345            });
346          });
347        }
348      },
349      init: () => {
350        let swiperSlider = document.querySelectorAll(".swiper");
351
352        if (swiperSlider) {
353          swiperSlider.forEach((item) => {
354            item.style.visibility = "visible";
355          });
356        }
357      },
358      click: isotopeFilter.arrange(),
359      click: isotopeFilter.label(),
360      click: (active = () => {
361        // Active button
362        document.body.addEventListener("click", function (event) {
363          if (event.target.matches(isotopeFilter.filterButton())) {
364            let teamsLogo = document.querySelectorAll(".teams-logo img"),
365              quicksearch = document.querySelector(".iso-filter-quicksearch");
366
367            teamsLogo.forEach((logo) => {
368              logo.classList.remove("opacity-100");
369            });
370
371            event.target.classList.add("opacity-100");
372
373            if (quicksearch) {
374              quicksearch.value = "";
375            }
376          } else {
377            return;
378          }
379        });
380      }),
381    },
382  });
383
384  
385  // Infinite scroll news
386 const newsPosts = document.querySelector(".news-posts-container");
387
388  if (newsPosts) {
389    let currentRecord = newsPosts.children.length;
390
391    let infScroll = new InfiniteScroll(newsPosts, {
392      path: function () {
393        return `https://pvl.ph/news/loadmore`;
394      },
395      responseBody: "json",
396      status: ".page-load-status",
397      scrollThreshold: 0,
398      history: false,
399      fetchOptions: {
400        method: "POST",
401        headers: {
402          "Content-Type": "application/x-www-form-urlencoded",
403        },
404        body: "group_no=51",
405      },
406    });
407
408    
409    let proxyElem = document.createElement("div");
410
411    infScroll.on("load", function (body) {
412      let records = body,
413        recordPaginate = currentRecord * infScroll.loadCount,
414        recordPaginateRange = recordPaginate + currentRecord,
415        recordSlice = records.slice(recordPaginate, recordPaginateRange);
416
417      if (recordSlice.length !== 0) {
418        let records = recordSlice;
419
420        var itemsHTML = records.map(getNewsPost).join("");
421
422        proxyElem.innerHTML = itemsHTML;
423
424        newsPosts.append(...proxyElem.children);
425      } else {
426        infScroll.destroy();
427      }
428    });
429
430    function getNewsPost({ title, date_posted, image_local, slug }) {
431      return `
432      <div class="col">
433        <div class="card text-white overflow-hidden">
434          <a href="/news/${slug}" class="text-white">
435            <div class="ratio ratio-4x3">
436              <img src="${image_local}" class="card-img object-fit-cover img-fluid" alt="${title}" width="493" height="207" />
437            </div>
438            <div class="card-img-overlay bg-gradient row align-items-end">
439              <div class="col">
440                <h2 class="card-title fs-8 fw-bold mb-0">${title}</h2>
441              </div>
442              <div class="col-auto">
443                <span class="card-text fs-5">${date_posted}</span>
444              </div>
445            </div>
446          </a>
447        </div>
448      </div>
449    `;
450    }
451  }  
452
453  
454const videosPosts = document.querySelector("#results");
455const searchInput = document.querySelector(".iso-filter-quicksearch");
456
457let proxyElem = document.createElement("div");
458
459let currentSearchKeyword = "";
460let infScroll;
461
462function getVideoPost({ title, date_posted, thumbnail, slug }) {
463  return `
464    <div class="col">
465      <div class="text-secondary fs-8 lh-sm">
466        <a href="/videos/${slug}" class="text-decoration-none text-secondary">
467          <div class="video-thumbnail mb-3 ratio ratio-16x9 position-relative">
468            <img src="${thumbnail}" class="object-fit-cover img-fluid" alt="${title}" width="480" height="360" />
469            <i class="bi bi-play-circle text-white position-absolute start-50 top-50 translate-middle d-flex justify-content-center align-items-center"></i>
470          </div>
471          <div class="d-flex justify-content-between">
472            <div class="video-description-wrapper">
473              <div class="video-title">
474                <h2 class="fs-8 fw-bold">${title}</h2>
475              </div>
476              <div class="video-description-wrapper text-nowrap fs-4">
477                <div class="video-date">
478                  <span>${date_posted}</span>
479                </div>
480              </div>
481            </div>
482          </div>
483        </a>
484      </div>
485    </div>
486  `;
487}
488
489function initInfiniteScroll(searchKeyword = "") {
490  if (infScroll) {
491    infScroll.destroy();
492  }
493
494  infScroll = new InfiniteScroll(videosPosts, {
495    path() {
496      return searchKeyword
497        ? `https://pvl.ph/searchvideos`
498        : `https://pvl.ph/loadmorevideos`;
499    },
500    responseBody: "json",
501    status: ".page-load-status",
502    scrollThreshold: 0,
503    history: false,
504
505    fetchOptions() {
506      let group_no = infScroll.loadCount + 1;
507      console.log("Fetching group:", group_no, " | Search:", searchKeyword);
508
509      return {
510        method: "POST",
511        headers: {
512          "Content-Type": "application/x-www-form-urlencoded",
513        },
514        body: searchKeyword
515          ? `search=${encodeURIComponent(searchKeyword)}&group_no=${group_no}`
516          : `group_no=${group_no}`,
517      };
518    },
519  });
520
521  infScroll.on("load", function (body) {
522    if (body.length !== 0) {
523      let itemsHTML = body.map(getVideoPost).join("");
524      proxyElem.innerHTML = itemsHTML;
525      videosPosts.append(...proxyElem.children);
526    } else {
527      console.log("No more videos.");
528      infScroll.destroy();
529    }
530  });
531}
532
533// Initial scroll setup (default list)
534if (videosPosts) {
535  initInfiniteScroll();
536}
537
538// Search functionality with debounce
539if (searchInput) {
540  let typingTimer;
541  const debounceTime = 500;
542
543  searchInput.addEventListener("keyup", function () {
544    clearTimeout(typingTimer);
545    typingTimer = setTimeout(() => {
546      currentSearchKeyword = this.value.trim();
547      console.log("Searching for:", currentSearchKeyword);
548
549      videosPosts.innerHTML = "";
550
551      fetch("https://pvl.ph/searchvideos", {
552        method: "POST",
553        headers: { "Content-Type": "application/x-www-form-urlencoded" },
554        body: `search=${encodeURIComponent(currentSearchKeyword)}&group_no=0`,
555      })
556        .then((res) => res.json())
557        .then((data) => {
558          if (data.length) {
559            videosPosts.innerHTML = data.map(getVideoPost).join("");
560          } else {
561            videosPosts.innerHTML = "<p>No results found</p>";
562          }
563
564          // Reinitialize Infinite Scroll for new search
565          initInfiniteScroll(currentSearchKeyword);
566        });
567    }, debounceTime);
568  });
569}
570
571
572
573  // Infinite scroll gallery
574  const galleryPosts = document.querySelector(".additional-gallery-container");
575
576  if (galleryPosts) {
577    //let currentRecord = galleryPosts.children.length;
578    //console.log(currentRecord);
579    let groupNo = 0;
580
581    let infScroll = new InfiniteScroll(galleryPosts, {
582      path: function () {
583        return "https://pvl.ph/loadmorephotos";
584      },
585      responseBody: "json",
586      status: ".page-load-status",
587      scrollThreshold: 0,
588      history: false,
589      fetchOptions: () => {
590        body = `group_no=${groupNo}`;
591        groupNo++; 
592        return {
593          method: "POST",
594          headers: {
595            "Content-Type": "application/x-www-form-urlencoded",
596          },
597          body: body,
598        };
599      },
600    });
601    // use element to turn HTML string into elements
602    let proxyElem = document.createElement("div");
603
604    infScroll.on("load", function (body) {
605      //console.log("Loaded batch:", body);
606    
607      if (body.length !== 0) {
608        let itemsHTML = body.map(getGalleryPosts).join("");
609    
610        proxyElem.innerHTML = itemsHTML;
611    
612        galleryPosts.append(...proxyElem.children);
613      } else {
614        //console.log("No more records.");
615        infScroll.destroy();
616      }
617    });
618    
619    function getGalleryPosts({ title, thumbnail, date_created, slug, image }) {
620      const imgSrc = (thumbnail === "" || thumbnail === "undefined") ? image : thumbnail;
621      return `
622      <div class="col">
623        <div class="card text-white overflow-hidden">
624          <a href="/gallery/${slug}" class="text-white">
625            <div class="ratio ratio-4x3">
626              <img src="${imgSrc}" class="card-img object-fit-cover img-fluid" alt="${title.replace(/_/g, " ")}" width="493" height="207"/>
627            </div>
628            <div class="card-img-overlay bg-gradient row row-cols-1 align-items-end">
629              <div class="col">
630                <h2 class="card-title fs-6 fw-bold mb-0">${title.replace(/_/g, " ")}</h2>
631                <span class="card-text fs-4 fw-bold">${date_created}</span>
632              </div>
633            </div>
634          </a>
635        </div>
636      </div>
637    `;
638    }
639  }
640
641  // Isotope Masonry
642  const announcementPosts = document.querySelector(".announcement-grid");
643
644  if (announcementPosts) {
645    var iso = new Isotope(announcementPosts, {
646      itemSelector: ".grid-item",
647      getSortData: {
648        number: "[data-timestamp]",
649      },
650      sortBy: "number",
651      sortAscending: announcementPosts.getAttribute("data-ascending") == "true" ? true : false,
652    });
653  }
654
655
656    /****** Window scroll ******/
657    $( window ).on( 'scroll', initScrollNavigate );
658
659    /****** Window scroll function ******/
660    function initScrollNavigate() {
661
662        var scrollPos = $( window ).scrollTop();
663
664        /****** Scroll to top ******/
665        if ( scrollPos > 150 ) {
666            $( '.scroll-top-arrow' ).fadeIn( '300' );
667        } else {
668            $( '.scroll-top-arrow' ).fadeOut( '300' );
669        }
670    }
671    /****** Scroll to top *****/
672    $( document ).on( 'click', '.scroll-top-arrow', function () {
673        $( 'html, body' ).animate({ scrollTop: 0 }, 800 );
674        return false;
675    });
676
677
678    
679})();

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.