PageSourceSearch

http://nmb48.com/js/footerRenderer.js

js nmb48.com collected 2026-10-02 14:40:08 UTC 3,978 bytes, 134 lines download raw bytes

1// JavaScript Document
2(() => {
3  "use strict";
4
5  async function fetchJson(url) {
6    const res = await fetch(url, { cache: "no-cache" });
7    if (!res.ok) throw new Error(`Failed to fetch JSON: ${res.status} ${res.statusText}`);
8    return res.json();
9  }
10
11  function ensureSlashEnd(path) {
12    return String(path || "").replace(/\/?$/, "/");
13  }
14
15  function renderFooterGroup(root, data) {
16    // data: { title: string, items: [{name, url, img, alt}] }
17    const imgBase = ensureSlashEnd(root.dataset.imgBase || "../images/");
18    const imgWidth = Number(root.dataset.imgWidth || 100);
19    const imgHeight = Number(root.dataset.imgHeight || 140);
20
21    const title = data?.title ?? "";
22    const items = Array.isArray(data?.items) ? data.items : [];
23
24    const ul = document.createElement("ul");
25
26    const liTitle = document.createElement("li");
27    liTitle.textContent = title;
28    ul.appendChild(liTitle);
29
30    for (const item of items) {
31      if (!item?.url || !item?.img) continue;
32
33      const li = document.createElement("li");
34
35      const a = document.createElement("a");
36      a.href = String(item.url);
37      a.target = "_blank";
38      a.rel = "noopener noreferrer";
39
40      const img = document.createElement("img");
41      img.src = imgBase + String(item.img).replace(/^\/+/, "");
42      img.alt = item.alt ? String(item.alt) : "";
43      img.width = imgWidth;
44      img.height = imgHeight;
45
46      a.appendChild(img);
47      li.appendChild(a);
48
49      li.insertAdjacentHTML("beforeend", "<br />");
50
51      // name に <br /> を含められるように innerHTML(JSON自前管理前提)
52      const nameSpan = document.createElement("span");
53      nameSpan.innerHTML = item.name ? String(item.name) : "";
54      li.appendChild(nameSpan);
55
56      ul.appendChild(li);
57    }
58
59    const wrap = document.createElement("div");
60    wrap.appendChild(ul);
61
62    root.innerHTML = "";
63    root.appendChild(wrap);
64  }
65
66  function renderFooterBnr(root, data) {
67    // data: { sections: [{label, items:[{url, titleHtml}]}] }
68    const sections = Array.isArray(data?.sections) ? data.sections : [];
69    const container = document.createElement("div");
70
71    for (const sec of sections) {
72      if (!sec?.label || !Array.isArray(sec?.items)) continue;
73
74      const ulOuter = document.createElement("ul");
75
76      const liLabel = document.createElement("li");
77      liLabel.textContent = String(sec.label);
78      ulOuter.appendChild(liLabel);
79
80      const liInnerWrap = document.createElement("li");
81      const ulInner = document.createElement("ul");
82
83      for (const item of sec.items) {
84        if (!item?.url) continue;
85
86        const li = document.createElement("li");
87        const a = document.createElement("a");
88        a.href = String(item.url);
89        a.target = "_blank";
90        a.rel = "noopener noreferrer";
91
92        // <br /> を許容するため innerHTML(JSON自前管理前提)
93        a.innerHTML = item.titleHtml ? String(item.titleHtml) : String(item.url);
94
95        li.appendChild(a);
96        ulInner.appendChild(li);
97      }
98
99      liInnerWrap.appendChild(ulInner);
100      ulOuter.appendChild(liInnerWrap);
101
102      container.appendChild(ulOuter);
103    }
104
105    root.innerHTML = "";
106    root.appendChild(container);
107  }
108
109  const renderers = {
110    footerGroup: renderFooterGroup,
111    footerBnr: renderFooterBnr
112  };
113
114  async function mount(el) {
115    const jsonUrl = el.dataset.json;
116    const renderKey = el.dataset.render;
117
118    if (!jsonUrl || !renderKey || !renderers[renderKey]) return;
119
120    try {
121      const data = await fetchJson(jsonUrl);
122      renderers[renderKey](el, data);
123    } catch (e) {
124      console.error(e);
125      el.innerHTML = "";
126    }
127  }
128
129  document.addEventListener("DOMContentLoaded", () => {
130    document
131      .querySelectorAll("[data-render][data-json]")
132      .forEach((el) => mount(el));
133  });
134})();

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.