PageSourceSearch

https://cathedralstl.org/themes/custom/sitetheme/components/text/text.js?tm8kcy

js cathedralstl.org collected 2026-10-02 09:58:09 UTC 3,171 bytes, 87 lines download raw bytes

1import currentlyInCanvasEditor from "../../lib/currentlyInCanvasEditor.js";
2
3/**
4 * "Read more" for a Text component whose author typed [more] in the rich text.
5 *
6 * Progressive enhancement, deliberately: the server renders BOTH halves visible,
7 * and this collapses the tail. With JS off — or if this file fails — the reader
8 * still gets the whole passage rather than a permanently truncated one, and
9 * search engines always see the full text.
10 *
11 * In the Canvas editor nothing is collapsed: an author editing the passage needs
12 * to see what they are writing. That has to be decided CLIENT-side, because the
13 * editor and the Preview share the same cached server render.
14 */
15
16const EXPANDED = "ct-text--expanded";
17
18function label(text) {
19  const button = document.createElement("button");
20  button.type = "button";
21  button.className = "ct-text__toggle";
22  button.textContent = text;
23  return button;
24}
25
26function enhance(root) {
27  if (root.dataset.ctTruncateReady) return;
28  root.dataset.ctTruncateReady = "1";
29
30  const preview = root.querySelector(".ct-text__preview");
31  const rest = root.querySelector(".ct-text__rest");
32  if (!preview || !rest) return;
33
34  // The inline "… Read more" goes INSIDE the preview's last block, so it reads
35  // as a continuation of the sentence rather than a button on its own line.
36  const tail = preview.lastElementChild || preview;
37  const inline = document.createElement("span");
38  inline.className = "ct-text__more";
39
40  // Only add an ellipsis when the preview stops mid-sentence. If the author's
41  // last line already ends in punctuation, "world." + "…" reads as four dots.
42  if (!/[.!?…:;,]["'”’)\]]?\s*$/.test(tail.textContent || "")) {
43    inline.append("… ");
44  } else {
45    inline.append(" ");
46  }
47
48  const more = label(Drupal.t("Read more"));
49  more.setAttribute("aria-expanded", "false");
50  if (rest.id) more.setAttribute("aria-controls", rest.id);
51  inline.appendChild(more);
52  tail.appendChild(inline);
53
54  const less = label(Drupal.t("Show less"));
55  less.classList.add("ct-text__toggle--less");
56  rest.appendChild(less);
57
58  const setExpanded = (open) => {
59    root.classList.toggle(EXPANDED, open);
60    more.setAttribute("aria-expanded", String(open));
61    inline.hidden = open;
62    if (open) {
63      // Move focus to the revealed text so keyboard and screen-reader users
64      // land on the new content instead of staying at the collapsed point.
65      rest.setAttribute("tabindex", "-1");
66      rest.focus({ preventScroll: true });
67    } else {
68      more.focus();
69    }
70  };
71
72  more.addEventListener("click", () => setExpanded(true));
73  less.addEventListener("click", () => setExpanded(false));
74
75  // This class — not the server-rendered attribute — is what the CSS hides on,
76  // so the tail only ever collapses once this script has run successfully.
77  root.classList.add("ct-text--collapsible");
78  root.classList.remove(EXPANDED);
79}
80
81Drupal.behaviors.sitethemeTextTruncate = {
82  attach(context) {
83    if (currentlyInCanvasEditor()) return;
84    const scope = context && context.querySelectorAll ? context : document;
85    scope.querySelectorAll("[data-ct-truncate]").forEach(enhance);
86  },
87};

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.