---
// サイトの使われ方の記録（2026-10-18）。Web改善の検証（Project の claude/web_improvement_trial.md）のため、
// Cloudflare Web Analytics では取れない「どこまで読んだか・何を押したか・どこで迷ったか」を数える。
// 最初は広く取り、データがたまってから見るものを絞る方針。
//
// 1回のページ表示 = 1件。ページが見えなくなったとき（閉じる・移る・タブを切り替える）に、まとめて /api/ev へ送る。
// 戻ってきてまた見えなくなったら、同じ番号で中身を足して送り直す（数えるときは番号ごとに最後の1件だけ使う）。
//
// 送るもの：ページ・サイト内のどのページから来たか（戻るボタンで戻ったか）・外から来たならそのサイトの名前（ドメイン）だけ・
// 画面の幅（pc/sp）・どこまで読んだか・表示していた時間・押したリンクやボタン（行き先とページのどの部分か）・
// リンクでない所を押した回数・短い間に同じ所を何度も押した回数・コピーした回数・動画を再生した回数・
// フォームに入力を始めたか・送信ボタンを押した回数・
// 人が操作したか（マウスを動かす・押す・指で触れる・ホイール・キーのどれかがあったか）・
// 自動で動くブラウザか（navigator.webdriver と、ブラウザの名乗りに Headless・bot などが入っているかの判定結果だけ）
// 送らないもの：入力した内容・コピーした文章・IP アドレス・ブラウザの種類・Cookie。
// 番号はこの1回の表示のためだけに作り、ブラウザに保存しない（別のページや次の来訪とはつながらない）。
//
// 送らない場合：
// - 「トラッキングしないで」（Do Not Track / Global Privacy Control）を設定したブラウザ
// - エイイチ自身の閲覧：どれかのページを ?p2m-notrack=1 を付けて開くと、そのブラウザでは以後送らない（=0 で元に戻す。=check で今の設定を表示）
// 数は /api/ev-stats（表）・/api/ev-stats?format=json で見る
---
<script>
  type Click = [string, string];
  const OPT = 'p2m-notrack';
  let off = false;
  let q: string | null = null;
  let saved = true;
  try {
    q = new URLSearchParams(location.search).get(OPT);
    if (q === '1') localStorage.setItem(OPT, '1');
    if (q === '0') localStorage.removeItem(OPT);
    off = localStorage.getItem(OPT) === '1';
  } catch { saved = false; /* 覚えられない環境では、送るかどうかを毎回の判定だけで決める */ }
  const nav = navigator as Navigator & { globalPrivacyControl?: boolean };
  const dnt = nav.doNotTrack === '1' || nav.globalPrivacyControl === true;
  if (dnt) off = true;
  // ?p2m-notrack=1 / 0 / check で開いたときだけ、このブラウザが今どちらの設定かを画面の上に6秒出す（設定できたかの確認用）
  if (q === '1' || q === '0' || q === 'check') {
    const note = document.createElement('div');
    note.textContent = dnt ? 'このブラウザは「トラッキングしない」設定のため記録しません'
      : !saved ? 'このブラウザでは設定を覚えられません（プライベートブラウズなど）'
      : off ? 'このブラウザの閲覧は記録しない設定です' : 'このブラウザの閲覧は記録する設定です';
    note.setAttribute('role', 'status');
    note.style.cssText = 'position:fixed;top:64px;left:50%;transform:translateX(-50%);z-index:100;width:max-content;max-width:calc(100% - 32px);padding:10px 16px;border-radius:8px;background:#242424;color:#fff;font:500 14px/1.5 sans-serif;box-shadow:0 4px 12px rgba(0,0,0,.2)';
    const show = () => { document.body.appendChild(note); setTimeout(() => note.remove(), 6000); };
    if (document.body) show(); else document.addEventListener('DOMContentLoaded', show);
  }

  if (!off) {
    const AUTO_UA = /headless|lighthouse|bot\/|bot;|\bbot\b|crawl|spider|slurp|preview|phantom|puppeteer|playwright|selenium/i;
    const newId = () => {
      try { return crypto.randomUUID().replace(/-/g, '').slice(0, 12); } catch { return Math.random().toString(36).slice(2, 14); }
    };
    // サイト内のどこから来たか。外から来たときは、そのサイトの名前（ドメイン）だけ
    const origin = () => {
      try {
        if (!document.referrer) return { f: '', r: '' };
        const u = new URL(document.referrer);
        if (u.host === location.host) return { f: u.pathname.slice(0, 160), r: '' };
        return { f: '', r: u.hostname.slice(0, 80) };
      } catch { return { f: '', r: '' }; }
    };
    const fresh = (from?: string) => {
      const o = origin();
      return {
        v: newId(), n: 0, p: location.pathname.slice(0, 160), f: from ?? o.f, r: from ? '' : o.r,
        w: window.innerWidth < 600 ? 'sp' : 'pc', s: 0, t: 0, c: [] as Click[],
        d: 0, g: 0, cp: 0, vp: 0, fs: 0, fx: 0, fv: 0,
        // 人と機械を分ける印（2026-10-25）。u＝人の操作があった、wd・hl＝自動で動くブラウザの印（名乗りそのものは送らない）
        u: 0, wd: navigator.webdriver ? 1 : 0, hl: AUTO_UA.test(navigator.userAgent || '') ? 1 : 0,
      };
    };
    let st = fresh();
    // 人の操作。1回あれば十分なので、最初の1回だけ印を付ける
    const touched = () => { if (!st.u) { st.u = 1; dirty = true; } };
    for (const ev of ['pointermove', 'pointerdown', 'touchstart', 'wheel', 'keydown']) document.addEventListener(ev, touched, { capture: true, passive: true });
    let visibleSince = document.hidden ? 0 : Date.now();
    let dirty = true;

    // どこまで読んだか（25％刻み）。画面に収まるページは最後まで読んだ扱い
    const depth = () => {
      const h = document.documentElement.scrollHeight;
      if (!h) return;
      const ratio = Math.min(1, (window.scrollY + window.innerHeight) / h);
      const b = ratio >= 0.98 ? 100 : Math.floor(ratio * 4) * 25;
      if (b > st.s) { st.s = b; dirty = true; }
    };
    window.addEventListener('scroll', depth, { passive: true });
    window.addEventListener('load', depth);
    setTimeout(depth, 1500);

    // ページのどの部分か
    const AREAS: [string, string][] = [
      ['.hk-parade', 'hakobu'], ['.site-header', 'header'], ['.site-footer', 'footer'], ['.crumbs', 'crumbs'],
      ['.toc', 'toc'], ['.consult', 'consult'], ['.pager', 'pager'], ['.related', 'related'], ['.richText', 'body'], ['main', 'main'],
    ];
    const areaOf = (el: Element) => {
      const tagged = el.closest('[data-ev-area]') as HTMLElement | null;
      if (tagged && tagged.dataset.evArea) return tagged.dataset.evArea.slice(0, 20);
      for (const [sel, name] of AREAS) if (el.closest(sel)) return name;
      return 'other';
    };
    const addClick = (target: string, area: string) => {
      if (st.c.length < 8) st.c.push([target.slice(0, 100), area]);
      dirty = true;
    };
    // ボタンの文字（アイコンの文字 .ms は除く）
    const textOf = (el: Element) => {
      const aria = el.getAttribute('aria-label');
      if (aria) return aria.slice(0, 24);
      const c = el.cloneNode(true) as Element;
      c.querySelectorAll('.ms').forEach((i) => i.remove());
      return (c.textContent || '').replace(/\s+/g, ' ').trim().slice(0, 24);
    };

    // 短い間に同じ所を何度も押した（3回以上・0.8秒以内・30px 以内）
    let taps: { x: number; y: number; at: number }[] = [];
    const INTERACTIVE = 'a,button,input,select,textarea,label,summary,video,audio,iframe,[role=button],[role=link],[onclick],[tabindex]';

    document.addEventListener('click', (e) => {
      try {
        const el = e.target as Element | null;
        if (!el || !(el instanceof Element)) return;
        const now = Date.now();
        taps = taps.filter((t) => now - t.at < 800 && Math.abs(t.x - e.clientX) < 30 && Math.abs(t.y - e.clientY) < 30);
        taps.push({ x: e.clientX, y: e.clientY, at: now });
        if (taps.length === 3) { st.g++; dirty = true; }

        const link = el.closest('a[href]') as HTMLAnchorElement | null;
        if (link) {
          const label = link.dataset.ev;
          let target = '';
          try {
            const u = new URL(link.href, location.href);
            if (u.protocol === 'mailto:' || u.protocol === 'tel:') target = 'mailto';
            else if (u.host !== location.host) target = u.hostname;
            else if (u.pathname === location.pathname && u.hash) target = '#' + decodeURIComponent(u.hash.slice(1)).slice(0, 40);
            else target = u.pathname + (u.search.includes('topic=') ? u.search : '');
          } catch { target = '(読めないリンク)'; }
          addClick(label ? `${label} ${target}` : target, areaOf(link));
          return;
        }
        const btn = el.closest('button,[role=button],summary') as HTMLElement | null;
        if (btn) { addClick('btn:' + (btn.dataset.ev || btn.id || textOf(btn) || btn.tagName.toLowerCase()), areaOf(btn)); return; }
        if (el.closest(INTERACTIVE)) return;
        // ページの余白（html・body・main そのもの）を押したのは迷いに数えない
        if (el === document.documentElement || el === document.body || el.tagName === 'MAIN') return;
        // リンクでない所を押した（文字を選んでいるときは除く）
        const sel = window.getSelection();
        if (sel && String(sel).length > 0) return;
        st.d++;
        addClick('(リンクなし):' + el.tagName.toLowerCase(), areaOf(el));
      } catch { /* 数え損ねてもサイトの動きには影響させない */ }
    }, { capture: true, passive: true });

    // 埋め込み（図解・YouTube）を押したとき：ページから枠の中は見えないので、枠に入ったことで数える
    window.addEventListener('blur', () => {
      setTimeout(() => {
        const a = document.activeElement;
        if (a && a.tagName === 'IFRAME') {
          let target = 'iframe';
          try {
            const u = new URL((a as HTMLIFrameElement).src, location.href);
            target = 'iframe:' + (u.host === location.host ? u.pathname.slice(0, 60) : u.hostname);
          } catch { /* src が読めないときは iframe とだけ残す */ }
          addClick(target, areaOf(a));
        }
      }, 0);
    });

    document.addEventListener('copy', () => { st.cp++; dirty = true; });
    // 動画の再生は、人が押して再生したときだけ数える（自動再生は数えない）。
    // 自動再生の動画は、その動画を直前（1.5秒以内）に押したときだけ数える
    let pressed: { el: Element | null; at: number } = { el: null, at: 0 };
    const markPress = (e: Event) => { pressed = { el: e.target as Element | null, at: Date.now() }; };
    document.addEventListener('pointerdown', markPress, { capture: true, passive: true });
    document.addEventListener('keydown', markPress, { capture: true, passive: true });
    document.addEventListener('play', (e) => {
      const v = e.target as HTMLMediaElement | null;
      if (!v || !(v instanceof HTMLMediaElement)) return;
      const byPress = !!pressed.el && (pressed.el === v || v.contains(pressed.el)) && Date.now() - pressed.at < 1500;
      if (v.autoplay && !byPress) return;
      st.vp++; dirty = true;
    }, true);
    document.addEventListener('focusin', (e) => {
      if (!st.fs && (e.target as Element | null)?.closest?.('form')) { st.fs = 1; dirty = true; }
    });
    document.addEventListener('submit', (e) => {
      st.fx++;
      try { if ((e.target as HTMLFormElement).checkValidity()) st.fv++; } catch { /* 判定できないときは押した回数だけ */ }
      dirty = true;
    }, true);

    const send = () => {
      if (visibleSince) { st.t += Math.round((Date.now() - visibleSince) / 1000); visibleSince = 0; }
      st.t = Math.min(st.t, 7200);
      if (!dirty) return;
      depth();
      const body = JSON.stringify(st);
      try {
        if (!navigator.sendBeacon || !navigator.sendBeacon('/api/ev', new Blob([body], { type: 'text/plain' }))) {
          fetch('/api/ev', { method: 'POST', body, keepalive: true }).catch(() => {});
        }
      } catch { /* 送れなくてもサイトの動きには影響させない */ }
      st.n++; dirty = false;
    };
    document.addEventListener('visibilitychange', () => {
      if (document.hidden) send();
      else visibleSince = Date.now();
    });
    window.addEventListener('pagehide', send);
    // 戻るボタンで、前の表示のまま戻ってきたとき：別の1回として数え直す
    window.addEventListener('pageshow', (e) => {
      if (e.persisted) { st = fresh('(戻る)'); visibleSince = Date.now(); dirty = true; setTimeout(depth, 300); }
    });
  }
</script>
