1(function (window) 2{ 3 'use strict'; 4 5 window.IvSmartFilter = function (parameters) 6 { 7 this.expandedLocalStorage = 'catalog_filter_expanded_state'; 8 9 this.catalogItemsBlockId = 'catalog_items'; 10 this.catalogItems = BX(this.catalogItemsBlockId); 11 12 this.idTopDesciption = 'wd-top-description'; 13 this.idBottomDesciption = 'wd-bottom-description'; 14 15 this.classCatalog = 'catalog'; 16 this.classCatalogContent = 'catalog__content'; 17 this.classCatalogAppend = 'catalog__append'; 18 this.classSeriesDesc = 'series__description'; 19 this.classFilterItem = 'filter__item'; 20 this.classFilterRange = 'filter-range'; 21 this.classFilterCollapse = 'filter__body'; 22 this.classFilterSection = 'filter__section'; 23 this.classSeries = 'filter-check__item'; 24 this.classFilterCount = 'count'; 25 this.classSeriesContainer = 'catalog__prepend'; 26 this.classSeriesTooltip = parameters.SERIES_TOOLTIP; 27 28 this.ajaxURL = parameters.AJAX_URL; 29 this.updateDelay = parameters.UPDATE_DELAY ?? 1000; 30 31 this.nodeCatalogContent = document.querySelector('div.' + this.classCatalogContent); 32 this.nodeSidebarFilter = BX(parameters.HTML_IDS.SIDEBAR_FILTER); 33 34 this.catalogTopFilterBlockId = parameters.HTML_IDS.CATALOG_TOP_FILTER; 35 this.nodeTopFilter = BX(parameters.HTML_IDS.CATALOG_TOP_FILTER); 36 37 this.seriesBlockId = parameters.HTML_IDS.FILTER_SERIES; 38 this.nodeSeriesFilter = BX(parameters.HTML_IDS.FILTER_SERIES); 39 this.btnReset = BX(parameters.HTML_IDS.FILTER_CONTROL_RESET); 40 this.nodeFounded = BX(parameters.HTML_IDS.FILTER_FOUNDED); 41 this.nodeFoundedCount = BX(parameters.HTML_IDS.FILTER_FOUNDED_COUNT); 42 this.nodeFoundedCountMobile = BX(parameters.HTML_IDS.FILTER_FOUNDED_COUNT_MOBILE); 43 44 this.cache = []; 45 this.cacheKey = BX.util.hashCode(JSON.stringify(this.getSelectedFilter())); 46 this.hashBrands = BX.util.hashCode(this.getSelectedBrands().toString()); 47 48 this.filterTags = new window.IvSmartFilterTags({ 49 filter: this, 50 idFilterTags: parameters.HTML_IDS.FILTER_TAGS, 51 }); 52 53 this.updateSeriesBlock(this.getSelectedBrands()); 54 this.initSeriesTooltip(); 55 this.initCollapseFilters(); 56 this.initAjaxNavigation(); 57 this.initBind(); 58 }; 59 60 window.IvSmartFilter.prototype = { 61 initBind: function () 62 { 63 this.bindEvent('change', 'change_filter', this.changeFilter, this.nodeSidebarFilter); 64 this.bindEvent('click', 'collapse_filter', this.collapseFilter, this.nodeSidebarFilter); 65 this.bindSeriesFilter(); 66 this.bindInStockFilter(); 67 }, 68 69 bindEvent: function(eventType, dataEvent, eventFunction, targetNode) 70 { 71 let nodes = targetNode.querySelectorAll('[data-event="' + dataEvent + '"]'); 72 73 for (let node of nodes) 74 { 75 if(node.getAttribute('data-event-type') === 'ajax') 76 { 77 $(node).on(eventType, BX.proxy(eventFunction, this)) 78 } 79 else 80 { 81 BX.bind(node, eventType, BX.proxy(eventFunction, this)); 82 } 83 } 84 }, 85 86 bindInStockFilter: function() 87 { 88 if(this.nodeTopFilter) 89 { 90 this.bindEvent('click', 'change_filter', this.changeFilter, this.nodeTopFilter); 91 } 92 }, 93 94 bindSeriesFilter: function() 95 { 96 if(this.nodeSeriesFilter) 97 { 98 this.bindEvent('click', 'change_series', this.changeSeries, this.nodeSeriesFilter); 99 } 100 }, 101 102 changeSeries: function(event) 103 { 104 let nodeSeries = event.target.closest('.' + this.classSeries); 105 106 if(nodeSeries) 107 { 108 let checkbox = nodeSeries.querySelector('input[type="checkbox"]'); 109 $(this.nodeSeriesFilter.querySelectorAll('input')).not(checkbox).prop('checked', false); 110 111 this.changeFilter(); 112 } 113 }, 114 115 updateSeriesBlock: function(selectedBrands) 116 { 117 let isShow = selectedBrands && selectedBrands.length === 1; 118 let nodeSeriesContainer = document.querySelector('.' + this.classCatalog + ' .' + this.classSeriesContainer); 119 if(nodeSeriesContainer) 120 { 121 if(isShow) 122 { 123 BX.show(nodeSeriesContainer); 124 } 125 else 126 { 127 BX.hide(nodeSeriesContainer); 128 } 129 } 130 }, 131 132 clearFilterSeries: function() 133 { 134 if(this.nodeSeriesFilter) 135 { 136 $(this.nodeSeriesFilter.querySelectorAll('input')).prop('checked', false); 137 } 138 }, 139 140 changeFilter: function(event) 141 { 142 if(!!this.timer) 143 { 144 clearTimeout(this.timer); 145 } 146 let input = null; 147 if(event && event.target) 148 { 149 input = event.target; 150 } 151 this.timer = setTimeout(BX.delegate(function(){ 152 this.updateFilter(input); 153 }, this), this.updateDelay); 154 }, 155 156 updateFilter: function(input) 157 { 158 this.curFilterinput = input;
159 let selectedBrands = this.getSelectedBrands(); 160 let hashBrands = BX.util.hashCode(selectedBrands.toString()); 161 if(hashBrands !== this.hashBrands) 162 { 163 this.clearFilterSeries(); 164 this.updateSeriesBlock(selectedBrands); 165 } 166 let filterValues = this.getSelectedFilter(); 167 let newCacheKey = BX.util.hashCode(JSON.stringify(filterValues)); 168 169 if(newCacheKey === this.cacheKey) 170 { 171 return; 172 } 173 174 this.cacheKey = newCacheKey; 175 this.freezeElement(this.nodeSidebarFilter); 176 this.freezeElement(this.nodeTopFilter); 177 this.freezeElement(this.nodeSeriesFilter); 178 179 if (this.cache[this.cacheKey]) 180 { 181 this.updateCatalog(this.cache[this.cacheKey], true); 182 } 183 else 184 { 185 BX.ajax.loadJSON( 186 this.ajaxURL, 187 filterValues, 188 BX.delegate(this.updateCatalog, this) 189 ); 190 } 191 }, 192 193 getSelectedFilter: function () 194 { 195 let filterValues = { 196 'ajax': 'y', 197 }; 198 199 let values = this.getSelectedValues(); 200 201 for (let value of values) 202 { 203 filterValues[value.name] = value.value; 204 } 205 206 return filterValues; 207 }, 208 209 getSelectedValues: function () 210 { 211 let values = []; 212 213 this.gatherInputsValues(values, this.nodeSidebarFilter.querySelectorAll('input')); 214 if(this.nodeTopFilter) 215 { 216 this.gatherInputsValues(values, this.nodeTopFilter.querySelectorAll('input')); 217 } 218 if(this.nodeSeriesFilter) 219 { 220 this.gatherInputsValues(values, this.nodeSeriesFilter.querySelectorAll('input')); 221 } 222 223 return values.filter((value) => value.value); 224 }, 225 226 gatherInputsValues: function (values, elements) 227 { 228 if(!elements) 229 { 230 return; 231 } 232 values = values || []; 233 234 for(let element of elements) 235 { 236 if (!element.dataset.type) 237 { 238 continue; 239 } 240 241 let value = { 242 id: element.id, 243 name: element.name, 244 value: element.value, 245 type: element.dataset.type, 246 text: element.dataset.tagValue, 247 }; 248 249 switch(element.dataset.type.toLowerCase()) 250 { 251 case 'checkbox': 252 if(element.checked) 253 { 254 values.push(value); 255 256 // ÐÐ»Ñ Ð¿Ð¾Ð´Ð´ÐµÑжки ÑÑаÑÑÑ ÑÑÑлок вида reference_parameter_1026-from-1-to-1 257 let filterNameMax = element.name.replace('_MIN', '_MAX'); 258 if(filterNameMax !== element.name) 259 { 260 let valueMax = BX.clone(value); 261 valueMax.name = filterNameMax; 262 values.push(valueMax); 263 } 264 } 265 break; 266 case 'range': 267 let nodeRange = element.closest('.' + this.classFilterRange); 268 if(nodeRange) 269 { 270 let minValue = Number(nodeRange.dataset.min); 271 let maxValue = Number(nodeRange.dataset.max); 272 let filterValue = Number(element.value); 273 274 if((element.dataset.inputMin && minValue < filterValue) || 275 (element.dataset.inputMax && maxValue > filterValue)) 276 { 277 values.push(value); 278 } 279 } 280 break; 281 case 'radio': 282 if(element.checked) 283 { 284 value.name = element.dataset.name; 285 values.push(value); 286 } 287 break; 288 case 'text': 289 case 'textarea': 290 case 'password': 291 case 'hidden': 292 case 'select-one': 293 case 'select-multiple': 294 default: 295 break; 296 } 297 } 298 }, 299 300 updateCatalog: function (result, fromCache) 301 { 302 this.unfreezeElement(this.nodeSidebarFilter); 303 this.unfreezeElement(this.nodeTopFilter); 304 305 if (!!result && !!result.ITEMS) 306 { 307 this.updateFilterItems(result); 308 this.updateControls(result); 309 this.showPopupMessage(result); 310 this.sendFilterResult(result); 311 312 this.freezeElement(this.nodeCatalogContent); 313 let url = this.updateBrowserUrl(result); 314 315 $.get(url, BX.proxy(this.onAfterCatalogLoad, this)); 316 } 317 318 if (!fromCache && this.cacheKey) 319 { 320 this.cache[this.cacheKey] = result; 321 } 322 323 if(fromCache) 324 { 325 let params = { 326 url: this.ajaxURL + 'ajax=y' 327 }; 328 BX.onCustomEvent('onAjaxSuccess', [result, params]); 329 } 330 }, 331 332 sendFilterResult: function (result) 333 { 334 this.nodeSidebarFilter.dispatchEvent(new CustomEvent('IvAjaxFilterResult', { 335 bubbles: true, 336 detail: { 337 result: result, 338 }, 339 })); 340 }, 341 342 updateControls: function (result) 343 { 344 if (!!this.nodeFounded && !!this.nodeFoundedCount) 345 { 346 let elementCount = parseInt(result.ELEMENT_COUNT); 347 this.nodeFoundedCount.innerText = elementCount; 348 if(!!this.nodeFoundedCountMobile) 349 { 350 this.nodeFoundedCountMobile.innerText = ' (' + elementCount + ')'; 351 } 352 } 353 354 if (result.JS_FILTER_PARAMS.FILTER_USED) 355 { 356 this.showElement(this.btnReset) 357 } 358 else 359 {
360 this.hideElement(this.btnReset) 361 } 362 }, 363 364 updateFilterItems: function (result) 365 { 366 for(let filterItem of BX.util.array_values(result.ITEMS)) 367 { 368 this.updateFilterItem(filterItem); 369 } 370 }, 371 372 updateFilterItem: function (filterItem) 373 { 374 if (!filterItem.VALUES) 375 { 376 return; 377 } 378 for (let value of BX.util.array_values(filterItem.VALUES)) 379 { 380 let control = BX(value.CONTROL_ID); 381 382 if (!!control) 383 { 384 if(!!value.DISABLED && !value.CHECKED) 385 { 386 BX.addClass(control, 'pseudo-disabled'); 387 $(control).prop('disabled', true); 388 } 389 else 390 { 391 BX.removeClass(control, 'pseudo-disabled'); 392 $(control).prop('disabled', false); 393 } 394 395 let controlContainer = control.closest('.' + this.classFilterItem); 396 if(controlContainer) 397 { 398 let counter = controlContainer.querySelector('.' + this.classFilterCount); 399 if (counter && value.hasOwnProperty('ELEMENT_COUNT')) 400 { 401 counter.innerHTML = '(' + value.ELEMENT_COUNT + ')'; 402 } 403 } 404 405 if(filterItem.DISPLAY_TYPE == 'F') 406 { 407 control.checked = !!value.CHECKED; 408 } 409 else if(filterItem.DISPLAY_TYPE == 'CUSTOM_CHECKBOX') 410 { 411 if(value.CONTROL_ID.includes('MIN')) 412 { 413 control.checked = !!value.HTML_VALUE; 414 control.disabled = filterItem.DISABLED && !value.HTML_VALUE; 415 } 416 } 417 else if(control.dataset.type == 'range') 418 { 419 let controlValue = value.HTML_VALUE ?? value.VALUE; 420 if(control.value != controlValue) 421 { 422 this.setRangeFilterValues(control, controlValue); 423 } 424 } 425 } 426 } 427 }, 428 429 setRangeFilterValues: function(rangeControl, value) 430 { 431 let nodeRange = rangeControl.closest('.' + this.classFilterRange); 432 433 if(rangeControl.dataset.inputMin) 434 { 435 nodeRange.FilterRange.updateValues({ valueMin: value ? value : nodeRange.dataset.min }); 436 } 437 else if(rangeControl.dataset.inputMax) 438 { 439 nodeRange.FilterRange.updateValues({ valueMax: value ? value : nodeRange.dataset.max }); 440 } 441 }, 442 443 initAjaxNavigation: function() 444 { 445 this.setBrowserHistoryState({'BASE_URL' : this.ajaxURL}); 446 window.addEventListener("hashchange", this.updateCurrentBrowserState.bind(this)); 447 setTimeout(function(){ 448 BX.bind(window, 'popstate', BX.proxy(this.applyBrowserHistoryState, this)); 449 }.bind(this), 500); 450 }, 451 452 updateBrowserUrl: function (result) 453 { 454 let url = BX.util.htmlspecialcharsback(result.FILTER_AJAX_URL) 455 || BX.util.htmlspecialcharsback(result.SEF_DEL_FILTER_URL); 456 457 if(url !== window.location.pathname) 458 { 459 result.CACHE_KEY = this.cacheKey; 460 result.CUR_FILTER_INPUT = this.curFilterinput ? this.curFilterinput.id : null; 461 this.setBrowserHistoryState(result, url, true); 462 } 463 return url; 464 }, 465 466 updateCurrentBrowserState: function() 467 { 468 if(window.history.state && Object.keys(window.history.state).length) 469 { 470 return; 471 } 472 let result = this.cache[this.cacheKey]; 473 if(result != undefined) 474 { 475 result.CACHE_KEY = this.cacheKey; 476 result.CUR_FILTER_INPUT = this.curFilterinput ? this.curFilterinput.id : null; 477 } 478 else 479 { 480 result = {}; 481 } 482 this.setBrowserHistoryState(result); 483 }, 484 485 setBrowserHistoryState: function(state, url, pushState = false) 486 { 487 if(!state) 488 { 489 state = this.cache[this.cacheKey]; 490 if(state != undefined) 491 { 492 state.CACHE_KEY = this.cacheKey; 493 state.CUR_FILTER_INPUT = this.curFilterinput ? this.curFilterinput.id : null; 494 } 495 else 496 { 497 state = {}; 498 } 499 } 500 if(!url) 501 { 502 url = window.location.pathname+window.location.search+window.location.hash; 503 } 504 let currentState = history.state; 505 let currentUrl = window.location.pathname+window.location.search+window.location.hash; 506 setTimeout(function(){ 507 if(pushState) 508 { 509 window.history.replaceState(currentState, '', currentUrl); 510 window.history.pushState(state, '', url); 511 } 512 else 513 { 514 window.history.replaceState(state, '', url); 515 } 516 }, 100); // webdebug.seo заÑиÑÐ°ÐµÑ ÑекÑÑий stage 517 }, 518 519 applyBrowserHistoryState: function(data) 520 { 521 let state = data.state; 522 if(!state || !Object.keys(state).length) 523 { 524 return; 525 } 526 if(state.BASE_URL) 527 { 528 let currentUrl = window.location.pathname+window.location.search+window.location.hash; 529 if(state.BASE_URL == currentUrl) 530 { 531 window.location.href = state.BASE_URL; 532 } 533 return; 534 } 535 if(state.CACHE_KEY === this.cacheKey) 536 { 537 return; 538 } 539 this.cacheKey = state.CACHE_KEY; 540 if(this.curFilterinput) 541 { 542 let currentFilterContainer = this.curFilterinput.closest(`[data-toggle="filter-tooltip"]`); 543 if(currentFilterContainer) 544 { 545 currentFilterContainer.ins.scrollToTarget(); 546 } 547 } 548 this.curFilterinput = BX(state.CUR_FILTER_INPUT); 549 this.updateCatalog(state, true); 550 }, 551 552 onAfterCatalogLoad: function (html) 553 { 554 let updatedElements = []; 555 let catalogHtml = $(html).find('div.' + this.classCatalogContent).html(); 556 let catalogAppendHtml = $(html).find('div.' + this.classCatalogAppend).html(); 557 let catalogItemsHtml = $(html).find('#' + this.catalogItemsBlockId).html(); 558 var catalogTopFilterHtml = $(html).find('#' + this.catalogTopFilterBlockId).html(); 559 560 let topSeoDescription = $(html).find('#' + this.idTopDesciption).html(); 561 let bottomSeoDescription = $(html).find('#' + this.idBottomDesciption).html(); 562 563 catalogHtml = BX.processHTML(catalogHtml); 564 catalogAppendHtml = BX.processHTML(catalogAppendHtml); 565 catalogItemsHtml = BX.processHTML(catalogItemsHtml); 566 catalogTopFilterHtml = BX.processHTML(catalogTopFilterHtml); 567 568 $(this.catalogItems).html(catalogItemsHtml.HTML); 569 $('div.' + this.classCatalogAppend).html(catalogAppendHtml.HTML); 570 571 $('#' + this.idTopDesciption).html(topSeoDescription); 572 $('#' + this.idBottomDesciption).html(bottomSeoDescription); 573 $('#' + this.catalogTopFilterBlockId).html(catalogTopFilterHtml.HTML); 574 575 BX.ajax.processScripts(catalogHtml.SCRIPT); 576 577 if(this.nodeSeriesFilter) 578 { 579 let hashBrands = BX.util.hashCode(this.getSelectedBrands().toString()); 580 let seriesChecked = $(html).find('#' + this.seriesBlockId + ' input:checked')[0] || {}; 581 let currentSeriesChecked = this.nodeSeriesFilter.querySelector('input:checked') || {}; 582 if(hashBrands !== this.hashBrands || (seriesChecked.id !== currentSeriesChecked.id)) 583 { 584 this.nodeSeriesFilter.innerHTML = $(html).find('#'+this.seriesBlockId).html();
585 updatedElements['SERIES_FILTER'] = true; 586 this.hashBrands = hashBrands; 587 } 588 589 let nodeSeriesDesc = this.nodeSeriesFilter.querySelector('.' + this.classSeriesDesc); 590 if (nodeSeriesDesc) 591 { 592 let description = $(html).find('.' + this.classSeriesDesc).html() || ''; 593 nodeSeriesDesc.innerHTML = description; 594 if (description.trim()) 595 { 596 BX.show(nodeSeriesDesc); 597 } else 598 { 599 BX.hide(nodeSeriesDesc); 600 } 601 } 602 } 603 604 this.filterTags.initBlock().drawCurrentTags(); 605 this.initAfterLoadedCatalog(updatedElements); 606 this.unfreezeElement(this.nodeCatalogContent); 607 this.unfreezeElement(this.nodeSeriesFilter); 608 }, 609 610 initAfterLoadedCatalog: function (updatedElements) 611 { 612 this.initCatalogBlock(); 613 614 if(updatedElements['SERIES_FILTER']) 615 { 616 this.initSeriesTooltip(); 617 this.bindSeriesFilter(); 618 } 619 }, 620 621 showPopupMessage: function(result) 622 { 623 if(this.curFilterinput) 624 { 625 let currentFilterContainer = this.curFilterinput.closest(`[data-toggle="filter-tooltip"]`); 626 if(currentFilterContainer) 627 { 628 let countProducts = parseInt(result.ELEMENT_COUNT); 629 currentFilterContainer.dataset.title = BX.message('FILTER_FOUND_TOTAL').replace("#COUNT#", countProducts); 630 currentFilterContainer.dispatchEvent(new Event('iv.filter-tooltip.show')); 631 } 632 } 633 }, 634 635 hideElement: function (element) 636 { 637 BX.hide(element); 638 }, 639 640 showElement: function (element) 641 { 642 BX.show(element); 643 }, 644 645 freezeElement: function (element) 646 { 647 if(element) 648 { 649 BX.addClass(element, 'loader'); 650 } 651 }, 652 653 unfreezeElement: function (element) 654 { 655 if(element) 656 { 657 BX.removeClass(element, 'loader'); 658 } 659 }, 660 661 getSelectedBrands: function () 662 { 663 let inputBrands = this.nodeSidebarFilter.querySelectorAll('[data-is-brand="true"]'); 664 let brandsSelected = []; 665 for (let inputBrand of inputBrands) 666 { 667 if (inputBrand.checked) 668 { 669 brandsSelected.push(inputBrand.id); 670 } 671 } 672 673 return brandsSelected; 674 }, 675 676 collapseFilter: function (event) 677 { 678 let sectionFilterNode = event.target.closest('.' + this.classFilterSection); 679 let collapseNode = sectionFilterNode.querySelector('.' + this.classFilterCollapse); 680 this.saveCollapseState(sectionFilterNode.dataset.propId, !collapseNode.classList.contains('show')); 681 }, 682 683 saveCollapseState: function (propertyId, state) 684 { 685 let collapseState = this.getCollapseState(); 686 collapseState[propertyId] = state; 687 localStorage.setItem(this.expandedLocalStorage, JSON.stringify(collapseState)); 688 }, 689 690 getCollapseState: function () 691 { 692 return JSON.parse(localStorage.getItem(this.expandedLocalStorage)) ?? {}; 693 }, 694 695 initCollapseFilters: function () 696 { 697 let collapseState = this.getCollapseState(); 698 let collapseNodes = this.nodeSidebarFilter.querySelectorAll('.' + this.classFilterSection + ' .' + this.classFilterCollapse); 699 700 for (let collapseNode of collapseNodes) 701 { 702 let sectionNode = collapseNode.closest('.' + this.classFilterSection); 703 if(sectionNode) 704 { 705 let propId = sectionNode.dataset.propId; 706 let isCheckedInput = !!sectionNode.querySelector('[checked]'); 707 if(!isCheckedInput && collapseState.hasOwnProperty(propId)) 708 { 709 let nodeFilterCollapse = sectionNode.querySelector('.' + this.classFilterCollapse); 710 if(collapseState[propId] === true) 711 { 712 this.openCollapse(nodeFilterCollapse); 713 } 714 else 715 { 716 this.closeCollapse(nodeFilterCollapse); 717 } 718 } 719 } 720 } 721 }, 722 723 openCollapse: function (node) 724 { 725 if(node) 726 { 727 $(node).collapse('show') 728 } 729 }, 730 731 closeCollapse: function (node) 732 { 733 if(node) 734 { 735 $(node).collapse('hide') 736 } 737 }, 738 739 initSeriesTooltip: function () 740 { 741 if(window.Block && window.Block.Tooltip) 742 { 743 let isMobile = BX.hasClass(document.documentElement, 'bx-touch'); 744 if(!isMobile && this.nodeSeriesFilter) 745 { 746 let seriesElements = this.nodeSeriesFilter.getElementsByClassName(this.classSeriesTooltip); 747 for(let node of seriesElements) 748 { 749 node.dataset.toggle = "tooltip"; 750 } 751 window.Block.Tooltip.initAllBlocks(); 752 } 753 } 754 }, 755 756 initCatalogBlock: function () 757 { 758 if(window.Block.Catalog && window.Block.Catalog.getAllInstances().length > 0) 759 { 760 window.Block.Catalog.getAllInstances()[0].update(); 761 } 762 if(window.Block.Popper) 763 { 764 window.Block.Popper.initAllBlocks(); 765 } 766 if(window.Block.ProductOptions && window.Block.ProductOptions.getAllBlocks().length > 0) 767 { 768 window.Block.ProductOptions.initAllBlocks(); 769 } 770 if (window.Block.SliderLine) 771 { 772 window.Block.SliderLine.initAllBlocks(); 773 } 774 this.bindInStockFilter(); 775 }, 776 777 }; 778})(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.