PageSourceSearch

https://isb.edu.vn/wp-content/plugins/google-onetap-capture/assets/js/core.js?ver=1789632646

js isb.edu.vn collected 2026-09-24 09:55:48 UTC 7,354 bytes, 193 lines download raw bytes

1/**
2 * google-onetap-capture — helper dùng chung.
3 *
4 * Nạp đầu tiên. Tạo namespace window.GOT cho phone-popup.js và onetap.js dùng.
5 * Cấu hình đến từ window.GOT_CFG do PHP in ra — xem includes/class-got-frontend.php.
6 */
7(function (w) {
8  'use strict';
9
10  var cfg = w.GOT_CFG || {};
11
12  var STORE_KEY = 'got_user';
13  var OPTOUT    = 'got_optout';      // cookie cũ của bản ≤1.4.1, nay chỉ để dọn đi
14  var LATER     = 'got_phone_later'; // đã hỏi SĐT rồi -> nghỉ cfg.dismissDays ngày
15
16  /* ---------- Nhận diện đã lưu ở máy ---------- */
17
18  function getUser()  { try { return JSON.parse(localStorage.getItem(STORE_KEY)); } catch (e) { return null; } }
19  function setUser(u) { try { localStorage.setItem(STORE_KEY, JSON.stringify(u)); } catch (e) {} }
20  function forgetUser(){ try { localStorage.removeItem(STORE_KEY); } catch (e) {} }
21
22  /* ---------- Cookie ---------- */
23
24  function getCookie(name) {
25    var v = '; ' + document.cookie;
26    var parts = v.split('; ' + name + '=');
27    if (parts.length === 2) return parts.pop().split(';').shift();
28    return '';
29  }
30
31  // Dùng cookie chứ không dùng localStorage: chính localStorage là thứ vừa bị xoá khi bấm X
32  function setCookie(name, days) {
33    // days = 0 -> cookie theo phiên (đóng trình duyệt là hết)
34    var age = days > 0 ? ';max-age=' + (days * 86400) : '';
35    document.cookie = name + '=1;path=/' + age + ';SameSite=Lax';
36  }
37
38  function delCookie(name) {
39    document.cookie = name + '=;path=/;max-age=0;SameSite=Lax';
40  }
41
42  function ss(k) { try { return sessionStorage.getItem(k) || ''; } catch (e) { return ''; } }
43
44  /* ---------- UTM ---------- */
45
46  var UTM_KEY    = 'got_utm';
47  var UTM_FIELDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
48
49  function readUtm() { try { return JSON.parse(sessionStorage.getItem(UTM_KEY)) || null; } catch (e) { return null; } }
50  function saveUtm(o) { try { sessionStorage.setItem(UTM_KEY, JSON.stringify(o)); } catch (e) {} }
51
52  /**
53   * Tự thu UTM, KHÔNG phụ thuộc tracking_source_optimized.js — script đó không
54   * được plugin nào enqueue, trên production nó vào qua GTM nên có thể vắng mặt.
55   * Chốt vào sessionStorage ngay trang đầu để còn nguyên khi user submit ở trang khác.
56   */
57  function captureUtm() {
58    var qs = null;
59    try { qs = new URLSearchParams(location.search); } catch (e) {}
60
61    if (qs) {
62      var found = {}, has = false;
63      UTM_FIELDS.forEach(function (k) {
64        var v = qs.get(k);
65        if (v) { found[k] = v; has = true; }
66      });
67      if (has) {                      // URL có UTM -> ưu tiên tuyệt đối, ghi đè cái cũ
68        found.landing_url = location.href;
69        saveUtm(found);
70        return found;
71      }
72    }
73
74    var stored = readUtm();
75    if (stored) return stored;        // đã chốt từ trang trước trong cùng phiên
76
77    // Chưa có gì: mượn giá trị của script tracking dùng chung, nếu nó đang chạy.
78    // Làm vậy để khớp đúng cách phân loại của pipeline CRM hiện có.
79    var shared = {}, got = false;
80    UTM_FIELDS.forEach(function (k) {
81      var v = ss(k);
82      if (v) { shared[k] = v; got = true; }
83    });
84    if (got) { shared.landing_url = location.href; saveUtm(shared); return shared; }
85
86    // Không có script đó -> tự phân loại tối giản theo referrer
87    var out = { utm_source: 'direct', utm_medium: 'typed', landing_url: location.href };
88    var ref = document.referrer || '';
89    if (ref) {
90      try {
91        var rh = new URL(ref).hostname.replace(/^www\./, '');
92        if (rh !== location.hostname.replace(/^www\./, '')) {
93          if (/(google|bing|yahoo|duckduckgo|coccoc|baidu|yandex|naver|ecosia)\./.test(rh)) {
94            out.utm_source = 'organic'; out.utm_medium = rh;
95          } else {
96            out.utm_source = rh; out.utm_medium = 'referral';
97          }
98        }
99      } catch (e) {}
100    }
101    saveUtm(out);
102    return out;
103  }
104
105  /**
106   * Ngữ cảnh gửi kèm SĐT: cookie_id để CDP nối với lịch sử duyệt web ẩn danh,
107   * utm_* lấy từ captureUtm().
108   */
109  function context() {
110    var uid = getCookie('_asm_uid');
111    if (!uid) { try { uid = localStorage.getItem('_asm_uid') || ''; } catch (e) { uid = ''; } }
112
113    var t = captureUtm();
114    return {
115      cookie_id:    uid,
116      page_url:     location.href,
117      landing_url:  t.landing_url || location.href,
118      page_title:   document.title,
119      utm_source:   t.utm_source   || '',
120      utm_medium:   t.utm_medium   || '',
121      utm_campaign: t.utm_campaign || '',
122      utm_content:  t.utm_content  || '',
123      utm_term:     t.utm_term     || ''
124    };
125  }
126
127  /* ---------- SĐT ---------- */
128
129  /**
130   * Một thông báo dùng chung cho mọi kiểu sai. Sửa ở Cài đặt → Popup thu số
131   * điện thoại → Báo lỗi SĐT (khoá cfg.phoneError).
132   */
133  function phoneError() {
134    return cfg.phoneError || 'Số điện thoại không hợp lệ, vui lòng nhập lại';
135  }
136
137  /**
138   * Chuẩn hoá + kiểm tra SĐT, trả về cả LÝ DO sai để hiện ra cho user.
139   *
140   * Vì SĐT ghi-một-lần, gõ nhầm là hỏng vĩnh viễn và không ai phát hiện ra —
141   * nên phải chặn ngay lúc nhập, và phải nói rõ sai chỗ nào.
142   *
143   * Quy tắc PHẢI khớp GOT_Payload::phone() bên PHP: lệch nhau thì user thấy
144   * "đã ghi nhận" nhưng server lại từ chối, mà luồng đó không báo lỗi.
145   * Không lọc theo đầu số nhà mạng — đầu số mới ra sẽ chặn nhầm khách thật.
146   */
147  function checkPhone(v) {
148    // `code` giữ lại để test và gỡ lỗi biết sai kiểu gì; user chỉ thấy một câu.
149    var fail = function (code) { return { ok: false, code: code, msg: phoneError(), value: '' }; };
150
151    var raw = (v || '').trim();
152    if (!raw) return fail('empty');
153
154    var d = raw.replace(/\D+/g, '');
155    if (!d) return fail('nodigit');
156
157    if (d.indexOf('00') === 0) d = d.slice(2);                          // 00 = tiền tố quốc tế cũ
158    if (d.indexOf('84') === 0 && d.length >= 11) d = '0' + d.slice(2);  // +84 -> 0
159    if (d.charAt(0) !== '0') d = '0' + d;
160
161    if (d.length === 10) return { ok: true, code: 'ok', msg: '', value: d };
162
163    return fail(d.length < 10 ? 'short' : 'long');
164  }
165
166  /** Giữ cho chỗ nào chỉ cần biết hợp lệ hay không. */
167  function validPhone(v) {
168    return checkPhone(v).value;
169  }
170
171  /* ---------- Thông báo nổi ---------- */
172
173  // Style ở assets/css/onetap.css (.got-toast)
174  function toast(msg) {
175    var t = document.createElement('div');
176    t.className = 'got-toast';
177    t.textContent = msg;
178    document.body.appendChild(t);
179    setTimeout(function () {
180      t.classList.add('got-toast--out');
181      setTimeout(function () { if (t.parentNode) t.parentNode.removeChild(t); }, 400);
182    }, 2600);
183  }
184
185  w.GOT = {
186    cfg: cfg,
187    STORE_KEY: STORE_KEY, OPTOUT: OPTOUT, LATER: LATER,
188    getUser: getUser, setUser: setUser, forgetUser: forgetUser,
189    getCookie: getCookie, setCookie: setCookie, delCookie: delCookie,
190    ss: ss, captureUtm: captureUtm, context: context,
191    checkPhone: checkPhone, validPhone: validPhone, toast: toast
192  };
193})(window);

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.