PageSourceSearch

https://maxai.co.kr/assets/js/base/wb_tracker.js

js maxai.co.kr collected 2026-10-02 01:04:17 UTC 8,103 bytes, 231 lines download raw bytes

1/**
2 * WB Visitor Tracker SDK v1.0
3 */
4(function (window, document) {
5    'use strict';
6
7    var VERSION = 1;
8    var STORAGE_KEY = 'track-session-id';
9    var SESSION_TIMEOUT = 30 * 60 * 1000;      // 30분
10    var MAX_SESSION_TIME = 24 * 60 * 60 * 1000; // 24시간
11
12    // site_code별 pixel.gif 도메인 라우팅
13    var PIXEL_URLS = {
14        speakingmax:  'https://track.speakingmaxapp.com/pixel.gif',
15        maxai:        'https://track.maxaiapp.com/pixel.gif',
16        ra:           'https://ra-track.speakingmaxapp.com/pixel.gif',
17        weaversbrain: 'https://track.speakingmaxapp.com/pixel.gif',
18        speakingdom:  'https://track.speakingmaxapp.com/pixel.gif'
19    };
20
21    var VALID_SITES = Object.keys(PIXEL_URLS);
22
23    // ---------------------------------------------------------------
24    // 유틸리티
25    // ---------------------------------------------------------------
26
27    function generateUUID() {
28        return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
29            var r = (Math.random() * 16) | 0;
30            return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
31        });
32    }
33
34    function now() {
35        return new Date().getTime();
36    }
37
38    // ---------------------------------------------------------------
39    // 세션 관리
40    // ---------------------------------------------------------------
41
42    function getSession() {
43        var stored = null;
44        try { stored = localStorage.getItem(STORAGE_KEY); } catch (e) { /* private browsing */ }
45
46        var ts = now();
47        var pattern = /^wb(\d+)\.(\d+)\.[0-9a-f]{32}\.(\d+)$/;
48
49        if (stored && pattern.test(stored)) {
50            var parts = stored.split('.');
51            var createdAt = Number(parts[1]);
52            var expireAt = Number(parts[3]);
53
54            if (ts <= expireAt && (createdAt + MAX_SESSION_TIME) > ts) {
55                parts[3] = String(ts + SESSION_TIMEOUT);
56                var renewed = parts.join('.');
57                try { localStorage.setItem(STORAGE_KEY, renewed); } catch (e) {}
58                return parseSession(renewed);
59            }
60        }
61
62        var newId = 'wb' + VERSION + '.' + ts + '.' + generateUUID() + '.' + (ts + SESSION_TIMEOUT);
63        try { localStorage.setItem(STORAGE_KEY, newId); } catch (e) {}
64        return parseSession(newId);
65    }
66
67    function parseSession(raw) {
68        var parts = raw.split('.');
69        return {
70            trackingId: raw,
71            sessionId: parts[0] + '.' + parts[1] + '.' + parts[2],
72            expireAt: Number(parts[3])
73        };
74    }
75
76    // ---------------------------------------------------------------
77    // 환경 판별
78    // ---------------------------------------------------------------
79
80    function detectEnv() {
81        var host = window.location.hostname || '';
82        if (host.indexOf('local') !== -1) return 'dev';
83        if (host.indexOf('dev') !== -1) return 'dev';
84        if (host.indexOf('qa') !== -1) return 'qa';
85        return 'prod';
86    }
87
88    // ---------------------------------------------------------------
89    // 디바이스 판별
90    // ---------------------------------------------------------------
91
92    function detectDevice() {
93        var ua = navigator.userAgent || '';
94        if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua)) {
95            return 'MO';
96        }
97        return 'PC';
98    }
99
100    // ---------------------------------------------------------------
101    // page_name 자동 추출
102    // ---------------------------------------------------------------
103
104    function extractPageName() {
105        var path = window.location.pathname || '/';
106        // 선행/후행 슬래시 제거 후, 슬래시 → 언더스코어
107        var name = path.replace(/^\/|\/$/g, '').replace(/\//g, '_');
108        return name || 'index';
109    }
110
111    // ---------------------------------------------------------------
112    // UTM 파라미터 추출
113    // ---------------------------------------------------------------
114
115    function getUtmParams() {
116        var params = {};
117        try {
118            var sp = new URL(window.location.href).searchParams;
119            var utmKeys = ['utm_source', 'utm_campaign', 'utm_medium', 'utm_term', 'utm_content'];
120            for (var i = 0; i < utmKeys.length; i++) {
121                var val = sp.get(utmKeys[i]);
122                if (val) params[utmKeys[i]] = val;
123            }
124        } catch (e) {}
125        return params;
126    }
127
128    // ---------------------------------------------------------------
129    // pixel.gif GET 전송
130    // ---------------------------------------------------------------
131
132    function sendPixel(baseUrl, params) {
133        var qs = [];
134        for (var key in params) {
135            if (params.hasOwnProperty(key) && params[key] !== '' && params[key] != null) {
136                qs.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
137            }
138        }
139        var url = baseUrl + '?' + qs.join('&');
140        new Image().src = url;
141    }
142
143    // ---------------------------------------------------------------
144    // 이벤트 빌더
145    // ---------------------------------------------------------------
146
147    function buildParams(eventName, session, siteCode, extra) {
148        var utm = getUtmParams();
149        var pageName = (extra && extra.page_name) ? extra.page_name : extractPageName();
150
151        var params = {
152            track_type:   siteCode,
153            env:          detectEnv(),
154            platform:     detectDevice(),
155            page_name:    pageName,
156            event_name:   eventName,
157            url:          window.location.href,
158            referrer:     document.referrer || '',
159            session_id:   session.sessionId
160        };
161
162        // UTM 파라미터
163        var utmKeys = ['utm_source', 'utm_campaign', 'utm_medium', 'utm_term', 'utm_content'];
164        for (var i = 0; i < utmKeys.length; i++) {
165            if (utm[utmKeys[i]]) {
166                params[utmKeys[i]] = utm[utmKeys[i]];
167            }
168        }
169
170        // extra에서 page_name 외의 추가 파라미터 병합
171        if (extra && typeof extra === 'object') {
172            for (var key in extra) {
173                if (extra.hasOwnProperty(key) && key !== 'page_name') {
174                    params[key] = extra[key];
175                }
176            }
177        }
178
179        return params;
180    }
181
182    // ---------------------------------------------------------------
183    // Public API
184    // ---------------------------------------------------------------
185
186    var WB_TRACKER = {
187        _config: {
188            pixelUrl: '',
189            site: '',
190            initialized: false
191        },
192
193        /**
194         * SDK 초기화 및 페이지뷰 자동 전송
195         * @param {string} siteCode - weaversbrain|speakingmax|maxai|speakingdom|ra
196         * @param {object} [options] - { pixelUrl: 'https://...' } (선택, 오버라이드용)
197         */
198        init: function (siteCode, options) {
199            if (this._config.initialized) return;
200
201            if (VALID_SITES.indexOf(siteCode) === -1) {
202                if (typeof console !== 'undefined') {
203                    console.warn('[wbTracker] Invalid site code: ' + siteCode);
204                }
205                return;
206            }
207
208            this._config.site = siteCode;
209            this._config.pixelUrl = (options && options.pixelUrl) ? options.pixelUrl : PIXEL_URLS[siteCode];
210            this._config.initialized = true;
211
212            this.track('page_view');
213        },
214
215        /**
216         * 커스텀 이벤트 전송
217         * @param {string} eventName - 이벤트 타입 (page_view, button_click, ...)
218         * @param {object} [extra] - 추가 데이터 (page_name 등)
219         */
220        track: function (eventName, extra) {
221            if (!this._config.initialized) return;
222
223            var session = getSession();
224            var params = buildParams(eventName, session, this._config.site, extra);
225            sendPixel(this._config.pixelUrl, params);
226        }
227    };
228
229    window.wbTracker = WB_TRACKER;
230
231})(window, document);

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.