PageSourceSearch

https://md-abu-bakker-siddik.github.io/assets/js/main.js

js md-abu-bakker-siddik.github.io collected 2026-10-03 09:35:29 UTC 12,495 bytes, 405 lines download raw bytes

1/**
2 * Portfolio — vanilla JS (no dependencies)
3 */
4(function () {
5  "use strict";
6
7  var header = document.getElementById("site-header");
8  var navToggle = document.getElementById("nav-toggle");
9  var mobileNav = document.getElementById("mobile-nav");
10  var mobileBackdrop = document.getElementById("mobile-nav-backdrop");
11  var scrollTopBtn = document.getElementById("scroll-top");
12  var scrollPercentWrap = document.getElementById("scroll-percent");
13  var scrollPercentRing = document.getElementById("scroll-percent-ring");
14  var scrollPercentValue = document.getElementById("scroll-percent-value");
15  var SCROLL_RING_CIRC = 132; // matches stroke-dasharray on .scroll-percent__ring
16  var contactForm = document.getElementById("contact-form");
17  var projectsGrid = document.getElementById("projects-grid");
18  var themesLoadMoreWrap = document.getElementById("themes-load-more-wrap");
19  var themesLoadMoreBtn = document.getElementById("themes-load-more-btn");
20  var whatsappPanel = document.getElementById("whatsapp-panel");
21  var whatsappToggle = document.getElementById("whatsapp-toggle");
22  var whatsappClose = document.getElementById("whatsapp-close");
23  var whatsappForm = document.getElementById("whatsapp-form");
24  var whatsappInput = document.getElementById("whatsapp-input");
25  var whatsappMessages = document.getElementById("whatsapp-messages");
26  var whatsappQuickBtns = document.querySelectorAll(".whatsapp-widget__quick-btn");
27  var WHATSAPP_NUMBER = "8801911500125";
28
29  var navLinks = document.querySelectorAll(
30    ".site-nav__link, .mobile-nav__link"
31  );
32  var sections = document.querySelectorAll("section[id]");
33  var filterBtns = document.querySelectorAll(".filter-tabs__btn");
34
35  var currentFilter = "all";
36  var projectsVisibleLimit = 6;
37  var projectsPerPage = 6;
38  var themeFallbackImage =
39    "assets/images/banner/hero-developer-workstation.jpeg";
40
41  var sectionIds = Array.prototype.map.call(sections, function (s) {
42    return s.id;
43  });
44
45  function getProjectCards() {
46    return document.querySelectorAll(".project-card");
47  }
48
49  function cardMatchesCategory(card, filter) {
50    var cats = (card.getAttribute("data-category") || "").split(" ");
51    var isTheme = cats.indexOf("themes") !== -1;
52
53    if (filter === "all") {
54      return true;
55    }
56    if (filter === "themes" || filter === "wordpress") {
57      return isTheme;
58    }
59    if (filter === "plugins") {
60      return cats.indexOf("plugins") !== -1;
61    }
62    return cats.indexOf(filter) !== -1;
63  }
64
65  function countMatchingCards(filter) {
66    var count = 0;
67    getProjectCards().forEach(function (card) {
68      if (cardMatchesCategory(card, filter)) {
69        count++;
70      }
71    });
72    return count;
73  }
74
75  function updateLoadMoreButton() {
76    if (!themesLoadMoreWrap) return;
77
78    var total = countMatchingCards(currentFilter);
79    themesLoadMoreWrap.hidden = projectsVisibleLimit >= total;
80  }
81
82  function applyFilter(filter) {
83    currentFilter = filter;
84    var visibleCount = 0;
85
86    getProjectCards().forEach(function (card) {
87      if (!cardMatchesCategory(card, filter)) {
88        card.classList.add("is-hidden");
89        return;
90      }
91
92      if (visibleCount < projectsVisibleLimit) {
93        card.classList.remove("is-hidden");
94        visibleCount++;
95      } else {
96        card.classList.add("is-hidden");
97      }
98    });
99
100    updateLoadMoreButton();
101  }
102
103  function createEl(tag, className, text) {
104    var el = document.createElement(tag);
105    if (className) el.className = className;
106    if (text) el.textContent = text;
107    return el;
108  }
109
110  function renderThemeCards() {
111    if (!projectsGrid || !window.THEME_DEMOS) return;
112
113    window.THEME_DEMOS.forEach(function (theme, index) {
114      var article = createEl("article", "project-card theme-card");
115      article.setAttribute("data-category", "themes wordpress");
116      article.setAttribute("data-theme-index", String(index));
117
118      var thumb = createEl("div", "project-card__thumb");
119      var img = document.createElement("img");
120      img.src = theme.image;
121      img.alt = theme.name + " WordPress theme demo";
122      img.loading = "lazy";
123      img.width = 640;
124      img.height = 400;
125
126      img.addEventListener("error", function onImgError() {
127        img.removeEventListener("error", onImgError);
128        if (img.src.indexOf(themeFallbackImage) === -1) {
129          img.src = themeFallbackImage;
130          return;
131        }
132        thumb.classList.add("project-card__thumb--fallback");
133        thumb.setAttribute("data-label", theme.name);
134        img.style.display = "none";
135      });
136
137      thumb.appendChild(img);
138
139      var body = createEl("div", "project-card__body");
140      body.appendChild(
141        createEl("span", "project-card__category", "ThemeForest · Live demo")
142      );
143      body.appendChild(createEl("h3", "project-card__title", theme.name));
144      body.appendChild(
145        createEl(
146          "p",
147          "project-card__desc",
148          "Premium WordPress theme—responsive layouts, niche demo, and production-ready frontend craft."
149        )
150      );
151
152      var tags = createEl("ul", "project-card__tags");
153      ["WordPress", "Theme"].forEach(function (label) {
154        var li = document.createElement("li");
155        li.appendChild(createEl("span", "tag", label));
156        tags.appendChild(li);
157      });
158      body.appendChild(tags);
159
160      var actions = createEl("div", "project-card__actions");
161      var link = createEl("a", "project-card__link project-card__link--primary", "Live site");
162      link.href = theme.url;
163      link.target = "_blank";
164      link.rel = "noopener noreferrer";
165      actions.appendChild(link);
166      body.appendChild(actions);
167
168      article.appendChild(thumb);
169      article.appendChild(body);
170      projectsGrid.appendChild(article);
171    });
172  }
173
174  function onScroll() {
175    var y = window.scrollY || document.documentElement.scrollTop;
176
177    if (header) {
178      header.classList.toggle("is-scrolled", y > 24);
179    }
180
181    if (scrollTopBtn) {
182      scrollTopBtn.classList.toggle("is-visible", y > 400);
183    }
184
185    if (scrollPercentWrap && scrollPercentRing && scrollPercentValue) {
186      var maxScroll = document.documentElement.scrollHeight - window.innerHeight;
187      var pct = maxScroll > 0 ? Math.min(100, Math.max(0, Math.round((y / maxScroll) * 100))) : 0;
188      scrollPercentValue.textContent = pct + "%";
189      scrollPercentRing.style.strokeDashoffset =
190        SCROLL_RING_CIRC - (SCROLL_RING_CIRC * pct) / 100;
191    }
192
193    updateActiveNav();
194  }
195
196  function updateActiveNav() {
197    var scrollPos = (window.scrollY || document.documentElement.scrollTop) + 120;
198    var current = "home";
199
200    sectionIds.forEach(function (id) {
201      var el = document.getElementById(id);
202      if (el && el.offsetTop <= scrollPos) {
203        current = id;
204      }
205    });
206
207    navLinks.forEach(function (link) {
208      var href = link.getAttribute("href");
209      var isActive = href === "#" + current;
210      link.classList.toggle("is-active", isActive);
211      if (link.closest(".mobile-nav")) {
212        link.setAttribute("aria-current", isActive ? "page" : null);
213      }
214    });
215  }
216
217  function openMobileNav() {
218    mobileNav.classList.add("is-open");
219    mobileNav.setAttribute("aria-hidden", "false");
220    navToggle.setAttribute("aria-expanded", "true");
221    document.body.style.overflow = "hidden";
222  }
223
224  function closeMobileNav() {
225    mobileNav.classList.remove("is-open");
226    mobileNav.setAttribute("aria-hidden", "true");
227    navToggle.setAttribute("aria-expanded", "false");
228    document.body.style.overflow = "";
229  }
230
231  if (navToggle) {
232    navToggle.addEventListener("click", function () {
233      if (mobileNav.classList.contains("is-open")) {
234        closeMobileNav();
235      } else {
236        openMobileNav();
237      }
238    });
239  }
240
241  if (mobileBackdrop) {
242    mobileBackdrop.addEventListener("click", closeMobileNav);
243  }
244
245  navLinks.forEach(function (link) {
246    link.addEventListener("click", closeMobileNav);
247  });
248
249  document.addEventListener("keydown", function (e) {
250    if (e.key === "Escape") {
251      closeMobileNav();
252      closeWhatsappPanel();
253    }
254  });
255
256  function openWhatsappPanel() {
257    if (!whatsappPanel || !whatsappToggle) return;
258    whatsappPanel.classList.add("is-open");
259    whatsappPanel.setAttribute("aria-hidden", "false");
260    whatsappToggle.setAttribute("aria-expanded", "true");
261    if (whatsappInput) {
262      window.setTimeout(function () {
263        whatsappInput.focus();
264      }, 150);
265    }
266  }
267
268  function closeWhatsappPanel() {
269    if (!whatsappPanel || !whatsappToggle) return;
270    whatsappPanel.classList.remove("is-open");
271    whatsappPanel.setAttribute("aria-hidden", "true");
272    whatsappToggle.setAttribute("aria-expanded", "false");
273  }
274
275  function appendWhatsappBubble(text, type) {
276    if (!whatsappMessages) return;
277
278    var bubble = document.createElement("p");
279    bubble.className =
280      "whatsapp-widget__bubble whatsapp-widget__bubble--" + (type || "user");
281    bubble.textContent = text;
282    whatsappMessages.appendChild(bubble);
283    whatsappMessages.scrollTop = whatsappMessages.scrollHeight;
284  }
285
286  function sendWhatsappMessage(message) {
287    var text = (message || (whatsappInput ? whatsappInput.value : "")).trim();
288    if (!text) return;
289
290    appendWhatsappBubble(text, "user");
291    if (whatsappInput) {
292      whatsappInput.value = "";
293    }
294
295    var url =
296      "https://wa.me/" +
297      WHATSAPP_NUMBER +
298      "?text=" +
299      encodeURIComponent(text);
300    window.open(url, "_blank", "noopener,noreferrer");
301  }
302
303  if (whatsappToggle) {
304    whatsappToggle.addEventListener("click", function () {
305      if (whatsappPanel && whatsappPanel.classList.contains("is-open")) {
306        closeWhatsappPanel();
307      } else {
308        openWhatsappPanel();
309      }
310    });
311  }
312
313  if (whatsappClose) {
314    whatsappClose.addEventListener("click", closeWhatsappPanel);
315  }
316
317  if (whatsappForm) {
318    whatsappForm.addEventListener("submit", function (e) {
319      e.preventDefault();
320      sendWhatsappMessage();
321    });
322  }
323
324  whatsappQuickBtns.forEach(function (btn) {
325    btn.addEventListener("click", function () {
326      sendWhatsappMessage(btn.getAttribute("data-message") || "");
327    });
328  });
329
330  document.querySelectorAll('a[href^="#"]').forEach(function (anchor) {
331    anchor.addEventListener("click", function (e) {
332      var targetId = this.getAttribute("href");
333      if (!targetId || targetId === "#") return;
334
335      var target = document.querySelector(targetId);
336      if (!target) return;
337
338      e.preventDefault();
339      var offset = header ? header.offsetHeight : 0;
340      var top =
341        target.getBoundingClientRect().top + window.pageYOffset - offset;
342
343      window.scrollTo({ top: top, behavior: "smooth" });
344      history.pushState(null, "", targetId);
345    });
346  });
347
348  if (scrollTopBtn) {
349    scrollTopBtn.addEventListener("click", function () {
350      window.scrollTo({ top: 0, behavior: "smooth" });
351    });
352  }
353
354  filterBtns.forEach(function (btn) {
355    btn.addEventListener("click", function () {
356      var filter = btn.getAttribute("data-filter");
357
358      filterBtns.forEach(function (b) {
359        var active = b === btn;
360        b.classList.toggle("is-active", active);
361        b.setAttribute("aria-selected", active ? "true" : "false");
362      });
363
364      projectsVisibleLimit = 6;
365      applyFilter(filter);
366    });
367  });
368
369  if (themesLoadMoreBtn) {
370    themesLoadMoreBtn.addEventListener("click", function () {
371      projectsVisibleLimit += projectsPerPage;
372      applyFilter(currentFilter);
373    });
374  }
375
376  if (contactForm) {
377    contactForm.addEventListener("submit", function (e) {
378      e.preventDefault();
379      if (!contactForm.checkValidity()) {
380        contactForm.reportValidity();
381        return;
382      }
383
384      var nameEl = document.getElementById("name");
385      var emailEl = document.getElementById("email");
386      var messageEl = document.getElementById("message");
387      var name = nameEl ? nameEl.value.trim() : "";
388      var email = emailEl ? emailEl.value.trim() : "";
389      var message = messageEl ? messageEl.value.trim() : "";
390      var subject = encodeURIComponent("Portfolio inquiry from " + name);
391      var body = encodeURIComponent(
392        "Name: " + name + "\nEmail: " + email + "\n\n" + message
393      );
394
395      window.location.href =
396        "mailto:[email protected]?subject=" + subject + "&body=" + body;
397    });
398  }
399
400  renderThemeCards();
401  applyFilter("all");
402
403  window.addEventListener("scroll", onScroll, { passive: true });
404  onScroll();
405})();

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.