/** * Web SDK v1.2.0 接入层。 * 页面/点击/导航沿用 jha-1043 的口径,短剧使用最新版 dramaEvent/dramaStatusChange。 */ (function (global) { "use strict"; var state = { initialized: false, initializing: false, queue: [], page: { key: "internal_other", name: "站内其他页面" }, referrer: { key: "direct", name: "直接访问" }, userType: "normal", pageStartedAt: Date.now(), dramaViewSent: false, adShown: {}, }; function stringValue(value, fallback) { var result = value == null ? "" : String(value).trim(); return result || (fallback == null ? "" : String(fallback)); } function integer(value, fallback) { var result = parseInt(value, 10); return isFinite(result) ? result : (fallback || 0); } function clamp(value, min, max) { var result = Number(value); if (!isFinite(result)) result = min; return Math.max(min, Math.min(max, result)); } function sdkAvailable() { return !!(global.WebSDK && global.WebSDK.track); } function logError() {} function invoke(method, payload) { if (!state.initialized || !sdkAvailable()) { return new Promise(function (resolve) { state.queue.push([method, payload, resolve]); }); } try { var fn = global.WebSDK.track[method]; if (typeof fn !== "function") { var missing = new Error("SDK method does not exist: " + method); logError(missing); return Promise.resolve({ sent: false, ok: false, reason: "unknown_method" }); } var result = fn(payload); return Promise.resolve(result).then(function (response) { if (response && response.ok === false) { logError(method + " rejected: " + JSON.stringify(response)); } return response; }).catch(function (error) { logError(error); return { sent: false, ok: false, reason: "exception" }; }); } catch (error) { logError(error); return Promise.resolve({ sent: false, ok: false, reason: "exception" }); } } function flushQueue() { var pending = state.queue.splice(0, state.queue.length); pending.forEach(function (entry) { Promise.resolve(invoke(entry[0], entry[1])).then(entry[2]); }); } function currentChannel(value) { var channel = stringValue(value); if (!channel && global.ChannelAttribution && typeof global.ChannelAttribution.get === "function") { channel = stringValue(global.ChannelAttribution.get()); } if (!channel) channel = stringValue(global.__CHANNEL_CODE__, "organic"); var normalized = channel.toLowerCase(); if (["self", "natural", "official", "channel", "channel://natural"].indexOf(normalized) >= 0) { return "organic"; } if (!channel || channel.length > 50 || !/^[A-Za-z0-9_.-]+$/.test(channel) || ["{}", "0", "-", "unknown"].indexOf(normalized) >= 0) { return "organic"; } return channel; } function normalizeUid(value) { var uid = stringValue(value); if (uid === "0" || uid.length < 4 || uid.length > 32 || !/^[A-Za-z0-9_]+$/.test(uid) || ["u_1001", "stress_user", "u_lab"].indexOf(uid.toLowerCase()) >= 0) return ""; return uid; } function inferPage(path) { path = stringValue(path, "/").replace(/\/$/, "") || "/"; if (path === "/") return { key: "home", name: "首页" }; var rules = [ [/^\/series-play(?:\/|$)/, "series_play", "AI成人剧集播放页"], [/^\/series(?:\/|$)/, "series", "AI成人剧集"], [/^\/play(?:\/|$)/, "play", "短剧播放页"], [/^\/detail(?:\/|$)/, "detail", "短剧详情"], [/^\/list(?:\/|$)/, "list", "短剧列表"], [/^\/rank(?:\/|$)/, "rank", "排行榜"], [/^\/topic-detail(?:\/|$)/, "topic_detail", "专题详情"], [/^\/topic(?:\/|$)/, "topic", "专题列表"], [/^\/tags(?:\/|$)/, "tags", "标签详情"], [/^\/search(?:-mobile)?(?:\/|$)/, "search", "搜索结果页"], [/^\/history(?:\/|$)/, "history", "观看历史"], [/^\/favorites(?:\/|$)/, "favorites", "我的收藏"], [/^\/profile(?:\/|$)/, "profile", "个人中心"], ]; for (var i = 0; i < rules.length; i += 1) { if (rules[i][0].test(path)) return { key: rules[i][1], name: rules[i][2] }; } return { key: "internal_other", name: "站内其他页面" }; } function readReferrer() { try { var saved = global.sessionStorage.getItem("__tracker_last_page"); if (saved) { var parsed = JSON.parse(saved); if (parsed && parsed.key) return parsed; } } catch (error) {} try { if (document.referrer) { var url = new URL(document.referrer, global.location.href); if (url.host === global.location.host) return inferPage(url.pathname); return { key: "external", name: url.host || "外部站点" }; } } catch (error) {} return { key: "direct", name: "直接访问" }; } function rememberPage() { try { global.sessionStorage.setItem("__tracker_last_page", JSON.stringify(state.page)); } catch (error) {} } function init(options) { if (state.initialized || state.initializing) return; options = options || {}; var inferred = inferPage(global.location && global.location.pathname); state.page = { key: stringValue(options.pageKey, inferred.key), name: stringValue(options.pageName, inferred.name), }; state.userType = options.userType === "vip" ? "vip" : "normal"; state.referrer = readReferrer(); state.initializing = true; var attempts = 0; var timer = global.setInterval(function () { attempts += 1; if (!sdkAvailable() && attempts < 100) return; global.clearInterval(timer); if (!sdkAvailable()) { state.initializing = false; return; } try { var result = global.WebSDK.init({ appId: stringValue(options.appId), uid: normalizeUid(options.uid), channel: currentChannel(options.channel), encryptedConfig: stringValue(options.encryptedConfig), appVersion: stringValue(options.appVersion, "1.0.0"), disableAutoEvents: ["app_page_view", "page_click"], }); Promise.resolve(result).then(function () { state.initialized = true; state.initializing = false; __commonFieldsReady = null; loadCommonFields(); flushQueue(); onReady(); }).catch(function (error) { state.initializing = false; logError(error); }); } catch (error) { state.initializing = false; logError(error); } }, 50); } function whenDOMReady(callback) { if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", callback, { once: true }); } else { callback(); } } function trackPageView(extra) { extra = extra || {}; invoke("appPageView", { user_type: stringValue(extra.user_type, state.userType), page_key: state.page.key, page_name: state.page.name, referrer_page_key: state.referrer.key, referrer_page_name: state.referrer.name, current_page_key: state.page.key, current_page_name: state.page.name, page_load_time: pageLoadDuration(), recommend_trace_id: stringValue(extra.recommend_trace_id), }); rememberPage(); } function pageLoadDuration() { try { if (global.performance && typeof global.performance.getEntriesByType === "function") { var entries = global.performance.getEntriesByType("navigation"); if (entries && entries[0]) { var navigation = entries[0]; var end = navigation.loadEventEnd || navigation.domContentLoadedEventEnd || global.performance.now(); if (isFinite(end)) return Math.max(0, Math.round(end)); } } if (global.performance && typeof global.performance.now === "function") { return Math.max(0, Math.round(global.performance.now())); } } catch (error) {} return Math.max(0, Date.now() - state.pageStartedAt); } function trackPageClick(event) { var doc = document.documentElement || {}; var viewportWidth = global.innerWidth || doc.clientWidth || 0; var viewportHeight = global.innerHeight || doc.clientHeight || 0; var x = integer(event.clientX, 0); var y = integer(event.clientY, 0); invoke("pageClick", { page_key: state.page.key, page_name: state.page.name, click_page_x: x, click_page_y: y, click_x_percent: viewportWidth ? Math.round(clamp(x / viewportWidth * 100, 0, 100)) : 0, click_y_percent: viewportHeight ? Math.round(clamp(y / viewportHeight * 100, 0, 100)) : 0, screen_width: integer(global.screen && global.screen.width, 0), screen_height: integer(global.screen && global.screen.height, 0), recommend_trace_id: "", }); } function trackNavigation(anchor) { if (!anchor || (!anchor.hasAttribute("data-navigation-key") && !anchor.hasAttribute("data-nav-key")) || !anchor.getAttribute("href")) return; var href = anchor.getAttribute("href"); if (!href || href.charAt(0) === "#" || /^javascript:/i.test(href)) return; var label = stringValue(anchor.getAttribute("aria-label") || anchor.textContent).replace(/\s+/g, " "); var navigationKey = stringValue( anchor.getAttribute("data-navigation-key") || anchor.getAttribute("data-nav-key") ); if (!/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(navigationKey)) { navigationKey = ""; try { var url = new URL(href, global.location.href); if (url.origin !== global.location.origin) { navigationKey = "nav_external"; } else { var targetPage = inferPage(url.pathname); if (targetPage.key !== "internal_other") { navigationKey = "nav_" + targetPage.key; } else { var firstSegment = stringValue(url.pathname.split("/").filter(Boolean)[0]) .toLowerCase() .replace(/[^a-z0-9]+/g, "_") .replace(/^_+|_+$/g, "") .slice(0, 48); navigationKey = firstSegment ? "nav_" + firstSegment : "nav_home"; } } } catch (error) { navigationKey = "nav_other"; } } invoke("navigation", { navigation_key: navigationKey.toLowerCase(), navigation_name: stringValue( anchor.getAttribute("data-navigation-name") || anchor.getAttribute("data-nav-name"), label || navigationKey ).slice(0, 255), }); } function adPayload(node) { var id = stringValue(node && node.getAttribute("data-ad-id")); if (!id) return null; var adType = stringValue(node.getAttribute("data-ad-type"), "banner"); var allSlots = document.querySelectorAll('[data-track-ad="external"][data-ad-id]'); var slotPosition = Array.prototype.indexOf.call(allSlots, node) + 1; if (slotPosition <= 0) slotPosition = 1; var fallbackSlotKey = state.page.key + "_content_banner_" + slotPosition; var fallbackSlotName = state.page.name + "内容横幅第" + slotPosition + "位"; if (adType === "interstitial") { fallbackSlotKey = state.page.key + "_popup_" + slotPosition; fallbackSlotName = state.page.name + "弹窗广告位"; } return { page_key: state.page.key, page_name: state.page.name, ad_slot_key: stringValue( node.getAttribute("data-ad-slot-key"), fallbackSlotKey ), ad_slot_name: stringValue( node.getAttribute("data-ad-slot-name"), fallbackSlotName ), ad_id: id, creative_id: stringValue(node.getAttribute("data-creative-id")), ad_type: adType, }; } function bindAds() { var selector = '[data-track-ad="external"][data-ad-id]'; var nodes = document.querySelectorAll(selector); function show(node) { trackExternalAd(node, "show"); } if (global.IntersectionObserver) { var observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) return; show(entry.target); observer.unobserve(entry.target); }); }, { threshold: 0.1 }); function observe(node) { if (!node || node.__trackerAdObserved) return; node.__trackerAdObserved = true; observer.observe(node); } Array.prototype.forEach.call(nodes, observe); if (global.MutationObserver) { new MutationObserver(function (records) { records.forEach(function (record) { Array.prototype.forEach.call(record.addedNodes || [], function (node) { if (!node || node.nodeType !== 1) return; if (node.matches && node.matches(selector)) observe(node); if (node.querySelectorAll) { Array.prototype.forEach.call(node.querySelectorAll(selector), observe); } }); }); }).observe(document.documentElement, { childList: true, subtree: true }); } } else { Array.prototype.forEach.call(nodes, show); } } function trackExternalAd(node, eventType) { if (!node || (eventType !== "show" && eventType !== "click")) return; var payload = adPayload(node); if (!payload) return; var key = payload.ad_slot_key + "|" + payload.ad_id; if (!state.adShown[key]) { state.adShown[key] = true; invoke("adImpression", payload); } if (eventType === "click" && node.getAttribute("data-ad-clickable") === "1") { return invoke("adClick", payload); } } function bindExternalAdEvents() { document.addEventListener("external-ad:track", function (event) { var detail = event && event.detail ? event.detail : {}; trackExternalAd(detail.element, stringValue(detail.eventType)); }); } function trackAdvertising(node, eventType) { if (!node || ["show", "click", "close"].indexOf(eventType) < 0) return; invoke("advertising", { event_type: eventType, advertising_key: stringValue(node.getAttribute("data-advertising-key"), "dialog_ad"), advertising_name: stringValue(node.getAttribute("data-advertising-name"), "弹窗广告"), advertising_id: stringValue(node.getAttribute("data-advertising-id") || node.getAttribute("data-dialog-ad-id")), }); } function bindAdvertisingEvents() { document.addEventListener("dialog-ad:track", function (event) { var detail = event && event.detail ? event.detail : {}; var node = detail.element; if (!node || !node.getAttribute) return; trackAdvertising(node, stringValue(detail.eventType)); }); } function dramaRoot() { return document.querySelector("[data-track-drama]"); } function dramaContext(root) { root = root || dramaRoot(); if (!root) return null; var data = root.dataset || {}; var dramaID = stringValue(data.dramaId); var videoID = stringValue(data.videoId); if (!dramaID || !videoID) return null; return { media_id: stringValue(data.mediaId), drama_id: dramaID, drama_title: stringValue(data.dramaTitle, "未命名短剧"), episode: Math.max(1, integer(data.episode, 1)), total_episode: Math.max(1, integer(data.totalEpisode, 1)), video_id: videoID, drama_type_id: stringValue(data.dramaTypeId, "default"), drama_type_name: stringValue(data.dramaTypeName, "默认分类"), drama_content_type: "short_drama", recommend_trace_id: stringValue(data.recommendTraceId), drama_tag_key: stringValue(data.dramaTagKey, "default"), drama_tag_name: stringValue(data.dramaTagName, "默认标签"), drama_duration: Math.max(0, integer(data.dramaDuration, 0)), }; } var dramaBehaviorNames = { ep_view: "展示", ep_play: "播放", ep_pause: "暂停", ep_forward: "快进", ep_rewind: "快退", ep_complete: "播放完成", ep_share: "分享", }; function videoPlaybackState(video) { if (!video) return null; if (!video.__trackerPlaybackState) { video.__trackerPlaybackState = { startedAt: 0, elapsedMs: 0, seekFrom: null, lastPosition: Number(video.currentTime) || 0, }; } return video.__trackerPlaybackState; } function beginPlayback(video) { var playback = videoPlaybackState(video); if (playback && !playback.startedAt) playback.startedAt = Date.now(); } function settlePlayback(video) { var playback = videoPlaybackState(video); if (!playback || !playback.startedAt) return; playback.elapsedMs += Math.max(0, Date.now() - playback.startedAt); playback.startedAt = 0; } function playbackSeconds(video, keepDuration) { var playback = videoPlaybackState(video); if (!playback) return 0; var now = Date.now(); var activeMs = playback.startedAt ? Math.max(0, now - playback.startedAt) : 0; var seconds = Math.max(0, Math.round((playback.elapsedMs + activeMs) / 1000)); if (!keepDuration) { playback.elapsedMs = 0; if (playback.startedAt) playback.startedAt = now; } return seconds; } function videoMetrics(video, context, keepDuration) { var duration = video && isFinite(video.duration) ? Math.round(video.duration) : context.drama_duration; var current = video && isFinite(video.currentTime) ? Math.round(video.currentTime) : 0; return { duration: Math.max(0, duration), current: Math.max(0, current), played: playbackSeconds(video, keepDuration), progress: duration > 0 ? Math.round(clamp(current / duration * 100, 0, 100)) : 0, speed: Math.max(1, Math.round(((video && video.playbackRate) || 1) * 100)), }; } function trackDramaEvent(behaviorKey, video, options) { var context = dramaContext(); if (!context) return; options = options || {}; var metrics = videoMetrics(video, context, options.keepPlaybackDuration === true); context.drama_duration = metrics.duration; context.play_duration = metrics.played; context.play_progress = options.forceProgress != null ? Math.round(clamp(options.forceProgress, 0, 100)) : behaviorKey === "ep_complete" ? 100 : metrics.progress; context.play_speed = metrics.speed; context.play_behavior_key = behaviorKey; context.play_behavior_name = dramaBehaviorNames[behaviorKey] || behaviorKey; return invoke("dramaEvent", context); } function trackDramaStatusChange(targetEpisode, targetVideoID, behaviorOverride) { var context = dramaContext(); if (!context) return; var nextEpisode = Math.max(1, integer(targetEpisode, context.episode)); var videoID = stringValue(targetVideoID, context.video_id); var behavior = stringValue(behaviorOverride); var names = { ep_auto_play: "自动播放", ep_previous: "上一集", ep_next: "下一集", ep_switch: "切换剧集", ep_unlock: "解锁", }; if (!behavior) { if (nextEpisode === context.episode && videoID === context.video_id) return; behavior = "ep_switch"; if (nextEpisode === context.episode + 1) behavior = "ep_next"; else if (nextEpisode === context.episode - 1) behavior = "ep_previous"; } if (!names[behavior]) return; return invoke("dramaStatusChange", { media_id: context.media_id, drama_id: context.drama_id, drama_title: context.drama_title, episode: nextEpisode, video_id: videoID, pre_episode: context.episode, drama_behavior_key: behavior, drama_behavior_name: names[behavior], }); } function trackDramaUnlock() { var context = dramaContext(); if (!context) return Promise.resolve({ sent: false, ok: false, reason: "no_drama_context" }); return trackDramaStatusChange(context.episode, context.video_id, "ep_unlock"); } function isMainDramaVideo(root, video) { var mode = video && video.__trackerDramaMode; if (!mode && root) { mode = root.getAttribute("data-play-mode") === "preview" ? "preview" : "main"; } return !!( root && video && video.tagName === "VIDEO" && root.contains(video) && mode !== "preview" ); } function rememberDramaVideoMode(root, video) { if (!root || !video || video.tagName !== "VIDEO" || !root.contains(video)) return; video.__trackerDramaMode = root.getAttribute("data-play-mode") === "preview" ? "preview" : "main"; } function prepareDramaModeChange(nextMode) { var root = dramaRoot(); var video = root && root.querySelector("video"); if (!root || !video) return; var previousMode = video.__trackerDramaMode || (root.getAttribute("data-play-mode") === "preview" ? "preview" : "main"); nextMode = nextMode === "preview" ? "preview" : "main"; if (previousMode === nextMode) return; if (previousMode === "main") { var playback = video.__trackerPlaybackState; var wasPlaying = !!(playback && (playback.startedAt || playback.elapsedMs > 0)); settlePlayback(video); if (wasPlaying) trackDramaEvent("ep_pause", video); } video.__trackerPlaybackState = null; video.__trackerDramaMode = nextMode; } function bindDramaEvents() { var root = dramaRoot(); if (!root) return; if (!state.dramaViewSent) { state.dramaViewSent = true; trackDramaEvent("ep_view", null); } document.addEventListener("play", function (event) { rememberDramaVideoMode(root, event.target); if (isMainDramaVideo(root, event.target)) { trackDramaEvent("ep_play", event.target); } }, true); document.addEventListener("playing", function (event) { rememberDramaVideoMode(root, event.target); if (isMainDramaVideo(root, event.target)) beginPlayback(event.target); }, true); document.addEventListener("loadedmetadata", function (event) { rememberDramaVideoMode(root, event.target); }, true); document.addEventListener("waiting", function (event) { if (isMainDramaVideo(root, event.target)) settlePlayback(event.target); }, true); document.addEventListener("pause", function (event) { if (isMainDramaVideo(root, event.target) && !event.target.ended) { var playback = event.target.__trackerPlaybackState; var wasPlaying = !!(playback && (playback.startedAt || playback.elapsedMs > 0)); settlePlayback(event.target); if (event.target.seeking || !wasPlaying) return; trackDramaEvent("ep_pause", event.target); } }, true); document.addEventListener("timeupdate", function (event) { if (!isMainDramaVideo(root, event.target) || event.target.seeking) return; videoPlaybackState(event.target).lastPosition = Number(event.target.currentTime) || 0; }, true); document.addEventListener("seeking", function (event) { if (!isMainDramaVideo(root, event.target)) return; var playback = videoPlaybackState(event.target); if (playback.seekFrom != null) return; settlePlayback(event.target); playback.seekFrom = playback.lastPosition; }, true); document.addEventListener("seeked", function (event) { if (!isMainDramaVideo(root, event.target)) return; var playback = videoPlaybackState(event.target); if (playback.seekFrom == null) return; var from = playback.seekFrom; var target = Number(event.target.currentTime) || 0; playback.seekFrom = null; playback.lastPosition = target; if (Math.abs(target - from) >= 0.5) { trackDramaEvent(target > from ? "ep_forward" : "ep_rewind", event.target); } if (!event.target.paused && !event.target.ended) beginPlayback(event.target); }, true); document.addEventListener("ended", function (event) { if (isMainDramaVideo(root, event.target)) { settlePlayback(event.target); trackDramaEvent("ep_complete", event.target, { keepPlaybackDuration: true }); trackDramaEvent("ep_play", event.target, { forceProgress: 100 }); } }, true); function reportPendingDramaProgress() { var video = root.querySelector("video"); if (!isMainDramaVideo(root, video)) return false; var playback = video.__trackerPlaybackState; if (!playback || (!playback.startedAt && playback.elapsedMs <= 0)) return false; var pendingMs = playback.elapsedMs + (playback.startedAt ? Math.max(0, Date.now() - playback.startedAt) : 0); if (pendingMs < 500) return false; settlePlayback(video); trackDramaEvent("ep_play", video); return true; } function flushSDKAfterEnqueue() { if (!global.WebSDK || typeof global.WebSDK.flush !== "function") return; function flush() { Promise.resolve(global.WebSDK.flush()).catch(logError); } Promise.resolve().then(function () { return Promise.resolve(); }).then(flush); global.setTimeout(flush, 50); global.setTimeout(flush, 200); } document.addEventListener("visibilitychange", function () { var video = root.querySelector("video"); if (document.visibilityState === "hidden") { if (reportPendingDramaProgress()) flushSDKAfterEnqueue(); if (isMainDramaVideo(root, video) && !video.paused && !video.ended) beginPlayback(video); return; } if (document.visibilityState === "visible" && isMainDramaVideo(root, video) && !video.paused && !video.ended) { beginPlayback(video); } }); global.addEventListener("beforeunload", function () { if (reportPendingDramaProgress()) flushSDKAfterEnqueue(); }); global.addEventListener("pagehide", function () { if (reportPendingDramaProgress()) flushSDKAfterEnqueue(); }); } function bindSearchEvents() { var container = document.querySelector("[data-search-results]"); if (!container) return; var keyword = stringValue(container.getAttribute("data-search-keyword")); if (!keyword) return; var grid = container.querySelector("[data-search-grid]"); var cards = grid ? Array.prototype.slice.call(grid.querySelectorAll("[data-search-item-id]")) : []; var resultCount = Math.max(0, integer(container.getAttribute("data-search-result-count"), cards.length)); var pageNum = Math.max(1, integer(container.getAttribute("data-search-page"), 1)); var pageSize = Math.max(1, integer(container.getAttribute("data-search-page-size"), cards.length || 1)); if (pageNum === 1) { invoke("keywordSearch", { keyword: keyword, search_result_count: resultCount, search_content_type: "", search_trace_id: "", search_id: "", }); } if (!grid) return; grid.addEventListener("click", function (event) { var target = event.target && event.target.closest ? event.target : null; var resultLink = target && target.closest("[data-search-result-click]"); var card = resultLink && resultLink.closest("[data-search-item-id]"); if (!card || !grid.contains(card)) return; var position = cards.indexOf(card); if (position < 0) return; invoke("keywordClick", { keyword: keyword, click_item_id: stringValue(card.getAttribute("data-search-item-id")), click_item_type_key: "short_drama", click_item_type_name: "短剧", click_position: (pageNum - 1) * pageSize + position + 1, search_trace_id: "", }); }, true); } function bindClicks() { document.addEventListener("click", function (event) { var target = event.target && event.target.closest ? event.target : null; if (!target) return; var popupClose = target.closest( "[data-dialog-ad-close],[data-ad-close],[data-register-close]," + "[data-download-close],[data-login-close],[data-recharge-close]," + "[data-play-coin-close],[data-setting-password-close],[data-drawer-close]" ); if (!popupClose) trackPageClick(event); var switcher = target.closest("[data-drama-switch]"); if (switcher) { trackDramaStatusChange(switcher.getAttribute("data-target-episode"), switcher.getAttribute("data-target-video-id")); } if (target.closest("[data-share-page]") && dramaRoot()) { var root = dramaRoot(); var video = root.querySelector("video"); trackDramaEvent("ep_share", isMainDramaVideo(root, video) ? video : null); } var ad = target.closest('[data-track-ad="external"][data-ad-clickable="1"][data-ad-id]'); if (ad) { trackExternalAd(ad, "click"); } trackNavigation(target.closest("a[data-navigation-key],a[data-nav-key]")); }, true); } function onReady() { whenDOMReady(function () { Promise.resolve(loadCommonFields()).then(function () { trackPageView(); }); bindClicks(); bindAds(); bindExternalAdEvents(); bindAdvertisingEvents(); bindDramaEvents(); bindSearchEvents(); try { global.dispatchEvent(new CustomEvent("tracker:ready", { detail: state.page })); } catch (error) {} }); } var COMMON_FIELD_KEYS = [ "sid", "device_id", "device", "device_fingerprint", "fp_version", ]; var __commonFields = null; var __commonFieldsReady = null; function emptyCommonFields() { var out = {}; for (var i = 0; i < COMMON_FIELD_KEYS.length; i++) { out[COMMON_FIELD_KEYS[i]] = ""; } return out; } function normalizeCommonFields(fields) { var out = emptyCommonFields(); fields = fields || {}; for (var i = 0; i < COMMON_FIELD_KEYS.length; i++) { var k = COMMON_FIELD_KEYS[i]; if (fields[k] != null) out[k] = String(fields[k]); } return out; } function publishCommonFields(fields) { __commonFields = normalizeCommonFields(fields); try { global.__trackerCommonFields = __commonFields; } catch (e) {} try { if (typeof CustomEvent === "function") { global.dispatchEvent( new CustomEvent("tracker:common-fields", { detail: __commonFields }) ); } } catch (e) {} return __commonFields; } function loadCommonFields() { if (__commonFieldsReady) return __commonFieldsReady; __commonFieldsReady = new Promise(function (resolve) { function finish(fields) { resolve(publishCommonFields(fields)); } if (!global.WebSDK || typeof global.WebSDK.getCommonFields !== "function") { finish(emptyCommonFields()); return; } try { Promise.resolve(global.WebSDK.getCommonFields(COMMON_FIELD_KEYS.slice())) .then(function (fields) { finish(fields || emptyCommonFields()); }) .catch(function (err) { logError(err); finish(emptyCommonFields()); }); } catch (err) { logError(err); finish(emptyCommonFields()); } }); return __commonFieldsReady; } function getCommonFieldsCached(keys) { return loadCommonFields().then(function (all) { if (!Array.isArray(keys) || !keys.length) return all; var out = {}; for (var i = 0; i < keys.length; i++) { var k = String(keys[i] || "").trim(); if (k) out[k] = Object.prototype.hasOwnProperty.call(all, k) ? all[k] : ""; } return out; }); } function getCommonFieldCached(key) { var k = String(key || "").trim(); if (!k) return Promise.resolve(""); return getCommonFieldsCached([k]).then(function (m) { return m[k] || ""; }); } function encodeSDKCommonHeader(fields) { var payload = normalizeCommonFields(fields); try { return btoa(unescape(encodeURIComponent(JSON.stringify(payload)))); } catch (e) { logError(e); return ""; } } function getSDKCommonHeader() { return loadCommonFields().then(function (fields) { return encodeSDKCommonHeader(fields); }); } global.trackerInit = init; global.tracker = { init: init, pageView: trackPageView, dramaEvent: trackDramaEvent, dramaStatusChange: trackDramaStatusChange, dramaUnlock: trackDramaUnlock, prepareDramaModeChange: prepareDramaModeChange, advertising: function (payload) { return invoke("advertising", payload || {}); }, externalAd: trackExternalAd, getDramaContext: function () { var context = dramaContext(); return context ? Object.assign({}, context) : {}; }, keywordSearch: function (payload) { return invoke("keywordSearch", payload || {}); }, keywordClick: function (payload) { return invoke("keywordClick", payload || {}); }, getCommonFields: getCommonFieldsCached, getCommonField: getCommonFieldCached, getCommonFieldsSync: function () { return __commonFields; }, encodeSDKCommonHeader: encodeSDKCommonHeader, getSDKCommonHeader: getSDKCommonHeader, flush: function () { if (!global.WebSDK || typeof global.WebSDK.flush !== "function") return Promise.resolve(); return Promise.resolve(global.WebSDK.flush()).catch(logError); }, setUid: function (uid) { if (global.WebSDK && global.WebSDK.setUid) global.WebSDK.setUid(normalizeUid(uid)); }, setChannel: function (channel) { if (global.WebSDK && global.WebSDK.setChannel) global.WebSDK.setChannel(currentChannel(channel)); }, setContext: function (context) { if (global.WebSDK && global.WebSDK.setContext) global.WebSDK.setContext(context || {}); }, }; init(global.__TRACKER_BOOT__ || {}); })(window);