PageSourceSearch

https://www.bangkokpost.com/v3/assets/js/custom/dgs-paywall.js?v=1790840058

js bangkokpost.com collected 2026-10-01 07:35:13 UTC 43,310 bytes, 841 lines download raw bytes

1(function () {
2    const isPostbag = window.location.pathname.indexOf("/opinion/postbag") !== -1;
3
4
5    function getCookie(name) {
6        const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
7        if (match) return match[2];
8        try {
9            const rawLocal = localStorage.getItem(name);
10            if (rawLocal) {
11                // 1. Check if item has JSON expiration wrapper
12                if (rawLocal.charAt(0) === '{' && rawLocal.charAt(rawLocal.length - 1) === '}') {
13                    try {
14                        const parsed = JSON.parse(rawLocal);
15                        if (parsed && typeof parsed.expires === 'number') {
16                            if (Date.now() > parsed.expires) {
17                                localStorage.removeItem(name);
18                                return null;
19                            }
20                            return parsed.value;
21                        }
22                    } catch (jsonErr) { }
23                }
24                // 2. Check if item is a JWT token with an internal exp claim
25                const parts = rawLocal.split('.');
26                if (parts.length === 3) {
27                    try {
28                        let b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
29                        while (b64.length % 4) b64 += '=';
30                        const payload = JSON.parse(atob(b64));
31                        if (payload && payload.exp && (payload.exp * 1000) < Date.now()) {
32                            localStorage.removeItem(name);
33                            return null;
34                        }
35                    } catch (jwtErr) { }
36                }
37                return rawLocal;
38            }
39
40            const rawSession = sessionStorage.getItem(name);
41            if (rawSession) return rawSession;
42        } catch (e) {
43            return null;
44        }
45        return null;
46    }
47
48    function setCookie(name, value, ms) {
49        const d = new Date();
50        const ttl = ms || (1 * 60 * 60 * 1000);
51        const expiresTime = d.getTime() + ttl;
52        d.setTime(expiresTime);
53        document.cookie = name + "=" + value + ";path=/;SameSite=Lax;expires=" + d.toUTCString();
54        try {
55            const itemObj = {
56                value: value,
57                expires: expiresTime
58            };
59            localStorage.setItem(name, JSON.stringify(itemObj));
60            sessionStorage.setItem(name, value);
61        } catch (e) { }
62    }
63
64    function isLoggedIn() {
65        const hasCookie = !!getCookie(window._m_c || "__bwp");
66        const dgsMetaUser = document.querySelector('meta[name="dgs-user"]');
67        const hasActiveSub = dgsMetaUser && dgsMetaUser.content === 'enable';
68        return hasCookie || hasActiveSub;
69    }
70
71    function logPaywallView(type, action, variant, variantName, customParams) {
72        if (!action) {
73            action = 'paywall_impression';
74        }
75        customParams = customParams || {};
76
77        const isPostbag = window.location.pathname.indexOf("/opinion/postbag") !== -1;
78        const current_section = (typeof customParams.section_name !== 'undefined' && customParams.section_name !== null)
79            ? customParams.section_name
80            : ((typeof g_section_name !== 'undefined' && g_section_name) ? g_section_name : "");
81
82        const isSubLanding = current_section === 'subscription' || current_section === 'digital-subscription' || current_section === 'subscriptions' || current_section === 'digital-subscriptions';
83
84        let log_variant = variant;
85        if (isSubLanding) {
86            log_variant = "";
87        } else if (typeof log_variant === 'undefined' || log_variant === null || log_variant === '') {
88            log_variant = window.DGS_PAYWALL_VARIANT || '';
89        }
90
91        let log_variant_name = variantName;
92        if (isSubLanding) {
93            log_variant_name = "";
94        } else if (typeof log_variant_name === 'undefined' || log_variant_name === null || log_variant_name === '') {
95            log_variant_name = window.DGS_PAYWALL_VARIANT_NAME || '';
96        }
97        if (!isSubLanding && log_variant && !log_variant_name && window.DGS_PAYWALL_WORDINGS_ALL && window.DGS_PAYWALL_WORDINGS_ALL[log_variant]) {
98            const vObj = window.DGS_PAYWALL_WORDINGS_ALL[log_variant];
99            log_variant_name = vObj['variant_name'] || '';
100        }
101
102        let dgsBase = "/v3/";
103        let log_type = isPostbag ? 'postbag' : type;
104        let log_content_id = isPostbag ? "" : ((typeof customParams.content_id !== 'undefined' && customParams.content_id !== null)
105            ? customParams.content_id
106            : ((typeof g_content_id !== 'undefined' && g_content_id) ? g_content_id : ""));
107
108        if (current_section === 'topic_main') {
109            log_type = 'topic_main';
110        } else if (current_section === 'topic_content') {
111            log_type = 'topic_content';
112        }
113
114        let log_topic_id = (typeof customParams.topic_id !== 'undefined' && customParams.topic_id !== null)
115            ? customParams.topic_id
116            : ((typeof g_topic_id !== 'undefined' && g_topic_id) ? g_topic_id : "");
117
118        if (current_section === 'topic_content' && log_topic_id) {
119            log_content_id = log_topic_id;
120        }
121
122        let log_category_id = (typeof customParams.category_id !== 'undefined' && customParams.category_id !== null)
123            ? customParams.category_id
124            : ((typeof g_category_id !== 'undefined' && g_category_id) ? g_category_id : "");
125
126        const logUrl = dgsBase + "api/log-action-paywall";
127        const data = {
128            type: log_type,
129            action: action,
130            content_id: log_content_id,
131            section_name: current_section,
132            category_id: log_category_id,
133            variant: log_variant || "",
134            variant_name: log_variant_name || ""
135        };
136
137        const params = new URLSearchParams(data);
138
139        // 1. ลองใช้ navigator.sendBeacon สำหรับการกด CTA หรือเปลี่ยนหน้า เพื่อป้องกัน Request โดน Abort เมื่อเบราว์เซอร์ Unload/Redirect
140        if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
141            try {
142                const blob = new Blob([params.toString()], { type: 'application/x-www-form-urlencoded' });
143                const sent = navigator.sendBeacon(logUrl, blob);
144                if (sent) {
145                    console.log("Paywall action logged via sendBeacon:", action, data);
146                    return;
147                }
148            } catch (beaconErr) {
149                console.warn("sendBeacon error, falling back to fetch/ajax:", beaconErr);
150            }
151        }
152
153        // 2. ใช้ fetch พร้อม keepalive: true (ทำงานได้ต่อเนื่องแม้เพจกำลังถูก Unload/Redirect)
154        if (typeof fetch === 'function') {
155            fetch(logUrl, {
156                method: "POST",
157                headers: {
158                    "Content-Type": "application/x-www-form-urlencoded"
159                },
160                body: params,
161                keepalive: true
162            })
163                .then(function (res) { return res.json(); })
164                .then(function (res) {
165                    console.log("Paywall action logged successfully via fetch:", res);
166                })
167                .catch(function (err) {
168                    console.error("Failed to log paywall view via fetch:", err);
169                });
170            return;
171        }
172
173        // 3. Fallback สำหรับเบราว์เซอร์เก่ามากที่ไม่มี fetch/sendBeacon
174        if (typeof $ !== 'undefined' && $.ajax) {
175            $.ajax({
176                url: logUrl,
177                method: "POST",
178                data: data,
179                dataType: "json",
180                success: function (res) {
181                    console.log("Paywall view logged successfully via jQuery:", res);
182                },
183                error: function (err) {
184                    console.error("Failed to log paywall view via jQuery:", err);
185                }
186            });
187        }
188    }
189    window.logPaywallView = logPaywallView;
190
191    function logPaywallLockDiagnostic(triggerSource) {
192        try {
193            const pageKey = 'dgs_diag_logged_' + window.location.pathname;
194            if (sessionStorage.getItem(pageKey)) {
195                return;
196            }
197            sessionStorage.setItem(pageKey, '1');
198
199            const cookieName = window._p_c || "__bwp_pw";
200            const loginCookieName = window._m_c || "__bwp";
201
202            // 1. ตรวจสอบคุกกี้ Login (__bwp)
203            const matchLogin = document.cookie.match(new RegExp('(^| )' + loginCookieName + '=([^;]+)'));
204            const hasLoginCookie = !!matchLogin;
205            let loggedInUserId = null;
206            if (hasLoginCookie) {
207                try {
208                    const loginRaw = decodeURIComponent(matchLogin[2]);
209                    const uidMatch = loginRaw.match(/userID["\s:;]+([0-9]+)/i) || loginRaw.match(/"id"[:\s]+([0-9]+)/i);
210                    if (uidMatch) loggedInUserId = uidMatch[1];
211                } catch (e) { }
212            }
213
214            // 2. ตรวจสอบคุกกี้สถานะสมาชิก (_bp_pdgs หรือ __bwp_pw)
215            const subCookie = getCookie(window._s_c || "_bp_pdgs");
216            const hasSubCookie = (subCookie === "1");
217            const matchPw = document.cookie.match(new RegExp('(^| )' + cookieName + '=([^;]+)'));
218            const hasPwCookie = !!matchPw;
219            let pwPayload = null;
220            let isPwExpired = false;
221            if (hasPwCookie) {
222                try {
223                    const parts = matchPw[2].split('.');
224                    if (parts.length === 3) {
225                        let b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
226                        while (b64.length % 4) b64 += '=';
227                        pwPayload = JSON.parse(atob(b64));
228                        if (pwPayload && pwPayload.exp) {
229                            isPwExpired = (pwPayload.exp * 1000) < Date.now();
230                        }
231                    }
232                } catch (e) { }
233            }
234
235            // 3. ตรวจสอบ LocalStorage Backup (ถ้ามีค้างอยู่)
236            let lsPayload = null;
237            let hasLs = false;
238            let isLsExpired = false;
239            try {
240                const lsToken = localStorage.getItem(cookieName);
241                if (lsToken) {
242                    hasLs = true;
243                    const parts = lsToken.split('.');
244                    if (parts.length === 3) {
245                        let b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
246                        while (b64.length % 4) b64 += '=';
247                        lsPayload = JSON.parse(atob(b64));
248                        if (lsPayload && lsPayload.exp) {
249                            isLsExpired = (lsPayload.exp * 1000) < Date.now();
250                        }
251                    }
252                }
253            } catch (e) { }
254
255            // ต้อง (1) Login อยู่ และ (2) มี Cookie สิทธิ์ (__bwp_sub หรือ __bwp_pw)
256            if (!hasLoginCookie || (!hasSubCookie && !hasPwCookie)) {
257                return;
258            }
259
260            // 4. ตรวจสอบ Meta Tags จาก Server / CDN
261            const dgsMetaContent = document.querySelector('meta[name="dgs-content"]');
262            const dgsMetaUser = document.querySelector('meta[name="dgs-user"]');
263            const metaContent = dgsMetaContent ? dgsMetaContent.content : null;
264            const metaUser = dgsMetaUser ? dgsMetaUser.content : null;
265
266            // 5. วิเคราะห์เหตุผลว่าทำไมมีสิทธิ์แล้วยังติดล็อก
267            let lockReason = 'UNKNOWN_LOCK';
268            if (isPwExpired || isLsExpired) {
269                lockReason = 'EXPIRED_TOKEN (Token ใน Cookie หรือ LocalStorage หมดอายุแล้ว)';
270            } else if (pwPayload && pwPayload.pw === 0 && pwPayload.sub === 0) {
271                lockReason = 'TOKEN_PW_ZERO (มี Token แต่สถานะ pw=0 และ sub=0 ไม่ใช่ Subscriber)';
272            } else if (loggedInUserId && pwPayload && pwPayload.uid && String(loggedInUserId) !== String(pwPayload.uid)) {
273                lockReason = 'UID_MISMATCH (User ID ที่ Login ไม่ตรงกับ UID ใน Token)';
274            } else if (metaUser === 'disable') {
275                lockReason = 'CDN_CACHE_MISMATCH (มี Login, มี Cookie สมาชิกครบ แต่ CDN ส่งหน้าแคช disable มา)';
276            } else {
277                lockReason = 'ALL_TOKENS_VALID_BUT_PAYWALL_TRIGGERED (ข้อมูลครบถ้วนแต่ติดล็อก)';
278            }
279
280            const diagPayload = {
281                trigger_source: triggerSource || 'unknown',
282                lock_reason: lockReason,
283                user_id: loggedInUserId || (pwPayload ? pwPayload.uid : null) || (lsPayload ? lsPayload.uid : null) || 'GUEST',
284                has_login_cookie: hasLoginCookie,
285                has_pw_cookie: hasPwCookie,
286                has_sub_cookie: hasSubCookie,
287                cookie_pw: pwPayload,
288                is_pw_cookie_expired: isPwExpired,
289                has_localstorage_pw: hasLs,
290                localstorage_pw: lsPayload,
291                is_ls_expired: isLsExpired,
292                meta_dgs_content: metaContent,
293                meta_dgs_user: metaUser,
294                page_url: window.location.href,
295                referrer: document.referrer || '',
296                timestamp: new Date().toISOString()
297            };
298
299            const logEndpoint = "/v3/api/log-paywall-diagnostic";
300            const bodyStr = JSON.stringify(diagPayload);
301
302            if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
303                const blob = new Blob([bodyStr], { type: 'application/json' });
304                navigator.sendBeacon(logEndpoint, blob);
305            } else if (typeof fetch === 'function') {
306                fetch(logEndpoint, {
307                    method: 'POST',
308                    headers: { 'Content-Type': 'application/json' },
309                    body: bodyStr,
310                    keepalive: true
311                }).catch(function () { });
312            }
313        } catch (err) {
314            console.warn("[Paywall] logPaywallLockDiagnostic error:", err);
315        }
316    }
317
318    function initPaywallScrollObserver() {
319        // ค้นหา Element ของแถบ Paywall หรือพื้นที่ที่โดนเบลอ (.sticky-blurred-paywall สำหรับ Life, .force_login--popup สำหรับ News)
320        const paywallEl = document.querySelector('.sticky-blurred-paywall, .force_login--popup, .paywall-locked-body');
321        if (!paywallEl) {
322            return;
323        }
324
325        // ใช้ Intersection Observer API สำหรับจับความเคลื่อนไหวเมื่อเลื่อนจอมาเจอ Element
326        if (typeof IntersectionObserver !== 'undefined') {
327            const observer = new IntersectionObserver((entries, obs) => {
328                entries.forEach(entry => {
329                    if (entry.isIntersecting) {
330                        console.log("Paywall popup encountered: User scrolled down and saw the paywall.");
331                        logPaywallView("article");
332                        logPaywallLockDiagnostic("scroll_blurred_article");
333                        obs.disconnect(); // สั่งหยุดจับความเคลื่อนไหวหลังจากบันทึก Log สำเร็จครั้งแรก
334                    }
335                });
336            }, {
337                threshold: 0.1 // ทำงานเมื่อ Element โผล่ขึ้นมาในจออย่างน้อย 10%
338            });
339            observer.observe(paywallEl);
340        } else {
341            // ระบบสำรองสำหรับบราวเซอร์รุ่นเก่า (ผูก Event Scroll)
342            const onScroll = function () {
343                const rect = paywallEl.getBoundingClientRect();
344                const viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
345                if (rect.top >= 0 && rect.top <= viewHeight) {
346                    console.log("Paywall popup encountered (fallback): User scrolled down and saw the paywall.");
347                    logPaywallView("article");
348                    logPaywallLockDiagnostic("scroll_blurred_article_fallback");
349                    window.removeEventListener('scroll', onScroll);
350                }
351            };
352            window.addEventListener('scroll', onScroll);
353        }
354    }
355
356    function initPopupTrigger() {
357        // Do not run paywall popup trigger on digital subscription pages
358        if (window.location.pathname.indexOf("/digital-subscription") !== -1) {
359            return;
360        }
361
362        if (window.location.pathname.indexOf("/subscriptions") !== -1) {
363            return;
364        }
365
366        // Do not run paywall popup trigger on premium publications (ebook) pages
367        if (window.location.pathname.indexOf("/premium-publications") !== -1) {
368            return;
369        }
370
371        // หน้า Postbag: ใช้เฉพาะ Lock Bar Paywall ในหน้าเนื้อหา (ไม่แสดง Popup Modal)
372        if (isPostbag) {
373            initPaywallScrollObserver();
374            return;
375        }
376
377        // อ่านค่าสถานะ Paywall และสิทธิ์การใช้งานตรงจาก Meta Tags ที่ส่งมาจาก Server
378        const dgsMetaArticle = document.querySelector('meta[name="dgs-content"]');
379        const dgsMetaUser = document.querySelector('meta[name="dgs-user"]');
380
381        const isContentLocked = dgsMetaArticle && dgsMetaArticle.content === 'enable';
382        const isUserDisabled = dgsMetaUser && dgsMetaUser.content === 'disable';
383
384        // จัดการแสดงผลตามเงื่อนไขของประเภทหน้าจอ (ยกเลิก Section Popup และ Popup ข่าว < 3 เดือน)
385        if (isUserDisabled) {
386            logPaywallLockDiagnostic("page_load_user_disabled");
387            if (isContentLocked) {
388                if (typeof g_content_id !== 'undefined' && g_content_id) {
389                    // หน้าข่าวเดี่ยวที่โดนล็อก (Print / Online > 3 เดือน): รันตัวตรวจจับการเลื่อนหน้าจอ (Scroll Observer)
390                    initPaywallScrollObserver();
391                }
392            }
393        }
394    }
395
396    // ปิดการบันทึก Log Action และการ Save ลง Cache (sessionStorage / localStorage) เมื่อกดปุ่ม Subscribe
397
398    document.addEventListener("click", function (e) {
399        const btnDefault = e.target.closest('.btn--default');
400        const btnBuy = e.target.closest('.btn-buynow, #subscribe_btn, .welcome-btn a, a[href*="/digital-subscription/payment/"], a[href*="Digital_subscription_c/payment/"]');
401        const btnModalLogin = e.target.closest('#signinModal .btnsignin, #signinModal .planlogin-desc a');
402
403        const path = window.location.pathname;
404        const isDigitalSubPage = path.indexOf("/digital-subscription") !== -1;
405        const isSubPage = path.indexOf("/subscriptions") !== -1 || path.indexOf("/subscription") !== -1;
406
407        if (btnDefault || (btnBuy && (isDigitalSubPage || isSubPage)) || (btnModalLogin && (isDigitalSubPage || isSubPage)) || btnBuy) {
408            const isArticlePopup = btnDefault && btnDefault.closest('.force_login--popup');
409            const isPopupLogin = btnDefault && btnDefault.closest('#popup-login');
410            const isEbookPopup = (btnDefault && btnDefault.closest('#popupdgs')) || path.indexOf("/premium-publications") !== -1;
411            const isPostbagPage = isPostbag || path.indexOf("/opinion/postbag") !== -1;
412
413            let pageType = 'section';
414            let clickContentId = (typeof g_content_id !== 'undefined' && g_content_id) ? g_content_id : "";
415            let currentSection = (typeof g_section_name !== 'undefined' && g_section_name) ? g_section_name : "";
416
417            let clickVariant = window.DGS_PAYWALL_VARIANT || '';
418
419            if (isEbookPopup) {
420                pageType = 'ebook';
421                currentSection = 'ebook';
422                clickVariant = window.DGS_PAYWALL_VARIANT || '';
423                logPaywallView("ebook", "paywall_cta_click", clickVariant);
424            } else if (isPostbagPage) {
425                pageType = 'postbag';
426                currentSection = 'opinion';
427                clickVariant = window.DGS_PAYWALL_VARIANT || '';
428                logPaywallView("postbag", "paywall_cta_click", clickVariant);
429            } else if (isArticlePopup) {
430                pageType = 'article';
431                clickVariant = window.DGS_PAYWALL_VARIANT || '';
432                logPaywallView("article", "paywall_cta_click", clickVariant);
433            } else if (isPopupLogin) {
434                pageType = 'article';
435                clickVariant = window.DGS_PAYWALL_VARIANT || '';
436                logPaywallView("article", "paywall_cta_click", clickVariant);
437            } else if (isDigitalSubPage || isSubPage) {
438                pageType = 'section';
439                currentSection = isDigitalSubPage ? 'digital-subscription' : 'subscription';
440                clickVariant = '';
441                logPaywallView("section", "paywall_cta_click", "", "", { section_name: currentSection });
442            } else {
443                // Fallback check based on globally available variables
444                pageType = (typeof g_content_id !== 'undefined' && g_content_id) ? 'article' : 'section';
445                clickVariant = window.DGS_PAYWALL_VARIANT || '';
446                if (!currentSection) {
447                    currentSection = (pageType === 'section') ? 'digital-subscription' : '';
448                }
449                logPaywallView(pageType, "paywall_cta_click", clickVariant);
450            }
451
452            if (currentSection === 'topic_main') {
453                pageType = 'topic_main';
454            } else if (currentSection === 'topic_content') {
455                pageType = 'topic_content';
456                if (typeof g_topic_id !== 'undefined' && g_topic_id) {
457                    clickContentId = g_topic_id;
458                }
459            }
460
461            if (pageType) {
462                const isSubLandingSection = currentSection === 'subscription' || currentSection === 'digital-subscription' || currentSection === 'subscriptions' || currentSection === 'digital-subscriptions';
463                const finalVariant = isSubLandingSection ? '' : clickVariant;
464
465                let clickVariantName = '';
466                if (!isSubLandingSection) {
467                    clickVariantName = window.DGS_PAYWALL_VARIANT_NAME || '';
468                    if (!clickVariantName && window.DGS_PAYWALL_WORDINGS_ALL && window.DGS_PAYWALL_WORDINGS_ALL[clickVariant]) {
469                        const vObj = window.DGS_PAYWALL_WORDINGS_ALL[clickVariant];
470                        clickVariantName = vObj['variant_name'] || '';
471                    }
472                }
473
474                sessionStorage.setItem('clicked_subscribe_paywall', 'true');
475                sessionStorage.setItem('subscribe_9_9_type', pageType);
476                sessionStorage.setItem('subscribe_9_9_content_id', clickContentId);
477                sessionStorage.setItem('subscribe_9_9_section_name', currentSection);
478                sessionStorage.setItem('subscribe_9_9_category_id', (typeof g_category_id !== 'undefined' && g_category_id) ? g_category_id : "");
479                sessionStorage.setItem('subscribe_9_9_variant', finalVariant);
480                sessionStorage.setItem('subscribe_9_9_variant_name', clickVariantName);
481
482                // Backup to localStorage in case of bank redirect opening in a new tab/session
483                localStorage.setItem('subscribe_9_9_type_backup', pageType);
484                localStorage.setItem('subscribe_9_9_content_id_backup', clickContentId);
485                localStorage.setItem('subscribe_9_9_section_name_backup', currentSection);
486                localStorage.setItem('subscribe_9_9_category_id_backup', (typeof g_category_id !== 'undefined' && g_category_id) ? g_category_id : "");
487                localStorage.setItem('subscribe_9_9_variant_backup', finalVariant);
488                localStorage.setItem('subscribe_9_9_variant_name_backup', clickVariantName);
489            }
490        }
491    });
492
493
494    function checkCheckoutStart() {
495        if (window.location.pathname.indexOf("/digital-subscription/payment/") !== -1) {
496            if (sessionStorage.getItem('clicked_subscribe_paywall') === 'true') {
497                sessionStorage.removeItem('clicked_subscribe_paywall');
498
499                const pageType = sessionStorage.getItem('subscribe_9_9_type') || localStorage.getItem('subscribe_9_9_type_backup') || 'section';
500                const contentId = sessionStorage.getItem('subscribe_9_9_content_id') || localStorage.getItem('subscribe_9_9_content_id_backup');
501                const sectionName = sessionStorage.getItem('subscribe_9_9_section_name') || localStorage.getItem('subscribe_9_9_section_name_backup') || 'digital-subscription';
502                const categoryId = sessionStorage.getItem('subscribe_9_9_category_id') || localStorage.getItem('subscribe_9_9_category_id_backup');
503
504                const isSubLandingSection = sectionName === 'subscription' || sectionName === 'digital-subscription' || sectionName === 'subscriptions' || sectionName === 'digital-subscriptions';
505                const variant = isSubLandingSection ? '' : (sessionStorage.getItem('subscribe_9_9_variant') || localStorage.getItem('subscribe_9_9_variant_backup') || '');
506                const variantName = isSubLandingSection ? '' : (sessionStorage.getItem('subscribe_9_9_variant_name') || localStorage.getItem('subscribe_9_9_variant_name_backup') || '');
507
508                logPaywallView(pageType, 'checkout_start', variant, variantName, {
509                    content_id: contentId,
510                    section_name: sectionName,
511                    category_id: categoryId,
512                    topic_id: pageType === 'topic_content' ? contentId : null
513                });
514                console.log("Logged checkout_start action for section: " + sectionName);
515            }
516        }
517    }
518
519    function checkPurchaseComplete() {
520        if (window.location.pathname.indexOf("/digital-subscription/payment/success/") !== -1) {
521            const urlParams = new URLSearchParams(window.location.search);
522            const ref = urlParams.get('Ref');
523            if (ref) {
524                const sessionKey = 'purchase_complete_logged_' + ref;
525                if (sessionStorage.getItem(sessionKey) !== 'true' && localStorage.getItem(sessionKey) !== 'true') {
526                    // Set immediately to prevent duplicate async trigger
527                    sessionStorage.setItem(sessionKey, 'true');
528                    localStorage.setItem(sessionKey, 'true');
529
530                    const pageType = sessionStorage.getItem('subscribe_9_9_type') || localStorage.getItem('subscribe_9_9_type_backup') || 'section';
531                    const contentId = sessionStorage.getItem('subscribe_9_9_content_id') || localStorage.getItem('subscribe_9_9_content_id_backup');
532                    const sectionName = sessionStorage.getItem('subscribe_9_9_section_name') || localStorage.getItem('subscribe_9_9_section_name_backup') || 'digital-subscription';
533                    const categoryId = sessionStorage.getItem('subscribe_9_9_category_id') || localStorage.getItem('subscribe_9_9_category_id_backup');
534
535                    const isSubLandingSection = sectionName === 'subscription' || sectionName === 'digital-subscription' || sectionName === 'subscriptions' || sectionName === 'digital-subscriptions';
536                    const variant = isSubLandingSection ? '' : (sessionStorage.getItem('subscribe_9_9_variant') || localStorage.getItem('subscribe_9_9_variant_backup') || '');
537                    const variantName = isSubLandingSection ? '' : (sessionStorage.getItem('subscribe_9_9_variant_name') || localStorage.getItem('subscribe_9_9_variant_name_backup') || '');
538
539                    logPaywallView(pageType, 'purchase_complete', variant, variantName, {
540                        content_id: contentId,
541                        section_name: sectionName,
542                        category_id: categoryId,
543                        topic_id: pageType === 'topic_content' ? contentId : null
544                    });
545                    console.log("Logged purchase_complete action with Ref: " + ref + " for section: " + sectionName);
546
547                    // Clear subscription flow context from sessionStorage
548                    sessionStorage.removeItem('subscribe_9_9_type');
549                    sessionStorage.removeItem('subscribe_9_9_content_id');
550                    sessionStorage.removeItem('subscribe_9_9_section_name');
551                    sessionStorage.removeItem('subscribe_9_9_category_id');
552                    sessionStorage.removeItem('pw_free_checked');
553                    sessionStorage.removeItem('subscribe_9_9_variant');
554                    sessionStorage.removeItem('subscribe_9_9_variant_name');
555
556                    // Clear backup from localStorage
557                    localStorage.removeItem('subscribe_9_9_type_backup');
558                    localStorage.removeItem('subscribe_9_9_content_id_backup');
559                    localStorage.removeItem('subscribe_9_9_section_name_backup');
560                    localStorage.removeItem('subscribe_9_9_category_id_backup');
561                    localStorage.removeItem('subscribe_9_9_variant_backup');
562                    localStorage.removeItem('subscribe_9_9_variant_name_backup');
563                }
564            }
565        }
566    }
567
568    function initSubscriptionModalLog() {
569        const isDigitalSub = window.location.pathname.indexOf("/digital-subscription") !== -1;
570        const isSub = window.location.pathname.indexOf("/subscriptions") !== -1 || window.location.pathname.indexOf("/subscription") !== -1;
571
572        if (!isDigitalSub && !isSub) {
573            return;
574        }
575
576        const sectionName = isDigitalSub ? "digital-subscription" : "subscription";
577        const actionShow = isDigitalSub ? "digital-subscriptions_popup_signin" : "subscriptions_popup_signin";
578        const actionLogin = isDigitalSub ? "digital-subscriptions_login" : "subscriptions_login_click";
579        const actionClose = isDigitalSub ? "digital-subscriptions_popup_close" : "subscriptions_popup_close";
580
581        let logged = false;
582        const handleModalShow = function () {
583            if (logged) return;
584            logged = true;
585            setTimeout(function () { logged = false; }, 1000);
586
587            logPaywallView("section", actionShow, null, null, {
588                content_id: "",
589                section_name: sectionName,
590                category_id: "null"
591            });
592        };
593
594        if (typeof $ !== 'undefined') {
595            $('#signinModal').on('show.bs.modal', handleModalShow);
596        } else {
597            const modalEl = document.getElementById('signinModal');
598            if (modalEl) {
599                modalEl.addEventListener('show.bs.modal', handleModalShow);
600            }
601        }
602
603        const modalEl = document.getElementById('signinModal');
604        if (modalEl) {
605            modalEl.addEventListener('click', function (e) {
606                const isSignIn = e.target.closest('.btnsignin');
607                const isRegister = e.target.closest('.planlogin-desc a');
608                const isClose = e.target.closest('.closex');
609
610                if (isSignIn || isRegister) {
611                    logPaywallView("section", actionLogin, null, null, {
612                        content_id: "",
613                        section_name: sectionName,
614                        category_id: "null"
615                    });
616                } else if (isClose) {
617                    logPaywallView("section", actionClose, null, null, {
618                        content_id: "",
619                        section_name: sectionName,
620                        category_id: "null"
621                    });
622                }
623            });
624        }
625    }
626
627
628
629    function initPaywallCopyProtection() {
630        // If developer cookie bpdgs_dev = 1 is set, bypass copy protection for testing
631        if (getCookie('bpdgs_dev') === '1') {
632            console.log("Paywall Copy Protection bypassed (bpdgs_dev = 1)");
633            return;
634        }
635
636        const dgsMetaArticle = document.querySelector('meta[name="dgs-content"]');
637        const isContentLocked = dgsMetaArticle && dgsMetaArticle.getAttribute('content') === 'enable';
638
639        if (!isContentLocked) {
640            return;
641        }
642
643        // 1. Inject CSS to disable text selection on article content elements
644        const style = document.createElement('style');
645        style.innerHTML = `
646            .article-content, #article-content, .box-postbag--user {
647                -webkit-user-select: none !important;
648                -moz-user-select: none !important;
649                -ms-user-select: none !important;
650                user-select: none !important;
651            }
652        `;
653        document.head.appendChild(style);
654
655        // 2. Attach event listeners to prevent copy, cut, right-click context menu, and dragging
656        const targetElements = document.querySelectorAll('#article-content, .article-content, .box-postbag--user');
657        targetElements.forEach(function (el) {
658            el.addEventListener('copy', function (e) {
659                e.preventDefault();
660                return false;
661            });
662            el.addEventListener('cut', function (e) {
663                e.preventDefault();
664                return false;
665            });
666            el.addEventListener('contextmenu', function (e) {
667                e.preventDefault();
668                return false;
669            });
670            el.addEventListener('dragstart', function (e) {
671                e.preventDefault();
672                return false;
673            });
674        });
675
676        // 3. Prevent keyboard copy/select shortcuts (Ctrl+C, Cmd+C, Ctrl+A, Cmd+A) when focused/selected inside article
677        document.addEventListener('keydown', function (e) {
678            if ((e.ctrlKey || e.metaKey) && (e.key === 'c' || e.key === 'C' || e.key === 'a' || e.key === 'A')) {
679                const selection = window.getSelection();
680                if (selection && selection.rangeCount > 0) {
681                    let container = selection.getRangeAt(0).commonAncestorContainer;
682                    if (container.nodeType === 3) container = container.parentNode;
683                    if (container && container.closest && container.closest('#article-content, .article-content, .box-postbag--user')) {
684                        e.preventDefault();
685                        return false;
686                    }
687                }
688            }
689        });
690    }
691
692    function hasActiveSubscriptionFromPaywallCookie() {
693        try {
694            // 1. ตรวจสอบจากคุกกี้สถานะหน้าบ้าน _bp_pdgs (ปลอดภัย 100% ตามมาตรฐาน Dual-Cookie)
695            const subCookie = getCookie(window._s_c || "_bp_pdgs");
696            if (subCookie === "1") {
697                return true;
698            }
699
700            // Fallback เผื่อสภาพแวดล้อม dev ที่คุกกี้ __bwp_pw ยังอ่านได้จาก JS
701            const rawCookie = getCookie(window._p_c || "__bwp_pw");
702            if (rawCookie) {
703                const parts = rawCookie.split('.');
704                if (parts.length === 3) {
705                    let b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
706                    while (b64.length % 4) b64 += '=';
707                    const payload = JSON.parse(atob(b64));
708                    if (payload && (payload.pw === 1 || payload.sub === 1)) {
709                        if (!payload.exp || (payload.exp * 1000) > Date.now()) {
710                            return true;
711                        }
712                    }
713                }
714            }
715
716            // 2. ตรวจสอบจาก meta tag dgs-user
717            const dgsMetaUser = document.querySelector('meta[name="dgs-user"]');
718            if (dgsMetaUser && dgsMetaUser.content === 'enable') {
719                return true;
720            }
721
722            return false;
723        } catch (e) {
724            return false;
725        }
726    }
727
728    function checkCdnSubscriberCacheMismatch() {
729        const isSubscriber = hasActiveSubscriptionFromPaywallCookie();
730        const dgsMetaArticle = document.querySelector('meta[name="dgs-content"]');
731        const isContentLocked = dgsMetaArticle && dgsMetaArticle.content === 'enable';
732        const dgsMetaUser = document.querySelector('meta[name="dgs-user"]');
733        const isUserDisabled = dgsMetaUser && dgsMetaUser.content === 'disable';
734        const pageKey = 'cdn_bypass_' + window.location.pathname;
735
736        // ถ้าผู้ใช้เป็นสมาชิกจาก cookie _bp_pdgs หรือ __bwp_pw แต่หน้าเว็บที่ได้จาก CDN เป็นหน้าติดล็อกของผู้ใช้ทั่วไป
737        if (isSubscriber && isContentLocked && isUserDisabled) {
738            if (!sessionStorage.getItem(pageKey)) {
739                sessionStorage.setItem(pageKey, '1');
740                console.warn("CDN Subscriber Cache Mismatch detected Reloading to fetch live content from origin...");
741
742                window.location.reload();
743                return true;
744            }
745        } else if (dgsMetaUser && dgsMetaUser.content === 'enable') {
746            sessionStorage.removeItem(pageKey);
747        }
748        return false;
749    }
750
751    /**
752     * กู้คืนสิทธิ์สมาชิกผ่าน Single Source of Truth (/member/get_member_login)
753     * ไม่ยิง /api/refresh-paywall-cookie ซ้ำซ้อน แต่ดักฟังผลจาก g_member ใน Header
754     * หากพบว่าเป็นสมาชิก (is_subscriber: true) แต่หน้าเว็บปัจจุบันติดแคช Inox CDN หน้าล็อก -> สั่ง Reload เพื่อ Bypass CDN
755     */
756    function checkAndRecoverPaywallCookie() {
757        const loginCookieName = window.BKP_COOKIE_MEMBER || "__bwp";
758        const hasLoginCookie = document.cookie.indexOf(loginCookieName + '=') !== -1;
759        const subCookie = getCookie(window._s_c || "_bp_pdgs");
760        const hasSubCookie = (subCookie === "1");
761        const pwCookieName = window._p_c || "__bwp_pw";
762        const hasPwCookie = document.cookie.indexOf(pwCookieName + '=') !== -1;
763
764        // ถ้ามีคุกกี้สิทธิ์อยู่แล้ว ไม่ต้องทำอะไร
765        if (hasSubCookie || hasPwCookie) {
766            return false;
767        }
768
769        // ตรวจสอบเฉพาะเมื่อผู้ใช้เข้าสู่ระบบอยู่ (มีคุกกี้ Login) และหน้านี้ติดล็อก
770        const dgsMetaArticle = document.querySelector('meta[name="dgs-content"]');
771        const isContentLocked = dgsMetaArticle && dgsMetaArticle.content === 'enable';
772        const dgsMetaUser = document.querySelector('meta[name="dgs-user"]');
773        const isUserDisabled = !dgsMetaUser || dgsMetaUser.content === 'disable';
774
775        if (hasLoginCookie && (isContentLocked || isUserDisabled)) {
776            const pageRecoveryKey = 'pw_recovery_' + window.location.pathname;
777            if (sessionStorage.getItem(pageRecoveryKey)) {
778                return false;
779            }
780
781            const triggerReloadIfSubscriber = function () {
782                const isSub = (window.g_member && window.g_member.is_subscriber) || hasActiveSubscriptionFromPaywallCookie();
783                if (isSub) {
784                    console.log("Paywall subscriber confirmed via get_member_login. Reloading to bypass CDN cache...");
785                    sessionStorage.setItem(pageRecoveryKey, '1');
786                    window.location.reload();
787                }
788            };
789
790            // ผูกกับ g_member.get_member_login ใน Header (Single Source of Truth) เพื่อไม่ให้ยิงซ้ำ
791            if (window.g_member && typeof window.g_member.get_member_login === 'function') {
792                window.g_member.get_member_login(function () {
793                    triggerReloadIfSubscriber();
794                });
795                return true;
796            }
797        }
798        return false;
799    }
800
801    /**
802     * ล้าง Token ออกจาก localStorage เพื่อความปลอดภัยตามมาตรฐานสากล (Token ต้องอยู่ใน HttpOnly Cookie เท่านั้น)
803     */
804    function cleanupPaywallLocalStorage() {
805        try {
806            const cookieName = window._p_c || "__bwp_pw";
807            if (localStorage.getItem(cookieName)) {
808                localStorage.removeItem(cookieName);
809            }
810        } catch (e) {
811            // silent fail
812        }
813    }
814
815    // Initialize trigger immediately on DOM ready without delay to prevent Flash of Content
816    function initDgsPaywall() {
817        // Clean up legacy _t parameter from URL bar if present to keep URL clean
818        if (window.location.search && window.location.search.indexOf('_t=') !== -1 && window.history && window.history.replaceState) {
819            const cleanSearch = window.location.search.replace(/([?&])_t=\d+(&|$)/, '$1').replace(/[?&]$/, '');
820            const cleanUrl = window.location.pathname + cleanSearch + window.location.hash;
821            window.history.replaceState(null, document.title, cleanUrl);
822        }
823
824        cleanupPaywallLocalStorage();
825        if (checkCdnSubscriberCacheMismatch()) {
826            return;
827        }
828        checkAndRecoverPaywallCookie();
829        initPopupTrigger();
830        initSubscriptionModalLog();
831        checkCheckoutStart();
832        checkPurchaseComplete();
833        initPaywallCopyProtection();
834    }
835
836    if (document.readyState === "complete" || document.readyState === "interactive") {
837        initDgsPaywall();
838    } else {
839        document.addEventListener("DOMContentLoaded", initDgsPaywall);
840    }
841})();

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.