1/** Consultation measurement v1, 2026-09-21. 2 * One delegated owner for click intent; generate_lead only after saved form response. 3 * No form fields, appointment identifiers, phone numbers or inferred residence sent. 4 * Pageviews remain owned by the existing two GA configurations. 5 */ 6(function () { 7 'use strict'; 8 if (window.bdConversions) return; 9 var path = location.pathname; 10 if (/^\/(admin|auth|report)(\/|$)/.test(path)) return; 11 var live = location.hostname === 'bdbddc.com'; 12 var pageType = /^\/concerns\//.test(path) ? 'concern' : /^\/guide/.test(path) ? 'guide' : /^\/(blog|column)\//.test(path) ? 'content' : /^\/area\//.test(path) ? 'area' : /^\/(en\/|jp\/)?reservation/.test(path) ? 'reservation' : 'other'; 13 var sent = Object.create(null); 14 var lastIntent = { name: '', at: 0 }; 15 function emit(name, extra, after) { 16 if (!live) { if (after) after(); return; } 17 // The live GTM/connected-tag configuration does not reliably route the default group. 18 // Explicit destinations preserve the two existing properties without another config/pageview. 19 var data = Object.assign({ send_to: ['G-LM9VKJSB9F', 'G-3NQP355YQM'], page_type: pageType, page_path: path, measurement_version: '20260921' }, extra || {}); 20 window.dataLayer = window.dataLayer || []; 21 var g = window.gtag || function () { window.dataLayer.push(arguments); }; 22 if (after) { 23 var finished = false, remaining = data.send_to.length; 24 function done() { if (finished) return; finished = true; window.clearTimeout(timer); after(); } 25 // Never leave a patient waiting if a tag is blocked or its callback is missing. 26 var timer = window.setTimeout(done, 700); 27 data.send_to.forEach(function (destination) { 28 var acknowledged = false; 29 try { g('event', name, Object.assign({}, data, { send_to: destination, event_timeout: 600, event_callback: function () { 30 if (acknowledged) return; acknowledged = true; 31 if (--remaining === 0) done(); 32 } })); } catch (_) { done(); } 33 }); 34 } else { 35 try { g('event', name, data); } catch (_) { /* Measurement must never block navigation. */ } 36 } 37 } 38 function intent(name, placement, after) { 39 // Inline legacy handlers and the capture listener see the same click. 40 if (lastIntent.name === name && Date.now() - lastIntent.at < 500) { if (after) after(); return; } 41 lastIntent = { name: name, at: Date.now() }; 42 emit(name, { cta_location: placement || 'content' }, after); 43 if (live && typeof window.fbq === 'function' && /^(phone_call_click|kakao_click)$/.test(name)) { 44 try { window.fbq('track', 'Contact', { content_name: name === 'phone_call_click' ? 'phone_call' : 'kakao_talk' }); } catch (_) {} 45 } 46 } 47 function action(a) { 48 var u; 49 try { u = new URL(a.getAttribute('href'), location.href); } catch (_) { return ''; } 50 if (u.protocol === 'tel:') return 'phone_call_click'; 51 if (u.hostname === 'pf.kakao.com' || u.hostname === 'open.kakao.com') return 'kakao_click'; 52 if (u.hostname === 'booking.naver.com' || (u.hostname === 'naver.me' && u.pathname === '/5yPnKmqQ') || (/(^|\.)place\.naver\.com$/.test(u.hostname) && /\/booking(?:\/|$)/.test(u.pathname))) return 'naver_booking_click'; 53 if (u.origin === location.origin && /^\/(?:en\/|jp\/)?reservation(?:\.html|\/)?$/.test(u.pathname)) return 'reservation_click'; 54 return ''; 55 } 56 function placement(a) { 57 var owner = a.closest('[data-cta-location]'); 58 if (owner) return owner.getAttribute('data-cta-location'); 59 if (a.closest('.bd-scta-bar,.mobile-bottom-cta,.floating-cta')) return 'floating'; 60 if (a.closest('header,#siteHeader,#header,nav')) return 'navigation'; 61 if (a.closest('footer')) return 'footer'; 62 if (a.closest('aside')) return 'sidebar'; 63 return 'content'; 64 } 65 function reservationAccepted(id, after) { 66 if (typeof id !== 'string' || !/^rsv-\d+-[a-z0-9]+$/.test(id) || sent[id]) { if (after) after(); return false; } 67 var key = 'bd_lead_sent:' + id; 68 try { if (sessionStorage.getItem(key)) { if (after) after(); return false; } sessionStorage.setItem(key, '1'); } catch (_) { /* in-memory dedup still works */ } 69 sent[id] = true; 70 // The identifier is used only in this tab, never in analytics payloads. 71 emit('generate_lead', { method: 'reservation_form', lead_stage: 'request_saved' }, after); 72 if (live && typeof window.fbq === 'function') { try { window.fbq('track', 'Lead', { content_name: 'consultation_request' }); } catch (_) {} } 73 return true; 74 } 75 window.bdConversions = { intent: intent, reservationAccepted: reservationAccepted, version: '20260921' }; 76 var navigating = false;
77 document.addEventListener('click', function (e) { 78 var a = e.target && e.target.closest ? e.target.closest('a[href]') : null; 79 if (!a) return; 80 var name = action(a); 81 if (name === 'reservation_click' && !e.defaultPrevented && e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey && !a.getAttribute('download') && (!a.getAttribute('target') || a.getAttribute('target') === '_self')) { 82 e.preventDefault(); 83 if (navigating) return; 84 navigating = true; 85 var destination = a.href; 86 intent(name, placement(a), function () { window.location.assign(destination); }); 87 return; 88 } 89 if (name) intent(name, placement(a)); 90 }, true); 91 function ready() { 92 if (!live) return; 93 // A visible call-to-action is an exposure, not a conversion. 94 if (typeof IntersectionObserver === 'function') { 95 var viewed = Object.create(null); 96 var observer = new IntersectionObserver(function (entries) { 97 entries.forEach(function (entry) { 98 if (!entry.isIntersecting || entry.intersectionRatio < 0.5) return; 99 var a = entry.target, name = action(a), where = placement(a), key = name + ':' + where; 100 if (name && !viewed[key]) { viewed[key] = true; emit('consultation_cta_view', { cta_action: name, cta_location: where }); } 101 observer.unobserve(a); 102 }); 103 }, { threshold: 0.5 }); 104 document.querySelectorAll('a[href]').forEach(function (a) { if (action(a)) observer.observe(a); }); 105 } 106 if (!/^(concern|guide|content)$/.test(pageType)) return; 107 var engaged = 0, last = Date.now(), reading = false; 108 window.addEventListener('scroll', function () { reading = true; }, { passive: true }); 109 window.setInterval(function () { 110 var now = Date.now(); 111 if (document.visibilityState === 'visible') engaged += Math.min(now - last, 1500); 112 last = now; 113 var height = document.documentElement.scrollHeight - window.innerHeight; 114 if (!window._bdReadComplete && reading && engaged >= 30000 && (height <= 0 || window.scrollY / height >= 0.75)) { 115 window._bdReadComplete = true; 116 emit('content_read_complete', { reading_rule: 'visible_30s_scroll_75' }); 117 } 118 }, 1000); 119 } 120 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ready); 121 else ready(); 122})();
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.