1(function () { 2 'use strict'; 3 4 window.migoShopFilters = (function () { 5 const FORM_SELECTOR = '[data-migo-shop-filter-form]'; 6 const CHECKBOX_SELECTOR = 'input[type="checkbox"]'; 7 const NUMBER_SELECTOR = 'input[type="number"]'; 8 const RANGE_NAME_REGEX = /^(.*)\[(min|max)\]$/; 9 const CHECKBOX_DEBOUNCE_MS = 200; 10 11 function parseNumericInputValue(input) { 12 const rawValue = (input.value || '').trim(); 13 14 if (rawValue === '') { 15 return null; 16 } 17 18 const normalizedValue = rawValue.replace(',', '.'); 19 const numericValue = Number(normalizedValue); 20 21 return Number.isFinite(numericValue) ? numericValue : null; 22 } 23 24 function validateRangeInputs(form) { 25 const inputs = Array.from(form.querySelectorAll(NUMBER_SELECTOR)); 26 const groups = new Map(); 27 28 inputs.forEach((input) => { 29 input.setCustomValidity(''); 30 31 const name = input.getAttribute('name') || ''; 32 const match = name.match(RANGE_NAME_REGEX); 33 34 if (!match) { 35 return; 36 } 37 38 const key = match[1]; 39 const bound = match[2]; 40 const group = groups.get(key) || {}; 41 42 group[bound] = input; 43 groups.set(key, group); 44 }); 45 46 for (const group of groups.values()) { 47 const minInput = group.min; 48 const maxInput = group.max; 49 50 if (!minInput || !maxInput) { 51 continue; 52 } 53 54 const minValue = parseNumericInputValue(minInput); 55 const maxValue = parseNumericInputValue(maxInput); 56 57 if (null === minValue || null === maxValue) { 58 continue; 59 } 60 61 if (minValue > maxValue) { 62 const message = 'Wartosc "do" nie moze byc mniejsza niz "od".'; 63 64 minInput.setCustomValidity(message); 65 maxInput.setCustomValidity(message); 66 maxInput.reportValidity(); 67 68 return false; 69 } 70 } 71 72 return true; 73 } 74 75 function bindForm(form) { 76 if (!form || form.dataset.migoShopFilterBound === '1') { 77 return; 78 } 79 80 let timerId = null; 81 82 const scheduleSubmit = (delay) => { 83 window.clearTimeout(timerId); 84 timerId = window.setTimeout(() => { 85 form.submit(); 86 }, delay); 87 }; 88 89 form.querySelectorAll(CHECKBOX_SELECTOR).forEach((input) => { 90 input.addEventListener('change', () => { 91 scheduleSubmit(CHECKBOX_DEBOUNCE_MS); 92 }); 93 }); 94 95 form.querySelectorAll(NUMBER_SELECTOR).forEach((input) => { 96 input.addEventListener('keydown', (event) => { 97 if (event.key !== 'Enter') { 98 return; 99 } 100 101 event.preventDefault(); 102 103 if (validateRangeInputs(form)) { 104 form.requestSubmit(); 105 } 106 }); 107 }); 108 109 form.addEventListener('submit', (event) => { 110 if (validateRangeInputs(form)) { 111 return; 112 } 113 114 event.preventDefault(); 115 }); 116 117 form.dataset.migoShopFilterBound = '1'; 118 } 119 120 function init() { 121 document.querySelectorAll(FORM_SELECTOR).forEach(bindForm); 122 } 123 124 return { 125 init, 126 }; 127 })(); 128 129 window.migoShopCart = (function () { 130 const BUTTON_SELECTOR = '.migo-shop-add-to-cart__button'; 131 const CART_SELECTOR = '.migo-shop-cart'; 132 const QTY_FORM_SELECTOR = '.migo-shop-cart__qty-form'; 133 const REMOVE_FORM_SELECTOR = '.migo-shop-cart__remove-form'; 134 const DELIVERY_SELECT_SELECTOR = '.migo-shop-cart__delivery-select'; 135 136 function getCartMessageContainer() { 137 return ( 138 document.querySelector('.migo-shop-cart') || 139 document.querySelector('.migo-shop-mini-cart') 140 ); 141 } 142 143 function getCartMessage(key) { 144 const container = getCartMessageContainer(); 145 146 return container ? container.dataset[key] : undefined; 147 } 148 149 function formatMessage(template, value) { 150 return (template || '').replace('%d', value); 151 } 152 153 function notifyIfTranslated(template, value, type) { 154 const message = formatMessage(getCartMessage(template), value); 155 156 if (message) { 157 showNotification(message, type); 158 } 159 } 160 161 function showNotification(message, type) { 162 const container = document.querySelector( 163 '.migo-shop-notification-container', 164 ); 165 if (container) { 166 container.remove(); 167 } 168 169 const el = document.createElement('div'); 170 el.className = 'migo-shop-notification-container'; 171 el.innerHTML = `<div class="migo-shop-notification migo-shop-notification--${type}">${message}</div>`; 172 document.body.appendChild(el); 173 174 setTimeout(() => { 175 if (el.parentNode) { 176 el.parentNode.removeChild(el); 177 } 178 }, 4000); 179 } 180 181 function updateMiniCartSummary(totalItems, grandTotalFormatted) { 182 const countEl = document.querySelector('.migo-shop-mini-cart__count'); 183 184 if (countEl && totalItems !== undefined) { 185 countEl.textContent = totalItems; 186 } 187 188 const totalEl = document.querySelector('.migo-shop-mini-cart__total'); 189 190 if (totalEl && grandTotalFormatted !== undefined) { 191 totalEl.textContent = grandTotalFormatted; 192 } 193 194 updateMiniCartAriaLabel(totalItems, grandTotalFormatted); 195 } 196 197 function updateMiniCartAriaLabel(totalItems, grandTotalFormatted) { 198 if (totalItems === undefined || grandTotalFormatted === undefined) return; 199 200 const container = document.querySelector('.migo-shop-mini-cart'); 201 if (!container) return; 202 203 const template = container.dataset.ariaLabelTemplate; 204 if (!template) return; 205 206 const labelEl = container.querySelector('.migo-shop-mini-cart__link') || container; 207 208 labelEl.setAttribute( 209 'aria-label', 210 template.replace('%s', totalItems).replace('%s', grandTotalFormatted), 211 ); 212 } 213 214 function updateCartTotals(subtotalFormatted, deliveryFormatted, grandTotalFormatted) { 215 const subtotalEl = document.querySelector('#js-cart-subtotal'); 216 if (subtotalEl && subtotalFormatted !== undefined) { 217 subtotalEl.textContent = subtotalFormatted; 218 } 219 220 const deliveryEl = document.querySelector('#js-cart-delivery-price'); 221 if (deliveryEl && deliveryFormatted !== undefined) { 222 deliveryEl.textContent = deliveryFormatted; 223 } 224 225 const grandTotalEl = document.querySelector('#js-cart-grand-total'); 226 if (grandTotalEl && grandTotalFormatted !== undefined) { 227 grandTotalEl.textContent = grandTotalFormatted; 228 } 229 } 230 231 function updateDeliveryOptions(deliveryMethods, selectedDeliveryId) { 232 const selectEl = document.querySelector(DELIVERY_SELECT_SELECTOR); 233 234 if (!selectEl || !Array.isArray(deliveryMethods) || deliveryMethods.length === 0) { 235 return; 236 } 237 238 while (selectEl.firstChild) { 239 selectEl.removeChild(selectEl.firstChild); 240 } 241 242 deliveryMethods.forEach((method) => { 243 const option = document.createElement('option');
244 option.value = String(method.id); 245 option.dataset.price = String(method.price || 0); 246 option.textContent = method.label || method.title || ''; 247 248 if (parseInt(option.value, 10) === (parseInt(selectedDeliveryId, 10) || 0)) { 249 option.selected = true; 250 } 251 252 selectEl.appendChild(option); 253 }); 254 } 255 256 function applyCartResponse(data) { 257 updateMiniCartSummary(data.totalItems, data.cartSubtotalFormatted); 258 updateCartTotals( 259 data.cartSubtotalFormatted, 260 data.deliveryPriceFormatted, 261 data.grandTotalFormatted, 262 ); 263 const currentDeliverySelect = document.querySelector(DELIVERY_SELECT_SELECTOR); 264 updateDeliveryOptions( 265 data.deliveryMethods, 266 currentDeliverySelect ? currentDeliverySelect.value : null, 267 ); 268 } 269 270 function showEmptyCart(cartEl) { 271 cartEl.innerHTML = `<p class="migo-shop-empty">${cartEl.dataset.emptyText || ''}</p>`; 272 } 273 274 async function updateQuantity(form, formData, previousValue) { 275 const quantity = parseInt(formData.get('quantity'), 10); 276 const input = form.querySelector('.migo-shop-cart__quantity-input'); 277 if (input) input.disabled = true; 278 279 try { 280 const response = await fetch('/shop-api/cart/update', { 281 method: 'POST', 282 body: formData, 283 }); 284 const data = await response.json(); 285 286 if (input) input.disabled = false; 287 288 if (!response.ok) { 289 let clampTarget = null; 290 291 if (data.error === 'max_item_quantity_exceeded') { 292 clampTarget = data.maxItemQuantity; 293 } else if (data.error === 'out_of_stock' && typeof data.maxStock === 'number') { 294 clampTarget = data.maxStock; 295 } 296 297 if (input) { 298 input.value = clampTarget !== null ? clampTarget : previousValue; 299 } 300 301 if (data.error === 'max_item_quantity_exceeded') { 302 notifyIfTranslated('maxItemQuantityText', data.maxItemQuantity, 'error'); 303 } else if (data.error === 'max_total_quantity_exceeded') { 304 notifyIfTranslated('maxTotalQuantityText', data.maxTotalQuantity, 'error'); 305 } else if (data.error === 'out_of_stock') { 306 notifyIfTranslated('outOfStockText', data.maxStock, 'error'); 307 } 308 309 if (clampTarget !== null && String(clampTarget) !== String(previousValue)) { 310 const retryCartEl = form.closest(CART_SELECTOR); 311 const retryToken = retryCartEl ? retryCartEl.dataset.requestToken : null; 312 const retryData = new FormData(form); 313 retryData.set('quantity', String(clampTarget)); 314 if (retryToken) retryData.set('REQUEST_TOKEN', retryToken); 315 updateQuantity(form, retryData, previousValue); 316 } 317 return; 318 } 319 320 if (input) input.dataset.confirmedQty = String(quantity); 321 322 applyCartResponse(data); 323 324 if (quantity <= 0 || data.removed) { 325 const item = form.closest('.migo-shop-cart__item'); 326 if (item) { 327 const cartEl = item.closest(CART_SELECTOR); 328 item.remove(); 329 const remaining = cartEl.querySelectorAll('.migo-shop-cart__item').length; 330 if (remaining === 0) { 331 showEmptyCart(cartEl); 332 } 333 } 334 return; 335 } 336 337 const item = form.closest('.migo-shop-cart__item'); 338 if (item && data.itemTotalFormatted) { 339 const itemTotal = item.querySelector('.migo-shop-cart__item-total'); 340 if (itemTotal) { 341 itemTotal.textContent = data.itemTotalFormatted; 342 } 343 } 344 } catch (error) { 345 if (input) { 346 input.disabled = false; 347 input.value = previousValue; 348 } 349 } 350 } 351 352 async function removeItem(form, formData) { 353 const submitBtn = form.querySelector('.migo-shop-cart__remove-button'); 354 if (submitBtn) submitBtn.disabled = true; 355 356 formData.set('quantity', '0'); 357 358 try { 359 const response = await fetch('/shop-api/cart/update', { 360 method: 'POST', 361 body: formData, 362 }); 363 const data = await response.json(); 364 if (submitBtn) submitBtn.disabled = false;
365 366 applyCartResponse(data); 367 368 const item = form.closest('.migo-shop-cart__item'); 369 if (item) { 370 const cartEl = item.closest(CART_SELECTOR); 371 item.remove(); 372 const remaining = cartEl.querySelectorAll('.migo-shop-cart__item').length; 373 if (remaining === 0) { 374 showEmptyCart(cartEl); 375 } 376 } 377 } catch (error) { 378 if (submitBtn) submitBtn.disabled = false; 379 } 380 } 381 382 async function updateDeliveryMethod(selectEl, token) { 383 const formData = new FormData(); 384 formData.append('delivery_method_id', selectEl.value); 385 386 if (token) { 387 formData.append('REQUEST_TOKEN', token); 388 } 389 390 try { 391 const response = await fetch('/shop-api/cart/delivery', { 392 method: 'POST', 393 body: formData, 394 }); 395 const data = await response.json(); 396 397 if (!response.ok) { 398 return; 399 } 400 401 applyCartResponse(data); 402 } catch (error) { 403 // Network/parse failure: leave the previous cart state in place. 404 } 405 } 406 407 async function addToCart(button) { 408 const productId = button.dataset.productId; 409 const token = button.dataset.requestToken; 410 const formData = new FormData(); 411 412 formData.append('product_id', productId); 413 formData.append('quantity', '1'); 414 formData.append('REQUEST_TOKEN', token); 415 416 button.disabled = true; 417 418 try { 419 const response = await fetch('/shop-api/cart/add', { 420 method: 'POST', 421 body: formData, 422 }); 423 const data = await response.json(); 424 425 if (!response.ok) { 426 button.textContent = button.dataset.originalText; 427 button.disabled = false; 428 429 if (data.error === 'max_item_quantity_exceeded') { 430 notifyIfTranslated('maxItemQuantityText', data.maxItemQuantity, 'error'); 431 } else if (data.error === 'max_total_quantity_exceeded') { 432 notifyIfTranslated('maxTotalQuantityText', data.maxTotalQuantity, 'error'); 433 } else if (data.error === 'out_of_stock') { 434 const message = button.dataset.outOfStockText || getCartMessage('outOfStockText'); 435 if (message) showNotification(message, 'error'); 436 } 437 return; 438 } 439 440 button.textContent = button.dataset.addedText; 441 button.classList.add('migo-shop-add-to-cart__button--added'); 442 setTimeout(() => { 443 button.textContent = button.dataset.originalText; 444 button.disabled = false; 445 button.classList.remove('migo-shop-add-to-cart__button--added'); 446 }, 4000); 447 448 updateMiniCartSummary(data.totalItems, data.cartSubtotalFormatted); 449 } catch (error) { 450 button.textContent = button.dataset.originalText; 451 button.disabled = false; 452 } 453 } 454 455 function bindButtons() { 456 document.querySelectorAll(BUTTON_SELECTOR).forEach((btn) => { 457 if (btn.dataset.migoShopCartBound === '1') { 458 return; 459 } 460 461 btn.dataset.originalText = btn.textContent; 462 btn.addEventListener('click', () => { 463 addToCart(btn); 464 }); 465 btn.dataset.migoShopCartBound = '1'; 466 }); 467 } 468 469 function bindCartForms() { 470 const cartEl = document.querySelector(CART_SELECTOR); 471 if (!cartEl) return; 472 473 const token = cartEl.dataset.requestToken; 474 475 cartEl 476 .querySelectorAll('.migo-shop-cart__quantity-input') 477 .forEach((input) => { 478 if (input.dataset.migoShopQtyBound === '1') return; 479 input.dataset.confirmedQty = input.value; 480 input.addEventListener('change', () => { 481 const form = input.closest(QTY_FORM_SELECTOR); 482 if (!form) return; 483 const previousValue = input.dataset.confirmedQty || input.value; 484 const formData = new FormData(form); 485 if (token) formData.set('REQUEST_TOKEN', token); 486 updateQuantity(form, formData, previousValue); 487 }); 488 input.dataset.migoShopQtyBound = '1'; 489 }); 490 491 cartEl 492 .querySelectorAll(REMOVE_FORM_SELECTOR) 493 .forEach((form) => { 494 if (form.dataset.migoShopRemoveBound === '1') return; 495 form.addEventListener('submit', (e) => { 496 e.preventDefault(); 497 const formData = new FormData(form); 498 if (token) formData.set('REQUEST_TOKEN', token); 499 removeItem(form, formData); 500 }); 501 form.dataset.migoShopRemoveBound = '1'; 502 }); 503 504 cartEl 505 .querySelectorAll(DELIVERY_SELECT_SELECTOR) 506 .forEach((selectEl) => { 507 if (selectEl.dataset.migoShopDeliveryBound === '1') return; 508 509 selectEl.addEventListener('change', () => { 510 updateDeliveryMethod(selectEl, token); 511 }); 512 513 selectEl.dataset.migoShopDeliveryBound = '1'; 514 }); 515 } 516
517 function init() { 518 bindButtons(); 519 bindCartForms(); 520 } 521 522 return { 523 init, 524 }; 525 })(); 526 527 const formatPrice = (cents) => { 528 const value = (parseInt(cents, 10) || 0) / 100; 529 const formatted = new Intl.NumberFormat('pl-PL', { 530 minimumFractionDigits: 2, 531 maximumFractionDigits: 2, 532 }).format(value); 533 534 return `${formatted} zÅ`; 535 }; 536 537 const getRequestToken = () => { 538 const el = document.querySelector('input[name="REQUEST_TOKEN"]'); 539 540 return el ? el.value : ''; 541 }; 542 543 const persistMethods = (deliveryMethodId, paymentMethodId) => { 544 const token = getRequestToken(); 545 546 if (!token) { 547 return; 548 } 549 550 const body = new FormData(); 551 body.set('REQUEST_TOKEN', token); 552 body.set('persist_methods', '1'); 553 body.set('deliveryMethodId', String(deliveryMethodId)); 554 body.set('paymentMethodId', String(paymentMethodId)); 555 556 fetch(window.location.href, { method: 'POST', body }); 557 }; 558 559 class CostSummary { 560 constructor(rootSelector, { subtotalEl, deliveryEl, paymentEl, grandEl, deliveryInput, paymentInput }) { 561 this.root = document.querySelector(rootSelector); 562 563 if (!this.root) { 564 return; 565 } 566 567 this.subtotalEl = this.root.querySelector(subtotalEl); 568 this.deliveryEl = this.root.querySelector(deliveryEl); 569 this.paymentEl = this.root.querySelector(paymentEl); 570 this.grandEl = this.root.querySelector(grandEl); 571 this.deliveryInputSelector = deliveryInput; 572 this.paymentInputSelector = paymentInput; 573 574 if (!this.subtotalEl) { 575 return; 576 } 577 578 this.bindEvents(); 579 this.recalc(); 580 } 581 582 getCheckedPrice(selector) { 583 const checked = this.root.querySelector(`${selector}:checked`); 584 585 if (!checked) { 586 return 0; 587 } 588 589 return parseInt(checked.dataset.price, 10) || 0; 590 } 591 592 bindEvents() { 593 const handler = () => this.recalc(); 594 595 this.root.querySelectorAll(this.deliveryInputSelector).forEach((el) => { 596 el.addEventListener('change', () => { 597 persistMethods(parseInt(el.value, 10) || 0, this.getCheckedPaymentId()); 598 handler(); 599 }); 600 }); 601 602 this.root.querySelectorAll(this.paymentInputSelector).forEach((el) => { 603 el.addEventListener('change', () => { 604 persistMethods(this.getCheckedDeliveryId(), parseInt(el.value, 10) || 0); 605 handler(); 606 }); 607 }); 608 } 609 610 getCheckedDeliveryId() { 611 const checked = this.root.querySelector(`${this.deliveryInputSelector}:checked`); 612 613 return checked ? parseInt(checked.value, 10) || 0 : 0; 614 } 615 616 getCheckedPaymentId() { 617 const checked = this.root.querySelector(`${this.paymentInputSelector}:checked`); 618 619 return checked ? parseInt(checked.value, 10) || 0 : 0; 620 } 621 622 recalc() { 623 const subtotal = parseInt(this.subtotalEl.dataset.value, 10) || 0; 624 const deliveryPrice = this.getCheckedPrice(this.deliveryInputSelector); 625 const paymentPrice = this.getCheckedPrice(this.paymentInputSelector); 626 const grandTotal = subtotal + deliveryPrice + paymentPrice; 627 628 if (this.deliveryEl) { 629 this.deliveryEl.textContent = formatPrice(deliveryPrice); 630 this.deliveryEl.dataset.value = deliveryPrice; 631 } 632 633 if (this.paymentEl) { 634 this.paymentEl.textContent = formatPrice(paymentPrice); 635 this.paymentEl.dataset.value = paymentPrice; 636 } 637 638 if (this.grandEl) { 639 this.grandEl.textContent = formatPrice(grandTotal); 640 this.grandEl.dataset.value = grandTotal; 641 } 642 } 643 } 644 645 class CartDelivery { 646 constructor(rootSelector, { selectEl, subtotalEl, deliveryEl, grandEl }) { 647 this.root = document.querySelector(rootSelector); 648 649 if (!this.root) { 650 return; 651 } 652 653 this.selectEl = this.root.querySelector(selectEl); 654 this.subtotalEl = this.root.querySelector(subtotalEl); 655 this.deliveryEl = this.root.querySelector(deliveryEl); 656 this.grandEl = this.root.querySelector(grandEl); 657 658 if (!this.selectEl || !this.subtotalEl) { 659 return; 660 } 661 662 this.bindEvents(); 663 } 664 665 bindEvents() { 666 this.selectEl.addEventListener('change', () => { 667 const option = this.selectEl.options[this.selectEl.selectedIndex]; 668 const deliveryPrice = option ? parseInt(option.dataset.price, 10) || 0 : 0; 669 const subtotal = parseInt(this.subtotalEl.dataset.value, 10) || 0; 670 671 if (this.deliveryEl) { 672 this.deliveryEl.textContent = formatPrice(deliveryPrice); 673 this.deliveryEl.dataset.value = deliveryPrice; 674 } 675 676 if (this.grandEl) { 677 const grandTotal = subtotal + deliveryPrice; 678 this.grandEl.textContent = formatPrice(grandTotal); 679 this.grandEl.dataset.value = grandTotal; 680 } 681 682 if (this.selectEl.form) { 683 this.selectEl.form.submit(); 684 } 685 }); 686 } 687 } 688 689 window.migoShopCost = { 690 init() { 691 new CostSummary('.migo-shop-checkout', { 692 subtotalEl: '#js-checkout-subtotal', 693 deliveryEl: '#js-checkout-delivery-price', 694 paymentEl: '#js-checkout-payment-price', 695 grandEl: '#js-checkout-grand-total', 696 deliveryInput: 'input[name="deliveryMethodId"]', 697 paymentInput: 'input[name="paymentMethodId"]', 698 }); 699 700 new CartDelivery('.migo-shop-cart', { 701 selectEl: '#migo-shop-cart-delivery', 702 subtotalEl: '#js-cart-subtotal', 703 deliveryEl: '#js-cart-delivery-price', 704 grandEl: '#js-cart-grand-total', 705 }); 706 }, 707 }; 708 709 $(document).ready(() => { 710 const $track = $('.partners-slider__track'); 711 if ($track.length) { 712 $track.slick({ 713 autoplay: true, 714 autoplaySpeed: 4000, 715 slidesToShow: 6, 716 slidesToScroll: 1, 717 arrows: true, 718 dots: false, 719 prevArrow: `<button type="button" class="slick-prev" aria-label="${$track.data('prev-text')}"></button>`, 720 nextArrow: `<button type="button" class="slick-next" aria-label="${$track.data('next-text')}"></button>`, 721 responsive: [ 722 { breakpoint: 1400, settings: { slidesToShow: 5 } }, 723 { breakpoint: 1200, settings: { slidesToShow: 4 } }, 724 { breakpoint: 992, settings: { slidesToShow: 3 } }, 725 { breakpoint: 600, settings: { slidesToShow: 2 } }, 726 ] 727 }); 728 } 729 }); 730 731 function bootMigoShop() { 732 if (window.migoShopFilters && typeof window.migoShopFilters.init === 'function') { 733 window.migoShopFilters.init(); 734 } 735 if (window.migoShopCart && typeof window.migoShopCart.init === 'function') { 736 window.migoShopCart.init(); 737 } 738 if (window.migoShopCost && typeof window.migoShopCost.init === 'function') {
739 window.migoShopCost.init(); 740 } 741 } 742 743 if (document.readyState === 'loading') { 744 document.addEventListener('DOMContentLoaded', bootMigoShop); 745 } else { 746 bootMigoShop(); 747 } 748 749})();
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.