PageSourceSearch

https://postiq.netlify.app/help/help.js

js postiq.netlify.app collected 2026-10-03 11:17:47 UTC 4,494 bytes, 89 lines download raw bytes

1(function(){
2  const articles = (window.POSTIQ_HELP_ARTICLES || []).slice();
3  const bySlug = new Map(articles.map(a => [a.slug, a]));
4
5  const homeEl = document.getElementById('helpHome');
6  const articleEl = document.getElementById('articleView');
7  const gridEl = document.getElementById('articlesGrid');
8  const recommendedEl = document.getElementById('recommendedWrap');
9
10  const supportModal = document.getElementById('supportModal');
11  const openSupportBtn = document.getElementById('openSupportBtn');
12  const closeSupportBtn = document.getElementById('closeSupportBtn');
13
14  function escapeHtml(value){
15    return String(value).replace(/[&<>'"]/g, (c) => ({ '&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;' }[c]));
16  }
17
18  function articleCard(article, opts){
19    const featuredClass = opts.featured ? ' featured' : '';
20    return `<a class="article-card${featuredClass}" href="?article=${encodeURIComponent(article.slug)}" data-open-article="${article.slug}" aria-label="Read guide: ${escapeHtml(article.title)}"><h3 class="article-title">${article.title}</h3><p class="article-summary">${article.summary}</p><span class="read-link">Read guide →</span></a>`;
21  }
22
23  function renderRecommended(){
24    const featured = articles.filter(a => a.featured === true).slice(0, 3);
25    recommendedEl.innerHTML = featured.length
26      ? `<div class="section-head">Start here</div><div class="featured-grid">${featured.map(item => articleCard(item, { featured: true })).join('')}</div>`
27      : '';
28    return new Set(featured.map(f => f.slug));
29  }
30
31  function renderHome(){
32    const hiddenSlugs = renderRecommended();
33    const baseList = articles.filter(a => !hiddenSlugs.has(a.slug));
34    gridEl.innerHTML = `<section><h2 class="section-head">All guides</h2><div class="articles-grid">${baseList.map(a => articleCard(a, { featured: false })).join('')}</div></section>`;
35  }
36
37  function renderArticle(slug){
38    const article = bySlug.get(slug);
39    if (!article){ articleEl.hidden = true; homeEl.hidden = false; renderHome(); return; }
40    const related = (article.relatedArticles || []).map(rs => bySlug.get(rs)).filter(Boolean);
41    articleEl.innerHTML = `<h2>${article.title}</h2><p class="meta">${article.category}</p><div class="article-body">${article.body}</div><div class="related-list">${related.length ? `<h3>Related guides</h3>${related.map(r => `<a href="?article=${encodeURIComponent(r.slug)}" data-open-article="${r.slug}">${r.title}</a>`).join('')}` : ''}</div><div class="article-nav"><a class="btn-link" href="/help/" data-open-home="true">← Back to all guides</a><a class="btn-link" href="/app.html">Back to PostIQ</a></div>`;
42    homeEl.hidden = true;
43    articleEl.hidden = false;
44    window.scrollTo({ top: 0, behavior: 'instant' });
45  }
46
47  function applyRoute(){
48    const slug = new URLSearchParams(window.location.search).get('article');
49    if (slug) renderArticle(slug); else { articleEl.hidden = true; homeEl.hidden = false; renderHome(); }
50  }
51
52  function openSupportModal() {
53    if (!supportModal) return;
54    supportModal.classList.remove('hidden');
55    supportModal.classList.add('is-open');
56    supportModal.setAttribute('aria-hidden', 'false');
57    document.body.style.overflow = 'hidden';
58    closeSupportBtn?.focus();
59  }
60
61  function closeSupportModal() {
62    if (!supportModal) return;
63    supportModal.classList.add('hidden');
64    supportModal.classList.remove('is-open');
65    supportModal.setAttribute('aria-hidden', 'true');
66    document.body.style.overflow = '';
67  }
68
69  openSupportBtn?.addEventListener('click', openSupportModal);
70  closeSupportBtn?.addEventListener('click', closeSupportModal);
71
72  supportModal?.addEventListener('click', function(event) {
73    if (event.target === supportModal) closeSupportModal();
74  });
75
76  document.addEventListener('keydown', function(event) {
77    if (event.key === 'Escape' && supportModal && supportModal.classList.contains('is-open')) closeSupportModal();
78  });
79
80  document.addEventListener('click', (e) => {
81    const articleLink = e.target.closest('[data-open-article]');
82    if (articleLink){ e.preventDefault(); history.pushState({}, '', `?article=${encodeURIComponent(articleLink.dataset.openArticle)}`); renderArticle(articleLink.dataset.openArticle); return; }
83    const homeLink = e.target.closest('[data-open-home]');
84    if (homeLink){ e.preventDefault(); history.pushState({}, '', '/help/');
84 applyRoute(); }
85  });
86
87  window.addEventListener('popstate', applyRoute);
88  applyRoute();
89})();

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.