1const checkImageLoading = () => { 2 document.querySelectorAll('.card-product_img')?.forEach(card => { 3 card.dataset.loadingInited = 'true'; 4 5 const img = card.querySelector('img'); 6 if (!img) return; 7 8 card.classList.add('loading'); 9 img.addEventListener('load', () => { 10 card.classList.remove('loading'); 11 }); 12 13 if (img.complete && img.naturalWidth !== 0) { 14 card.classList.remove('loading'); 15 } 16 }); 17} 18 19jQuery(document).ready(function ($) { 20 21 /* PROUCT FILTER BLOCK */ 22 let tabs = $('.s-products_content .tab-filter'); 23 tabs.on('click', function (e) { 24 e.preventDefault(); 25 let current_tab = this; 26 tabs.each(function () { 27 if (this !== current_tab) { 28 $(this).removeClass('active'); 29 } 30 }); 31 $(current_tab).addClass('active'); 32 $('.tab-content').addClass('preload'); 33 let tag_id = $(this).attr('data-tag-id'); 34 let count = $(this).attr('data-count'); 35 $.ajax({ 36 url: '/wp-json/artilab_filter_products/v1/product-filter', 37 method: 'GET', 38 data: { 39 tag_id: tag_id, 40 count: count, 41 }, 42 success: function (response) { 43 if (response) { 44 $('.tab-content .s-products_wrap').html(response); 45 checkImageLoading(); 46 $('.tab-content ').removeClass('preload'); 47 } 48 49 }, 50 error: function (err) { 51 console.error('Error:', err); 52 } 53 }); 54 55 }); 56 57 /* SHOP PAGE*/ 58 if ((pageInfo.isShopPage || pageInfo.isProductCat) && !pageInfo.isSearch) { 59 const debouncedSendAjax = debounce(sendAjax, 300); 60 let inputs = $('.catalog-filter input'); 61 let select_list = $('.select_list__item'); 62 let ajaxurl = ajax_object.ajaxurl; 63 64 // Apply filters from URL on page load 65 applyFiltersFromUrl(); 66 67 inputs.on('change input', function () { 68 debouncedSendAjax(); 69 }); 70 select_list.on('click', function () { 71 $('.select_title__text').data('type', $(this).data('type')); 72 sendAjax(); 73 }); 74 $('.input-min, .input-max').on('change', function () { 75 sendAjax(); 76 }); 77 updatePaginationLinks(); 78 79 // Handle pagination clicks 80 $(document).on('click', '.pagination_wrap a', function (e) { 81 e.preventDefault(); 82 let href = $(this).attr('href'); 83 if (!href || href === '#') return false; 84 85 // Try to extract page number from different URL formats (query param or pretty path) 86 let pageNumber = 1; 87 try { 88 let url = new URL(href, window.location.origin); 89 if (url.searchParams.has('paged')) { 90 pageNumber = parseInt(url.searchParams.get('paged'), 10) || 1; 91 } else if (url.searchParams.has('page')) { 92 pageNumber = parseInt(url.searchParams.get('page'), 10) || 1; 93 } else { 94 let pathMatch = url.pathname.match(/\/page\/(\d+)\/?/); 95 pageNumber = pathMatch ? parseInt(pathMatch[1], 10) : 1; 96 } 97 } catch (err) { 98 // Fallback: regex on href string 99 let pageMatch = href.match(/(?:[?&]paged=|\/page\/)(\d+)/); 100 pageNumber = pageMatch ? parseInt(pageMatch[1], 10) : 1; 101 } 102 103 sendAjax(pageNumber, true); 104 return false; 105 }); 106 } 107 108 function applyFiltersFromUrl() { 109 const urlParams = new URLSearchParams(window.location.search); 110 const sortFromUrl = urlParams.get('filter_sort') || urlParams.get('sort') || 'popularity'; 111 const filterSort = sortFromUrl === 'default' ? 'popularity' : sortFromUrl; 112
113 $('.select_title__text').data('type', filterSort); 114 115 const sortTexts = { 116 'lp': 'за зÑоÑÑаннÑм ÑÑни', 117 'hp': 'за ÑпаданнÑм ÑÑни', 118 'date': 'за новизноÑ', 119 'rating': 'за ÑейÑингом', 120 'popularity': 'за попÑлÑÑнÑÑÑÑ', 121 'default': 'за замовÑÑваннÑм' 122 }; 123 124 if (sortTexts[filterSort]) { 125 $('.select_title__text').text(sortTexts[filterSort]); 126 } 127 // Apply categories filter 128 const filterCategories = urlParams.get('filter_categories'); 129 if (filterCategories) { 130 const categories = filterCategories.split('_'); 131 categories.forEach(cat => { 132 $('.catalog-filter .filter-categories input[value="' + cat + '"]').prop('checked', true); 133 }); 134 } 135 136 // Apply tags filter 137 const filterTags = urlParams.get('filter_tags'); 138 if (filterTags) { 139 const tags = filterTags.split('_');
140 tags.forEach(tag => { 141 $('.catalog-filter .filter-tags input[value="' + tag + '"]').prop('checked', true); 142 }); 143 } 144 145 // Apply price range 146 const rangeMin = urlParams.get('range_min'); 147 const rangeMax = urlParams.get('range_max'); 148 if (rangeMin) { 149 $('.input-min').val(rangeMin); 150 $('.range-input .range-min').val(rangeMin); 151 } 152 if (rangeMax) { 153 $('.input-max').val(rangeMax); 154 $('.range-input .range-max').val(rangeMax); 155 } 156 157 // Update range slider visually if exists 158 if (rangeMin || rangeMax) { 159 updateRangeSlider(); 160 } 161 162 // Update filter clear button state 163 switchFiltering(); 164 } 165 166 function updateRangeSlider() { 167 const min = parseFloat($('.range-min').attr('min')) || 0; 168 const max = parseFloat($('.range-max').attr('max')) || 100; 169 const minVal = parseFloat($('.range-min').val()) || min; 170 const maxVal = parseFloat($('.range-max').val()) || max; 171 172 const minPercent = ((minVal - min) / (max - min)) * 100; 173 const maxPercent = ((max - maxVal) / (max - min)) * 100; 174 175 $('.slider .progress').css({ 176 'left': minPercent + '%', 177 'right': maxPercent + '%' 178 }); 179 } 180 181 function getFilterParams() { 182 let params = {}; 183 let selectedSort = $('.select_title__text').data('type') || 'popularity'; 184 selectedSort = selectedSort === 'default' ? 'popularity' : selectedSort; 185 let checked_cat = []; 186 $('.catalog-filter .filter-categories input[type="checkbox"]:checked').each(function () { 187 checked_cat.push($(this).val()); 188 }); 189 190 let checked_tags = []; 191 $('.catalog-filter .filter-tags input[type="checkbox"]:checked').each(function () { 192 checked_tags.push($(this).val()); 193 }); 194 195 params.categories = checked_cat; 196 params.tags = checked_tags; 197 params.range_min = $('.input-min').val(); 198 params.range_max = $('.input-max').val(); 199 params.sort = selectedSort; 200 return params; 201 } 202 203 function buildQueryString(params) { 204 const queryParts = []; 205 206 const keyOrder = ['categories', 'tags', 'range_min', 'range_max', 'sort']; 207 208 keyOrder.forEach(key => { 209 const value = params[key]; 210 211 if (!value || (Array.isArray(value) && value.length === 0)) return; 212 213 if (Array.isArray(value)) { 214 // Use underscore-separated values to avoid conflicts 215 queryParts.push(`filter_${key}=${encodeURIComponent(value.join('_'))}`); 216 } else { 217 if (key === 'sort') { 218 queryParts.push(`filter_${key}=${encodeURIComponent(value)}`); 219 } else { 220 queryParts.push(`${key}=${encodeURIComponent(value)}`); 221 } 222 } 223 }); 224 225 return queryParts.join('&'); 226 } 227 228 function updatePaginationLinks() { 229 const queryString = buildQueryString(getFilterParams()); 230 let currentPageUrl = null; 231 const baseHref = window.wooShopUrl || '/shop'; 232 233 $('.pagination_wrap a').each(function () { 234 let $link = $(this); 235 let href = $link.attr('href'); 236 237 if (!href || href === '#') return; 238 239 // Parse page number and detect original style 240 let pageNumber = 1; 241 let originalHasPagedParam = false; 242 try { 243 let url = new URL(href, window.location.origin); 244 if (url.searchParams.has('paged')) { 245 pageNumber = parseInt(url.searchParams.get('paged'), 10) || 1; 246 originalHasPagedParam = true; 247 } else if (url.searchParams.has('page')) { 248 pageNumber = parseInt(url.searchParams.get('page'), 10) || 1; 249 originalHasPagedParam = true; 250 } else { 251 let pathMatch = url.pathname.match(/\/page\/(\d+)\/?/); 252 pageNumber = pathMatch ? parseInt(pathMatch[1], 10) : 1; 253 } 254 } catch (err) { 255 let pageMatch = href.match(/(?:[?&]paged=|\/page\/)(\d+)/); 256 pageNumber = pageMatch ? parseInt(pageMatch[1], 10) : 1; 257 } 258 259 // Build new href preserving pagination style 260 let newHref = ''; 261 const shopBase = (baseHref || (window.wooShopUrl || '/shop')).replace(/\/$/, ''); 262 if (originalHasPagedParam) { 263 // Keep paged as query parameter 264 let urlBase = href.split('?')[0]; 265 newHref = urlBase + (urlBase.indexOf('?') === -1 ? '?' : '&') + 'paged=' + pageNumber; 266 if (queryString) newHref += '&' + queryString; 267 } else { 268 // Use pretty /page/X/ structure 269 if (pageNumber > 1) { 270 newHref = shopBase + '/page/' + pageNumber + '/'; 271 } else { 272 newHref = shopBase + '/'; 273 } 274 if (queryString) newHref += '?' + queryString; 275 } 276 277 $link.attr('href', newHref); 278 }); 279 280 // Update current page URL for browser history 281 const $currentSpan = $('.pagination_wrap span.current'); 282 if ($currentSpan.length) { 283 const currentPageNumber = parseInt($currentSpan.text(), 10); 284 285 // Remove trailing slash from baseHref to avoid double slash 286 const cleanBaseHref = baseHref.replace(/\/$/, ''); 287 288 if (currentPageNumber > 1) { 289 currentPageUrl = cleanBaseHref + '/page/' + currentPageNumber + '/'; 290 } else { 291 currentPageUrl = cleanBaseHref + '/'; 292 } 293 294 if (queryString) { 295 currentPageUrl += '?' + queryString; 296 } 297 } else { 298 currentPageUrl = baseHref; 299 if (queryString) { 300 currentPageUrl += '?' + queryString; 301 } 302 } 303 304 return currentPageUrl; 305 } 306 307 function scrollToCatalogTop() { 308 const $target = $('.catalog_head').first(); 309 if (!$target.length) return; 310 311 const headerOffset = $('.header_top').outerHeight() || 0; 312 const top = Math.max($target.offset().top - headerOffset - 20, 0); 313 314 $('html, body').stop(true).animate({ scrollTop: top }, 400); 315 } 316 317 function sendAjax(page = 1, scrollAfterLoad = false) { 318 let ajaxurl = ajax_object.ajaxurl; 319 let params = getFilterParams(); 320 let nonce = ajax_object.nonce; 321 $('.catalog_main').addClass('preload'); 322 $.ajax({ 323 url: ajaxurl, 324 type: 'POST', 325 data: { 326 action: 'filter_products_adv', 327 categories: params, 328 paged: page, 329 security: nonce 330 }, 331 success: function (response) { 332 $('.pag-box').html(response.data.pagination); 333 $('.catalog_main').html(response.data.products); 334 $('.title-page_count').html(response.data.count);
335 $('.title-page h1').text('Ðагазин'); 336 switchFiltering(); 337 updatePaginationLinks(); // Update pagination links with filter params 338 changeUrl(); 339 checkImageLoading(); 340 $('.breadcrumbs_list li:nth-child(3)').remove(); 341 $('.catalog_main').removeClass('preload'); 342 343 if (scrollAfterLoad) { 344 scrollToCatalogTop(); 345 } 346 } 347 }); 348 } 349 350 /* Open cart PopUp */ 351 // $('.open_cart_popup').on('click', function (e) { 352 // e.preventDefault(); 353 // $('div.cc-compass').trigger('click'); 354 // }); 355 $('#no_phone_confirmation').on('change', function () { 356 if ($('#no_phone_confirmation').is(':checked')) { 357 $('#order_comments').val('Ðе ÑелеÑонÑваÑи Ð¼ÐµÐ½Ñ Ð´Ð»Ñ Ð¿ÑдÑвеÑÐ´Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð¼Ð¾Ð²Ð»ÐµÐ½Ð½Ñ'); 358 } else { 359 $('#order_comments').val(''); 360 } 361 }); 362 363 jQuery(function ($) { 364 const target = document.body; 365 366 const observer = new MutationObserver(function (mutationsList) { 367 mutationsList.forEach(function (mutation) { 368 mutation.addedNodes.forEach(function (node) { 369 if (node.nodeType === 1 && node.classList.contains('added_to_cart')) { 370 node.remove(); 371 } 372 }); 373 }); 374 }); 375 376 observer.observe(target, { 377 childList: true, 378 subtree: true 379 }); 380 }); 381 382 $('.filter-clear').on('click', function () { 383 $('.catalog-filter input[type="checkbox"]').prop('checked', false); 384 $('.select_title__text').text('за замовÑÑваннÑм').data('type', 'popularity'); 385 $('.input-min').val(0); 386 $('.input-max').val($('.range-max').attr('max')); 387 $('.slider .progress').attr('style', "left: 0; right: 0"); 388 $('.range-input .range-min').val(0); 389 $('.range-input .range-max').val($('.range-max').attr('max')); 390 sendAjax(); 391 }); 392 393 function changeUrl() { 394 const newUrl = updatePaginationLinks(); 395 window.history.replaceState({}, document.title, newUrl); 396 } 397 398 function switchFiltering() { 399 let inputs = $('.catalog-filter input'); 400 let is_checkbox = false; 401 let is_selected = false; 402 let range = false; 403 404 inputs.each(function (index, element) { 405 if ($(element).is(':checkbox') && $(element).is(':checked')) { 406 is_checkbox = true; 407 } 408 }); 409 const selectedSort = $('.select_title__text').data('type'); 410 if (selectedSort && selectedSort !== 'default' && selectedSort !== 'popularity') { 411 is_selected = true; 412 } 413 414 if ( 415 parseFloat($('.input-min').val()) > 0 || 416 parseFloat($('.input-max').val()) < parseFloat($('.range-max').attr('max')) 417 ) { 418 range = true; 419 } 420 if (is_checkbox || is_selected || range) { 421 $('.filter-clear').addClass('active'); 422 } else { 423 $('.filter-clear').removeClass('active'); 424 } 425 } 426 427 function debounce(func, wait) { 428 let timeout; 429 return function () { 430 clearTimeout(timeout); 431 timeout = setTimeout(() => func.apply(this), wait); 432 }; 433 } 434 435 $(document).on('click', '.custom-ajax-add-to-cart', function (e) { 436 e.preventDefault(); 437 var $button = $(this); 438 $button.addClass('loading').prop('disabled', true); 439 var product_id = $button.data('product_id'); 440 var variation_id = $button.data('variation_id'); 441 var quantity = $button.data('quantity') || 1; 442 443 var variation = {}; 444 $.each($button[0].attributes, function () { 445 if (this.name.startsWith('data-attribute_')) { 446 var attrName = this.name.replace('data-', ''); 447 variation[attrName] = this.value; 448 } 449 }); 450 451 $.ajax({ 452 type: 'POST', 453 url: wc_add_to_cart_params.ajax_url, 454 data: { 455 action: 'variation_add_to_cart', 456 product_id: product_id, 457 variation_id: variation_id, 458 quantity: quantity, 459 variation: variation, 460 security: woocommerce_params.ajax_nonce || pageInfo.nonce 461 }, 462 success: function (response) { 463 if (response.success) { 464 if (response.data && response.data.fragments) { 465 $.each(response.data.fragments, function (key, value) { 466 $(key).replaceWith(value); 467 }); 468 } 469 $button.removeClass('loading').addClass('added').prop('disabled', false); 470 $(document.body).trigger('added_to_cart', [ 471 response.data.fragments, 472 response.data.cart_hash, 473 $button 474 ]); 475 } else { 476 $button.removeClass('loading').prop('disabled', false); 477 if (response.data && response.data.message) { 478 479 if (typeof wc_add_to_cart_params !== 'undefined' && 480 wc_add_to_cart_params.i18n_view_cart && 481 $('.woocommerce-error, .woocommerce-message').length === 0) { 482 483 $('body').append('<div class="woocommerce-error">' + response.data.message + '</div>'); 484 $(document.body).trigger('wc_fragments_refreshed'); 485 } else { 486 alert(response.data.message); 487 } 488 } 489 } 490 }, 491 error: function (jqXHR, textStatus, errorThrown) { 492 $button.removeClass('loading').prop('disabled', false); 493 console.error('AJAX error:', textStatus, errorThrown); 494 alert('Ðомилка пÑд ÑÐ°Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ ÑоваÑÑ Ð´Ð¾ коÑика: ' + errorThrown); 495 } 496 }); 497 }); 498 499 $(document).on('click', '.add_to_cart_button', function () { 500 if (typeof pageData !== 'undefined' && pageData && pageData.is_thankyoupage) { 501 setTimeout(function () { 502 $(document.body).trigger('added_to_cart'); 503 }, 1000); 504 } 505 }); 506});
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.