diff --git a/web/static/js/subject-actions.v1.js b/web/static/js/subject-actions.v1.js new file mode 100644 index 0000000..641e2bc --- /dev/null +++ b/web/static/js/subject-actions.v1.js @@ -0,0 +1,185 @@ +/* subject-actions.v1.js + Popup for .subject-pill anchors with: + - WOL Search (uses current href) + - Insight Book (only if subject exists in Insight index) + - Wikipedia Search + - Google Search + - Database Search (only for 1–2 word subjects) + + Insight index: + - The script merges a small built-in seed with (optional) /static/data/insight-index.v1.json + - JSON format: { "abraham": "120000001", "babel": "1200000456", ... } +*/ + +(function(){ + // ---- Config + var WOL_LANG = 'en'; + var WOL_LP = 'e'; + + // Where to fetch your full Insight index (optional, but recommended) + var INSIGHT_INDEX_URL = '/static/data/insight-index.v1.json'; + + // ---- Helpers + function norm(s){ return String(s||'').toLowerCase().replace(/[^\p{L}\p{N}\s'-]/gu,'').replace(/\s+/g,' ').trim(); } + function isOneOrTwoWords(s){ return norm(s).split(' ').filter(Boolean).length <= 2; } + function getSearchBase(){ + var el = document.getElementById('subject-list'); + return el ? el.getAttribute('data-search-url') || '/search' : '/search'; + } + + function wolSearchURL(q){ + return "https://wol.jw.org/"+WOL_LANG+"/wol/s/r1/lp-"+WOL_LP+"?q="+encodeURIComponent(q); + } + function insightURL(id){ + // canonical article path + return "https://wol.jw.org/"+WOL_LANG+"/wol/d/r1/lp-"+WOL_LP+"/"+id; + } + function wikipediaURL(q){ + return "https://en.wikipedia.org/wiki/Special:Search?search="+encodeURIComponent(q); + } + function googleURL(q){ + return "https://www.google.com/search?q="+encodeURIComponent(q); + } + function databaseURL(q){ + // Searches your DB limiting to subject field (adjust query key as needed) + // If your search uses a different param (e.g., 'fields'), change here: + return getSearchBase()+"?q="+encodeURIComponent(q)+"&fields=subject"; + } + + // ---- Built-in tiny seed (you will extend via JSON file) + var INSIGHT_INDEX = { + "abraham": "120000001", // sample IDs; replace with real ones in your JSON + "adam": "120000002", + "babel": "120000003", + "babylon": "120000004", + "daniel": "120000005", + "david": "120000006", + "ezra": "120000007", + "faith": "120000008", + "jerusalem": "120000009", + "jesus christ": "120000010", + "love": "120000011", + "moses": "120000012", + "paul": "120000013", + "peter": "120000014", + "resurrection": "120000015", + "sabbath": "120000016", + "salvation": "120000017", + "samuel": "120000018", + "satan": "120000019", + "wisdom": "120000020" + }; + + // Try to extend from external JSON (if present). Safe to fail silently. + (function loadExternalIndex(){ + try{ + var xhr = new XMLHttpRequest(); + xhr.open('GET', INSIGHT_INDEX_URL, true); + xhr.onreadystatechange = function(){ + if (xhr.readyState !== 4) return; + if (xhr.status >= 200 && xhr.status < 300) { + try { + var data = JSON.parse(xhr.responseText || '{}'); + if (data && typeof data === 'object'){ + for (var k in data){ if (data.hasOwnProperty(k)) INSIGHT_INDEX[norm(k)] = String(data[k]); } + } + } catch(_){} + } + }; + xhr.send(); + } catch(_){} + })(); + + // ---- Popup (reuses same visual style as scripture-actions; inject if not present) + var popupEl; + function ensurePopup(){ + if (popupEl) return popupEl; + // Minimal CSS (skip if scripture-actions already injected it — harmless to duplicate) + var style = document.createElement('style'); + style.textContent = + ".subject-actions-pop{position:absolute;z-index:9999;background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;box-shadow:0 10px 20px rgba(0,0,0,.1);padding:.5rem;min-width:220px;display:none}" + + ".subject-actions-pop .hdr{display:flex;justify-content:space-between;align-items:center;font-weight:600;font-size:.9rem;color:#1f2937;margin-bottom:.25rem}" + + ".subject-actions-pop .close-x{cursor:pointer;padding:.1rem .4rem;border-radius:.375rem}" + + ".subject-actions-pop .close-x:hover{background:#f3f4f6}" + + ".subject-actions-pop .btn-row{display:grid;gap:.4rem;margin-top:.25rem}" + + ".subject-actions-pop a.action{display:block;text-decoration:none;padding:.45rem .55rem;border:1px solid #e5e7eb;border-radius:.45rem;color:#1f2937}" + + ".subject-actions-pop a.action:hover{background:#f9fafb}" + + ".subject-actions-pop a.action[hidden]{display:none}"; + document.head.appendChild(style); + + popupEl = document.createElement('div'); + popupEl.className = 'subject-actions-pop'; + popupEl.innerHTML = + '
Open subject
' + + '
' + + 'WOL Search' + + 'Insight Book' + + 'Wikipedia' + + 'Google' + + 'Database Search' + + '
'; + document.body.appendChild(popupEl); + + popupEl.querySelector('.close-x').addEventListener('click', hidePopup); + document.addEventListener('click', function(e){ + if (!popupEl.contains(e.target) && !e.target.closest('.subject-pill')) hidePopup(); + }, true); + document.addEventListener('keydown', function(e){ if (e.key === 'Escape') hidePopup(); }); + + return popupEl; + } + function showPopup(x,y,links,options){ + var el = ensurePopup(); + el.style.left = x + 'px'; + el.style.top = y + 'px'; + + el.querySelector('.act-wol').href = links.wol; + el.querySelector('.act-wiki').href = links.wiki; + el.querySelector('.act-google').href = links.google; + el.querySelector('.act-db').href = links.db; + + // Insight visibility + var ins = el.querySelector('.act-insight'); + if (links.insight) { ins.removeAttribute('hidden'); ins.href = links.insight; } + else { ins.setAttribute('hidden',''); } + + // DB visibility for >2 words + var db = el.querySelector('.act-db'); + if (options && options.enableDB) db.removeAttribute('hidden'); + else db.setAttribute('hidden',''); + + el.style.display = 'block'; + } + function hidePopup(){ if (popupEl) popupEl.style.display = 'none'; } + + // ---- Click handler + document.addEventListener('click', function(e){ + var pill = e.target.closest('.subject-pill'); + if (!pill) return; + + e.preventDefault(); + + var text = (pill.getAttribute('data-ref') || pill.textContent || '').trim(); + var n = norm(text); + + // Build links + var wol = pill.getAttribute('href') || wolSearchURL(text); + var wiki = wikipediaURL(text); + var goog = googleURL(text); + + // DB link only for 1–2 words + var dbEnabled = isOneOrTwoWords(text); + var db = dbEnabled ? databaseURL(text) : '#'; + + // Insight visible only if we have an exact match + var insightId = INSIGHT_INDEX[n]; + var insight = insightId ? insightURL(insightId) : null; + + var rect = pill.getBoundingClientRect(); + var x = rect.left + window.scrollX + rect.width/2 - 110; + var y = rect.top + window.scrollY + rect.height + 6; + + showPopup(x, y, { wol:wol, wiki:wiki, google:goog, db:db, insight:insight }, { enableDB: dbEnabled }); + }); + +})(); \ No newline at end of file