(function (){
"use strict";
var config=window.XNCCTrackerConfig||{};
if(!config.endpoint) return;
if(config.respectDnt&&(navigator.doNotTrack==="1"||window.doNotTrack==="1")) return;
var COOKIE_PREFIX="xncc_";
var scrollSent={};
var clickHistory=[];
var startedAt=Date.now();
var engagementSent=false;
function randomId(){
if(window.crypto&&window.crypto.randomUUID) return window.crypto.randomUUID();
if(window.crypto&&window.crypto.getRandomValues){
var bytes=new Uint8Array(16);
window.crypto.getRandomValues(bytes);
return Array.prototype.map.call(bytes, function (b){
return b.toString(16).padStart(2, "0");
}).join("");
}
return Date.now().toString(36) + Math.random().toString(36).slice(2);
}
function readCookie(name){
var prefix=name + "=";
var parts=document.cookie ? document.cookie.split("; "):[];
for (var i=0; i < parts.length; i++){
if(parts[i].indexOf(prefix)===0) return decodeURIComponent(parts[i].slice(prefix.length));
}
return "";
}
function writeCookie(name, value, seconds){
var cookie=name + "=" + encodeURIComponent(value) + "; Path=/; Max-Age=" + seconds + "; SameSite=Lax";
if(location.protocol==="https:") cookie +="; Secure";
document.cookie=cookie;
}
function readStoredVisitor(){
try { return window.localStorage.getItem(COOKIE_PREFIX + "vid")||""; } catch (_){ return ""; }}
function storeVisitor(value){
try { window.localStorage.setItem(COOKIE_PREFIX + "vid", value); } catch (_){}}
var visitorId=readStoredVisitor()||readCookie(COOKIE_PREFIX + "vid")||randomId();
var sessionId=readCookie(COOKIE_PREFIX + "sid")||randomId();
storeVisitor(visitorId);
writeCookie(COOKIE_PREFIX + "vid", visitorId, 31536000);
writeCookie(COOKIE_PREFIX + "sid", sessionId, 1800);
function safePath(value){
try {
return new URL(value, location.origin).pathname.slice(0, 512)||"/";
} catch (_){
return "/";
}}
function referrerHost(){
if(!document.referrer) return "";
try {
var host=new URL(document.referrer).hostname.toLowerCase();
return host===location.hostname.toLowerCase() ? "":host.slice(0, 190);
} catch (_){
return "";
}}
function detectDevice(){
var width=Math.min(window.screen.width||innerWidth, innerWidth||window.screen.width);
var ua=navigator.userAgent||"";
if(/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
if(/mobile|iphone|ipod|android/i.test(ua)||width < 760) return "mobile";
return "desktop";
}
function detectSource(){
var params=new URLSearchParams(location.search);
var campaign=readCookie(COOKIE_PREFIX + "campaign")||params.get("utm_campaign")||"";
var source=readCookie(COOKIE_PREFIX + "source")||params.get("utm_source")||"";
if(!source&&campaign) source=campaign.split("-")[0];
if(!source){
var host=referrerHost();
if(/xiaohongshu|xhslink|rednote/.test(host)) source="xiaohongshu";
else if(/google\./.test(host)) source="google";
else if(/bing\./.test(host)) source="bing";
else if(/baidu\./.test(host)) source="baidu";
else if(/wechat|weixin/.test(host)) source="wechat";
else if(host) source=host.replace(/^www\./, "");
else source="direct";
}
return {
source: String(source).toLowerCase().replace(/[^a-z0-9._-]/g, "-").slice(0, 100),
campaign: String(campaign).toLowerCase().replace(/[^a-z0-9_-]/g, "-").slice(0, 100)
};}
var attribution=detectSource();
function send(eventType, properties){
writeCookie(COOKIE_PREFIX + "sid", sessionId, 1800);
var payload={
event_type: eventType,
visitor_id: visitorId,
session_id: sessionId,
page_path: location.pathname,
source_key: attribution.source,
campaign_key: attribution.campaign,
referrer_host: referrerHost(),
device_type: detectDevice(),
properties: properties||{}};
var body=JSON.stringify(payload);
if(eventType!=="pageview"&&navigator.sendBeacon){
navigator.sendBeacon(config.endpoint, new Blob([body], { type: "application/json" }));
return;
}
fetch(config.endpoint, {
method: "POST",
credentials: "same-origin",
keepalive: true,
headers: { "Content-Type": "application/json" },
body: body
}).then(function (response){
if(!response.ok) throw new Error("XNCC HTTP " + response.status);
}).catch(function (){
if(navigator.sendBeacon){
navigator.sendBeacon(config.endpoint, new Blob([body], { type: "application/json" }));
}});
}
function targetDescriptor(target){
if(!target) return { type: "unknown", path: "" };
var interactive=target.closest ? target.closest("a,button,[role='button'],input[type='submit']"):target;
if(!interactive) return { type: "other", path: "" };
var type=interactive.tagName ? interactive.tagName.toLowerCase():"other";
var path="";
if(interactive.href) path=safePath(interactive.href);
return {
element: interactive,
type: type,
path: path,
key: type + "|" + path + "|" + (interactive.id||interactive.getAttribute("data-xncc-action")||"")
};}
function inferAction(element){
var explicit=(element.getAttribute("data-xncc-action")||"").slice(0, 190);
if(explicit) return explicit;
var href=String(element.getAttribute("href")||"").toLowerCase();
if(href.indexOf("tel:")===0) return "phone";
if(href.indexOf("mailto:")===0) return "email";
if(/google\.[^/]+\/maps|maps\.app\.goo\.gl|goo\.gl\/maps/.test(href)) return "map";
if(/reservation|reserve|yoyaku|予約/.test(href)) return "reservation";
if(/\/menu(?:\/|$)|メニュー/.test(href)) return "menu";
if(element.hasAttribute("data-xncc-conversion")) return "custom";
return "";
}
document.addEventListener("click", function (event){
var target=targetDescriptor(event.target);
if(!target.element) return;
var action=inferAction(target.element);
send(action ? "conversion":"click", {
target_type: target.type,
target_path: target.path,
action: action
});
var now=Date.now();
clickHistory=clickHistory.filter(function (item){ return now - item.time < 2000; });
clickHistory.push({ key: target.key, time: now });
var repeats=clickHistory.filter(function (item){ return item.key===target.key; }).length;
if(repeats >=5){
send("suspicious_click", {
target_type: target.type,
target_path: target.path,
repeat_count: repeats,
automation_hint: event.isTrusted===false
});
clickHistory=[];
}}, true);
document.addEventListener("submit", function (event){
var form=event.target;
send("form_submit", {
target_type: "form",
target_path: safePath(form.action||location.href),
action: (form.getAttribute("data-xncc-action")||"form_submit").slice(0, 190)
});
}, true);
function checkScroll(){
var max=Math.max(document.documentElement.scrollHeight, document.body ? document.body.scrollHeight:0);
var current=window.scrollY + window.innerHeight;
var depth=max > 0 ? Math.round((current / max) * 100):100;
[50, 90].forEach(function (threshold){
if(depth >=threshold&&!scrollSent[threshold]){
scrollSent[threshold]=true;
send("scroll", { scroll_depth: threshold });
}});
}
window.addEventListener("scroll", checkScroll, { passive: true });
window.addEventListener("error", function (event){
send("client_error", {
error_type: event&&event.error&&event.error.name ? String(event.error.name).slice(0, 80):"script_error",
target_path: event&&event.filename ? safePath(event.filename):""
});
});
window.addEventListener("unhandledrejection", function (){
send("client_error", { error_type: "unhandled_rejection" });
});
function pageview(){
var connection=navigator.connection||navigator.mozConnection||navigator.webkitConnection||{};
send("pageview", {
viewport_width: Math.max(document.documentElement.clientWidth||0, window.innerWidth||0),
viewport_height: Math.max(document.documentElement.clientHeight||0, window.innerHeight||0),
language: String(navigator.language||"").slice(0, 20),
connection_type: String(connection.effectiveType||"").slice(0, 20)
});
}
window.setTimeout(function (){
if(document.visibilityState==="visible"&&!engagementSent){
engagementSent=true;
send("engagement", { duration_ms: Date.now() - startedAt });
}}, 10000);
if(document.readyState==="loading"){
document.addEventListener("DOMContentLoaded", pageview);
}else{
pageview();
}
window.addEventListener("pagehide", function (){
send("performance", {
duration_ms: Math.min(Date.now() - startedAt, 3600000)
});
});
}());