1/** 2 * Template Name: ZSSF 3 * Updated: July 30 2023 4 * Author: Ramadhanmkoma.me 5 * License: https://zssf.or.tz/license/ 6 */ 7window.document.addEventListener("DOMContentLoaded", function () { 8 (function () { 9 "use strict"; 10 11 /** 12 * Easy selector helper function 13 */ 14 const select = (el, all = false) => { 15 el = el.trim(); 16 if (all) { 17 return [...document.querySelectorAll(el)]; 18 } else { 19 return document.querySelector(el); 20 } 21 }; 22 23 /** 24 * Easy event listener function 25 */ 26 const on = (type, el, listener, all = false) => { 27 let selectEl = select(el, all); 28 if (selectEl) { 29 if (all) { 30 selectEl.forEach((e) => e.addEventListener(type, listener)); 31 } else { 32 selectEl.addEventListener(type, listener); 33 } 34 } 35 }; 36 37 /** 38 * Easy on scroll event listener 39 */ 40 const onscroll = (el, listener) => { 41 el.addEventListener("scroll", listener); 42 }; 43 44 /** 45 * Navbar links active state on scroll 46 */ 47 let navbarlinks = select("#navbar .scrollto", true); 48 const navbarlinksActive = () => { 49 let position = window.scrollY + 200; 50 navbarlinks.forEach((navbarlink) => { 51 if (!navbarlink.hash) return; 52 let section = select(navbarlink.hash); 53 if (!section) return; 54 if ( 55 position >= section.offsetTop && 56 position <= section.offsetTop + section.offsetHeight 57 ) { 58 navbarlink.classList.add("active"); 59 } else { 60 navbarlink.classList.remove("active"); 61 } 62 }); 63 }; 64 window.addEventListener("load", navbarlinksActive); 65 onscroll(document, navbarlinksActive); 66 67 /** 68 * Scrolls to an element with header offset 69 */ 70 const scrollto = (el) => { 71 let header = select("#header"); 72 let offset = header.offsetHeight; 73 74 if (!header.classList.contains("header-scrolled")) { 75 offset -= 16; 76 } 77 78 let elementPos = select(el).offsetTop; 79 window.scrollTo({ 80 top: elementPos - offset, 81 behavior: "smooth", 82 }); 83 }; 84 85 /** 86 * Header fixed top on scroll 87 */ 88 let selectHeader = select("#header"); 89 if (selectHeader) { 90 let headerOffset = selectHeader.offsetTop; 91 let nextElement = selectHeader.nextElementSibling; 92 const headerFixed = () => { 93 if (headerOffset - window.scrollY <= 0) { 94 selectHeader.classList.add("fixed-top"); 95 nextElement.classList.add("scrolled-offset"); 96 } else { 97 selectHeader.classList.remove("fixed-top"); 98 nextElement.classList.remove("scrolled-offset"); 99 } 100 }; 101 window.addEventListener("load", headerFixed); 102 onscroll(document, headerFixed); 103 } 104 105 /** 106 * Back to top button 107 */ 108 let backtotop = select(".back-to-top"); 109 if (backtotop) { 110 const toggleBacktotop = () => { 111 if (window.scrollY > 100) { 112 backtotop.classList.add("active"); 113 } else { 114 backtotop.classList.remove("active"); 115 } 116 }; 117 window.addEventListener("load", toggleBacktotop); 118 onscroll(document, toggleBacktotop); 119 } 120 121 /** 122 * Mobile nav toggle 123 */ 124 on("click", ".mobile-nav-toggle", function (e) { 125 select("#navbar").classList.toggle("navbar-mobile"); 126 this.classList.toggle("bi-list"); 127 this.classList.toggle("bi-x"); 128 }); 129 130 /** 131 * Mobile nav dropdowns activate 132 */ 133 on( 134 "click", 135 ".navbar .dropdown > a", 136 function (e) { 137 if (select("#navbar").classList.contains("navbar-mobile")) { 138 e.preventDefault(); 139 this.nextElementSibling.classList.toggle("dropdown-active"); 140 } 141 }, 142 true 143 ); 144 145 /** 146 * Scrool with ofset on links with a class name .scrollto 147 */ 148 on( 149 "click", 150 ".scrollto", 151 function (e) { 152 if (select(this.hash)) { 153 e.preventDefault(); 154 155 let navbar = select("#navbar"); 156 if (navbar.classList.contains("navbar-mobile")) { 157 navbar.classList.remove("navbar-mobile"); 158 let navbarToggle = select(".mobile-nav-toggle"); 159 navbarToggle.classList.toggle("bi-list"); 160 navbarToggle.classList.toggle("bi-x"); 161 } 162 scrollto(this.hash); 163 } 164 }, 165 true 166 ); 167 168 /** 169 * Scroll with ofset on page load with hash links in the url 170 */ 171 window.addEventListener("load", () => { 172 if (window.location.hash) { 173 if (select(window.location.hash)) { 174 scrollto(window.location.hash); 175 } 176 } 177 }); 178 179 /** 180 * Hero carousel indicators 181 */ 182 let heroCarouselIndicators = select("#hero-carousel-indicators"); 183 let heroCarouselItems = select("#heroCarousel .carousel-item", true); 184
185 heroCarouselItems.forEach((item, index) => { 186 index === 0 187 ? (heroCarouselIndicators.innerHTML += 188 "<li data-bs-target='#heroCarousel' data-bs-slide-to='" + 189 index + 190 "' class='active'></li>") 191 : (heroCarouselIndicators.innerHTML += 192 "<li data-bs-target='#heroCarousel' data-bs-slide-to='" + 193 index + 194 "'></li>"); 195 }); 196 197 /** 198 * Clients Slider 199 */ 200 new Swiper(".clients-slider", { 201 speed: 400, 202 loop: true, 203 autoplay: { 204 delay: 5000, 205 disableOnInteraction: false, 206 }, 207 slidesPerView: "auto", 208 pagination: { 209 el: ".swiper-pagination", 210 type: "bullets", 211 clickable: true, 212 }, 213 breakpoints: { 214 320: { 215 slidesPerView: 2, 216 spaceBetween: 40, 217 }, 218 480: { 219 slidesPerView: 3, 220 spaceBetween: 60, 221 }, 222 640: { 223 slidesPerView: 4, 224 spaceBetween: 80, 225 }, 226 992: { 227 slidesPerView: 6, 228 spaceBetween: 120, 229 }, 230 }, 231 }); 232 233 /** 234 * Porfolio isotope and filter 235 */ 236 window.addEventListener("load", () => { 237 let portfolioContainer = select(".portfolio-container"); 238 if (portfolioContainer) { 239 let portfolioIsotope = new Isotope(portfolioContainer, { 240 itemSelector: ".portfolio-item", 241 }); 242 243 let portfolioFilters = select("#portfolio-flters li", true); 244 245 on( 246 "click", 247 "#portfolio-flters li", 248 function (e) { 249 e.preventDefault(); 250 portfolioFilters.forEach(function (el) { 251 el.classList.remove("filter-active"); 252 }); 253 this.classList.add("filter-active"); 254 255 portfolioIsotope.arrange({ 256 filter: this.getAttribute("data-filter"), 257 }); 258 }, 259 true 260 ); 261 } 262 }); 263 264 /** 265 * Initiate portfolio lightbox 266 */ 267 // const portfolioLightbox = GLightbox({ 268 // selector: ".portfolio-lightbox", 269 // }); 270 271 /** 272 * Portfolio details slider 273 */ 274 new Swiper(".portfolio-details-slider", { 275 speed: 400, 276 loop: true, 277 autoplay: { 278 delay: 5000, 279 disableOnInteraction: false, 280 }, 281 pagination: { 282 el: ".swiper-pagination", 283 type: "bullets", 284 clickable: true, 285 }, 286 }); 287 })(); 288});
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.