1/** 2 * íì´ì§ ì²´ë¥ ìê° ì¶ì ì¤í¬ë¦½í¸ (ìµì¢ ê°ì ë²ì ) 3 * - í ë«ê¸°/ì´ë ì ì¦ì ì ì¡ (Debounce ì ê±°) 4 * - BFCache ìë²½ ëì 5 * - navigator.sendBeacon ìµì í 6 */ 7 8var PageTracker = (function () { 9 var startTime = null; 10 var page_title = window.PAGE_TITLE || document.title || null; 11 var page_type = window.PAGE_TYPE || null; 12 var pageUrl = window.location.pathname + window.location.search; 13 var sessionId = getSessionId(); 14 15 var heartbeatInterval = null; 16 var isActive = true; 17 var lastActivityTime = null; 18 19 var dataSent = false; // í´ë¹ ì¸ì (íì´ì§ ë·°)ìì ìµì¢ ë°ì´í° ì ì¡ ì¬ë¶ 20 var isSending = false; // íì¬ ì ì¡ íë¡ì¸ì¤ ì§í ì¤ ì¬ë¶ 21 22 // ì¸ì ID ê´ë¦¬ 23 function getSessionId() { 24 var sid = getCookie('tracker_session_id'); 25 if (!sid) { 26 sid = generateUUID(); 27 } 28 setCookie('tracker_session_id', sid, 30); 29 return sid; 30 } 31 32 function generateUUID() { 33 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 34 var r = Math.random() * 16 | 0; 35 var v = c === 'x' ? r : (r & 0x3 | 0x8); 36 return v.toString(16); 37 }); 38 } 39 40 function setCookie(name, value, minutes) { 41 var expires = ""; 42 if (minutes) { 43 var date = new Date(); 44 date.setTime(date.getTime() + (minutes * 60 * 1000)); 45 expires = "; expires=" + date.toUTCString(); 46 } 47 document.cookie = name + "=" + (value || "") + expires + "; path=/; SameSite=Lax"; 48 } 49 50 function getCookie(name) { 51 var nameEQ = name + "="; 52 var ca = document.cookie.split(';'); 53 for (var i = 0; i < ca.length; i++) { 54 var c = ca[i]; 55 while (c.charAt(0) === ' ') c = c.substring(1, c.length); 56 if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); 57 } 58 return null; 59 } 60 61 // === ë©ì¸ ë¡ì§ === 62 function start() { 63 startTime = Date.now(); 64 lastActivityTime = startTime; 65 dataSent = false; 66 67 addActivityListeners(); 68 startHeartbeat(); 69 70 // ìµì ë¸ë¼ì°ì ë visibilitychangeê° ê°ì¥ ì íí©ëë¤. 71 document.addEventListener('visibilitychange', handleVisibilityChange, false); 72 // 모ë°ì¼ ì¬í리 ë° BFCache ëì 73 window.addEventListener('pagehide', handlePageHide, false); 74 } 75 76 function startHeartbeat() { 77 if (heartbeatInterval) clearInterval(heartbeatInterval); 78 heartbeatInterval = setInterval(function () { 79 if (isActive && !document.hidden) { 80 sendHeartbeat(); 81 } 82 }, 30000); 83 } 84 85 function stopHeartbeat() { 86 if (heartbeatInterval) { 87 clearInterval(heartbeatInterval); 88 heartbeatInterval = null; 89 } 90 } 91 92 function addActivityListeners() { 93 var events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click']; 94 var lastUpdate = 0; 95 var throttleMs = 1000; 96 97 events.forEach(function (event) { 98 document.addEventListener(event, function () { 99 var now = Date.now(); 100 if (now - lastUpdate < throttleMs) return; 101 lastUpdate = now; 102 isActive = true; 103 }, { passive: true }); 104 }); 105 } 106 107 function handleVisibilityChange() { 108 if (document.visibilityState === 'hidden') { 109 isActive = false; 110 stopHeartbeat(); 111 // íì´ ê°ë ¤ì§ë ìê° ì¦ì ì ì¡ ìë (ë¹ë기 ë기 ìì) 112 sendFinalData({ isFinal: true }); 113 } else if (document.visibilityState === 'visible') { 114 isActive = true; 115 lastActivityTime = Date.now(); 116 startHeartbeat(); 117 } 118 } 119 120 function handlePageHide(event) { 121 // visibilitychangeìì ë¯¸ì² ì ì¡ëì§ ììì ê²½ì°ë¥¼ ëë¹í 2ì°¨ ë°©ì´ì 122 sendFinalData({ isFinal: true }); 123 } 124 125 function sendHeartbeat() { 126 var now = Date.now(); 127 var duration = Math.round((now - startTime) / 1000); 128 129 $.ajax({ 130 url: '/tracking/heartbeat.asp', 131 type: 'POST', 132 data: { 133 session_id: sessionId, 134 page_url: pageUrl, 135 duration: duration, 136 is_active: isActive ? 1 : 0 137 }, 138 async: true 139 }); 140 } 141 142 /** 143 * ìµì¢ ë°ì´í° ì ì¡ ê²°ì í¨ì 144 */ 145 function sendFinalData(options) { 146 if (dataSent || isSending) return; 147 148 isSending = true; 149 150 // ì¤ì: ìµì¢ ì ì¡(isFinal)ììë setTimeoutì ì ë ì¬ì©íì§ ììµëë¤. 151 // ë¸ë¼ì°ì ì¢ ë£ ì§ì ìë 1msì ì§ì°ì¼ë¡ë íë¡ì¸ì¤ê° ì¢ ë£ë ì ììµëë¤. 152 var result = sendTrackingData(options); 153 154 if (result) { 155 dataSent = true; 156 } 157 isSending = false;
158 } 159 160 function sendTrackingData(opts) { 161 var endTime = Date.now(); 162 var totalDuration = Math.round((endTime - startTime) / 1000); 163 var activeDuration = Math.round((lastActivityTime - startTime) / 1000); 164 165 if (totalDuration < 1) return false; 166 167 var data = { 168 session_id: String(sessionId || ''), 169 page_title: String(page_title || ''), 170 page_type: String(page_type || ''), 171 page_url: String(pageUrl || ''), 172 referrer: String(document.referrer || '-'), 173 total_duration: Number(totalDuration), 174 active_duration: Number(activeDuration > 0 ? activeDuration : totalDuration), 175 screen_width: Number(screen.width), 176 screen_height: Number(screen.height), 177 user_agent: String(navigator.userAgent), 178 is_final: opts.isFinal ? 1 : 0 179 }; 180 181 // --- ìì í¬ì¸í¸: URLSearchParams ì¬ì© --- 182 var params = new URLSearchParams(); 183 for (var key in data) { 184 params.append(key, data[key]); 185 } 186 187 if (navigator.sendBeacon) { 188 // URLSearchParams를 ë³´ë´ë©´ ë¸ë¼ì°ì ê° ìëì¼ë¡ 189 // application/x-www-form-urlencoded;charset=UTF-8 ë¡ ì¤ì í©ëë¤. 190 var beaconResult = navigator.sendBeacon('/tracking/save_tracking.asp', params); 191 if (beaconResult) return true; 192 } 193 // --------------------------------------- 194 195 // AJAX í´ë°± (기존 ì ì§) 196 try { 197 $.ajax({ 198 url: '/tracking/save_tracking.asp', 199 type: 'POST', 200 data: data, // jQueryë 기본ì ì¼ë¡ x-www-form-urlencodedë¡ ì²ë¦¬í¨ 201 async: false, 202 timeout: 500 203 }); 204 return true; 205 } catch (e) { 206 return false; 207 } 208 } 209 210 function handlePageShow(event) { 211 // BFCache(ë¤ë¡ê°ê¸°)ë¡ ë³µìë ê²½ì° ë³ì ì´ê¸°í ë° ì¬ìì 212 if (event.persisted) { 213 startTime = Date.now(); 214 lastActivityTime = startTime; 215 dataSent = false; 216 isSending = false; 217 isActive = true; 218 startHeartbeat(); 219 } 220 } 221 222 return { 223 init: function () { 224 if (typeof jQuery === 'undefined') return; 225 226 window.addEventListener('pageshow', handlePageShow, false); 227 228 $(document).ready(function () { 229 start(); 230 }); 231 } 232 }; 233})(); 234 235PageTracker.init();
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.