PageSourceSearch

https://www.stylenote.co.jp/books/assets/news.js?v=20260928

js stylenote.co.jp collected 2026-10-02 18:43:06 UTC 23,190 bytes, 565 lines download raw bytes

1/* ============================================================================
2   news.js  ニュース&リリース・広告掲載情報 用の追加JS(2026-09-28)
3   置き場所:/books/assets/news.js(FTP)。フッターの Advanced Text に
4             script タグ(?v= 付き)を1行足して読み込む。全ページで読まれるので、
5             対象ページ以外では最初の判定で何もせずに終わる。
6   見た目は追加CSS【22】(news.css)。このJSは #primary に .nw-js を付けて
7   【22】の 22-9 以降に書いた形を組むだけ。JS が動かなくても【22】の土台で読める。
8
9   ・既存ノードは移動とクラス・属性の付け足しだけ。文字の書き換え・削除はしない。
10     例外は仕様で決めた1つだけ:一覧で「...」で切れたタイトルを、リンクの title 属性
11     (「Permanent Link to 全文」)の全文に戻す。
12   ・新しく作るのは入れ物・見出しまわり(切替・英字ラベル・表の見出し行)・
13     日付の表示用の写し(YYYY.MM.DD)・「掲載予定」・対象書籍の書名の写し・
14     書籍カード・一覧へ戻るボタン。元のノードで使わないものは .nw-src で隠すだけ
15   ・広告の掲載日:記事のスラッグ ad{YYYYMMDD}(末尾に b などが付くことがある)。
16     本文の「YYYY年M月D日の」と食い違えば本文を優先(スラッグの年の誤りがあるため)。
17     どちらも無ければ投稿日
18   ・広告記事の書籍:版元ドットコムの埋め込みが出ていれば、その中身を移してカードに組む。
19     出ていなければ(「対象の書籍はみつかりません」)og:image の ISBN と本文の『』から組み、
20     /data/books/index.json(自社に置いた書誌の写し)で定価・発売日を補う。
21     取れなければ書名+「書誌ページへ」だけ
22   ・書影:/data/bookimg/covers.json にある本は自社 _250 → 版元ドットコム(3秒で見切る)
23     → 代替表紙(【19】の .bd-cover-alt)。無い本は版元ドットコム → 代替表紙
24   ・手元確認用に ?nw-today=YYYYMMDD で「今日」を変えられる(「掲載予定」の確認用)
25   ============================================================================ */
26(function () {
27  'use strict';
28
29  /* ---- 設定(テーマやプラグインの出力が変わったら、ここだけ直す) ---- */
30  var SEL = {
31    primary: '#primary',
32    singleNews: '.content.s-category-news',       /* 記事:body にカテゴリが出ないので、テーマの入れ物で見分ける */
33    singleAd: '.content.s-category-adnews',
34    crumbs: 'ul.breadcrumbs',
35    pageTitle: 'h2.page-title',                   /* 一覧の見出し(» 付き・.hide) */
36    post: 'div.post.hentry',                      /* 一覧:1件分 */
37    titleLink: 'h2.entry-title > a[rel="bookmark"]',
38    published: '.entry-header .published',        /* 投稿日「2026年7月23日」 */
39    excerpt: '.entry-content > p',
40    entry: 'div.hentry',                          /* 記事:本体 */
41    header: '.entry-header',
42    content: '.entry-content',
43    nav: '.navigation-links.single-page-navigation',
44    navLink: '.nav-previous > a, .nav-next > a',
45    bookList: 'ul.side_by_side',                  /* 広告記事:版元ドットコムの埋め込み(1冊ごとに1つ) */
46    bookItem: 'li',
47    bookLink: 'a[href*="/bd/isbn/"]',
48    bookTitle: 'strong.h5',
49    bookDate: 'span.h6',
50    bookPrice: 'strong.text-danger',
51    bookTax: 'span.small',
52    ogImage: 'meta[property="og:image"]'
53  };
54  var URL_NEWS = '/news/';
55  var URL_ADNEWS = '/adnews/';
56  var URL_BD = '/bd/isbn/';
57  var URL_SEARCH_TITLE = '/bd/search/title/';
58  var OWN_IMG = '/data/bookimg/';
59  var OWN_SIZE = '_250.jpg';
60  var COVERS_JSON = '/data/bookimg/covers.json';
61  var BOOKS_INDEX = '/data/books/index.json';
62  var HANMOTO_IMG = 'https://www.hanmoto.com/bd/img/';
63  var HANMOTO_SIZE = '_130.jpg';
64  var TIMEOUT = 3000;
65  var TITLE_PREFIX = /^\s*Permanent Link to\s*/;
66  var TAX = 1.1;
67
68  var primary = document.querySelector(SEL.primary);
69  if (!primary || /(^| )nw-js( |$)/.test(primary.className)) { return; }
70
71  /* ---- ページの種類 ---- */
72  var body = document.body;
73  var mode = null;
74  if (/(^| )category-news( |$)/.test(body.className)) {
75    mode = 'news';
76  } else if (/(^| )category-adnews( |$)/.test(body.className)) {
77    mode = 'adlist';
78  } else if (document.querySelector(SEL.singleNews + ' ' + SEL.primary)) {
79    mode = 'newsitem';
80  } else if (document.querySelector(SEL.singleAd + ' ' + SEL.primary)) {
81    mode = 'aditem';
82  }
83  if (!mode) { return; }
84
85  /* ---- 小さな道具 ---- */
86  function el(tag, cls, str) {
87    var e = document.createElement(tag);
88    if (cls) { e.className = cls; }
89    if (str) { e.textContent = str; }
90    return e;
91  }
92  function link(href, cls, str) {
93    var a = el('a', cls, str);
94    a.setAttribute('href', href);
95    return a;
96  }
97  function text(e) {
98    return e ? (e.textContent || '').replace(/\s+/g, ' ').trim() : '';
99  }
100  function addClass(e, c) {
101    if (e && (' ' + e.className + ' ').indexOf(' ' + c + ' ') < 0) {
102      e.className = (e.className ? e.className + ' ' : '') + c;
103    }
104  }
105  function each(list, fn) {
106    for (var i = 0; i < list.length; i++) { fn(list[i], i); }
107  }
108  function after(ref, node) {
109    ref.parentNode.insertBefore(node, ref.nextSibling);
110  }
111
112  /* ---- 日付 ---- */
113  function mkDate(y, m, d) {
114    y = +y; m = +m; d = +d;
115    if (!y || m < 1 || m > 12 || d < 1 || d > 31) { return null; }
116    return { y: y, m: m, d: d };
117  }
118  function pad(n) { return (n < 10 ? '0' : '') + n; }
119  function fmt(d) { return d.y + '.' + pad(d.m) + '.' + pad(d.d); }
120  function iso(d) { return d.y + '-' + pad(d.m) + '-' + pad(d.d); }
121  function key(d) { return d.y * 10000 + d.m * 100 + d.d; }
122  function jaDate(s) {
123    var m = (s || '').match(/(\d{4})年(\d{1,2})月(\d{1,2})日/);
124    return m ? mkDate(m[1], m[2], m[3]) : null;
125  }
126  /* 本文の「2026年10月3日の毎日新聞」の日付 */
127  function bodyDate(s) {
128    var m = (s || '').match(/(\d{4})年(\d{1,2})月(\d{1,2})日の/);
129    return m ? mkDate(m[1], m[2], m[3]) : null;
130  }
131  /* スラッグ ad20261003(ad20250612b のように後ろに文字が付くことがある) */
132  function slugDate(href) {
133    var m = (href || '').match(/\/ad(\d{4})(\d{2})(\d{2})[a-z]?\/?(?:[?#]|$)/);
134    return m ? mkDate(m[1], m[2], m[3]) : null;
135  }
136  function adDate(slug, str, posted) {
137    var b = bodyDate(str);
138    if (slug && b && key(slug) !== key(b)) { return b; }
139    return slug || b || posted;
140  }
141  function todayKey() {
142    var m = location.search.match(/[?&]nw-today=(\d{8})/);
143    if (m) { return +m[1]; }
144    var t = new Date();
145    return t.getFullYear() * 10000 + (t.getMonth() + 1) * 100 + t.getDate();
146  }
147  var TODAY = todayKey();
148
149  function timeEl(d) {
150    var t = el('time', 'nw-date', fmt(d));
151    t.setAttribute('datetime', iso(d));
152    return t;
153  }
154  function upcomingTag(d) {
155    return key(d) >= TODAY ? el('span', 'nw-tag', '掲載予定') : null;
156  }
157  /* 本文・抜粋から『』で囲まれた書名を取り出す(重複は1つに) */
158  function bookTitles(str) {
159    var out = [];
160    var re = /『([^』]+)』/g;
161    var m;
162    while ((m = re.exec(str || ''))) {
163      if (out.indexOf(m[1]) < 0) { out.push(m[1]); }
164    }
165    return out;
166  }
167
168  /* ---- 一覧:見出し(パンくず+見出し+2ページの切替) ---- */
169  function buildListHead(isAd) {
170    var h = primary.querySelector(SEL.pageTitle);
171    if (!h) { return; }
172    var crumbs = primary.querySelector(SEL.crumbs);
173    var head = el('div', 'nw-head');
174    var main = el('div', 'nw-head-main');
175    primary.insertBefore(head, crumbs && crumbs.parentNode === primary ? crumbs : h);
176    if (crumbs) { main.appendChild(crumbs); }
177    main.appendChild(h);
178    head.appendChild(main);
179
180    var seg = el('nav', 'nw-seg');
181    seg.setAttribute('aria-label', 'ニュースの種類');
182    [['ニュース&リリース', URL_NEWS, !isAd], ['広告掲載情報', URL_ADNEWS, isAd]].forEach(function (s) {
183      var a = link(s[1], '', s[0]);
184      if (s[2]) { a.setAttribute('aria-current', 'page'); }
185      seg.appendChild(a);
186    });
187    head.appendChild(seg);
188  }
189
190  /* 切れたタイトルを title 属性の全文に戻す */
191  function fullTitle(a) {
192    var t = (a.getAttribute('title') || '').replace(TITLE_PREFIX, '').trim();
193    if (t && text(a) !== t) { a.textContent = t; }
194  }
195
196  /* 投稿日を YYYY.MM.DD の写しに(元の span は隠す) */
197  function copyDate(span) {
198    var d = span && jaDate(text(span));
199    if (!d) { return null; }
200    addClass(span, 'nw-src');
201    after(span, timeEl(d));
202    return d;
203  }
204
205  /* 抜粋の末尾の「[…]」を span に包んで隠す */
206  function hideEllipsis(p) {
207    var n = p && p.lastChild;
208    if (!n || n.nodeType !== 3) { return; }
209    var m = n.nodeValue.match(/\s*\[(?:…|\.\.\.)\]\s*$/);
210    if (!m) { return; }
211    var tail = n.splitText(m.index);
212    var s = el('span', 'nw-src');
213    p.insertBefore(s, tail);
214    s.appendChild(tail);
215  }
216
217  /* ---- A. ニュース一覧(1a) ---- */
218  function buildNewsList() {
219    buildListHead(false);
220    each(primary.querySelectorAll(SEL.post), function (post) {
221      var a = post.querySelector(SEL.titleLink);
222      if (a) { fullTitle(a); }
223      copyDate(post.querySelector(SEL.published));
224      hideEllipsis(post.querySelector(SEL.excerpt));
225    });
226  }
227
228  /* ---- B. 広告一覧(2b):掲載日|掲載|対象書籍|→ の表 ---- */
229  function buildAdList() {
230    buildListHead(true);
231    var posts = primary.querySelectorAll(SEL.post);
232    if (!posts.length) { return; }
233    var thead = el('div', 'nw-thead');
234    thead.setAttribute('aria-hidden', 'true');
235    ['掲載日', '掲載', '対象書籍', ''].forEach(function (s) { thead.appendChild(el('span', '', s)); });
236    posts[0].parentNode.insertBefore(thead, posts[0]);
237
238    each(posts, function (post) {
239      var a = post.querySelector(SEL.titleLink);
240      if (a) { fullTitle(a); }
241      var ex = text(post.querySelector(SEL.excerpt));
242      var posted = jaDate(text(post.querySelector(SEL.published)));
243      var d = adDate(slugDate(a && a.getAttribute('href')), ex, posted);
244
245      var cell = el('div', 'nw-ad-date');
246      if (d) {
247        cell.appendChild(timeEl(d));
248        var tag = upcomingTag(d);
249        if (tag) { cell.appendChild(tag); }
250      }
251      post.insertBefore(cell, post.firstChild);
252
253      var titles = bookTitles(ex);
254      post.appendChild(el('span', 'nw-ad-books', titles.length ? '『' + titles.join('』『') + '』' : ''));
255      addClass(post, 'nw-ad-row');
256    });
257  }
258
259  /* ---- 書影:自社 → 版元ドットコム → 代替表紙(booklist.js と同じ考え方) ---- */
260  var coversPromise = null;
261  function fetchJson(url) {
262    if (!window.fetch || !window.Promise) { return { then: function (f) { f(null); } }; }
263    return Promise.race([
264      fetch(url).then(function (r) { return r.ok ? r.json() : null; }).catch(function () { return null; }),
265      new Promise(function (res) { setTimeout(function () { res(null); }, TIMEOUT); })
266    ]);
267  }
268  function ownCovers(done) {
269    if (!coversPromise) { coversPromise = fetchJson(COVERS_JSON); }
270    coversPromise.then(function (d) {
271      var own = {};
272      each((d && d.isbn) || [], function (s) { own[s] = true; });
273      done(own);
274    });
275  }
276  function drawAlt(img, isbn, title) {
277    var box = img.parentNode;
278    if (!box || box.querySelector('.bd-cover-alt')) { return; }
279    var alt = el('div', 'bd-cover-alt');
280    alt.setAttribute('role', 'img');
281    alt.setAttribute('aria-label', (title || '書籍') + '(書影なし)');
282    alt.appendChild(el('div', 'bd-cover-alt-brand', 'STYLENOTE'));
283    var mid = el('div');
284    mid.appendChild(el('p', 'bd-cover-alt-title', title || ''));
285    alt.appendChild(mid);
286    alt.appendChild(el('div', 'bd-cover-alt-foot', 'ISBN' + isbn));
287    img.style.display = 'none';
288    box.appendChild(alt);
289  }
290  function waitImg(img, timeout, ok, ng) {
291    var done = false;
292    var timer = null;
293    function finish(good) {
294      if (done) { return; }
295      done = true;
296      clearTimeout(timer);
297      img.removeEventListener('load', onLoad);
298      img.removeEventListener('error', onError);
299      if (good) { ok(); } else { ng(); }
300    }
301    function onLoad() { finish(img.naturalWidth > 0); }
302    function onError() { finish(false); }
303    if (img.complete && img.getAttribute('src')) {
304      finish(img.naturalWidth > 0);
305      return;
306    }
307    img.addEventListener('load', onLoad);
308    img.addEventListener('error', onError);
309    if (timeout) {
310      timer = setTimeout(function () { finish(img.complete && img.naturalWidth > 0); }, timeout);
311    }
312  }
313  /* img の src は版元ドットコムのもの(無ければ空)で渡す */
314  function setCover(img, isbn, title) {
315    var hanmoto = img.getAttribute('src') || HANMOTO_IMG + isbn + HANMOTO_SIZE;
316    function toHanmoto() {
317      img.setAttribute('src', hanmoto);
318      waitImg(img, TIMEOUT, function () {}, function () { drawAlt(img, isbn, title); });
319    }
320    img.setAttribute('decoding', 'async');
321    ownCovers(function (own) {
322      if (own[isbn]) {
323        img.setAttribute('src', OWN_IMG + isbn + OWN_SIZE);
324        waitImg(img, 0, function () {}, toHanmoto);
325      } else if (img.getAttribute('src')) {
326        waitImg(img, TIMEOUT, function () {}, function () { drawAlt(img, isbn, title); });
327      } else {
328        toHanmoto();
329      }
330    });
331  }
332
333  /* ---- 書籍カード ---- */
334  function bookButton(isbn) {
335    var a = link(URL_BD + isbn + '/', 'nw-book-btn');
336    a.appendChild(el('span', '', '書誌ページへ'));
337    a.appendChild(el('span', 'nw-arrow', '→'));
338    return a;
339  }
340  function cardShell() {
341    var card = el('div', 'nw-book');
342    var cover = el('div', 'nw-book-cover');
343    var info = el('div', 'nw-book-info');
344    info.appendChild(el('span', 'nw-book-label', '広告掲載書籍'));
345    card.appendChild(cover);
346    card.appendChild(info);
347    return { card: card, cover: cover, info: info };
348  }
349  /* 版元ドットコムの埋め込み(li)の中身を移して組む */
350  function cardFromItem(li) {
351    var a = li.querySelector(SEL.bookLink);
352    var m = a && (a.getAttribute('href') || '').match(/(97[89]\d{10})/);
353    if (!m) { return null; }
354    var isbn = m[1];
355    var c = cardShell();
356    var img = a.querySelector('img');
357    addClass(a, 'nw-book-img');
358    c.cover.appendChild(a);
359    var t = li.querySelector(SEL.bookTitle);
360    if (t) { addClass(t, 'nw-book-title'); c.info.appendChild(t); }
361    var d = li.querySelector(SEL.bookDate);
362    if (d) { addClass(d, 'nw-book-date'); c.info.appendChild(d); }
363    var p = li.querySelector(SEL.bookPrice);
364    if (p) {
365      var row = el('div', 'nw-book-price');
366      row.appendChild(p);
367      var tax = li.querySelector(SEL.bookTax);
368      if (tax) { row.appendChild(tax); }
369      c.info.appendChild(row);
370    }
371    c.info.appendChild(bookButton(isbn));
372    if (img) { setCover(img, isbn, img.getAttribute('alt') || text(t)); }
373    return c.card;
374  }
375  /* 埋め込みが無いとき:ISBN と書名から組む。data は index.json の1冊分(無ã
375‘れば null) */
376  function cardFromData(isbn, title, data) {
377    var c = cardShell();
378    var name = (data && data.title) || title || '';
379    var a = link(URL_BD + isbn + '/', 'nw-book-img');
380    var img = el('img');
381    img.setAttribute('alt', name);
382    a.appendChild(img);
383    c.cover.appendChild(a);
384    if (name) {
385      var t = el('strong', 'nw-book-title');
386      t.appendChild(link(URL_BD + isbn + '/', '', name));
387      c.info.appendChild(t);
388    }
389    if (data && /^\d{8}$/.test(data.date_release || '')) {
390      var r = data.date_release;
391      c.info.appendChild(el('span', 'nw-book-date', '書店発売日 ' + r.slice(0, 4) + '.' + r.slice(4, 6) + '.' + r.slice(6, 8)));
392    }
393    if (data && +data.price > 0) {
394      var row = el('div', 'nw-book-price');
395      row.appendChild(el('strong', '', Math.round(+data.price * TAX).toLocaleString('ja-JP') + '円'));
396      row.appendChild(el('span', 'nw-tax', '(税込)'));
397      c.info.appendChild(row);
398    }
399    c.info.appendChild(bookButton(isbn));
400    setCover(img, isbn, name);
401    return c.card;
402  }
403  /* ISBN が分からない2冊目以降:書名だけの行(書名の検索へ) */
404  function bookLine(title) {
405    var p = el('p', 'nw-book-line');
406    p.appendChild(link(URL_SEARCH_TITLE + encodeURIComponent(title) + '/', '', '『' + title + '』'));
407    return p;
408  }
409  function norm(s) { return (s || '').replace(/[\s ]+/g, ''); }
410  /* 書名で index.json の本を探す。完全一致を優先し、無ければ「書名+《…》など」の前方一致で一番長いもの。
411     それも無く、『日本の繊維と人Ⅰ・Ⅱ』のように巻をまとめた書き方なら、その巻を全部(書名順) */
412  var VOLS = '[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ上中下0-90-9]';
413  function findBooks(books, title) {
414    var one = findBook(books, title);
415    if (one) { return [one]; }
416    var n = norm(title);
417    var m = n.match(new RegExp('^(.{4,}?)' + VOLS + '+(?:[・,、~〜-]' + VOLS + '+)+$'));
418    if (!m) { return []; }
419    var re = new RegExp('^' + VOLS + '+$');
420    return books.filter(function (b) {
421      var bt = norm(b.title);
422      return bt.indexOf(m[1]) === 0 && re.test(bt.slice(m[1].length));
423    }).sort(function (a, b) { return a.title < b.title ? -1 : 1; });
424  }
425  function findBook(books, title) {
426    var n = norm(title);
427    var hit = null;
428    each(books, function (b) {
429      var bt = norm(b.title);
430      if (!bt) { return; }
431      if (bt === n) { hit = { b: b, len: 1e9 }; }
432      else if (bt.length >= 4 && n.indexOf(bt) === 0 && /^[《((〈【]/.test(n.slice(bt.length)) && (!hit || bt.length > hit.len)) {
433        hit = { b: b, len: bt.length };
434      }
435    });
436    return hit && hit.b;
437  }
438  function byIsbn(books, isbn) {
439    for (var i = 0; i < books.length; i++) { if (books[i].isbn13 === isbn) { return books[i]; } }
440    return null;
441  }
442
443  function buildBooks(content, bodyEl, firstText) {
444    var lists = content.querySelectorAll(SEL.bookList);
445    var box = el('div', 'nw-books');
446    if (lists.length) { lists[0].parentNode.insertBefore(box, lists[0]); } else { bodyEl.appendChild(box); }
447
448    var items = [];
449    each(lists, function (ul) {
450      each(ul.querySelectorAll(SEL.bookItem), function (li) { items.push(li); });
451      addClass(ul, 'nw-src');
452    });
453
454    /* 1. 埋め込みが出ている:中身を移す */
455    var made = 0;
456    each(items, function (li) {
457      var card = cardFromItem(li);
458      if (card) { box.appendChild(card); made++; }
459    });
460    if (made) { return; }
461
462    /* 2. 出ていない:og:image の ISBN と本文の『』から */
463    var og = document.querySelector(SEL.ogImage);
464    var m = og && (og.getAttribute('content') || '').match(/\/bd\/img\/(97[89]\d{10})/);
465    var ogIsbn = m ? m[1] : '';
466    var titles = bookTitles(firstText);
467    if (!ogIsbn && !titles.length) { box.parentNode.removeChild(box); return; }
468
469    var loading = el('p', 'nw-book-loading', '読み込み中');
470    box.appendChild(loading);
471    fetchJson(BOOKS_INDEX).then(function (d) {
472      var books = (d && d.books) || [];
473      box.removeChild(loading);
474      var done = {};
475      var list = titles.length ? titles : [''];
476      each(list, function (title, i) {
477        var hits = title ? findBooks(books, title) : [];
478        if (!hits.length && i === 0 && ogIsbn) { hits = [byIsbn(books, ogIsbn) || { isbn13: ogIsbn }]; }
479        var shown = 0;
480        each(hits, function (b) {
481          if (done[b.isbn13]) { return; }
482          done[b.isbn13] = true;
483          box.appendChild(cardFromData(b.isbn13, title, b.title ? b : null));
484          shown++;
485        });
486        if (!shown && title && !hits.length) { box.appendChild(bookLine(title)); }
487      });
488      if (!box.firstChild) { box.parentNode.removeChild(box); }
489    });
490  }
491
492  /* ---- C/D. 記事(3a・3b) ---- */
493  function buildItem(isAd) {
494    var entry = primary.querySelector(SEL.entry);
495    var header = entry && entry.querySelector(SEL.header);
496    var content = entry && entry.querySelector(SEL.content);
497    if (!entry || !header || !content) { return false; }
498
499    /* 見出しの左:英字ラベル+日付(広告は掲載日と「掲載予定」) */
500    var span = header.querySelector('.published');
501    var posted = span ? jaDate(text(span)) : null;
502    header.insertBefore(el('span', 'nw-kicker', isAd ? '広告掲載' : 'NEWS'), header.firstChild);
503    if (!isAd) {
504      copyDate(span);
505    } else {
506      var d = adDate(slugDate(location.pathname), text(content), posted);
507      if (span) { addClass(span.parentNode.nodeName === 'A' ? span.parentNode : span, 'nw-src'); }
508      if (d) {
509        header.appendChild(timeEl(d));
510        var tag = upcomingTag(d);
511        if (tag) { header.appendChild(tag); }
512      }
513    }
514
515    /* 本文:左の列(広告は「投稿 YYYY.MM.DD」)と右の本文に分ける */
516    var side = el('div', 'nw-side');
517    var main = el('div', 'nw-body');
518    while (content.firstChild) { main.appendChild(content.firstChild); }
519    if (isAd && posted) {
520      side.appendChild(el('span', 'nw-posted-label', '投稿'));
521      side.appendChild(timeEl(posted));
522    }
523    content.appendChild(side);
524    content.appendChild(main);
525
526    if (isAd) {
527      var first = main.querySelector('p');
528      buildBooks(main, main, text(first));
529    }
530
531    /* 前後の記事:広告はスラッグから日付を出す(前が新しい・次が古いなど、並びと合わなければ出さない) */
532    var nav = primary.querySelector(SEL.nav);
533    var here = isAd ? adDate(slugDate(location.pathname), text(content), posted) : posted;
534    if (nav && isAd && here) {
535      each(nav.querySelectorAll(SEL.navLink), function (a) {
536        var nd = slugDate(a.getAttribute('href'));
537        if (!nd) { return; }
538        var isPrev = /nav-previous/.test(a.parentNode.className);
539        if (isPrev ? key(nd) <= key(here) : key(nd) >= key(here)) {
540          a.setAttribute('data-nw-date', fmt(nd));
541        }
542      });
543    }
544
545    /* 末尾:一覧へ戻る */
546    var back = link(isAd ? URL_ADNEWS : URL_NEWS, 'nw-back');
547    back.appendChild(el('span', '', isAd ? '広告掲載情報一覧へ' : 'ニュース&リリース一覧へ'));
548    back.appendChild(el('span', 'nw-arrow', '→'));
549    var wrap = el('div', 'nw-back-wrap');
550    wrap.appendChild(back);
551    after(nav || entry, wrap);
552    return true;
553  }
554
555  /* ---- 実行 ---- */
556  var ok = true;
557  if (mode === 'news') { buildNewsList(); }
558  else if (mode === 'adlist') { buildAdList(); }
559  else { ok = buildItem(mode === 'aditem'); }
560  if (ok) {
561    addClass(primary, 'nw-js');
562    addClass(primary, 'nw-' + mode);
563    if (mode === 'newsitem' || mode === 'aditem') { addClass(primary, 'nw-item'); }
564  }
565})();

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.