PageSourceSearch

https://37degrees.co.kr/js/brandscope.js?1779923336

js 37degrees.co.kr collected 2026-09-24 12:47:58 UTC 7,567 bytes, 229 lines download raw bytes

1// BrandScope 초기화 및 메서드 사용을 위한 래퍼 함수
2(function (window, document, script, BrandScope, firstScript) {
3	// 매출상승도구 미리보기(freeShipNotify) 진입 시 트래킹 비활성화
4	// - sales_tool_preview=freeShipNotify URL 파라미터로 호출된 경우, 미리보기 행위가 실데이터로 잡히지 않도록 함
5	// - 모든 BrandScope.* 호출은 window.BrandScope 글로벌을 거치므로 여기서 no-op로 정의하면 후속 호출 전체가 차단됨
6	// - SDK 스크립트는 로딩 자체를 스킵해서 SDK가 window.BrandScope 메서드를 덮어쓰지 못하도록 함
7	let isSalesToolFreeShipNotifyPreview = false;
8	try {
9		const params = new URLSearchParams(window.location.search);
10		isSalesToolFreeShipNotifyPreview = params.get('sales_tool_preview') === 'freeShipNotify';
11	} catch (e) {
12		isSalesToolFreeShipNotifyPreview = false;
13	}
14
15	if (isSalesToolFreeShipNotifyPreview) {
16		const noop = () => {};
17		window.BrandScope = {
18			init: noop,
19			identify: noop,
20			track: noop,
21			getImwebClientInfo: noop,
22			sessionResetByLogout: noop,
23			timeEvent: noop,
24			q: [],
25		};
26		return;
27	}
28
29	window.BrandScope = window.BrandScope || {
30		init: function () {
31			(window.BrandScope.q = window.BrandScope.q || [])
32				.push(['init'].concat(Array.prototype.slice.call(arguments)))
33		},
34		identify: function () {
35			(window.BrandScope.q = window.BrandScope.q || [])
36				.push(['identify'].concat(Array.prototype.slice.call(arguments)))
37		},
38		track: function () {
39			// TEST_SERVER 환경에서만 로그 출력
40			if (typeof TEST_SERVER !== 'undefined' && TEST_SERVER) {
41				console.log('[BrandScope] 이벤트 트래킹(래퍼): ', arguments[0], arguments[1]);
42			}
43			(window.BrandScope.q = window.BrandScope.q || [])
44				.push(['track'].concat(Array.prototype.slice.call(arguments)))
45		},
46		getImwebClientInfo: function () {
47			(window.BrandScope.q = window.BrandScope.q || [])
48				.push(['getImwebClientInfo'].concat(Array.prototype.slice.call(arguments)))
49		},
50		sessionResetByLogout: function () {
51			(window.BrandScope.q = window.BrandScope.q || [])
52				.push(['sessionResetByLogout'].concat(Array.prototype.slice.call(arguments)))
53		},
54		timeEvent: function () {
55			(window.BrandScope.q = window.BrandScope.q || [])
56				.push(['timeEvent'].concat(Array.prototype.slice.call(arguments)))
57		},
58	}
59
60	// BrandScope SDK 스크립트 로드
61	BrandScope = window.BrandScope
62	// BrandScope 스크립트 동적 로딩
63	script = document.createElement('script')
64	script.type = 'module'
65	script.async = true
66	script.src = (typeof TEST_SERVER !== 'undefined' && TEST_SERVER)
67		? '//cdn-brandscope.imtest.me/bs.umd.js'
68		: '//static.imweb.me/brand-scope/bs.umd.js';
69	
70	// TEST_SERVER 환경에서만 SDK track 함수 래핑
71	if (typeof TEST_SERVER !== 'undefined' && TEST_SERVER) {
72		script.onload = function() {
73			if (window.BrandScope && typeof window.BrandScope.track === 'function') {
74				var originalTrack = window.BrandScope.track;
75				window.BrandScope.track = function() {
76					console.log('[BrandScope] 이벤트 트래킹: ', arguments[0], arguments[1]);
77					return originalTrack.apply(this, arguments);
78				};
79			}
80		};
81	}
82	
83	// BrandScope 스크립트 삽입
84	firstScript = document.getElementsByTagName('script')[0]
85	firstScript.parentNode.insertBefore(script, firstScript)
86})(window, document);
87
88
89
90// data-bs-event 속성을 가진 요소들에 클릭 트래킹 추가 (독립 실행)
91(function initBrandScopeClickTracking() {
92	if (document.readyState === 'loading') {
93		document.addEventListener('DOMContentLoaded', initBrandScopeClickTracking);
94		return;
95	}
96
97
98	// 기존 요소들에 트래킹 추가
99	function addTrackingToElements() {
100		const elements = document.querySelectorAll('[data-bs-action="click"][data-bs-content]');
101
102		elements.forEach(function(element) {
103			// 이미 트래킹이 추가된 요소는 스킵
104			if (element.hasAttribute('data-bs-tracked')) return;
105			
106			const action = element.getAttribute('data-bs-action');
107			const content = element.getAttribute('data-bs-content');
108			
109			// action과 content는 필수
110			if (!action || !content) return;
111			
112			// click 액션만 처리
113			if (action !== 'click') return;
114			
115			
116			element.setAttribute('data-bs-tracked', 'true');
117			
118			element.addEventListener('click', function(event) {
119				try {
120					// 이벤트명 생성: action_target_content_where (존재하는 것만)
121					const eventParts = [action];
122					const trackingData = { action: action, content: content };
123					
124					const target = element.getAttribute('data-bs-target');
125					const where = element.getAttribute('data-bs-where');
126					
127					if (target) {
128						eventParts.push(target);
129						trackingData.target = target;
130					}
131					
132					eventParts.push(content);
133					
134					if (where) {
135						eventParts.push(where);
136						trackingData.where = where;
137					}
138					
139					const eventName = eventParts.join('_');
140
141					// data-bs-로 시작하는 다른 속성들을 snake_case로 변환하여 추가
142					const excludeAttrs = ['data-bs-action', 'data-bs-target', 'data-bs-content', 'data-bs-where', 'data-bs-tracked'];
143					Array.from(element.attributes).forEach(function(attr) {
144						if (attr.name.startsWith('data-bs-') && !excludeAttrs.includes(attr.name)) {
145							// data-bs- 접두사 제거하고 케밥케이스를 스네이크케이스로 변환
146							const key = attr.name.replace('data-bs-', '').replace(/-/g, '_');
147							let value = attr.value;
148							
149							// data-bs-is-로 시작하는 속성의 경우 boolean 변환
150							if (attr.name.startsWith('data-bs-is-')) {
151								if (value === 'true') {
152									value = true;
153								} else if (value === 'false') {
154									value = false;
155								}
156							}
157							
158							trackingData[key] = value;
159						}
160					});
161
162
163					if (typeof BrandScope !== "undefined") {
164						BrandScope.track(eventName, trackingData);
165					}
166				} catch (error) {
167					// 트래킹 오류 무시
168				}
169			});
170		});
171
172	}
173
174	// 초기 요소들에 트래킹 추가
175	addTrackingToElements();
176
177	// 동적으로 추가되는 요소들을 위한 MutationObserver
178	if (typeof MutationObserver !== 'undefined') {
179		const observer = new MutationObserver(function(mutations) {
180			
181			let hasNewElements = false;
182			mutations.forEach(function(mutation) {
183				if (mutation.type === 'childList') {
184					mutation.addedNodes.forEach(function(node) {
185						if (node.nodeType === Node.ELEMENT_NODE) {
186							// 추가된 노드 자체가 data-bs-action="click"과 data-bs-content를 가지거나, 하위에 가진 경우
187							if (node.hasAttribute && node.getAttribute('data-bs-action') === 'click' && node.hasAttribute('data-bs-content')) {
188								hasNewElements = true;
189							} else if (node.querySelector && node.querySelector('[data-bs-action="click"][data-bs-content]')) {
190								hasNewElements = true;
191							}
192						}
193					});
194				}
195			});
196
197
198			if (hasNewElements) {
199				addTrackingToElements();
200			}
201		});
202
203		observer.observe(document.body, {
204			childList: true,
205			subtree: true
206		});
207
208	}
209})();
210
211(function initBrandScope() {
212	if (document.readyState === 'loading') {
213		document.addEventListener('DOMContentLoaded', initBrandScope);
214		return;
215	}
216
217	// 상품 상세 위젯이 있을 경우 위젯에서 상품 데이터와 함께 초기화
218	const hasShopViewWidget = !!document.querySelector('[data-widget-type="shop_view"]');
219	if (hasShopViewWidget) {
220		return;
221	}
222
223	BrandScope.init({
224		projectToken: 'bs-im-019d09ae-1518-75f7-8c4f-33cf513a7a8a',
225		props: {
226			ownership: 'behavior-tracking-analytics',
227		}
228	});
229})();

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.