PageSourceSearch

https://onlinepolicingsolutions.com/assets/js/news.js

js onlinepolicingsolutions.com collected 2026-10-01 23:45:13 UTC 4,989 bytes, 138 lines download raw bytes

1(function () {
2  function articleHref(slug) {
3    var path = window.location.pathname.replace(/\\/g, '/');
4    if (/\/pages(\/|$)/i.test(path)) return '../news/' + encodeURIComponent(slug) + '.html';
5    if (/\/news(\/|$)/i.test(path)) return encodeURIComponent(slug) + '.html';
6    return 'news/' + encodeURIComponent(slug) + '.html';
7  }
8
9  function formatDate(iso) {
10    if (!iso) return '';
11    var d = new Date(iso + 'T12:00:00');
12    if (isNaN(d.getTime())) return iso;
13    return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
14  }
15
16  function escapeHtml(s) {
17    return String(s || '')
18      .replace(/&/g, '&')
19      .replace(/</g, '&lt;')
20      .replace(/>/g, '&gt;')
21      .replace(/"/g, '&quot;');
22  }
23
24  function published(list) {
25    return (list || []).filter(function (a) {
26      return a && a.published !== false && a.slug && a.title;
27    });
28  }
29
30  function mediaUrl(src) {
31    if (!src) return '';
32    if (/^https?:\/\//i.test(src) || src.indexOf('data:') === 0) return src;
33    var clean = String(src).replace(/^\.\.\//, '');
34    var path = window.location.pathname.replace(/\\/g, '/');
35    if (/\/pages(\/|$)/i.test(path) || /\/news(\/|$)/i.test(path)) return '../' + clean;
36    return clean;
37  }
38
39  function truncate(text, max) {
40    var t = String(text || '').replace(/\s+/g, ' ').trim();
41    if (t.length <= max) return t;
42    return t.slice(0, max - 1).replace(/\s+\S*$/, '') + '…';
43  }
44
45  function cardHtml(a, opts) {
46    opts = opts || {};
47    var excerpt = opts.short ? truncate(a.excerpt || '', 140) : (a.excerpt || '');
48    var href = articleHref(a.slug);
49    return (
50      '<article class="news-card reveal is-visible">' +
51      '<a class="news-card-media" href="' + href + '">' +
52      '<img src="' + escapeHtml(mediaUrl(a.image)) + '" alt="" width="640" height="400" loading="lazy">' +
53      '</a>' +
54      '<div class="news-card-body">' +
55      '<h3><a href="' + href + '">' + escapeHtml(a.title) + '</a></h3>' +
56      '<p class="news-date">' + escapeHtml(formatDate(a.date)) + '</p>' +
57      '<p class="news-excerpt">' + escapeHtml(excerpt) + '</p>' +
58      '<a class="read-more" href="' + href + '">read more</a>' +
59      '</div>' +
60      '</article>'
61    );
62  }
63
64  function renderHome(list) {
65    var el = document.getElementById('home-news-grid');
66    if (!el) return;
67    var items = published(list).slice(0, 3);
68    if (!items.length) {
69      el.innerHTML = '<p class="lede">News coming soon.</p>';
70      return;
71    }
72    el.innerHTML = items.map(function (a) { return cardHtml(a, { short: true }); }).join('');
73  }
74
75  function renderNewsPage(list) {
76    var el = document.getElementById('news-grid');
77    if (!el) return;
78    var items = published(list);
79    var q = (document.getElementById('news-search') || {}).value || '';
80    q = String(q).trim().toLowerCase();
81    if (q) {
82      items = items.filter(function (a) {
83        return (
84          (a.title || '').toLowerCase().indexOf(q) !== -1 ||
85          (a.excerpt || '').toLowerCase().indexOf(q) !== -1
86        );
87      });
88    }
89    var count = document.getElementById('news-count');
90    if (count) count.textContent = items.length + ' article' + (items.length === 1 ? '' : 's');
91    if (!items.length) {
92      el.innerHTML = '<p class="lede">No articles match your search.</p>';
93      return;
94    }
95    el.innerHTML = items.map(function (a) { return cardHtml(a); }).join('');
96  }
97
98  function applyNews(list) {
99    window.__OPS_NEWS__ = list;
100    renderHome(list);
101    renderNewsPage(list);
102    var search = document.getElementById('news-search');
103    if (search && !search._opsBound) {
104      search._opsBound = true;
105      search.addEventListener('input', function () {
106        renderNewsPage(window.__OPS_NEWS__ || []);
107      });
108    }
109  }
110
111  function newsJsonUrl() {
112    var path = window.location.pathname.replace(/\\/g, '/');
113    if (/\/pages(\/|$)/i.test(path) || /\/news(\/|$)/i.test(path)) return '../data/news.json';
114    return 'data/news.json';
115  }
116
117  function showError() {
118    var home = document.getElementById('home-news-grid');
119    var grid = document.getElementById('news-grid');
120    var msg = 'Unable to load news. Upload <code>data/news-data.js</code> (and <code>data/news.json</code>) with the site.';
121    if (home && /Loading/i.test(home.textContent || '')) home.innerHTML = '<p class="lede">' + msg + '</p>';
122    if (grid) grid.innerHTML = '<p class="lede">' + msg + '</p>';
123  }
124
125  // Prefer embedded data (works on file:// and HTTP). Fall back to fetch.
126  if (Array.isArray(window.__OPS_NEWS__) && window.__OPS_NEWS__.length) {
127    applyNews(window.__OPS_NEWS__);
128    return;
129  }
130
131  fetch(newsJsonUrl(), { cache: 'no-store' })
132    .then(function (r) {
133      if (!r.ok) throw new Error('news load failed');
134      return r.json();
135    })
136    .then(applyNews)
137    .catch(showError);
138})();

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.