Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 41 additions & 60 deletions click-id-tracking.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,27 @@
// Persist Google Ads click identifiers (gclid / wbraid / gbraid) from the
// landing URL into cookies scoped to photon.codes, so conversions can be
// attributed after the visitor navigates within the site.
//
// Mintlify auto-includes any `.js` file in the content directory on every
// page and runs it once the page is interactive. That captures the initial
// landing URL, which is all click-ID attribution needs.
//
// A single identifier is stored (Google normally provides one). A genuinely
// new click replaces prior attribution and resets the capture timestamp,
// while a reload of the same click preserves the original 90-day window.

(() => {
const CLICK_ID_KEYS = ['gclid', 'wbraid', 'gbraid']
const CLICK_COOKIE_PREFIX = 'photon_'
const CAPTURED_AT_COOKIE_NAME = 'photon_ads_click_captured_at'
const COOKIE_PREFIX = 'photon_'
const CAPTURED_AT_COOKIE = 'photon_ads_click_captured_at'
const COOKIE_DOMAIN = 'photon.codes'
const COOKIE_MAX_AGE = 90 * 24 * 60 * 60
const COOKIE_MAX_AGE_SECONDS = 90 * 24 * 60 * 60
const MAX_CLICK_ID_LENGTH = 512
const COOKIE_ATTRIBUTES

const cookieAttributes
= `Domain=${COOKIE_DOMAIN}; Path=/; SameSite=Lax; Secure`

const readCookie = (name) => {
const prefix = `${name}=`
const pair = document.cookie
const cookie = document.cookie
.split(';')
.map(item => item.trim())
.find(item => item.startsWith(prefix))

if (!pair) {
if (!cookie) {
return null
}

try {
return decodeURIComponent(pair.slice(prefix.length))
return decodeURIComponent(cookie.slice(prefix.length))
}
catch {
return null
Expand All @@ -42,72 +31,64 @@
const writeCookie = (name, value) => {
document.cookie
= `${name}=${encodeURIComponent(value)}; `
+ `Max-Age=${COOKIE_MAX_AGE}; ${
COOKIE_ATTRIBUTES}`
+ `Max-Age=${COOKIE_MAX_AGE_SECONDS}; ${
cookieAttributes}`
}

const deleteCookie = (name) => {
document.cookie
= `${name}=; Max-Age=0; ${
COOKIE_ATTRIBUTES}`
= `${name}=; Max-Age=0; ${cookieAttributes}`
}

const params = new URLSearchParams(window.location.search)

let selectedClick = null

// Google normally provides only one identifier. This order is the
// deterministic fallback if a malformed URL contains more than one.
for (const key of CLICK_ID_KEYS) {
const value = params.get(key)

if (value && value.length <= MAX_CLICK_ID_LENGTH) {
selectedClick = { key, value }
break
}
}
const selectedClick = CLICK_ID_KEYS
.map(type => ({
type,
value: params.get(type),
}))
.find(
({ value }) =>
value
&& value.trim().length > 0
&& value.length <= MAX_CLICK_ID_LENGTH,
)

// Organic/direct visit: preserve any attribution already stored.
// 没有新的 Google Ads 点击参数时,保留现有归因。
if (!selectedClick) {
return
}

const selectedCookieName
= `${CLICK_COOKIE_PREFIX}${selectedClick.key}`

const cookieName = `${COOKIE_PREFIX}${selectedClick.type}`
const isSameClick
= readCookie(selectedCookieName) === selectedClick.value
= readCookie(cookieName) === selectedClick.value

// 同一次点击:不刷新 90 天有效期,只清除其他类型的 click ID。
if (isSameClick) {
// Remove stale identifiers of another type, but don't refresh this
// click's 90-day lifetime merely because the page was reloaded.
for (const key of CLICK_ID_KEYS) {
if (key !== selectedClick.key) {
deleteCookie(`${CLICK_COOKIE_PREFIX}${key}`)
for (const type of CLICK_ID_KEYS) {
if (type !== selectedClick.type) {
deleteCookie(`${COOKIE_PREFIX}${type}`)
}
}

const existingCapturedAt = Number(
readCookie(CAPTURED_AT_COOKIE_NAME),
)
const capturedAt = Number(readCookie(CAPTURED_AT_COOKIE))

// Migration path for cookies created by the previous script.
if (
!Number.isFinite(existingCapturedAt)
|| existingCapturedAt <= 0
) {
writeCookie(CAPTURED_AT_COOKIE_NAME, String(Date.now()))
if (!Number.isInteger(capturedAt) || capturedAt <= 0) {
writeCookie(CAPTURED_AT_COOKIE, String(Date.now()))
}

return
}

// A genuinely new Google Ads click replaces the previous attribution.
for (const key of CLICK_ID_KEYS) {
deleteCookie(`${CLICK_COOKIE_PREFIX}${key}`)
// 新点击替换旧归因。
for (const type of CLICK_ID_KEYS) {
deleteCookie(`${COOKIE_PREFIX}${type}`)
}
deleteCookie(CAPTURED_AT_COOKIE_NAME)

writeCookie(selectedCookieName, selectedClick.value)
writeCookie(CAPTURED_AT_COOKIE_NAME, String(Date.now()))
deleteCookie(CAPTURED_AT_COOKIE)

const capturedAt = Date.now()

writeCookie(cookieName, selectedClick.value)
writeCookie(CAPTURED_AT_COOKIE, String(capturedAt))
})()
Loading