diff --git a/web/static/js/source-validator.v1.js b/web/static/js/source-validator.v1.js index a04360e..2d5cacd 100644 --- a/web/static/js/source-validator.v1.js +++ b/web/static/js/source-validator.v1.js @@ -5,25 +5,90 @@ - SourceValidator.buildWOLSearchURL(text) -> string */ window.SourceValidator = (function () { - // Publications / codes that produce valid WOL links (from your template list) + // Publications / codes that produce valid WOL links. + // Added: uw, su, re, lvs, rs (rs was already present). const PUB_CODES = [ "wp","ws","yb","mwb","w","g","ap","apf","be","bh","br","bt","btg","cf","cl","ct","dp", "fg","fy","gt","hb","im","ip","it","jv","ka","kj","kl","lf","lff","ll","ly","my","od", - "pe","po","pt","rr","rs","sg","sh","si","td","tp","tr","ts","un","jy" + "pe","po","pt","rr","rs","sg","sh","si","td","tp","tr","ts","un","jy", + "uw","su","re","lvs" // new ]; + // Year validation rules (applies only if a year can be parsed from the source). + // Watchtower (w/wp/ws) back to 1950; Awake (g) back to 1970. + const YEAR_RULES = [ + { codes: ["w","wp","ws"], minYear: 1950 }, + { codes: ["g"], minYear: 1970 } + ]; + + // Normalize helper function normalize(s) { return (s || "").trim().toLowerCase(); } + // Choose the longest matching code at the start (so "ws" beats "w"). + const PUB_CODES_SORTED = [...PUB_CODES].sort((a,b)=>b.length-a.length); + + function leadingCode(textLower) { + for (const code of PUB_CODES_SORTED) { + if (textLower.startsWith(code)) return code; + } + return null; + } + + // Try to extract a year that appears right after the leading code (allow spaces), + // accepting either 4-digit (e.g., 1955, 2001) or 2-digit (e.g., 55, 95, 12) forms. + function extractYearAfterCode(textLower, code) { + let s = textLower.slice(code.length).trim(); + + // 1) Look for a 4-digit year first + let m = s.match(/\b(1[89]\d{2}|20\d{2})\b/); // 1800-2099 (broad, but OK) + if (m) { + return parseInt(m[1], 10); + } + + // 2) If not found, accept a 2-digit year at the *start* of the remainder, + // or right after an optional space: e.g., "w55 1/1", "w 95", "g70 1/22" + m = s.match(/^\s*(\d{2})\b/); + if (m) { + const yy = parseInt(m[1], 10); + // Infer century based on publication + threshold logic + // - For Watchtower: 50–99 -> 1950–1999; 00–49 -> 2000–2049 + // - For Awake: 70–99 -> 1970–1999; 00–69 -> 2000–2069 + if (code === "g") { + return yy >= 70 ? (1900 + yy) : (2000 + yy); + } + if (code === "w" || code === "wp" || code === "ws") { + return yy >= 50 ? (1900 + yy) : (2000 + yy); + } + // For other pubs, if they ever include 2-digit years, assume 1900+yy≥70 else 2000+yy + return yy >= 70 ? (1900 + yy) : (2000 + yy); + } + + // No recognizable year → don't enforce year limits + return null; + } + + function passesYearRuleIfPresent(textLower, code) { + const rule = YEAR_RULES.find(r => r.codes.includes(code)); + if (!rule) return true; // no year rule for this pub + + const y = extractYearAfterCode(textLower, code); + if (y == null) return true; // no year present → allow + return y >= rule.minYear; + } + function isWOLSource(text) { const t = normalize(text); if (!t) return false; - // Keep this simple: if the string starts with any known code, treat it as WOL-capable. - return PUB_CODES.some(code => t.startsWith(code)); + + const code = leadingCode(t); + if (!code) return false; + + // If starts with a known pub code, it’s WOL-capable — but enforce year rules where applicable. + return passesYearRuleIfPresent(t, code); } function buildWOLSearchURL(text) { const q = encodeURIComponent(text || ""); - // Same search endpoint you’re already using return `https://wol.jw.org/en/wol/l/r1/lp-e?q=${q}`; }