PageSourceSearch

https://www.zikpanmall.com/contents/js/Service-Worker.js

js zikpanmall.com collected 2026-09-25 14:22:47 UTC 25,902 bytes, 545 lines download raw bytes

1// 서비스 워커는 ie11 지원 안함. 따라서 ie11 지원 여부 무시.
2// 당일에 정지된 캠페인을 실행시키거나, 실행중인 캠페인을 정지시켜도 바로 적용됨
3// 대신 동일 캠페인을 같은날 정지 => 실행 시키면 같은 팝업 두번 볼 수 도 있음
4// 즉, 실행중 => 사용자가 팝업봄 => 정지 => 실행 => 다시 팝업봄    이 가능
5importScripts("/contents/js/InedxedDB_v1.js");
6
7// 서비스워크 설치
8self.addEventListener("install", function (event) {
9    event.waitUntil(self.skipWaiting());
10});
11
12// 서비스워커 활성화
13self.addEventListener("activate", function (event) {
14    event.waitUntil(
15        clients
16            .claim()
17            .then()
18    );
19});
20
21
22// 전역변수
23var db1 = new IndexedDbHelper();
24var db2 = new IndexedDbHelper();
25var lock = false; // 락. 잡혀 있으면 아직 비동기 작업 안끝난거. 로그인에만 걸어둠.
26var first_login = false; // 로그인을 통한 첫 접근인지 확인을 위한 변수
27var no_campaign; // 타겟 캠페인이 있는지 확인하기 위한 변수
28
29
30// 인덱스드 디비 오브젝트 스토어
31var NON_MEMBER_STORE = { name: "NOMEMBER", keyPath: "ck_user" };
32var LIKEGOODS1_STORE = { name: "LIKEGOODS1", keyPath: ["sc_idx", "mg_code"], };
33var HELPCHOICE_STORE = { name: "HELPCHOICE", keyPath: "sc_idx" };
34var MASTER_STORE = { name: "MASTER_STORE", keyPath: ["sc_idx", "cc_idx"] };
35var CAMPAIGN_RANKING_STORE = { name: "CAMPAIGN_RANK", keyPath: "sc_idx" };
36var LASTSETTING_STORE = { name: "LAST_SETTING", keyPath: "sc_idx" };
37var MOVING_COUNT_STORE = { name: "MOVING_COUNT", keyPath: "sc_idx" };
38var NON_MEMBER_POPUP = { name: "NOMEMBER", keyPath: "image" };
39var EXCLUDE_GOODS = { name: "EXCLUDE_GOODS", keyPath: "mg_code" };
40
41
42// 인덱스드 디비 인덱스 
43var MASTER_STORE_INDEX = { MASTER_STORE: [{ index: "popup_flag", unique: false }, { index: ["sc_idx", "type"], unique: false }], };
44
45db2.dbName = "NON_MEMBER_DB";
46
47
48// 클라이언트에서 메시지 받기
49self.addEventListener("message", async function (event) {
50    var now = new Date();
51    var today3AM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), "03", 0, 0); // 오늘 새벽 3시
52
53    // 로그인. 누적 데이터 삭제 요청
54    if (event.data.type == "LOGIN") {
55        var sc_idx = event.data.message.sc_idx;
56
57        first_login = true;
58        db1.dbName = sc_idx;
59
60        getLock(); // 락걸기. 안걸면 LOGIN 요청 직후 INDEX 요청도 들어와서 인덱스드디비 비동기 동시 접근 오류 발생
61
62        db1.getData("LAST_SETTING", sc_idx, "sc_idx", function (e) {
63            var objStoreNames;
64            if (e == null || e.target.result == null || (e.target.result.date <= today3AM && now > today3AM)) {  // 초기화 요청이 내역이 없거나, 초기화 한게 오늘 3시 이전일때. 오늘 첫 로그인
65                objStoreNames = ["MASTER_STORE", "CAMPAIGN_RANK", "MOVING_COUNT", "LIKEGOODS1", "HELPCHOICE"];
66            } else { // 캠페인 정보가 모두 옳바르면 누적 데이터만 날리기
67                objStoreNames = ["MOVING_COUNT", "LIKEGOODS1", "HELPCHOICE"];
68            }
69            db1.clearData(objStoreNames, function () {
70                releaseLock();
71            });
72        });
73    }
74
75    // 팝업 보여줌. 팝업 플래그 업데이트 요청
76    if (event.data.type === "COMPLETE") {
77
78        var popupTarget = event.data.message.popupTarget;
79        var sc_idx = event.data.message.sc_idx;
80        if (popupTarget == "success") {
81            return;
82        }
83
84        popupTarget.popup_flag = "Y";
85        updateMasterStorePoupFlag(popupTarget, sc_idx);
86    }
87
88    // 일반 화면 진입. 팝업 체크 요청
89    if (event.data.type === "INDEX") {
90        var sc_idx = event.data.message.sc_idx;
91        var ck_user = event.data.message.ck_user;
92        var page = event.data.message.page; // 요청 들어온 페이지     DETAIL / HOME / LIST
93        var mg_code = event.data.message.mg_code;
94        var nomember_cc_idx = event.data.message.nomember_cc_idx; // 회원가입 유도 캠페인 idx
95        var onsite_term = event.data.message.onsite_term; // 온사이트 팝업 휴식 텀. 기본값 2
96        var last_update_date = new Date(event.data.message.update_date.replace(" ", "T")); // 캠페인이 마지막으로 수정된 시간. "T"를 추가하여 ISO 형식으로 변환
97        var soldout = event.data.message.soldout; // 상품 상세의 경우 품절 상품인지
98
99        db1.dbName = sc_idx; // 회원번호로 db를 만듬
100
101        if (sc_idx == 0 && nomember_cc_idx >
101 0) { // 비회원이고 회원가입 유도 활성화중일때
102
103            // crm 으로 접근했으면 안보여줌
104            var is_crm_approach = event.data.message.is_crm_approach;
105            if (is_crm_approach) {
106                return;
107            }
108
109            db2.initialize(NON_MEMBER_STORE, null, function () {
110                db2.getData("NOMEMBER", ck_user, "ck_user",
111                    async function (e) {
112                        if (e == null || e.target.result == null) { // 첫 접근일때
113                            db2.writeData("NOMEMBER", {
114                                ck_user: ck_user,
115                                cc_idx: nomember_cc_idx,
116                                popup_flag: "N",
117                                save_date: now,
118                            });
119                        } else if (e.target.result.save_date <= today3AM && now > today3AM) { // 이전 접근기록이 있으나 새벽 3시 이전 접근일때
120                            db2.updateData("NOMEMBER", ck_user, "ck_user", {
121                                ck_user: ck_user,
122                                cc_idx: e.target.result.cc_idx,
123                                popup_flag: "N",
124                                save_date: now,
125                            });
126                        } else {
127                            var result = e.target.result;
128                            if (result.popup_flag == "N") {// 두번째 접근일때
129                                db2.updateData("NOMEMBER", ck_user, "ck_user", {
130                                    ck_user: ck_user,
131                                    cc_idx: result.cc_idx,
132                                    popup_flag: "Y",
133                                    save_date: now,
134                                });
135
136                                var paramsObj = new URLSearchParams({ cc_idx: nomember_cc_idx, cct_type: "NOMEMBER", page: page });
137
138                                var response = await fetch("/CRM/PortletPopUp?" + paramsObj.toString(), {
139                                    method: "GET",
140                                    headers: { "Content-Type": "application/json" }
141                                });
142
143                                response = await response.text();
144                                var popupTarget = "success";
145                                event.ports[0].postMessage({ response: response, cc_idx: nomember_cc_idx, popupTarget: popupTarget });
146                            }
147                        }
148                    });
149            });
150        } else if (sc_idx > 0) { // 회원일때
151            db1.getData("LAST_SETTING", sc_idx, "sc_idx", function (e) {
152                waitForLockChange(function () {
153                    if (e == null || e.target.result == null || (e.target.result.date <= today3AM && now > today3AM)) {  // 초기화 요청이 내역이 없거나, 초기화 한게 오늘 3시 이전일때
154                        settingIndexedDb(function () {
155                            if (no_campaign) { // 보여줘야 할 켐페인이 없으면 바로 리턴.
156                                return;
157                            }
158                            checkPopup(function (target) { // 바로 보여줘야 하는 팝업 있는지 확인
159                                showPopupAndUpdate(target); // 있으면 보여주고 락 해제
160                            });
161                        });
162                    } else { // 현재 인덱스드 디비 데이터가 최신 데이터일때
163                        if (e.target.result.date <= last_update_date) { // 인덱스드 디비 초기화한 시간이 마지막 캠페인 업데이트 시간 이전이면 새롭게 초기화
164                            settingIndexedDb(function () {
165                                if (no_campaign) {
166                                    return;
167                                }
168                                checkPopup(function (target) { // 바로 보여줘야 하는 팝업 있는지 확인
169                                    showPopupAndUpdate(target);
170                                });
171                            });
172                        } else { // 캠페인 변동사항 없음.
173                            if (no_campaign) {
174                                return;
175                            }
176                            addMovingCount(); // 페이지 이동 횟수 카운트
177                            checkPopup(function (target) { // 팝업 보여줘야 하는게 있는지 체크
178                                showPopupAndUpdate(target);
179                            });
180                        }
181                    }
182                });
183            });
184        }
185    }
186
187    function getLock() {
188        lock = true;
189    }
190
191    function releaseLock() {
192        lock = false;
193    }
194
195    // 로그인을 했을 경우, 로그인에서 한 인덱스드 디비 요청이 끝난 이후에 인덱스드 디비에 접근해야 함. 아니면 충돌.
196    // lock 이 걸려져 있으면 루프에 갇혀서 주기적으로 lock 이 해제됐는지 확인. 해제되어야 콜백 실행.
197    function waitForLockChange(callback) {
198        if (lock === false) {
199            callback();
200        } else {
201            setTimeout(function () { waitForLockChange(callback) }, 50); // 50ms 마다 재확인.
202        }
203    }
204
205    // 인덱스드디비 초기 셋팅
206    async function settingIndexedDb(callBack) {
207
208        var response = await fetch("/CRM/SettingIndexedDb", { // 서버에서 대상 캠페인 데이터 받아오기.
209            method: "post",
210            headers: { "Content-Type": "application/json" },
211        });
212
213        var responseData = await response.json();
214
215        if (responseData.data == null) { // 대상 캠페인이 없으면 셋팅 날짜만 저장하고 끝.
216            no_campaign = true;
217            db1.initialize(LASTSETTING_STORE, null, function () {
218                db1.updateData("LAST_SETTING", sc_idx, "sc_idx", { sc_idx: sc_idx, date: now });
219            });
220            return;
221        }
222
223        no_campaign = false;
224
225        // 서버에서 받아온 데이터 배열로 변환. 우선순위순으로 정렬된 데이터로 받음.
226        var idxs = responseData.data.campaignIdxs.split(","); // 캠페인 idx
227        var types = responseData.data.type.split(","); // 캠페인 타입
228        var time = responseData.data.time.split(","); // 팝업 노출 시간
229        var excludeGoods = responseData.data.exclude_goods.split(","); // 제외 상품
230
231        var masterStoreData = []; // 마스터 스토어에 담길 데이터
232        var campaigns = []; // 순위 캠페인 배열
233        var objStoresName = []; // 새로 생성할 오브젝트 스토어 이름
234
235        db1.getAll("MASTER_STORE", function (e) { // 새롭게 추가될  캠페인, 삭제될 캠페인 뽑기
236            var allreadyIn = [];
237            var deleteList = [];
238            var oldList = [];
239
240            if (e != null) {
241                allreadyIn = e.target.result;
242            }
243
244            for (var old of allreadyIn) { // 기존 캠페인 리스트
245                oldList.push({ type: old.type, cc_idx: old.cc_idx });
246            }
247
248            for (var old of oldList) {
249                if (!types.includes(old.type)) { // 삭제된 캠페인 뽑기
250                    deleteList.push([sc_idx, old.cc_idx]);
251                }
252            }
253
254            // 추가된 캠페인 뽑기
255            for (var i = 0; i < types.length; i++) {  // 일단 받아온 대상 캠페인 모두 반복문 돔
256                var type = types[i];
257                var continueFlag = false;
258
259                campaigns.push({ type: types[i], cc_idx: idxs[i], rank: i });
260
261                for (var item of allreadyIn) {
262                    if (item.type == type) { // 새롭게 추가된 캠페인이 아니면 for문 나가기. 다음 반복.
263                        continueFlag = true;
264                        break
265                    }
266                }
267
268                if (continueFlag) {
269                    continue;
270                }
271
272                if (type == "LIKEGOODS1") { // 대상 캠페인에 관심상품1 이 존재하면 오브젝트 스토어 만드릭
273                    objStoresName.push(LIKEGOODS1_STORE);
274                } else if (type == "HELPCHOICE") { // 대상 캠페인에 선택 도와주기가 존재하면 오브젝트 스토어 만드릭
275                    objStoresName.push(HELPCHOICE_STORE);
276                }
277
278                // 마스터 스토어 데이터 만들기
279                var showTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), time[i], 0, 0);
280                var data = { sc_idx: sc_idx, cc_idx: idxs[i], time: showTime, type: types[i], popup_flag: "N", };
281                masterStoreData.push(data);
282            }
283
284
285            for (var idx in excludeGoods) { // 제외 상품 리스트
286                excludeGoods[idx] = { mg_code: excludeGoods[idx] };
287            }
288
289            objStoresName.push(MASTER_STORE);
290            objStoresName.push(LASTSETTING_STORE);
291            objStoresName.push(CAMPAIGN_RANKING_STORE);
292            objStoresName.push(MOVING_COUNT_STORE);
293            objStoresName.push(EXCLUDE_GOODS);
294
295            db1.initialize(objStoresName, MASTER_STORE_INDEX, function () { // db1 셋팅
296                db1.updateData("LAST_SETTING", sc_idx, "sc_idx", { sc_idx: sc_idx, date: now, }, function () { // 데이터 초기화 시간 업데이트
297                    addMovingCount(); // 페이지 이동 횟수 초기 데이터 셋팅
298                    db1.writeData("MASTER_STORE", masterStoreData, function () {  // 마스터 스토어에 받아온 데이터 넣기
299                        db1.updateData("CAMPAIGN_RANK", sc_idx, "sc_idx", { sc_idx: sc_idx, campaigns: campaigns, }, function () { // 우선순위 테이블에 값 넣기. 업데이트
300                            db1.deleteData("MASTER_STORE", deleteList, function () {  // 삭제될 캠페인 있으면 삭제.
301                                db1.clearData("EXCLUDE_GOODS", function () {
302                                    db1.writeData("EXCLUDE_GOODS", excludeGoods, function () {
303                                        callBack();
304                                    });
305                                });
306                            });
307                        });
308                    });
309                });
310            });
311        });
312    }
313
314
315    // 팝업 타겟 찾기
316    function checkPopup(callback) {
317        db1.getData("MASTER_STORE", "N", "popup_flag", function (e) { // 팝업을 안보여준 캠페인만 뽑기.
318            var targets = [];
319            var masterStoreResult = e.target.result;
320            var masterStoreData = {};
321            var today12AM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), "00", 0, 0
321); // 오늘 자정
322            var ignoredCouponData = null;
323
324            masterStoreResult.forEach(function (data) { // 팝업 보여줄 시간이 지난 캠페인만 targets 에 담기
325                if (now >= today12AM && now <= today3AM) { // 00시에서 03시 사이는 쿠폰 만료 팝업 안보여줌. 재필터링이 힘들어서.
326                    if (data.type == "EXPIRECOUPON") {
327                        data.popup_flag = 'Y';
328                        ignoredCouponData = data;
329                    }
330                }
331
332                if (data.time != null && data.time <= now) {
333                    targets.push(data.type);
334                }
335
336                masterStoreData[data.type] = data;
337            });
338
339            if (ignoredCouponData != null) {
340                db1.updateData("MASTER_STORE", [sc_idx, ignoredCouponData.cc_idx], ["sc_idx", "cc_idx"], ignoredCouponData, function () { // 쿠폰 만료 팝업은 무시.
341                    filterAccumulativeDataAndUpdate(targets, function (filtered) { // targets 캠페인이 db1 조건도 만족하는지 확인
342                        ifMoveMoreThanCountOrNotShown(onsite_term, function () { // 페이지 이동 횟수가 만족되는지 확인
343                            getHighestTarget(filtered, masterStoreData, function (target) { // 가장 우선순위 높은거 1개만 뽑기
344                                callback(target);
345                            });
346                        });
347                    });
348                });
349            } else {
350                filterAccumulativeDataAndUpdate(targets, function (filtered) {
351                    ifMoveMoreThanCountOrNotShown(onsite_term, function () {
352                        getHighestTarget(filtered, masterStoreData, function (target) {
353                            callback(target);
354                        });
355                    });
356                });
357            }
358        });
359    }
360
361
362    // db1에 있는 캠페인들이 조건이 만족 여부 확인하고 카운트 세기. 누적 데이터 확인.
363    function filterAccumulativeDataAndUpdate(targets, callback) {
364        if (page != "DETAIL") {
365            targets = targets.filter(function (e) { return e != "LIKEGOODS1" && e != "HELPCHOICE" });
366            callback(targets);
367            return;
368
369        } else {
370
371            db1.getData("EXCLUDE_GOODS", mg_code, "mg_code", function (e) {
372
373                if (e != null && e.target.result != undefined) { // 제외 상품 페이지일때. - 관심상품1 대상 제외
374
375                    targets = targets.filter(function (e) { return e != "LIKEGOODS1" });
376
377                    checkHelpChoicePopup(targets, callback);
378
379                } else { // 제외 상품 페이지가 아닐때
380                    db1.getData("LIKEGOODS1", [sc_idx, mg_code], ["sc_idx", "mg_code"], function (e) { // 관심상품1 일때
381
382                        if (e != null && e.target.result == null) { // 데이터가 없을때. 2번째 접근이 아니므로 타겟 제외 
383                            targets = targets.filter(function (e) { return e != "LIKEGOODS1" });
384                            db1.writeData("LIKEGOODS1", { sc_idx: sc_idx, mg_code: mg_code });
385                        } else if (e != null && e.target.result != null && soldout) {
386                            targets = targets.filter(function (e) { return e != "LIKEGOODS1" });
387                        }
388
389                        checkHelpChoicePopup(targets, callback);
390                    });
391                }
392            });
393        }
394    }
395
396    // 선택 도와주기 체크
397    function checkHelpChoicePopup(targets, callback) {
398
399        db1.getData("HELPCHOICE", sc_idx, "sc_idx", function (e) {
400
401            if (e != null && e.target.result == null) {
402                targets = targets.filter(function (e) { return e != "HELPCHOICE" });
403                db1.writeData("HELPCHOICE", { sc_idx: sc_idx, count: 0 });
404
405            } else if (e != null && e.target.result.count < 2) {
406                var oldData = e.target.result;
407                targets = targets.filter(function (e) { return e != "HELPCHOICE" });
408                oldData.count = oldData.count + 1;
409                db1.updateData("HELPCHOICE", sc_idx, "sc_idx", oldData);
410            }
411
412            callback(targets);
413        });
414
415    }
416
417    // 팝업을 보여준적 없거나 이동 횟수 조건이 충족된 애들만 콜백 함수 실행
418    function ifMoveMoreThanCountOrNotShown(count, callback) {
419        count++;
420        if (first_login) {
421            first_login = false;
422            callback();
423        } else {
424            db1.getData("MOVING_COUNT", sc_idx, "sc_idx", function (e) {
425                if (e.target.result != null) {
426                    if (e.target.result.last_popup == null || (e.target.result.last_popup != null && e.target.result.count >= count)) { // 팝업을 보여준적 없거나, 팝업 보여주고 이동 횟수가 2이상일때만
427                        callback();
428                    }
429                }
430            });
431        }
432    }
433
434    // 타겟중 최우선 순위 찾기. 우선순위 필터링
435    function getHighestTarget(targets, masterStoreData, callback) {
436        var popupTarget;
437        var rank = 100;
438
439        if (!isMondayToThursday) {
440            targets.filter(function (e) { return e != "REACTIVE" }
440) // 단골 활성화는 월요일에서 목요일 사이에만
441        }
442        if (!isSunday) {
443            targets.filter(function (e) { return e != "CARTGOODS" }) // 장바구니 구매는 일요일에만
444        }
445
446        if (targets.length > 1) {// 타겟이 여러개면 우선순위 높은거
447            db1.getData("CAMPAIGN_RANK", sc_idx, "sc_idx", function (e) {
448                var allcampaigns = e.target.result.campaigns;
449                allcampaigns.forEach(function (campaign) {
450                    if (targets.includes(campaign.type) && campaign.rank < rank) { // 타겟에 존재하고 랭크가 가장 낮은 캠페인 찾기
451                        rank = campaign.rank;
452                        popupTarget = masterStoreData[campaign.type];
453                    }
454                });
455                popupTarget = masterStoreData[popupTarget.type];
456                callback(popupTarget);
457            });
458        } else { // 타겟이 1개 이하라면
459            popupTarget = masterStoreData[targets];
460            callback(popupTarget); // 마스터 스토어 객체로 타겟 넘겨주기
461        }
462    }
463
464    function isMondayToThursday() { // 월요일에서 목요일 사이면 true
465        var today = now.getDay();
466        return today >= 1 && today <= 4; // 월요일(1) ~ 목요일(4)
467    }
468
469    function isSunday() { // 일요일이면 true
470        var today = now.getDay();
471        return today == 6; // 월요일(1) ~ 목요일(4)
472    }
473
474    // 팝업 보여주고 마스터 스토어 팝업 플래그 업데이트
475    async function showPopupAndUpdate(popupTarget) {
476        if (popupTarget == null) { return; }
477
478        if (page != 'DETAIL') {
479            var paramsObj = new URLSearchParams({ cc_idx: popupTarget.cc_idx, cct_type: popupTarget.type, page: page });
480        } else {
481            var paramsObj = new URLSearchParams({ cc_idx: popupTarget.cc_idx, cct_type: popupTarget.type, mg_code: mg_code, page: page });
482        }
483
484
485        var response = await fetch("/CRM/PortletPopUp?" + paramsObj.toString(), {
486            method: "GET",
487            headers: { "Content-Type": "application/json" }
488        });
489
490        response = await response.text();
491        if (response == "N") {
492            popupTarget.popup_flag = "Y";
493            updateMasterStorePoupFlag(popupTarget, sc_idx, function () {
494            });
495            return;
496        }
497
498        let isLikeOrHelp = popupTarget.type === "LIKEGOODS1" || popupTarget.type === "HELPCHOICE";
499        var cc_idx = popupTarget.cc_idx;
500
501        if (isLikeOrHelp) { // 관심상품1 또는 선택 도와주기 같은 경우는 상품 코드도 보냄.
502            if (page == "DETAIL") {
503                event.ports[0].postMessage({ response, mg_code, cc_idx, popupTarget });
504            }
505        } else {
506            event.ports[0].postMessage({ response, cc_idx, popupTarget });
507        }
508    }
509
510
511    // 페이지 이동 횟수 카운트.
512    function addMovingCount() {
513        db1.getData("MOVING_COUNT", sc_idx, "sc_idx", function (e) {
514            if (e.target.result != null) {
515                e.target.result.count += 1;
516                db1.updateData("MOVING_COUNT", sc_idx, "sc_idx", e.target.result);
517            } else {
518                var data = { sc_idx: sc_idx, count: 0, last_popup: null };
519                db1.writeData("MOVING_COUNT", data);
520            }
521        });
522    }
523
524
525    // 마스터스토어의 팝업 플래그 업데이트
526    async function updateMasterStorePoupFlag(popupTarget, sc_idx) {
527        db1.updateData("MASTER_STORE", [sc_idx, popupTarget.cc_idx], ["sc_idx", "cc_idx"], popupTarget, function () {
528            var data = { sc_idx: sc_idx, last_popup: popupTarget.type, count: 0 };
529            db1.updateData("MOVING_COUNT", sc_idx, "sc_idx", data, function () {
530                if (popupTarget.type == "HELPCHOICE") {
531                    db1.getData("HELPCHOICE", sc_idx, "sc_idx", function (e) {// 선택 도와주기일때
532                        if (e.target.result.count >= 4) { // 카운트가 4이상이면 5번째 접근. 팝업 타겟 유지. 카운트는 0으로 업데이트하기.
533                            var oldData = e.target.result;
534                            oldData.count = 0;
535                            db1.updateData("HELPCHOICE", sc_idx, "sc_idx", oldData);
536                        }
537                    });
538                }
539                event.ports[0].postMessage({ result: "success" });
540            });
541        });
542    }
543});
544
545

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.