PageSourceSearch

https://pioneerbusinesssolutions.in/blog/main.js

js pioneerbusinesssolutions.in collected 2026-10-08 20:01:13 UTC 10,465 bytes, 288 lines download raw bytes

1// main.js — Blog page logic: search, filter, sort, pagination
2
3(function () {
4  const POSTS_PER_PAGE = 6;
5
6  let state = {
7    category: 'all',
8    search: '',
9    tag: '',
10    sort: 'newest',
11    page: 1
12  };
13
14  // --- DOM refs ---
15  const grid         = document.getElementById('blogGrid');
16  const pagination   = document.getElementById('pagination');
17  const paginationWrap = document.getElementById('paginationWrap');
18  const noResults    = document.getElementById('noResults');
19  const resultsCount = document.getElementById('resultsCount');
20  const searchInput  = document.getElementById('searchInput');
21  const clearSearch  = document.getElementById('clearSearch');
22  const sortSelect   = document.getElementById('sortSelect');
23  const activeFilters= document.getElementById('activeFilters');
24
25  // --- Category counts ---
26  function updateCounts() {
27    const counts = { all: BLOGS.length, ai: 0, microsoft: 0, hr: 0, other: 0, guide: 0 };
28    BLOGS.forEach(b => { if (counts[b.category] !== undefined) counts[b.category]++; });
29    document.getElementById('countAll').textContent   = counts.all;
30    document.getElementById('countAI').textContent    = counts.ai;
31    document.getElementById('countMS').textContent    = counts.microsoft;
32    document.getElementById('countHR').textContent    = counts.hr;
33    document.getElementById('countOther').textContent = counts.other;
34    document.getElementById('countGuide').textContent = counts.guide;
35  }
36
37  // --- Filter & sort posts ---
38  function getFiltered() {
39    let list = [...BLOGS];
40
41    if (state.category !== 'all') {
42      list = list.filter(b => b.category === state.category);
43    }
44    if (state.search) {
45      const q = state.search.toLowerCase();
46      list = list.filter(b =>
47        b.title.toLowerCase().includes(q) ||
48        b.excerpt.toLowerCase().includes(q) ||
49        b.tags.some(t => t.toLowerCase().includes(q)) ||
50        b.badgeText.toLowerCase().includes(q)
51      );
52    }
53    if (state.tag) {
54      list = list.filter(b => b.tags.includes(state.tag));
55    }
56
57    if (state.sort === 'newest') {
58      list.sort((a, b) => new Date(b.date) - new Date(a.date));
59    } else if (state.sort === 'oldest') {
60      list.sort((a, b) => new Date(a.date) - new Date(b.date));
61    } else if (state.sort === 'readtime') {
62      list.sort((a, b) => a.readtime - b.readtime);
63    }
64
65    return list;
66  }
67
68  // --- Format date ---
69  function fmtDate(d) {
70    return new Date(d).toLocaleDateString('en-IN', { year: 'numeric', month: 'short', day: 'numeric' });
71  }
72
73  // --- Render card ---
74  function renderCard(b) {
75    return `
76      <div class="col-md-6 col-lg-6 blog-card-col" data-id="${b.id}">
77        <div class="blog-card h-100" onclick="window.open('${b.url}','_blank')" role="button" tabindex="0" aria-label="Read: ${b.title}">
78          <div class="card-thumb">
79           <div class="card-thumb">
80  <img src="${b.thumbImage}" alt="${b.title}" class="card-img-top" />
81  <span class="card-cat-badge ${b.badgeClass}">
82    <i class="bi ${b.badgeIcon}"></i>${b.badgeText}
83  </span>
84</div>
85            <span class="card-cat-badge ${b.badgeClass}"><i class="bi ${b.badgeIcon}"></i>${b.badgeText}</span>
86          </div>
87          <div class="card-body-inner">
88            <div class="card-meta">
89              <span><i class="bi bi-calendar3"></i>${fmtDate(b.date)}</span>
90              <span><i class="bi bi-clock"></i>${b.readtime} min read</span>
91            </div>
92            <h3 class="card-title">${b.title}</h3>
93            <p class="card-excerpt">${b.excerpt}</p>
94            <div class="card-footer-row">
95              <a href="${b.url}" target="_blank" class="read-more-link" onclick="event.stopPropagation()">
96                Read More <i class="bi bi-arrow-right"></i>
97              </a>
98              <div class="card-tags">
99                ${b.tags.slice(0, 2).map(t => `<span class="card-tag">${t}</span>`).join('')}
100              </div>
101            </div>
102          </div>
103        </div>
104      </div>`;
105  }
106
107  // --- Render pagination ---
108  function renderPagination(total, current) {
109    const pages = Math.ceil(total / POSTS_PER_PAGE);
110    pagination.innerHTML = '';
111    if (pages <= 1) { paginationWrap.style.display = 'none'; return; }
112    paginationWrap.style.display = '';
113
114    const prev = document.createElement('li');
115    prev.className = `page-item ${current === 1 ? 'disabled' : ''}`;
116    prev.innerHTML = `<button class="page-link" aria-label="Previous"><i class="bi bi-chevron-left"></i></button>`;
117    prev.addEventListener('click', () => { if (current > 1) goPage(current - 1); });
118    pagination.appendChild(prev);
119
120    for (let i = 1; i <= pages; i++) {
121      const li = document.createElement('li');
122      li.className = `page-item ${i === current ? 'active' : ''}`;
123      li.innerHTML = `<button class="page-link">${i}</button>`;
124      li.addEventListener('click', () => goPage(i));
125      pagination.appendChild(li);
126    }
127
128    const next = document.createElement('li');
129    next.className = `page-item ${current === pages ? 'disabled' : ''}`;
130    next.innerHTML = `<button class="page-link" aria-label="Next"><i class="bi bi-chevron-right"></i></button>`;
131    next.addEventListener('click', () => { if (current < pages) goPage(current + 1); });
132    pagination.appendChild(next);
133  }
134
135  function goPage(n) {
136    state.page = n;
137    render();
138    window.scrollTo({ top: document.querySelector('.blog-main').offsetTop - 80, behavior: 'smooth' });
139  }
140
141  // --- Active filter chips ---
142  function renderActiveFilters() {
143    activeFilters.innerHTML = '';
144    if (state.category !== 'all') {
145      const labels = { ai: 'AI Certifications', microsoft: 'Microsoft Certs', hr: 'HR & L&D', other: 'Other Certifications', guide: 'Guides' };
146      activeFilters.innerHTML += `<span class="filter-chip" onclick="resetCategory()"><i class="bi bi-x"></i>${labels[state.category]}</span>`;
147    }
148    if (state.tag) {
149      activeFilters.innerHTML += `<span class="filter-chip" onclick="resetTag()"><i class="bi bi-x"></i>${state.tag}</span>`;
150    }
151    if (state.search) {
152      activeFilters.innerHTML += `<span class="filter-chip" onclick="resetSearch()"><i class="bi bi-x"></i>"${state.search}"</span>`;
153    }
154  }
155
156  // --- Main render ---
157  function render() {
158    const filtered = getFiltered();
159    const start = (state.page - 1) * POSTS_PER_PAGE;
160    const paginated = filtered.slice(start, start + POSTS_PER_PAGE);
161
162    renderActiveFilters();
163
164    if (filtered.length === 0) {
165      grid.innerHTML = '';
166      noResults.classList.remove('d-none');
167      resultsCount.innerHTML = 'Showing <strong>0</strong> articles';
168      renderPagination(0, 1);
169      return;
170    }
171
172    noResults.classList.add('d-none');
173    resultsCount.innerHTML = `Showing <strong>${filtered.length}</strong> article${filtered.length !== 1 ? 's' : ''}`;
174    grid.innerHTML = paginated.map(renderCard).join('');
175    renderPagination(filtered.length, state.page);
176  }
177
178  // --- Event listeners ---
179  // Category buttons
180  document.getElementById('categoryList').addEventListener('click', e => {
181    const btn = e.target.closest('.cat-btn');
182    if (!btn) return;
183    document.querySelectorAll('.cat-btn').forEach(b => b.classList.remove('active'));
184    btn.classList.add('active');
185    state.category = btn.dataset.cat;
186    state.page = 1;
187    render();
188  });
189
190  // Search
191  searchInput.addEventListener('input', () => {
192    state.search = searchInput.value.trim();
193    state.page = 1;
194    clearSearch.classList.toggle('d-none', !state.search);
195    render();
196  });
197  clearSearch.addEventListener('click', () => {
198    searchInput.value = '';
199    state.search = '';
200    clearSearch.classList.add('d-none');
201    state.page = 1;
202    render();
203  });
204
205  // Sort
206  sortSelect.addEventListener('change', () => {
207    state.sort = sortSelect.value;
208    state.page = 1;
209    render();
210  });
211
212  // Tags
213  document.getElementById('tagCloud').addEventListener('click', e => {
214    const pill = e.target.closest('.tag-pill');
215    if (!pill) return;
216    if (state.tag === pill.dataset.tag) {
217      state.tag = '';
218      pill.classList.remove('active');
219    } else {
220      document.querySelectorAll('.tag-pill').forEach(p => p.classList.remove('active'));
221      state.tag = pill.dataset.tag;
222      pill.classList.add('active');
223    }
224    state.page = 1;
225    render();
226  });
227
228  // --- Global reset helpers ---
229  window.resetFilters = function () {
230    state = { category: 'all', search: '', tag: '', sort: 'newest', page: 1 };
231    searchInput.value = '';
232    clearSearch.classList.add('d-none');
233    sortSelect.value = 'newest';
234    document.querySelectorAll('.cat-btn').forEach(b => b.classList.remove('active'));
235    document.querySelector('.cat-btn[data-cat="all"]').classList.add('active');
236    document.querySelectorAll('.tag-pill').forEach(p => p.classList.remove('active'));
237    render();
238  };
239  window.resetCategory = function () {
240    state.category = 'all';
241    state.page = 1;
242    document.querySelectorAll('.cat-btn').forEach(b => b.classList.remove('active'));
243    document.querySelector('.cat-btn[data-cat="all"]').classList.add('active');
244    render();
245  };
246  window.resetTag = function () {
247    state.tag = '';
248    state.page = 1;
249    document.querySelectorAll('.tag-pill').forEach(p => p.classList.remove('active'));
250    render();
251  };
252  window.resetSearch = function () {
253    state.search = '';
254    state.page = 1;
255    searchInput.value = '';
256    clearSearch.classList.add('d-none');
257    render();
258  };
259  window.filterCat = function (cat) {
260    window.scrollTo({ top: 0, behavior: 'smooth' });
261    state.category = cat;
262    state.page = 1;
263    document.querySelectorAll('.cat-btn').forEach(b => b.classList.remove('active'));
264    const btn = document.querySelector(`.cat-btn[data-cat="${cat}"]`);
265    if (btn) btn.classList.add('active');
266    render();
267  };
268
269  // Newsletter
270  window.subscribeNewsletter = function () {
271    const emailEl = document.getElementById('newsletterEmail');
272    const msgEl   = document.getElementById('newsletterMsg');
273    if (!emailEl || !emailEl.value.includes('@')) {
274      msgEl.textContent = 'Please enter a valid email address.';
275      msgEl.style.color = '#e53e3e';
276      msgEl.classList.remove('d-none');
277      return;
278    }
279    msgEl.textContent = '✓ You\'re subscribed! Welcome to Pioneer insights.';
280    msgEl.style.color = '#6ee7b7';
281    msgEl.classList.remove('d-none');
282    emailEl.value = '';
283  };
284
285  // Init
286  updateCounts();
287  render();
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.