PageSourceSearch

https://www.celes-perfume.com/wp-content/themes/shop-isle-pro/cust…custom-product-filter.js?1790364028&ver=1.0

js celes-perfume.com collected 2026-09-25 19:21:54 UTC 51,953 bytes, 1,281 lines download raw bytes

1
2// // ********************************************************************
3// // || 機能概要 || 再送返品データ出力
4// // ||          ||
5// // || 作成者   || Bui Minh Duc
6// // || 作成日   || 2024/03/28
7// // || 更新履歴 ||
8// // 2024/03/28  Bui Minh Duc
9// // =========================================================================
10// // || 戻り値   || クエリ結果
11// // ********************************************************************
12
13function getStockStatus(productStock, pmVariantPriceString) {
14    try {
15        if (productStock === 'outofstock') {
16            return '在庫なし';
17        }
18        
19        pmVariantPriceString = pmVariantPriceString.substring(1, pmVariantPriceString.length - 1);
20        var pairs = pmVariantPriceString.split("},");
21        var allOutOfStock = true;
22        var partOutOfStock = false;
23        pairs.forEach(function(pair) {
24            if (pair.charAt(pair.length - 1) !== "}") {
25                pair += "}";
26            }
27            var variantPriceObj;
28            try {
29                variantPriceObj = eval("(" + pair + ")");
30            } catch (error) {
31                return; 
32            }
33
34            if (variantPriceObj.stock_status !== "outofstock") {
35                allOutOfStock = false;
36            }
37
38            if (!variantPriceObj.variant.includes('フルボトル') && variantPriceObj.stock_status === "outofstock") {
39                partOutOfStock = true;
40            }
41        });
42
43        if (allOutOfStock) {
44            return '在庫なし';
45        }
46
47        if (partOutOfStock) {
48            return '一部在庫なし';
49        }
50    } catch (error) {
51        console.error("Unexpected error:", error);
52        return []; 
53    }
54}
55
56function isFullBottleAvailable(pmVariantPriceString) {
57    try {
58        pmVariantPriceString = pmVariantPriceString.substring(1, pmVariantPriceString.length - 1);
59        var pairs = pmVariantPriceString.split("},");
60        var isFullBottleAvailable = false;
61        pairs.forEach(function(pair) {
62            if (pair.charAt(pair.length - 1) !== "}") {
63                pair += "}";
64            }
65            var variantPriceObj;
66            try {
67                variantPriceObj = eval("(" + pair + ")");
68            } catch (error) {
69                console.error("Error parsing variant price object:", error);
70                return; 
71            }
72            if (variantPriceObj.variant.includes('フルボトル') && variantPriceObj.stock_status === "instock") {
73                isFullBottleAvailable = true;
74            }
75        });
76
77        return isFullBottleAvailable;
78    } catch (error) {
79        console.error("Unexpected error:", error);
80        return []; 
81    }
82}
83
84
85function getPriceForVariant2(pmVariantPriceString) {
86    pmVariantPriceString = pmVariantPriceString.substring(1, pmVariantPriceString.length - 1);
87    var pairs = pmVariantPriceString.split("},");
88    var prices = []; 
89
90    pairs.forEach(function(pair) {
91        if (pair.charAt(pair.length - 1) !== "}") {
92            pair += "}";
93        }
94        var variantPriceObj = eval("(" + pair + ")");
95        
96        prices.push(variantPriceObj);
97    });
98
99    var selectedLabelsString = sessionStorage.getItem('selectedLabels');
100    if (selectedLabelsString) {
101        var selectedLabels = JSON.parse(selectedLabelsString);
102        var fullBottleRegex = /フルボトル.*/;
103        if (selectedLabels.length >= 1) {
104            var filteredPrices = prices.filter(function(variant) {
105                return selectedLabels.includes(variant.variant) 
106                || (variant.stock_status === 'instock' && selectedLabels.includes('フルボトル') && fullBottleRegex.test(variant.variant));
107            });
108            if (filteredPrices.length > 0) {
109                prices = filteredPrices;
110            }
111        }
112    }
113
114    // If the variants contains full bottle, only take the smallest price of full bottle
115    var cheapestFullBottle = prices
116        .filter(item => item.variant.startsWith("フルボトル"))
117        .reduce((cheapest, current) => {
118            return current.price < cheapest.price ? current : cheapest;
119        }, { price: Infinity });
120    var variantPrices = prices
121        .filter(item => !item.variant.startsWith("フルボトル") || item.price === cheapestFullBottle.price);    
122
123    var sortValue = sessionStorage.getItem('sort');
124    if (sortValue?.startsWith('price') && sortValue.endsWith('desc')) {
125        variantPrices.sort(function(a, b) {
126            return b.price - a.price;
127        });
128    } else {
129        variantPrices.sort(function(a, b) {
130            return a.price - b.price;
131        });
132    }
133
134    if (variantPrices.length > 0) {
135        return variantPrices[0].price;
136    } else {
137        return 0;
138    }
139}
140
141function checkForSelectedWeights() {
142    var filterBlock = document.getElementById('filter-block-weight');
143    var checkboxes = filterBlock.querySelectorAll('input[type="checkbox"]:checked');
144    return checkboxes.length > 0;
145}
146
147function validateSearchPrice() {
148    var priceMinElement = document.getElementById('price-min');
149    var priceMaxElement = document.getElementById('price-max');
150    
151    if (priceMinElement && priceMaxElement && parseInt(priceMinElement.value) > parseInt(priceMaxElement.value)) {
152        return false;
153    }
154    return true;
155}
156
157function validateBeforeSearch() {
158    // Validate weight
159    let hasWeight = checkForSelectedWeights();
160    var filterWeight = document.getElementById('filter-block-weight');
161    if (!hasWeight) {
162        filterWeight.classList.add('error');
163    } else {
164        filterWeight.classList.remove('error');
165    }
166
167    // Validate price
168    let validPrice = validateSearchPrice();
169    var filterPrice = document.getElementById('filter-block-price');
170    if (!validPrice) {
171        filterPrice.classList.add('error');
172    } else {
173        filterPrice.classList.remove('error');
174    }
175
176    return hasWeight && validPrice;
177}
178
179function hasFilterPrice() {
180    var hasFilterPrice = false;
181    var filterWeightBlock = document.getElementById('filter-block-weight');
182    var checkboxes = filterWeightBlock.querySelectorAll('input[type="checkbox"]');
183    var selectedWeights = filterWeightBlock.querySelectorAll('input[type="checkbox"]:checked');
184    
185    var minPrice = document.getElementById('price-min').value;
186    var maxPrice = document.getElementById('price-max').value;
187    
188    if (checkboxes.length !== selectedWeights.length || minPrice || maxPrice) {
189        hasFilterPrice = true;
190    }
191    return hasFilterPrice;
192}
193
194function updateSelectedFilterPrice() {
195    var filterPriceLabel = document.getElementById('price');
196
197    if (filterPriceLabel) {
198        if (hasFilterPrice()) {
199            filterPriceLabel.classList.add('active');
200        } else {
201            filterPriceLabel.classList.remove('active');
202        }
203    }
204}
205
206function toggleFilterFields(show) {
207    var filterBlocks = document.querySelectorAll('.accordion2.filter-block');
208    document.querySelector('.more-filter').classList.toggle('hidden', !show);
209    filterBlocks.forEach(function(block) {
210        block.classList.toggle('hidden', !show);
211    });
212
213    var resultSection = document.getElementById('result-list-filter');
214    var filterStatus = document.getElementById('filter-status');
215    var filterFields = document.getElementById('filter-fields');
216    resultSection.style.display = show ? 'none' : 'block';
217    filterFields.style.display = show ? 'none' : 'block';
218    filterStatus.style.display = show ? 'none' : 'block';
219
220    var filterFieldElements = document.querySelectorAll('.filter-field');
221    filterFieldElements.forEach(function(filterField) {
222        filterField.classList.toggle('show-filter', show);
223    });
224}
225
226function hideFilterFields() {
227    toggleFilterFields(false);
228}
229
230function showFilterFields() {
231    toggleFilterFields(true);
232}
233
234function removeBrandSEOInfo() {
235    let brandSEOTagEle = document.getElementById('brand-seo-tag');
236    if (brandSEOTagEle) {
237        brandSEOTagEle.remove();
238    }
239
240    let topBrandEle = document.querySelector('.section-collection-brand');
241    if (topBrandEle) {
242        topBrandEle.remove();
243    }
244
245    let brandReviewsEle = document.getElementById('section-brand-reviews');
246    if (brandReviewsEle) {
247        brandReviewsEle.remove();
248    }
249
250}
251
252async function handleFilter() {
253    let isValidSeach = validateBeforeSearch();
254
255    if (isValidSeach) {
256        await fetchNewPageData(1);
257    }
258}
259
260document.getElementById('filter-button').addEventListener('click', async (e) => {
261    e.preventDefault();
262    await handleFilter();
263});
264
265function getSelectedPriceFilters() {
266    const selectedLabelsArray = [
267        { label: "ワンタップ (お肌に1回お試し)", value: "price2" },
268        { label: "15回プッシュ (0.75ml)", value: "price3" },
269        { label: "50回プッシュ (2.5ml)", value: "price5" },
270        { label: "100回プッシュ (5ml)", value: "price6" },
271        { label: "フルボトル", value: "price7" }
272    ];
273
274    var selectedLabelsString = sessionStorage.getItem('selectedLabels');
275    var selectedLabels = [];
276
277    if (selectedLabelsString) {
278        selectedLabels = JSON.parse(selectedLabelsString);
279    }
280
281    return selectedLabelsArray
282        .filter(item => selectedLabels.includes(item.label))
283        .map(item => item.value);
284}
285
286function addPriceFilter(query){
287    // Default: min price: 0, max price: 999999999
288    const minPrice = document.getElementById('price-min').value || 0;
289    const maxPrice = document.getElementById('price-max').value || 999999999;
290
291    const params = new URLSearchParams(query);
292    const selectedPriceFilters = getSelectedPriceFilters();
293    selectedPriceFilters.forEach(item => {
294        // Add filter price for each selected label
295        params.set(item, `${minPrice}-${maxPrice}`);
296
297        // Add filter field
298        const currentFilterFields = params.get('FilterFields') || '';
299        params.set('FilterFields', `${currentFilterFields}:${item}`);
300    });
301
302    return params.toString();
303}
304
305function fetchNewPageData(pageNumber) {
306    var finalString = sessionStorage.getItem('finalString');
307    var sortValue = sessionStorage.getItem('sort');
308    var searchKeywords = sessionStorage.getItem('searchKeywords');
309
310    const urlParams = new URLSearchParams(finalString);
311    if (searchKeywords) {
312        const searchKeywordsParams = new URLSearchParams(searchKeywords);
313        let keywords = searchKeywordsParams.get('Keywords');
314        if (keywords) {
315            urlParams.set('s', keywords);
316        }
317    }
318    
319    urlParams.delete('CountPerPage');
320    urlParams.delete('FilterFields');
321    urlParams.set('Page', pageNumber);
322    urlParams.set('Sort', (sortValue ? sortValue : 'total_sales desc'));
323
324    var selectedLabelsString = sessionStorage.getItem('selectedLabels');
325    var selectedLabels = [];
326
327    if (urlParams.has('pa_brand_id')) {
328        let brandIdsString = urlParams.get('pa_brand_id');
329        let brandIds = brandIdsString.split(',');
330        let brandSlugsString = '';
331        brandIds.forEach(brandId => {
332            let brandAlphabetElement = document.getElementById(`pa_brand_id-${brandId}-alphabet`);
333            let brandSlug = brandAlphabetElement?.getAttribute('data-brand-slug');
334
335            if (brandSlugsString.length > 0) {
336                brandSlugsString += ',';
337            }
338            brandSlugsString += brandSlug;
339        });
340        urlParams.set('pa_brand', brandSlugsString);
341        urlParams.delete('pa_brand_id');
342    }
343
344    if (selectedLabelsString) {
345        selectedLabels = JSON.parse(selectedLabelsString);
346        const labelsString = selectedLabels.join(',');
347        urlParams.set('selected_labels', labelsString);
348    }
349
350    if (hasFilterPrice()) {
351        const minPrice = document.getElementById('price-min').value || '';
352        const maxPrice = document.getElementById('price-max').value || '';
353
354        const selectedPriceFilters = getSelectedPriceFilters();
355        selectedPriceFilters.forEach(item => {
356            urlParams.set(item, `${minPrice}-${maxPrice}`);
357        });
358    }
359
360    // Remove null parameters
361    for (const [key, value] of urlParams.entries()) {
362        if (key === 'null') {
363            urlParams.delete(key);
364        }
365    }
366
367    window.location.href = window.location.pathname + '?' + urlParams.toString();
368}
369
370async function handleChangePage(page) {
371    let response = await fetchNewPageData(page);
372    addHistoryState(response?.historyParams, response?.data);
373}
374
375function updateSelectedLabelsSessionStorage() {
376    var filterBlock = document.getElementById('filter-block-weight');
377    var checkboxes = filterBlock.querySelectorAll('input[type="checkbox"]');
378    var selectedLabels = [];
379    checkboxes.forEach(function(checkbox) {
380        if (checkbox.checked) {
381            var label = document.querySelector('label[for="' + checkbox.id + '"]');
382            selectedLabels.push(label.textContent.trim());
383        }
384    });
385    sessionStorage.setItem('selectedLabels', JSON.stringify(selectedLabels));
386}
387
388document.addEventListener('DOMContentLoaded', () => {
389    var filterBlock = document.getElementById('filter-block-weight');
390    var checkboxes = filterBlock.querySelectorAll('input[type="checkbox"]');
391    var sortItem = document.querySelectorAll('span[name="sort"]');
392    var btnSort = document.getElementById('btn-sort');
393    var sortPrice = document.querySelector('select[name="sort-price"]');
394    var filterPrices = document.querySelectorAll('.filter-price');
395
396    updateSelectedLabelsSessionStorage();
397
398    // Add event listener for checkbox changes
399    checkboxes.forEach(function(checkbox) {
400        checkbox.addEventListener('change', function() {
401            updateSelectedLabelsSessionStorage();
402            updateSelectedFilterPrice();
403            getSearchResult(selectedValues);
404        });
405    });
406
407    // Add event listener for sort item: 人気順, 新着順, レビューの多い順
408    sortItem.forEach(function(checkbox) {
409        checkbox.addEventListener('click', async function(e) {
410            let isValidSeach = validateBeforeSearch();
411            if (isValidSeach && !this.classList.contains("active")) {
412                // Remove selected sort item
413                sortItem.forEach(element => {
414                    element.classList.remove("active");
415                });
416                // Remove selected sort price
417                sortPrice.classList.remove("active");
418                // Select 価格順 option on sort price
419                sortPrice.selectedIndex = 0;
420
421                this.classList.add("active");
422                document.querySelector('#btn-sort span').textContent = this.textContent;
423
424                var sortName = this.getAttribute('data-sort-name');
425                var sortType = this.getAttribute('data-sort-type');
426                sessionStorage.setItem('sort', `${sortName} ${sortType}`);
427                hideFilterFields();
428                await fetchNewPageData(1);
429            }
430        });
431    });
432
433    // Add event listener for sort by price
434    sortPrice.addEventListener('change', async function() {
435        let isValidSeach = validateBeforeSearch();
436        if (isValidSeach) {
437            // Remove selected sort item
438            sortItem.forEach(element => {
439                element.classList.remove("active");
440            });
441
442            sortPrice.classList.add("active");
443            var selectedIndex = sortPrice.selectedIndex;
444            document.querySelector('#btn-sort span').textContent = sortPrice.options[selectedIndex].text;
445
446            var sortName = sortPrice.value;
447            if (sortName === 'highest_price') {
448                sessionStorage.setItem('sort', 'price desc');
449            } else {
450                sessionStorage.setItem('sort', 'price asc');
451            }
452            hideFilterFields();
453            let response = await fetchNewPageData(1);
454            addHistoryState(response?.historyParams, response?.data);
455        }
456    });
457
458    // Add event listener for toggle show sort item
459    btnSort.addEventListener('click', function(e) {
460        var sortBlock = document.getElementById('sort-block');
461        if (sortBlock.classList.contains("show")) {
462            sortBlock.classList.remove("show");
463        } else {
464            sortBlock.classList.add("show");
465        }
466        ignoreScroll = true;
467        e.preventDefault();
468    });
469
470    filterPrices.forEach(function(filterPrice) {
471        filterPrice.addEventListener('input', function() {
472            updateSelectedFilterPrice();
473            getSearchResult(selectedValues);
474        });
475    });
476
477    // Padding bottom to prevent content overlap by search button
478    var htmlToInsert = '<div class="padding-bottom-product-filter"></div>';
479    document.body.insertAdjacentHTML('beforeend', htmlToInsert);
480
481    var filterAction = document.getElementById('filter-action');
482    var paddingContainer = document.querySelector('.padding-bottom-product-filter');
483    var filterActionObserver = new MutationObserver(function(mutationsList, observer) {
484        mutationsList.forEach(function(mutation) {
485            if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
486                if (filterAction.classList.contains('filter-action-hidden')) {
487                    paddingContainer.classList.add('no-padding');
488                } else {
489                    paddingContainer.classList.remove('no-padding');
490                }
491            }
492        });
493    });
494    filterActionObserver.observe(document.querySelector('#filter-action'), { attributes: true });
495});
496
497function setSearchKeyWordsToSessionStorage (keyword) {
498    var searchKeywordsString = '';
499    searchKeywordsString = 'Keywords=' + keyword + '&KeywordsSearchFields=item_name:item_e_name:pa_brand_name:pa_brand_name_kana:"post_excerpt"';
500
501    sessionStorage.setItem('searchKeywords', searchKeywordsString);
502    
503    return searchKeywordsString;
504}
505
506function updateSortBlock(sortName, sortType) {
507    var sortItem = document.querySelectorAll('span[name="sort"]');
508    var sortPrice = document.querySelector('select[name="sort-price"]');
509    // Remove selected sort item
510    sortItem.forEach(element => {
511        if (element.getAttribute('data-sort-name') === sortName && element.getAttribute('data-sort-type') === sortType) {
512            element.classList.add("active");
513            document.querySelector('#btn-sort span').textContent = element.textContent;
514        } else {
515            element.classList.remove("active");
516        }
517    });
518    if (sortName === 'price') {
519        if (sortType === 'desc') {
520            sortPrice.value = 'highest_price';
521        } else {
522            sortPrice.value = 'lowest_price';
523        }
524        sortPrice.classList.add("active");
525        var selectedIndex = sortPrice.selectedIndex;
526        document.querySelector('#btn-sort span').textContent = sortPrice.options[selectedIndex].text;
527    } else {
528        sortPrice.classList.remove("active");
529    }
530}
531
532var selectedValues = {};
533var initPage = 1;
534var debounceTimerCheckbox;
535document.addEventListener('DOMContentLoaded', function() {
536    sessionStorage.removeItem('finalString');
537    sessionStorage.removeItem('searchKeywords');
538    sessionStorage.removeItem('sort');
539
540    let hasFilter = loadFiltersFromUrl();
541    var searchText = document.getElementById('search_text');
542
543    var debounceTimer;
544    if (searchText) {
545        if (searchText && searchText.value)  {
546            setSearchKeyWordsToSessionStorage(searchText.value);
547        }
548
549        searchText.addEventListener('input', function() {
550            var test = '';
551            test = setSearchKeyWordsToSessionStorage(this.value);
552            sessionStorage.removeItem('sort');
553            clearTimeout(debounceTimer);
554            debounceTimer = setTimeout(() => {
555                var finalString = sessionStorage.getItem('finalString');
556                if (!finalString || finalString === '&FilterFields=%2b') {
557                    sendAjaxRequest('', test);
558                } else {
559                    sendAjaxRequest(finalString, test);
560                }
561            }, 1000);
562        });
563
564        searchText.addEventListener("keydown", async function(event) {
565            if (event.key === "Enter") {
566                event.preventDefault();
567                setSearchKeyWordsToSessionStorage(this.value);
568                sessionStorage.removeItem('sort');
569                await handleFilter();
570            }
571        });
572    }
573    
574    if (hasFilter) {
575        updateSelectedFilterField(selectedValues);
576        updateSelectedFilterPrice();
577        showFooter();
578        scrollToSearchResult();
579        hideSearchButton();
580        // Hide filter_form
581        document.querySelector('#filter_form').classList.add('scrolled-past-header', 'section-header-hidden', 'section-header-sticky');
582        isLoading = false;
583    } else {
584        selectedValues = {};
585        setDefaultFilter();
586        showFilterFields();
587    }
588
589    window.addEventListener('pageshow', (event) => {
590        if (event.persisted) {
591            selectedValues = {};
592            let hasFilter = loadFiltersFromUrl();
593            if (hasFilter) {
594                updateSelectedFilterField(selectedValues);
595                updateSelectedFilterPrice();
596                showFooter();
597                hideSearchButton();
598                // Hide filter_form
599                document.querySelector('#filter_form').classList.add('scrolled-past-header', 'section-header-hidden', 'section-header-sticky');
600                isLoading = false;
601            } else {
602                selectedValues = {};
603                setDefaultFilter();
604                showFilterFields();
605                hideFooter();
606            }
607        }
608    });
609
610    addChangeEventListeners();
611});
612
613function scrollToSearchResult() {
614    var element = document.getElementById('result-list-filter');
615    var headerHeight = document.querySelector('.navbar[role="navigation"]').offsetHeight;
616    var elementPosition = element.getBoundingClientRect().top + window.scrollY;
617    var offsetPosition = elementPosition - headerHeight;
618    window.scrollTo({
619        top: offsetPosition,
620        behavior: 'smooth'
621    });
622}
623
624function hideSearchButton() {
625    var navbarHeight = document.querySelector('.navbar').clientHeight
626    var resultListFilterHeight = document.querySelector('#result-list-filter').clientHeight;
627    var bottomPageWrapHeight = document.querySelector('.bottom-page-wrap').clientHeight;
628    var windowHeight = window.innerHeight;
629    if (navbarHeight + resultListFilterHeight + bottomPageWrapHeight > windowHeight) {
630        document.querySelector('#filter-action').classList.add('filter-action-hidden');
631    }
632}
633
634function setDefaultFilter() {
635    let filterTextFields = document.querySelectorAll('#filter_form input[type=text]');
636    filterTextFields.forEach(element => {
637        element.value = '';
638    });
639
640    let filterCheckBoxs = document.querySelectorAll('input[type=checkbox][name=girlboy], input[type=checkbox][name=brand], input[type=checkbox][name=fragrancespice]');
641    filterCheckBoxs.forEach(element => {
642        element.checked = false;
643    });
644
645    let itemAlphabets = document.querySelectorAll('#modal-brand-list input[type=checkbox], #modal-fragrancespice-list input[type=checkbox]');
646    itemAlphabets.forEach(element => {
647        element.checked = false;
648    });
649
650    let selectWeightCheckBoxs = document.querySelectorAll('#filter-block-weight input[type=checkbox]');
651    selectWeightCheckBoxs.forEach(element => {
652        element.checked = true;
653    });
654
655    let selectedFilters = document.querySelectorAll('.filter-control:has(.remove-filter');
656    selectedFilters.forEach(element => {
657        element.closest('.filter-block-grid').removeChild(element);
658    });
659
660    sessionStorage.removeItem('searchKeywords');
661    sessionStorage.removeItem('finalString');
662    sessionStorage.removeItem('sort');
663    updateSelectedLabelsSessionStorage();
664
665    // Reset filter field
666    var filterFieldElements = document.querySelectorAll('.filter-field');
667    filterFieldElements.forEach(function(filterField) {
668        filterField.classList.remove('active');
669    });
670
671    // Reset #filter_form scroll
672    var filterContainer = document.getElementById('filter_form');
673    filterContainer.classList.remove('section-header-hidden', 'section-header-sticky', 'filter-action-hidden');
674
675    // Collapse filter block
676    var filterBlocks = document.querySelectorAll('.accordion2.filter-block');
677    filterBlocks.forEach(function(block) {
678        block.getElementsByClassName('toggle filter-block-toggle')[0].checked = false;
679    });
680
681    // Only show 4 filter block
682    var filterContainer = document.querySelector('.filter-container');
683    filterContainer.classList.remove('toggle-filter');
684
685    // Show filter button
686    document.querySelector('#filter-action').classList.remove('filter-action-hidden');
687}
688 
689function addChangeEventListeners() {
690    document.querySelectorAll('input[type=checkbox][name=girlboy], input[type=checkbox][name=brand], input[type=checkbox][name=fragrancespice]')
691        .forEach(input => input.addEventListener('change', (event) => handleInputChange(event.target, selectedValues)));
692    
693    // Click button confirm selected brand
694    var btnConfirmSelectedBrand = document.getElementById('confirm-selected-brand');
695    btnConfirmSelectedBrand.addEventListener('click', function() {
696        handleConfirmSelectedOnModal('brand');
697    });
698
699    // Click button confirm selected fragrancespice
700    var btnConfirmSelectedFragrancespice = document.getElementById('confirm-selected-fragrancespice');
701    btnConfirmSelectedFragrancespice.addEventListener('click', function() {
702        handleConfirmSelectedOnModal('fragrancespice');
703    });
704}
705
706function loadFiltersFromUrl() {
707    const acceptedParam = [
708        'Keywords',
709        's',
710        'pa_brand',
711        'pa_brand_id',
712        'pa_girlboy',
713        'pa_smell-type',
714        'pa_smelltype',
715        'pa_香料',
716        'pa_fragrancespice',
717        'pa_image',
718        'pa_feeling',
719        'pa_scene',
720        'pa_seasons',
721        'selected_labels',
722        'price2',
723        'price3',
724        'price5',
725        'price6',
726        'price7',
727        'Sort',
728        'sort-by',
729        'sort-type',
730        'Page'
731    ];
732    var hasSearchQuery = false;
733
734    const urlParams = new URLSearchParams(window.location.search);
735    for (let param of acceptedParam) {
736        if (urlParams.has(param)) {
737            hasSearchQuery = true;
738            let value = urlParams.get(param);
739            switch (param) {
740                case 'Keywords':
741                case 's':
742                    document.getElementById('search_text').value = value;
743                    setSearchKeyWordsToSessionStorage(value);
744                    break;
745                case 'pa_brand':
746                    let brandSlugs = value.split(',');
747                    brandSlugs.forEach(slug => {
748                        let brandAlphabetElement = document.querySelector(`.list-brand-alphabet[data-brand-slug="${slug}"]`);
749                        if (brandAlphabetElement) {
750                            let brandId = brandAlphabetElement.getAttribute('data-brand-id');
751                            selectedValues['pa_brand_id=' + brandId] = true;
752                            setFinalStringToSessionStorage(selectedValues);
753                            let brandNameEn = brandAlphabetElement.getAttribute('data-brand-name-en');
754                            let brandNameJp = brandAlphabetElement.getAttribute('data-brand-name-jp');
755                            updateFilterBlock('pa_brand_id=' + brandId, brandNameEn, brandNameJp, true);
756                            updateTopFilterBlock('pa_brand_id=' + brandId, true);
757                            updateItemListAlphabet('brand', selectedValues);
758                        }
759                    });
760                    break;
761                case 'pa_brand_id':
762                    let brandIds = value.split(',');
763                    brandIds.forEach(brandId => {
764                        selectedValues['pa_brand_id=' + brandId] = true;
765                        setFinalStringToSessionStorage(selectedValues);
766                        let brandAlphabetElement = document.getElementById(`pa_brand_id-${brandId}-alphabet`);
767                        let brandNameEn = brandAlphabetElement?.getAttribute('data-brand-name-en');
768                        let brandNameJp = brandAlphabetElement?.getAttribute('data-brand-name-jp');
769                        updateFilterBlock('pa_brand_id=' + brandId, brandNameEn, brandNameJp, true);
770                        updateTopFilterBlock('pa_brand_id=' + brandId, true);
771                        updateItemListAlphabet('brand', selectedValues);
772                    });
773                    break;
774                case 'pa_girlboy':
775                    let girlboyIds = value.split(',');
776                    girlboyIds.forEach(girlboyId => {
777                        selectedValues['pa_girlboy=' + girlboyId] = true;
778                        setFinalStringToSessionStorage(selectedValues);
779                        updateTopFilterBlock('pa_girlboy=' + girlboyId, true);
780                    });
781                    break;
782                case 'pa_smell-type':
783                    let smelltype = document.querySelector('#search_smell_type')?.value;
784                    selectedValues['pa_smelltype=' + smelltype] = true;
785                    setFinalStringToSessionStorage(selectedValues);
786                    updateTopFilterBlock('pa_smelltype=' + smelltype, true);
787                    break;
788                case 'pa_smelltype':
789                    let smelltypes = value.split(',');
790                    smelltypes.forEach(smelltype => {
791                        selectedValues['pa_smelltype=' + smelltype] = true;
792                        setFinalStringToSessionStorage(selectedValues);
793                        updateTopFilterBlock('pa_smelltype=' + smelltype, true);
794                    });
795                    break;
796                case 'pa_香料':
797                    let fragrancespice = document.querySelector('#search_fragrance')?.value;
798                    selectedValues['pa_fragrancespice=' + fragrancespice] = true;
799                    setFinalStringToSessionStorage(selectedValues);
800                    let fragrancespiceAlphabetElement = document.getElementById(`pa_fragrancespice-${fragrancespice}-alphabet`);
801                    let fragrancespiceName = fragrancespiceAlphabetElement?.getAttribute('data-fragrancespice-name');
802                    updateFilterBlock('pa_fragrancespice=' + fragrancespice, fragrancespiceName, '', true);
803                    updateTopFilterBlock('pa_fragrancespice=' + fragrancespice, true);
804                    updateItemListAlphabet('fragrancespice', selectedValues);
805                    break;
806                case 'pa_fragrancespice':
807                    let fragrancespices = value.split(',');
808                    fragrancespices.forEach(fragrancespice => {
809                        selectedValues['pa_fragrancespice=' + fragrancespice] = true;
810                        setFinalStringToSessionStorage(selectedValues);
811                        let fragrancespiceAlphabetElement = document.getElementById(`pa_fragrancespice-${fragrancespice}-alphabet`);
812                        let fragrancespiceName = fragrancespiceAlphabetElement?.getAttribute('data-fragrancespice-name');
813                        updateFilterBlock('pa_fragrancespice=' + fragrancespice, fragrancespiceName, '', true);
814                        updateTopFilterBlock('pa_fragrancespice=' + fragrancespice, true);
815                        updateItemListAlphabet('fragrancespice', selectedValues);
816                    });
817                    break;
818                case 'pa_image':
819                    let images = value.split(',');
820                    images.forEach(image => {
821                        selectedValues['pa_image=' + image] = true;
822                        setFinalStringToSessionStorage(selectedValues);
823                        updateTopFilterBlock('pa_image=' + image, true);
824                    });
825                    break;
826                case 'pa_feeling':
827                    let feelings = value.split(',');
828                    feelings.forEach(feeling => {
829                        selectedValues['pa_feeling=' + feeling] = true;
830                        setFinalStringToSessionStorage(selectedValues);
831                        updateTopFilterBlock('pa_feeling=' + feeling, true);
832                    });
833                    break;
834                case 'pa_scene':
835                    let scenes = value.split(',');
836                    scenes.forEach(scene => {
837                        selectedValues['pa_scene=' + scene] = true;
838                        setFinalStringToSessionStorage(selectedValues);
839                        updateTopFilterBlock('pa_scene=' + scene, true);
840                    });
841                    break;
842                case 'pa_seasons':
843                    let seasons = value.split(',');
844                    seasons.forEach(season => {
845                        selectedValues['pa_seasons=' + season] = true;
846                        setFinalStringToSessionStorage(selectedValues);
847                        updateTopFilterBlock('pa_seasons=' + season, true);
848                    });
849                    break;
850                case 'selected_labels':
851                    let selectedLabels = value.split(',');
852                    let selectWeightElements = document.querySelectorAll('#filter-block-weight input[type="checkbox"]');
853                    selectWeightElements.forEach(element => {
854                        element.checked = selectedLabels.includes(element.value);
855                    });
856                    updateSelectedLabelsSessionStorage();
857                    break;
858                case 'price2':
859                case 'price3':
860                case 'price5':
861                case 'price6':
862                case 'price7':
863                    let prices = value.split('-');
864                    document.getElementById('price-min').value = prices[0];
865                    document.getElementById('price-max').value = prices[1];
866                    break;
867                case 'Sort':
868                    let sort = value.split(' ');
869                    sessionStorage.setItem('sort', value);
870                    updateSortBlock(sort[0], sort[1]);
871                    break;
872                case 'sort-by':
873                    let sortName = urlParams.get('sort-by');
874                    let sortType = urlParams.get('sort-type') ?? 'desc';
875                    let sortValue = sortName + ' ' + sortType;
876                    sessionStorage.setItem('sort', sortValue);
877                    updateSortBlock(sortName, sortType);
878                    break;
879                case 'Page':
880                    initPage = parseInt(value);
881                    break;
882                default:
883                    break;
884            }
885        }
886    }
887
888    return hasSearchQuery;
889}
890
891function sendAjaxRequest(filterValue,keywordValue) {
892    var param1 = '';
893    if (filterValue && keywordValue) {
894        param1 = filterValue + '&' + keywordValue;
895    } else if (filterValue || keywordValue) {
896        param1 = filterValue || keywordValue;
897    }
898    var bodyData = {
899        param1: param1,
900    };
901
902    if (hasFilterPrice()) {
903        bodyData.param1 = addPriceFilter(bodyData.param1);
904    }
905
906    var requestOptions = {
907        method: 'POST',
908        headers: {
909            'Content-Type': 'application/json'
910        },
911        body: JSON.stringify(bodyData)
912    };
913    fetch('/wp-json/custom/v1/fetch-products/', requestOptions)
914        .then(response => response.json())
915        .then(data => {
916            totalCount = data.code && data.code === 'no_data' ? 0 : data.HitCount;
917            document.querySelector('.filter-action-button span').textContent = '(' + totalCount.toLocaleStr
917ing() + '件)';
918        })
919        .catch(error => console.error('Fetch error: ' + error));
920}
921
922/**
923 * 
924 * @param {filterType} string - The type of action to perform. Must be either 'brand' or 'fragrancespice'.
925 * @param {selectedValues} object - selected values
926 */
927function updateItemListAlphabet(filterType, selectedValues) {
928    var selectedItemIds = [];
929    for (var key in selectedValues) {
930        if ((filterType === 'brand' && key.indexOf(`pa_brand_id`) === 0) || (filterType === 'fragrancespice' && key.indexOf(`pa_fragrancespice`) === 0)) {
931            var id = key.split("=")[1];
932            selectedItemIds.push(id);
933        }
934    }
935
936    // Check to selected item on modal, otherwise uncheck
937    var itemListElement = document.querySelectorAll(`.list-${filterType}-alphabet`);
938    itemListElement.forEach(element => {
939        element.checked = selectedItemIds.includes(element.getAttribute(`data-${filterType}-id`));
940    });
941}
942
943function setFinalStringToSessionStorage(selectedValues) {
944    var result = [];
945    for (var key in selectedValues) {
946        if (key === 'pa_girlboy' || key === 'pa_brand') {
947            result.push(selectedValues[key]);
948        } else {
949            result.push(key);
950        }
951    }
952
953    const processed = result.reduce((acc, curr) => {
954        const [key, value] = curr.split('=');
955        if (!acc[key]) {
956            acc[key] = [];
957        }
958        acc[key].push(value);
959        return acc;
960    }, {});
961
962    const result2 = Object.entries(processed).map(([key, values]) => {
963        return `${key}=${values.sort((a, b) => a - b).join(',')}`;
964    });
965
966    const keysArray = Object.keys(processed);
967    const filterFields = keysArray.join('+');
968    const finalString = `${result2.join('&')}&FilterFields=%2b${encodeURIComponent(filterFields)}`;
969
970    sessionStorage.setItem('finalString', finalString);
971    return finalString;
972}
973
974function getSearchResult(selectedValues) {
975    let finalString = setFinalStringToSessionStorage(selectedValues);
976
977    const searchKeywords = sessionStorage.getItem('searchKeywords');
978
979    clearTimeout(debounceTimerCheckbox);
980    debounceTimerCheckbox = setTimeout(function() {
981        if(searchKeywords && searchKeywords.length > 0) {
982            if (finalString !== '&FilterFields=%2b') {
983                sendAjaxRequest(finalString, searchKeywords);
984            }
985            else {
986                sendAjaxRequest('', searchKeywords);
987            }
988            
989        } else {
990            if (finalString !== '&FilterFields=%2b') {
991                sendAjaxRequest(finalString,'');
992            }
993            else{
994                sendAjaxRequest('','');
995            }
996        }
997    }, 1000);
998}
999
1000function updateSelectedFilterField(selectedValues) {
1001    const filterFieldsMap = {
1002        "pa_brand_id": "#brand",
1003        "pa_girlboy": "#gender",
1004        "pa_smelltype": "#smelltype",
1005        "pa_fragrancespice": "#fragrancespice",
1006        "pa_image": "#image",
1007        "pa_feeling": "#feeling",
1008        "pa_scene": "#scene",
1009        "pa_seasons": "#seasons"
1010    };
1011    for (const [selectedKey, filterFieldSelector] of Object.entries(filterFieldsMap)) {
1012        const filterField = document.querySelector(filterFieldSelector);
1013        if (Object.keys(selectedValues).some(key => key.startsWith(selectedKey))) {
1014            if (filterField && !filterField.classList.contains('active')) {
1015                filterField.classList.add('active');
1016            }
1017        } else if (filterField && filterField.classList.contains('active')) {
1018            filterField.classList.remove('active');
1019        }
1020    }
1021}
1022
1023function handleInputChange(element, selectedValues) {
1024    var filterType = element.getAttribute('name');
1025    var paramName = 'pa_' + filterType;
1026    var selectedValue = element.value;
1027
1028
1029    if (element.getAttribute('type') === 'radio' && element.checked) {
1030        if (selectedValues[paramName]) {
1031            delete selectedValues[paramName];
1032        }
1033        selectedValues[paramName] = selectedValue;
1034    } else if (element.getAttribute('type') === 'checkbox') {
1035        
1036        var brandNameElement = element.parentNode.querySelector('label .name');
1037        var smallTextElement = element.parentNode.querySelector('label .small');
1038    
1039        if (brandNameElement) {
1040            var brandName = brandNameElement.textContent;
1041            var smallText = smallTextElement?.textContent ?? '';
1042    
1043            if (element.checked) {
1044                updateFilterBlock(selectedValue, brandName, smallText, true);
1045                selectedValues[selectedValue] = true;
1046            
1047            } else {
1048                updateFilterBlock(selectedValue, brandName, smallText, false);
1049                delete selectedValues[selectedValue];
1050                
1051            }
1052        } else {
1053            if (element.checked) {
1054                selectedValues[selectedValue] = true;
1055                
1056            } else {
1057                delete selectedValues[selectedValue];
1058              
1059            }
1060        }
1061        
1062    }
1063
1064    if (filterType === 'brand' || filterType === 'fragrancespice') {
1065        updateItemListAlphabet(filterType, selectedValues);
1066    }
1067    updateSelectedFilterField(selectedValues);
1068
1069    getSearchResult(selectedValues);
1070}
1071
1072function updateSelectedValues(selectedValue, checked) {
1073    if (checked) {
1074        selectedValues[selectedValue] = checked;
1075    } else {
1076        delete selectedValues[selectedValue];
1077    }
1078}
1079
1080function handleSelectBrand(type, selectedValue, brandName, smallText, checked) {
1081    updateSelectedValues(selectedValue, checked);
1082    if (type == 'search-result'){
1083        updateFilterBlock(selectedValue, brandName, smallText, checked);
1084    }
1085    updateTopFilterBlock(selectedValue, checked);
1086    if (selectedValue.startsWith('pa_brand_id')) {
1087        updateItemListAlphabet('brand', selectedValues);
1088    } else if (selectedValue.startsWith('pa_fragrancespice')) {
1089        updateItemListAlphabet('fragrancespice', selectedValues);
1090    }
1091    getSearchResult(selectedValues);
1092    updateSelectedFilterField(selectedValues);
1093}
1094
1095function updateTopFilterBlock(selectedValue, checked) {
1096    var checkbox = document.getElementById(selectedValue);
1097    if (checkbox) {
1098        checkbox.checked = checked;
1099    }
1100}
1101
1102/**
1103 * 
1104 * @param {filterType} string - The type of action to perform. Must be either 'brand' or 'fragrancespice'.
1105 */
1106function handleConfirmSelectedOnModal(filterType) {
1107    var selectedItemOnModal = document.querySelectorAll(`#modal-${filterType}-list .list-${filterType}-alphabet:checked`);
1108
1109    // Update selected item
1110    let selectedBrandValues = [];
1111    selectedItemOnModal.forEach(element => {
1112        let selectedValue = element.value;
1113        selectedValues[selectedValue] = true;
1114        selectedBrandValues.push(selectedValue);
1115        if (filterType === 'brand') {
1116            updateFilterBlock(selectedValue, element.getAttribute('data-brand-name-en'), element.getAttribute('data-brand-name-jp'), true);
1117        } else {
1118            updateFilterBlock(selectedValue, element.getAttribute('data-fragrancespice-name'), null, true);
1119        }
1120        updateTopFilterBlock(selectedValue, true);
1121    });
1122
1123    // Remove unselected item
1124    for (var selectedValue in selectedValues) {
1125        if (selectedValue.startsWith(`pa_${filterType}_id`) && !selectedBrandValues.includes(selectedValue)) {
1126            delete selectedValues[selectedValue];
1127            updateFilterBlock(selectedValue, null, null, false, true);
1128            updateTopFilterBlock(selectedValue, false);
1129        }
1130    }
1131    updateSelectedFilterField(selectedValues);
1132
1133    getSearchResult(selectedValues);
1134}
1135
1136function removeFilter(selectedValue, brandName, smallText = null) {
1137    updateSelectedValues(selectedValue, false);
1138    updateFilterBlock(selectedValue, brandName, smallText, false);
1139    updateTopFilterBlock(selectedValue, false);
1140    if (selectedValue.startsWith('pa_brand_id')) {
1141        updateItemListAlphabet('brand', selectedValues);
1142    } else if (selectedValue.startsWith('pa_fragrancespice')) {
1143        updateItemListAlphabet('fragrancespice', selectedValues);
1144    }
1145    getSearchResult(selectedValues);
1146    updateSelectedFilterField(selectedValues);
1147}
1148
1149function encodeHtml(str) {
1150    return str.replace(/&/g, '&amp;')
1151              .replace(/</g, '&lt;')
1152              .replace(/>/g, '&gt;')
1153              .replace(/"/g, '&quot;')
1154              .replace(/'/g, '&#39;');
1155}
1156
1157/** 
1158 * @param type - type of filter block
1159 *  + search-result (render filter block on search result area)
1160 *  + selected (render filter block on selected brand area)
1161*/
1162function createFilterBlockHtml(type, id, brandName, smallText, checked) {
1163    return `
1164    <label for="${id}" class="filter-control grid-item" id="${encodeHtml(id)}-block">
1165        <input type="checkbox" ${checked ? 'checked' : ''} name="brand" id="${encodeHtml(id)}-${encodeHtml(type)}" value="${encodeHtml(id)}" data-value="${encodeHtml(id)}" onchange="handleSelectBrand(&quot;${encodeHtml(type)}&quot;, &quot;${encodeHtml(id)}&quot;, &quot;${encodeHtml(brandName)}&quot;, &quot;${smallText ? encodeHtml(smallText) : smallText}&quot;, this.checked)">
1166        <span class="check-mark" style="position: relative; z-index: 0;">
1167            <svg class="uncheck" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
1168                <path d="M2.3 17.5C1.8 17.5 1.375 17.325 1.025 16.975C0.675 16.625 0.5 16.2 0.5 15.7V2.3C0.5 1.8 0.675 1.375 1.025 1.025C1.375 0.675 1.8 0.5 2.3 0.5H15.7C16.2 
11680.5 16.625 0.675 16.975 1.025C17.325 1.375 17.5 1.8 17.5 2.3V15.7C17.5 16.2 17.325 16.625 16.975 16.975C16.625 17.325 16.2 17.5 15.7 17.5H2.3ZM2.3 16H15.7C15.7667 16 15.8333 15.9667 15.9 15.9C15.9667 15.8333 16 15.7667 16 15.7V2.3C16 2.23333 15.9667 2.16667 15.9 2.1C15.8333 2.03333 15.7667 2 15.7 2H2.3C2.23333 2 2.16667 2.03333 2.1 2.1C2.03333 2.16667 2 2.23333 2 2.3V15.7C2 15.7667 2.03333 15.8333 2.1 15.9C2.16667 15.9667 2.23333 16 2.3 16Z" fill="#B2C2C1"/>
1169            </svg>
1170            <svg class="checked" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
1171                <path d="M7.6 12.85L14.325 6.125L13.275 5.075L7.6 10.75L4.75 7.9L3.7 8.95L7.6 12.85ZM2.3 17.5C1.8 17.5 1.375 17.325 1.025 16.975C0.675 16.625 0.5 16.2 0.5 15.7V2.3C0.5 1.8 0.675 1.375 1.025 1.025C1.375 0.675 1.8 0.5 2.3 0.5H15.7C16.2 0.5 16.625 0.675 16.975 1.025C17.325 1.375 17.5 1.8 17.5 2.3V15.7C17.5 16.2 17.325 16.625 16.975 16.975C16.625 17.325 16.2 17.5 15.7 17.5H2.3Z" fill="#21D4C8"/>
1172            </svg>
1173        </span>
1174        ${type == 'search-result' ? '<span class="filter-bg"></span>' : ''}
1175        <span class="text-content" style="position: relative; z-index: 0;"><span class="name">${brandName}</span>${smallText ? `<br><span class="small m-top">${smallText}</span>` : ''}</span>
1176        ${type == 'selected' ? '<span class="remove-filter" onclick="removeFilter(&quot;' + encodeHtml(id) + '&quot;, &quot;' + encodeHtml(brandName) + '&quot;' + (smallText ? (', &quot;' + encodeHtml(smallText) + '&quot;') : '') + ')">×</span>' : ''}
1177    </label>`;
1178}
1179
1180function updateFilterBlock(id, brandName, smallText, checked) {
1181    var existingBlock = document.getElementById(`${id}-block`);
1182    var filterBlockGrid;
1183    var gridClass;
1184
1185    switch (true) {
1186        case id.includes('pa_brand'):
1187            gridClass = '.filter-block-grid.brand-unique-class';
1188            break;
1189        case id.includes('pa_fragrancespice'):
1190            gridClass = '.filter-block-grid.brand-unique-class-2';
1191            break;
1192        // Thêm các case khác nếu cần
1193        default:
1194            return; // Trường hợp mặc định, thoát khỏi hàm nếu không có grid phù hợp
1195    }
1196    filterBlockGrid = document.querySelector(gridClass);
1197    
1198    if (!filterBlockGrid) return; // Kiểm tra nếu không tìm thấy grid tương ứng, thoát khỏi hàm
1199
1200    if (filterBlockGrid.contains(existingBlock)) {
1201        if (!checked) {
1202            filterBlockGrid.removeChild(existingBlock);
1203        } else {
1204            var checkboxRefId = CSS.escape(`${id}-selected`);
1205            existingBlock.querySelector(`#${checkboxRefId}`).checked = true;
1206        }
1207    } else {
1208        if (checked) {
1209            filterBlockGrid.insertAdjacentHTML('beforeend', createFilterBlockHtml('selected', id, brandName, smallText, true));
1210        }
1211    }
1212}
1213
1214function toggleFooter(isShow) {
1215    document.querySelector('.bottom-page-wrap').style.display = isShow ? 'block' : '';
1216    ignoreScroll = true;
1217}
1218
1219function showFooter(){
1220    toggleFooter(true);
1221}
1222
1223function hideFooter(){
1224    toggleFooter(false);
1225}
1226
1227var filterFields = document.querySelectorAll('.filter-field');
1228
1229// Duyệt qua từng phần tử span
1230filterFields.forEach(function(field) {
1231    // Lắng nghe sự kiện click cho mỗi phần tử
1232    field.addEventListener('click', function() {
1233        // Lấy id của phần tử span được click
1234        var id = this.getAttribute('id');
1235
1236        // Tạo id của khối accordion2 filter-block tương ứng
1237        var blockId = id + '-filter-block';
1238
1239        // Lấy phần tử khối tương ứng
1240        var block = document.getElementById(blockId);
1241
1242        // Kiểm tra nếu khối đã được hiển thị, thì ẩn nó đi và ngược lại
1243        if (block.classList.contains('hidden')) {
1244            block.classList.remove('hidden');
1245
1246            // Expand filter block
1247            block.getElementsByClassName('toggle filter-block-toggle')[0].checked = true;
1248
1249            // Show more than 4 filter block
1250            block.closest('.filter-container').classList.add('toggle-filter');
1251
1252            // Scroll to expanded filter block
1253            var filterContainer = document.getElementById('filter_form');
1254            if (!filterContainer.classList.contains('section-header-sticky')) {
1255                var targetElement = document.querySelector(`#${id}-filter-block`);
1256                var headerHeight = document.querySelector('.navbar.navbar-fixed-top').offsetHeight;
1257
1258                var elementPosition = targetElement.offsetTop + window.scrollY;
1259                var offsetPosition = elementPosition - headerHeight;
1260
1261                window.scrollTo({
1262                    top: offsetPosition
1263                });
1264            } else {
1265                goToView(`#${id}-filter-block`);
1266            }
1267        } else {
1268            block.classList.add('hidden');
1269        }
1270
1271        // Highlight selected filter field
1272        if (this.classList.contains('show-filter')) {
1273            this.classList.remove('show-filter');
1274        } else {
1275            this.classList.add('show-filter');
1276            document.querySelector('#filter-action').classList.remove('filter-action-hidden');
1277        }
1278
1279        ignoreScroll = true;
1280    });
vendor: 3 bytes, line 1281
1281});

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.