PageSourceSearch

https://www.toli.co.jp/js/toli-consent-loader.js?20261001

js toli.co.jp collected 2026-10-02 07:19:02 UTC 10,481 bytes, 210 lines download raw bytes

1/*!
2 * toli-consent-loader.js
3 * PP同意ゲート+C360A(MC Personalization)起動ローダー
4 * 方式: パターン3(Webサイト側で同意後にSDKを読み込む)/A案・自社実装
5 * 設計書: docs/pp-consent/consent-implementation-design.md
6 * 依存: /js/vendor/cookieconsent-3.1.0.umd.js(先読み必須・自社配信)
7 *
8 * 動作原則:
9 *   - 同意前は C360A ビーコンを一切ロードしない(Personalization許諾は
10 *     SDK側でデフォルトONのため、ロード=送信開始。ゲートはここが唯一の砦)
11 *   - 既同意なら head 解析時点で即時ロード(早期起動)
12 *   - 初回同意は 保存→1回リロード(その場昇格は ZeroFlicker 非保証のため不採用)
13 *   - 撤回は OptOut通知(暫定)→SF系cookie削除→リロードで静的フォールバック復元
14 */
15(function (w, d) {
16  'use strict';
17
18  /* ===== 設定 ===== */
19  // 本番ビーコンID(2026-10-01 公開・東リ様経由でベンダー支給)。検証環境 ai-prv-toli は preview-sync の注入スクリプトで検証用IDに書き戻す
20  var BEACON_SRC = 'https://cdn.c360a.salesforce.com/beacon/c360a/1a237dc6-b37c-4232-8ed1-c7b231835dc9/scripts/c360a.min.js';
21  var COOKIE_NAME = 'toli_cc';   // first-party cookie 単一正本
22  var REVISION = 2;              // 同意文言・対象を変えたら +1(再同意を促す)
23  var EXPIRES_DAYS = 182;
24  var CATEGORY = 'personalization';
25  var POLICY_PAGE = '/info/privacy1.html';
26  var COOKIE_POLICY = 'https://www.toli.co.jp/info/privacy2.html'; // バナー本文「Cookieポリシー」リンク先
27
28  /* ===== C360A起動(1回だけ・二重起動防止) ===== */
29  function loadBeacon() {
30    if (w.__toliC360aRequested) { return; }
31    w.__toliC360aRequested = true;
32    var s = d.createElement('script');
33    s.src = BEACON_SRC;
34    s.async = false; // head早期の既同意時に読み込み順序を維持する
35    s.onerror = function () {
36      // ロード失敗時は静的フォールバックのまま(リトライは次回訪問時)
37      w.__toliC360aRequested = false;
38    };
39    var ref = d.currentScript || d.getElementsByTagName('script')[0];
40    ref.parentNode.insertBefore(s, ref);
41  }
42
43  /* ===== 既同意の早期判定(CookieConsent初期化を待たず cookie を直接読む) ===== */
44  function hasStoredConsent() {
45    try {
46      var m = d.cookie.match(new RegExp('(?:^|; )' + COOKIE_NAME + '=([^;]*)'));
47      if (!m) { return false; }
48      var v = JSON.parse(decodeURIComponent(m[1]));
49      if (!v || v.revision !== REVISION) { return false; }
50      return !!(v.categories && v.categories.indexOf(CATEGORY) !== -1);
51    } catch (e) {
52      return false; // 破損cookieは未同意扱い→バナー再表示に任せる
53    }
54  }
55
56  if (hasStoredConsent()) { loadBeacon(); }
57
58  /* ===== 撤回処理(OptOut通知は best-effort・SDK は getSalesforceInteractions() で取得) ===== */
59  function withdraw() {
60    try {
61      var SI = (typeof w.getSalesforceInteractions === 'function') ? w.getSalesforceInteractions() : w.SalesforceInteractions;
62      if (SI && typeof SI.updateConsents === 'function' && SI.ConsentStatus) {
63        SI.updateConsents([{ purpose: 'Personalization', status: SI.ConsentStatus.OptOut }]);
64      }
65    } catch (e) { /* OptOut通知は best-effort */ }
66    // SF系cookieの削除(正式な全cookie一覧はベンダー確認Cの回答で更新)
67    var names = d.cookie.split(';');
68    var host = w.location.hostname;
69    var domains = ['', host, '.' + host, '.' + host.split('.').slice(-2).join('.')];
70    for (var i = 0; i < names.length; i++) {
71      var name = names[i].split('=')[0].replace(/^\s+/, '');
72      if (/^(_evga_|evergage|_sfid_)/.test(name)) {
73        for (var j = 0; j < domains.length; j++) {
74          d.cookie = name + '=; Max-Age=0; path=/' + (domains[j] ? '; domain=' + domains[j] : '');
75        }
76      }
77    }
78    w.location.reload(); // リロードで静的フォールバックへ復元
79  }
80
81  /* 再オープン導線はフッターの「Cookieの設定」リンク(data-cc="show-preferencesModal")が担う
82     (旧・左下常駐「プライバシー設定」ピルは 2026-08-26 クライアント指示で削除) */
83
84  /* ===== バナーの X ボタン(閉じる=同意しない) =====
85     UI/UX チーム答申(2026-08-26): 右上角・ヒット領域40px(SP44px)・SVG 1.5px stroke・
86     aria-label は副作用(拒否保存)まで伝える「同意せずに閉じる」。
87     bar レイアウトでは本体組込の closeIcon が出なã
87„ため独自挿入。
88     動作は本体 X と同じ acceptCategory([])=必須のみ承認(=拒否として保存)。 */
89  function rejectAndHide() {
90    if (w.CookieConsent) {
91      w.CookieConsent.acceptCategory([]);
92      w.CookieConsent.hide();
93    }
94  }
95
96  function reorderButtons(modal) {
97    var g = modal && modal.querySelector('.cm__btn-group');
98    if (!g) { return; }
99    var show = g.querySelector('[data-role="show"]');
100    var all  = g.querySelector('[data-role="all"]');
101    if (show && all && show.nextElementSibling !== all) { g.insertBefore(show, all); }
102  }
103
104  function addCloseX(modal) {
105    if (!modal || modal.querySelector('.toli-cc-close')) { return; }
106    var b = d.createElement('button');
107    b.type = 'button';
108    b.className = 'toli-cc-close';
109    b.setAttribute('aria-label', '同意せずに閉じる');
110    b.innerHTML = '<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false">' +
111      '<path d="M2 2 L14 14 M14 2 L2 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>';
112    b.addEventListener('click', rejectAndHide);
113    modal.appendChild(b); // DOM末尾=Tab順は 本文リンク→詳細設定→同意する→X
114  }
115
116  // Esc はバナー表示中のみ X と完全同一動作(設定モーダル表示中はそちらの Esc に譲る)
117  function onEscKey(e) {
118    if (e.keyCode !== 27) { return; }
119    var root = d.documentElement;
120    if (root.classList.contains('show--consent') && !root.classList.contains('show--preferences')) {
121      rejectAndHide();
122    }
123  }
124
125  /* ===== CookieConsent v3 初期化 ===== */
126  function init() {
127    if (!w.CookieConsent) { return; }
128    w.CookieConsent.run({
129      cookie: { name: COOKIE_NAME, expiresAfterDays: EXPIRES_DAYS, sameSite: 'Lax' },
130      revision: REVISION,
131      disablePageInteraction: false, // 通知型寄り: 閲覧は妨げない
132      guiOptions: {
133        consentModal: { layout: 'bar inline', position: 'bottom center', equalWeightButtons: false, flipButtons: false },
134        preferencesModal: { layout: 'box', equalWeightButtons: true }
135      },
136      categories: {
137        necessary: { readOnly: true, enabled: true },
138        personalization: {}
139      },
140      onModalReady: function (p) {
141        if (p && p.modalName === 'consentModal') { reorderButtons(p.modal); addCloseX(p.modal); }
142      },
143      onFirstConsent: function (p) {
144        // 初回同意: 保存→1回リロード(リロード後は早期判定が即時ロード)。拒否時は何もしない
145        if (p && p.cookie && p.cookie.categories.indexOf(CATEGORY) !== -1) {
146          w.location.reload();
147        }
148      },
149      onChange: function (p) {
150        var accepted = p.cookie.categories.indexOf(CATEGORY) !== -1;
151        if (accepted && !w.__toliC360aRequested) { w.location.reload(); return; }
152        if (!accepted && w.__toliC360aRequested) { withdraw(); }
153      },
154      language: {
155        default: 'ja',
156        translations: {
157          ja: {
158            consentModal: {
159              title: 'Cookieの使用について',
160              description: '当サイトでは、お客様により快適にご利用いただくため、Cookieを使用しています。Cookieは、サイトの利用状況の分析、サービスの改善、およびお客様に適した情報提供のために利用されます。<br>Cookieの利用に同意いただける場合は「同意する」をクリックしてください。詳細は「<a href="' + COOKIE_POLICY + '">Cookieポリシー</a>」をご確認ください。なお、Cookieの設定はいつでも変更できます。',
161              acceptAllBtn: '同意する',
162              showPreferencesBtn: '詳細設定'
163            },
164            preferencesModal: {
165              title: 'Cookieの詳細設定',
166              acceptAllBtn: 'すべて同意する',
167              acceptNecessaryBtn: 'すべて拒否する',
168              savePreferencesBtn: '設定を保存する',
169              closeIconLabel: '閉じる',
170              sections: [
171                {
172                  title: 'Cookieの利用について',
173                  description: '当サイトでのCookieの利用目的をカテゴリごとに選択できます。設定はいつでもこの画面から変更できます。'
174                },
175                {
176                  title: '必須Cookie',
177                  description: 'サイトの基本機能の提供に必要なもので、無効にできません。',
178                  linkedCategory: 'necessary'
179                },
180                {
181                  title: 'パーソナライズ(おすすめ表示)',
182                  description: '閲覧状況に応じたおすすめコンテンツの表示のã
182Ÿã‚ã€å¤–部サービス(Salesforce)に閲覧情報が送信されます。無効の場合は標準のコンテンツが表示されます。',
183                  linkedCategory: 'personalization'
184                },
185                {
186                  title: 'プライバシーポリシー',
187                  description: 'Cookieの取り扱いを含む個人情報保護の詳細については、<a href="' + POLICY_PAGE + '">プライバシーポリシー</a>をご覧ください。'
188                }
189              ]
190            }
191          }
192        }
193      }
194    });
195
196    d.addEventListener('keydown', onEscKey, true);
197  }
198
199  /* ===== フッター等から呼ぶ公開API(「プライバシー設定」常設リンク用) ===== */
200  w.toliConsent = {
201    showPreferences: function () { if (w.CookieConsent) { w.CookieConsent.showPreferences(); } },
202    showBanner: function () { if (w.CookieConsent) { w.CookieConsent.show(true); } }
203  };
204
205  if (d.readyState === 'loading') {
206    d.addEventListener('DOMContentLoaded', init);
207  } else {
208    init();
209  }
210})(window, document);

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.