PageSourceSearch

https://api.perfect4u.ru/widget/catalog-widget.js?v=20260924-1

js perfect4u.ru collected 2026-10-05 03:14:49 UTC 6,414 bytes, 122 lines download raw bytes

1(function () {
2  'use strict';
3  if (window.p4uCatalogLoaded) return;
4  window.p4uCatalogLoaded = true;
5
6  var savedButtons = new WeakMap();
7  var pending = false;
8  var buySelector = '.t744__btn:not(.p4u-marketplaces-btn), .t-store__prod-popup__btn, .t-store__card__btn[href="#order"]';
9
10  // Tilda calls this helper before each catalog initializer, including when its
11  // asynchronous catalog script arrives after the page has finished loading.
12  function patchCatalogInit() {
13    var original = window.t_store_init;
14    if (typeof original !== 'function' || original.p4uShowAllStock) return;
15    function init(recid, options) {
16      if (options) options.defaultSort = Object.assign({}, options.defaultSort, { in_stock: false });
17      return original.apply(this, arguments);
18    }
19    init.p4uShowAllStock = true;
20    window.t_store_init = init;
21  }
22  function patchFunctionLoader() {
23    if (typeof window.t_onFuncLoad !== 'function' || window.t_onFuncLoad.p4uShowAllStock) return;
24    var onFuncLoad = window.t_onFuncLoad;
25    window.t_onFuncLoad = function (name, callback) {
26      var args = Array.prototype.slice.call(arguments);
27      if (name === 't_store_init') args[1] = function () {
28        patchCatalogInit();
29        return callback.apply(this, arguments);
30      };
31      return onFuncLoad.apply(this, args);
32    };
33    window.t_onFuncLoad.p4uShowAllStock = true;
34  }
35  patchFunctionLoader();
36  patchCatalogInit();
37
38  function withoutStockFilter(value) {
39    var url = new URL(value, window.location.href);
40    if (url.origin !== window.location.origin) return null;
41    var changed = false;
42    Array.from(url.searchParams.keys()).forEach(function (key) {
43      if (/^tfc_quantity\[\d+\]$/.test(key) && url.searchParams.get(key) === 'y') {
44        url.searchParams.delete(key);
45        changed = true;
46      }
47    });
48    return changed ? url.pathname + url.search + url.hash : null;
49  }
50  var cleanLocation = withoutStockFilter(window.location.href);
51  if (cleanLocation) window.history.replaceState(window.history.state, '', cleanLocation);
52
53  function setAvailability(product) {
54    var raw = product.getAttribute('data-product-inv');
55    // An empty inventory field means unlimited stock in Tilda, not zero.
56    var unavailable = raw !== null && raw.trim() !== '' && Number.isFinite(Number(raw)) && Number(raw) <= 0;
57    product.classList.toggle('p4u-product-unavailable', unavailable);
58    product.querySelectorAll(buySelector).forEach(function (button) {
59      if (button.closest('.js-product') !== product || button.classList.contains('p4u-marketplaces-btn')) return;
60      var label = button.querySelector('.t-btnflex__text, .js-store-prod-popup-buy-btn-txt, .js-store-prod-buy-btn-txt, .t-store__card__btn-text');
61      if (!label) return;
62      if (unavailable) {
63        if (!savedButtons.has(button)) savedButtons.set(button, {
64          text: label.textContent, tabindex: button.getAttribute('tabindex'),
65          role: button.getAttribute('role'), aria: button.getAttribute('aria-disabled'),
66        });
67        button.classList.add('p4u-stock-unavailable');
68        button.setAttribute('aria-disabled', 'true');
69        button.setAttribute('role', 'button');
70        button.setAttribute('tabindex', '-1');
71        if (label.textContent !== 'Нет в наличии') label.textContent = 'Нет в наличии';
72      } else if (savedButtons.has(button)) {
73        var saved = savedButtons.get(button);
74        button.classList.remove('p4u-stock-unavailable');
75        [['tabindex', saved.tabindex], ['role', saved.role], ['aria-disabled', saved.aria]].forEac
75h(function (item) {
76          if (item[1] === null) button.removeAttribute(item[0]);
77          else button.setAttribute(item[0], item[1]);
78        });
79        if (label.textContent === 'Нет в наличии') label.textContent = /нет в наличии|out of stock/i.test(saved.text) ? 'Купить' : saved.text;
80        savedButtons.delete(button);
81      }
82    });
83  }
84
85  function refresh() {
86    pending = false;
87    document.querySelectorAll('.js-product[data-product-inv]').forEach(setAvailability);
88    document.querySelectorAll('a[href*="tfc_quantity"]').forEach(function (link) {
89      var clean = withoutStockFilter(link.getAttribute('href'));
90      if (clean) link.setAttribute('href', clean);
91    });
92  }
93  function schedule() {
94    if (pending) return;
95    pending = true;
96    window.requestAnimationFrame(refresh);
97  }
98
99  var style = document.createElement('style');
100  style.textContent = '#allrecords .p4u-stock-unavailable,#allrecords .p4u-stock-unavailable:hover,#allrecords .p4u-stock-unavailable:focus-visible{display:inline-flex!important;align-items:center;justify-content:center;box-sizing:border-box;min-height:44px;color:#315e60!important;background:#eef3f2!important;border:1px solid #c9d8d6!important;border-radius:5px!important;opacity:1!important;cursor:default!important;pointer-events:auto!important;box-shadow:none!important;transform:none!important;font-family:proximanova,Arial,sans-serif!important;font-weight:400!important;letter-spacing:.02em;text-transform:none!important}#allrecords .p4u-stock-unavailable .t-btnflex__text{color:inherit!important}#allrecords .t-store__card__sold-out-msg{display:inline-block;margin-top:12px;padding:6px 10px;border-radius:5px;background:#eef3f2;color:#315e60;font-family:proximanova,Arial,sans-serif;font-size:13px;font-weight:400;line-height:1.4;letter-spacing:.02em}#allrecords .p4u-product-unavailable:has(.p4u-stock-unavailable) .js-store-prod-sold-out{display:none!important}';
101  document.head.appendChild(style);
102  document.addEventListener('click', function (event) {
103    var button = event.target.closest && event.target.closest('.p4u-stock-unavailable');
104    if (!button) return;
105    event.preventDefault();
106    event.stopImmediatePropagation();
107  }, true);
108
109  function start() {
110    // The deferred core script can replace the early inline t_onFuncLoad helper.
111    // Install again in the capture phase, before block DOMContentLoaded handlers.
112    patchFunctionLoader();
113    patchCatalogInit();
114    refresh();
115    new MutationObserver(schedule).observe(document.body, {
116      childList: true, subtree: true, characterData: true,
117      attributes: true, attributeFilter: ['data-product-inv'],
118    });
119  }
120  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, true);
121  else start();
122})();

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.