PageSourceSearch

https://ressto.co/blog/wp-content/themes/ressto-blog-theme/js/script.js?ver=3.0.0

js ressto.co collected 2026-10-08 22:59:37 UTC 8,473 bytes, 204 lines download raw bytes

1/**
2 * Deonde Blog Theme v2.0 — JavaScript
3 * Mobile menu, search dropdown, AJAX load more, back to top
4 */
5
6
7(function () {
8  'use strict';
9
10  document.addEventListener('DOMContentLoaded', function () {
11
12    // ══════════════════════════════════════════
13    // 1. MOBILE DRAWER
14    // ══════════════════════════════════════════
15    var mobileBtn     = document.getElementById('mobileToggle');   // was 'navMobileBtn'
16    var mobileDrawer  = document.getElementById('mobileNav');      // was 'mobileDrawer'
17    var mobileOverlay = document.getElementById('mobileOverlay');
18
19    function openMobileNav() {
20      mobileDrawer.classList.add('is-open');        // was 'open'
21      if (mobileOverlay) mobileOverlay.classList.add('is-open');
22      mobileBtn.setAttribute('aria-expanded', 'true');
23      document.body.style.overflow = 'hidden';
24    }
25    function closeMobileNav() {
26      mobileDrawer.classList.remove('is-open');     // was 'open'
27      if (mobileOverlay) mobileOverlay.classList.remove('is-open');
28      mobileBtn.setAttribute('aria-expanded', 'false');
29      document.body.style.overflow = '';
30    }
31
32    if (mobileBtn && mobileDrawer) {
33      mobileBtn.addEventListener('click', openMobileNav);
34
35      // Close button inside drawer
36      var mobileClose = document.getElementById('mobileClose');
37      if (mobileClose) mobileClose.addEventListener('click', closeMobileNav);
38
39      // Close when clicking overlay
40      if (mobileOverlay) mobileOverlay.addEventListener('click', closeMobileNav);
41
42      // Close on ESC
43      document.addEventListener('keydown', function (e) {
44        if (e.key === 'Escape' && mobileDrawer.classList.contains('is-open')) {
45          closeMobileNav();
46        }
47      });
48    }
49
50    document.querySelectorAll('.mobile-group__toggle').forEach(function (btn) {
51    btn.addEventListener('click', function () {
52      var group  = btn.closest('.mobile-group');
53      var isOpen = group.classList.toggle('is-open');
54      btn.setAttribute('aria-expanded', isOpen);
55    });
56  });
57
58    // ══════════════════════════════════════════
59    // 2. SEARCH DROPDOWN
60    // ══════════════════════════════════════════
61    var searchInput = document.getElementById('heroSearchInput');
62    var searchDrop  = document.getElementById('heroSearchDrop');
63    var searchWrap  = document.getElementById('searchWrap');
64
65    if (searchInput && searchDrop) {
66      searchInput.addEventListener('focus', function () {
67        searchDrop.classList.add('open');
68      });
69      searchInput.addEventListener('input', function () {
70        if (this.value.trim()) searchDrop.classList.add('open');
71      });
72
73      document.addEventListener('click', function (e) {
74        if (searchWrap && !searchWrap.contains(e.target)) {
75          searchDrop.classList.remove('open');
76        }
77      });
78
79      document.addEventListener('keydown', function (e) {
80        if (e.key === 'Escape') {
81          searchDrop.classList.remove('open');
82          searchInput.blur();
83        }
84      });
85    }
86
87    // ══════════════════════════════════════════
88    // 3. AJAX LOAD MORE
89    // ══════════════════════════════════════════
90    var loadMoreBtn = document.getElementById('loadMoreBtn');
91    var blogGrid    = document.getElementById('blogGrid');
92
93    if (loadMoreBtn && blogGrid && typeof deonde_ajax !== 'undefined') {
94      var currentPage = 1;
95      var maxPages    = parseInt(loadMoreBtn.getAttribute('data-max')) || parseInt(deonde_ajax.max_pages) || 1;
96      var isLoading   = false;
97
98      if (currentPage >= maxPages) {
99        loadMoreBtn.style.display = 'none';
100      }
101
102      loadMoreBtn.addEventListener('click', function () {
103        if (isLoading) return;
104        isLoading = true;
105        currentPage++;
106
107        var originalHTML = loadMoreBtn.innerHTML;
108        loadMoreBtn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="animation:spin .7s linear infinite"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg> Loading...';
109        loadMoreBtn.disabled = true;
110        loadMoreBtn.style.opacity = '0.7';
111
112        var formData = new FormData();
113        formData.append('action', 'deonde_load_more');
114        formData.append('page', currentPage);
115        formData.append('posts_per_page', deonde_ajax.posts_per_page || 9);
116        formData.append('nonce', deonde_ajax.nonce);
117
118        fetch(deonde_ajax.ajax_url, {
119          method: 'POST',
120          body: formData,
121          credentials: 'same-origin',
122        })
123          .then(function (r) { return r.json(); })
124          .then(function (data) {
125            if (data.success && data.data.posts) {
126              var tmp = document.createElement('div');
127              tmp.innerHTML = data.data.posts;
128
129              var newCards = tmp.querySelectorAll('.blog-card');
130              newCards.forEach(function (card, idx) {
131                blogGrid.appendChild(card);
132                setTimeout(function () {
133                  card.style.transition = 'opacity .4s ease, transform .4s ease';
134                  card.style.opacity    = '1';
135                  card.style.transform  = 'translateY(0)';
136                }, idx * 60);
137              });
138
139              maxPages = parseInt(data.data.max_pages) || maxPages;
140              if (currentPage >= maxPages) {
141                loadMoreBtn.style.display = 'none';
142              } else {
143                loadMoreBtn.innerHTML = originalHTML;
144                loadMoreBtn.disabled  = false;
145                loadMoreBtn.style.opacity = '1';
146              }
147            } else {
148              loadMoreBtn.style.display = 'none';
149            }
150            isLoading = false;
151          })
152          .catch(function () {
153            loadMoreBtn.innerHTML = originalHTML;
154            loadMoreBtn.disabled  = false;
155            loadMoreBtn.style.opacity = '1';
156            isLoading = false;
157          });
158      });
159    }
160
161    // ══════════════════════════════════════════
162    // 4. BACK TO TOP
163    // ══════════════════════════════════════════
164    var backTopBtn = document.getElementById('backTop');
165    if (backTopBtn) {
166      window.addEventListener('scroll', function () {
167        backTopBtn.classList.toggle('show', window.scrollY > 500);
168      }, { passive: true });
169
170      backTopBtn.addEventListener('click', function () {
171        window.scrollTo({ top: 0, behavior: 'smooth' });
172      });
173    }
174
175    // ══════════════════════════════════════════
176    // 5. SMOOTH SCROLL for anchor links
177    // ══════════════════════════════════════════
178    document.querySelectorAll('a[href^="#"]').forEach(function (anchor) {
179      anchor.addEventListener('click', function (e) {
180        var href = this.getAttribute('href');
181        if (href !== '#' && href !== '#!') {
182          var target = document.querySelector(href);
183          if (target) {
184            e.preventDefault();
185            var offset = target.getBoundingClientRect().top + window.pageYOffset - 80;
186            window.scrollTo({ top: offset, behavior: 'smooth' });
187          }
188        }
189      });
190    });
191
192    // ══════════════════════════════════════════
193    // 6. SPIN KEYFRAME (for load more)
194    // ══════════════════════════════════════════
195    if (!document.getElementById('deonde-spin-style')) {
196      var style = document.createElement('style');
197      style.id = 'deonde-spin-style';
198      style.textContent = '@keyframes spin { to { transform: rotate(360deg); } }';
199      document.head.appendChild(style);
200    }
201
202  });
203
204})();

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.