1/* 2 * Fire a Plausible "Article Read" custom event on post pages, with 3 * title / primary_tag / author props extracted from the rendered DOM. 4 * 5 * Why on 'load' rather than inline: Ghost's proxied Plausible build 6 * replaces window.plausible without replaying the queue shim, so 7 * queued events fired during parse get silently dropped. Waiting for 8 * 'load' guarantees the deferred tracker has installed the real 9 * sender first. 10 */ 11window.addEventListener("load", () => { 12 if (!window.plausible) return; 13 const titleEl = document.querySelector(".article-title"); 14 const title = titleEl ? titleEl.textContent.trim().slice(0, 200) : document.title; 15 16 // Structural tags are public, so they render as pills and used to win the 17 // primary_tag slot: "featured" and "uncategorized" sit first on a third of 18 // all posts, which filed a third of reads under a non-topic and made 19 // top-topics unusable for comparing demand against output. Skip them and 20 // take the first real topic instead. Issue tags (winter-2026) are the same 21 // problem in a form that grows every quarter, hence the pattern. 22 const NON_TOPIC = ["featured", "uncategorized", "journal", "trivial-link", 23 "mere-fidelity", "earthen-vessels", "passages", "podcast"]; 24 const isNonTopic = (slug) => 25 NON_TOPIC.indexOf(slug) !== -1 || /^(winter|spring|summer|fall|autumn)-\d{4}$/.test(slug); 26 27 const tagEls = document.querySelectorAll(".article-topic-tag[data-tag-slug]"); 28 let primary_tag = ""; 29 let fallback_tag = ""; 30 let author = ""; 31 tagEls.forEach((el) => { 32 const slug = el.getAttribute("data-tag-slug") || ""; 33 if (!slug) return; 34 if (slug.indexOf("author-") === 0) { 35 if (!author) author = slug.replace(/^author-/, ""); 36 } else if (isNonTopic(slug)) { 37 // Keep it only as a last resort: a post tagged nothing but "featured" 38 // should still report something rather than an empty string. 39 if (!fallback_tag) fallback_tag = slug; 40 } else if (!primary_tag) { 41 primary_tag = slug; 42 } 43 }); 44 if (!primary_tag) primary_tag = fallback_tag; 45 46 window.plausible("Article Read", { 47 props: { title, primary_tag, author }, 48 }); 49});
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.