148 lines
6.1 KiB
JavaScript
148 lines
6.1 KiB
JavaScript
/*
|
|
* pw-crtc-collapse.js — progressive-enhancement reading aid for the long
|
|
* Canada CRTC carrier page (public/services/telecom/canada-crtc/index.html).
|
|
*
|
|
* Why a runtime script instead of editing the HTML: that page is a single
|
|
* 183 KB minified file with auto-generated markup whose section headings sit
|
|
* at inconsistent DOM depths. Rewriting it by hand risks corrupting the markup
|
|
* and is hard to maintain. This script enhances it non-destructively:
|
|
*
|
|
* 1. Injects a "5-minute read" TL;DR + jump menu at the top of the article.
|
|
* 2. Collapses a curated set of deep-detail H2 sections into <details>-style
|
|
* accordions (collapsed by default) so the page reads short, while every
|
|
* word remains present for SEO and for anyone who expands it.
|
|
*
|
|
* It is idempotent (guards against double-run) and degrades gracefully: with
|
|
* JS off, the full page renders exactly as before.
|
|
*/
|
|
(function () {
|
|
"use strict";
|
|
if (window.__pwCrtcCollapse) return;
|
|
window.__pwCrtcCollapse = true;
|
|
|
|
// H2 headings (matched by normalized text prefix) to collapse by default.
|
|
// Order here is also the order used in the TL;DR jump menu.
|
|
var COLLAPSE = [
|
|
{ key: "why canada", label: "Why Canada: FCC vs CRTC" },
|
|
{ key: "corporate tax comparison", label: "Corporate tax comparison (BC vs US)" },
|
|
{ key: "canada telecom m&a", label: "Canada telecom M&A climate" },
|
|
{ key: "step 6: canadian business banking", label: "Canadian business banking" },
|
|
{ key: "included: canadian wholesale", label: "Wholesale vendor directory" },
|
|
{ key: "frequently asked questions", label: "FAQ", open: false },
|
|
{ key: "us wholesale voice market", label: "US wholesale / DID restrictions" },
|
|
{ key: "the growing burden", label: "The growing burden of a US carrier" },
|
|
{ key: "what you'll need", label: "What you'll need to get started" }
|
|
];
|
|
|
|
function norm(s) {
|
|
return (s || "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
}
|
|
|
|
function ready(fn) {
|
|
if (document.readyState !== "loading") fn();
|
|
else document.addEventListener("DOMContentLoaded", fn);
|
|
}
|
|
|
|
ready(function () {
|
|
var h2s = Array.prototype.slice.call(document.querySelectorAll("h2"));
|
|
if (!h2s.length) return;
|
|
|
|
var made = [];
|
|
|
|
COLLAPSE.forEach(function (cfg) {
|
|
var h2 = h2s.find(function (h) {
|
|
return norm(h.textContent).indexOf(cfg.key) === 0 ||
|
|
norm(h.textContent).indexOf(cfg.key) !== -1;
|
|
});
|
|
if (!h2 || h2.dataset.pwCollapsed) return;
|
|
|
|
var parent = h2.parentNode;
|
|
// Collect this H2 plus its following siblings up to the next H2 sibling.
|
|
var nodes = [h2];
|
|
var n = h2.nextSibling;
|
|
while (n) {
|
|
if (n.nodeType === 1 && n.tagName === "H2") break;
|
|
var next = n.nextSibling;
|
|
// Skip (don't absorb) a standalone block such as the "is this real?"
|
|
// proof expander — it must stay visible outside any collapsed body.
|
|
// We leave it in place; surrounding siblings still get collected, so it
|
|
// ends up rendered right after this accordion.
|
|
if (!(n.nodeType === 1 && n.classList &&
|
|
n.classList.contains("pw-standalone"))) {
|
|
nodes.push(n);
|
|
}
|
|
n = next;
|
|
}
|
|
if (nodes.length < 2) {
|
|
// Heading has no sibling body at this level (body nested elsewhere);
|
|
// skip rather than risk an empty/incorrect accordion.
|
|
return;
|
|
}
|
|
|
|
var details = document.createElement("details");
|
|
details.className = "pw-collapse";
|
|
if (cfg.open) details.open = true;
|
|
|
|
var summary = document.createElement("summary");
|
|
summary.className = "pw-collapse-summary";
|
|
summary.textContent = h2.textContent.replace(/\s+/g, " ").trim();
|
|
details.appendChild(summary);
|
|
|
|
var body = document.createElement("div");
|
|
body.className = "pw-collapse-body";
|
|
|
|
// Move the original H2 (hidden, keeps heading in DOM for SEO) + siblings in.
|
|
h2.style.display = "none";
|
|
h2.dataset.pwCollapsed = "1";
|
|
parent.insertBefore(details, nodes[0]);
|
|
nodes.forEach(function (node) { body.appendChild(node); });
|
|
details.appendChild(body);
|
|
|
|
var id = "crtc-" + cfg.key.replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
details.id = id;
|
|
made.push({ id: id, label: cfg.label });
|
|
});
|
|
|
|
if (!made.length) return;
|
|
|
|
// Build the TL;DR + jump menu and insert it right after the hero/pricing card.
|
|
var anchor = document.querySelector("nav[aria-label], .max-w-4xl") || document.body;
|
|
var tldr = document.createElement("div");
|
|
tldr.className = "pw-tldr";
|
|
tldr.innerHTML =
|
|
'<div class="pw-tldr-head">' +
|
|
'<span class="pw-tldr-badge">5-min read</span>' +
|
|
'<strong>The short version</strong></div>' +
|
|
'<p class="pw-tldr-lead">Become a single <strong>Canadian CRTC/BITS carrier</strong> ' +
|
|
'and lawfully serve US & international customers \u2014 without taking on the US ' +
|
|
'FCC 499/USF, RMD, STIR/SHAKEN and state-PUC stack a US carrier carries. ' +
|
|
'A Canadian carrier serving US customers as one entity is a common, established ' +
|
|
'structure (per public FCC records). Details below are optional \u2014 tap any to expand.</p>' +
|
|
'<div class="pw-tldr-jump"><span>Jump to:</span></div>';
|
|
|
|
var jump = tldr.querySelector(".pw-tldr-jump");
|
|
made.forEach(function (m) {
|
|
var a = document.createElement("a");
|
|
a.href = "#" + m.id;
|
|
a.className = "pw-tldr-chip";
|
|
a.textContent = m.label;
|
|
a.addEventListener("click", function (e) {
|
|
var d = document.getElementById(m.id);
|
|
if (d) {
|
|
d.open = true;
|
|
e.preventDefault();
|
|
d.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
}
|
|
});
|
|
jump.appendChild(a);
|
|
});
|
|
|
|
// Insert TL;DR before the first collapsed section for a clean top-of-article placement.
|
|
var firstDetails = document.querySelector("details.pw-collapse");
|
|
if (firstDetails && firstDetails.parentNode) {
|
|
firstDetails.parentNode.insertBefore(tldr, firstDetails);
|
|
} else {
|
|
anchor.insertBefore(tldr, anchor.firstChild);
|
|
}
|
|
});
|
|
})();
|