1/* rf_footer.js â вÑнеÑено из footer.post.tpl 22.09.2026 (Ð4 ÑазбоÑа 2 docs/brand_page_load_analysis_20260922.md): два блока пÑиезжали инлайном 3 в каждом HTML (~28 ÐÐ) и не кÑÑиÑовалиÑÑ. Токен window.rfTk оÑÑаÑÑÑÑ Ð² HTML. 4 ÐеÑÑÐ¸Ñ Ð² ?v= ÑÐ²Ð¾Ñ Ð¸ не завиÑÐ¸Ñ Ð¾Ñ cache_id, Ñайл пеÑÐµÐ¶Ð¸Ð²Ð°ÐµÑ Ð¿ÐµÑеÑбоÑÐºÑ Ð±Ð°Ð½Ð´Ð»Ð¾Ð². 5 1) Ðедавно пÑоÑмоÑÑеннÑе (localStorage + ES) 2) ÐзбÑанное без пеÑезагÑÑзки */ 6/* Hybrid "Ðедавно пÑоÑмоÑÑеннÑе": instant localStorage render + background ES refresh. 7 Runs on all product pages (localStorage + ES). 8 СкÑÑÑ Ð² лиÑном кабинеÑе (profiles/orders/auth/...): rfRvOff ÑÑавиÑÑÑ Smarty вÑÑе; 9 на ÑÑÑаниÑе «ÐÑложеннÑе ÑоваÑÑ» (wishlist.view) блок оÑÑаÑÑÑÑ. */ 10(function () { 11 if (window.rfRvOff) return; 12 13 var API = '/index.php?dispatch=rf_recently_viewed'; 14 var LS_UID = 'rf_uid', LS_ITEMS = 'rf_rv_items', MAX = 20; 15 16 function is404Page() { 17 var code = document.querySelector('.ty-exception__code'); 18 if (code && /\b404\b/.test(code.textContent || '')) return true; 19 var ogTitle = document.querySelector('meta[property="og:title"], meta[name="twitter:title"]'); 20 var titleText = ((ogTitle && ogTitle.getAttribute('content')) || '') + ' ' + (document.title || ''); 21 return !!(document.querySelector('.ty-exception') && /СÑÑаниÑа не найдена|Page not found|404/i.test(titleText)); 22 } 23 if (is404Page()) return; 24 25 // Suppress the slow legacy block_manager.render AJAX (~880ms) for the 26 // recently-viewed widget on test URLs â the hybrid replaces it. 27 try { 28 var $$ = (window.Tygh && Tygh.$) || window.jQuery || window.$; 29 if ($$ && $$(document).ajaxSend) { 30 $$(document).ajaxSend(function (e, xhr, settings) { 31 if (settings && settings.url && settings.url.indexOf('block_manager.render') > -1) { 32 try { xhr.abort(); } catch (er) {} 33 } 34 }); 35 } 36 } catch (er) {} 37 38 function priceText(sel) { 39 var e = document.querySelector(sel); 40 if (!e) return ''; 41 var c = e.cloneNode(true); 42 [].forEach.call(c.querySelectorAll('sup, .ty-price__decimals, [class*="decimal"], [class*="cents"], .ty-price-num__sup'), function (x) { x.remove(); }); 43 return (c.textContent || '').replace(/\s+/g, ' ').trim(); 44 } 45 46 function uid() { 47 var u = null; 48 try { u = localStorage.getItem(LS_UID); } catch (e) {} 49 if (!u) { 50 u = 'rv_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 10); 51 try { localStorage.setItem(LS_UID, u); } catch (e) {} 52 } 53 return u; 54 } 55 function getItems() { try { return JSON.parse(localStorage.getItem(LS_ITEMS) || '[]'); } catch (e) { return []; } } 56 function compactItems(a) { 57 var out = []; 58 (a || []).forEach(function (it) { 59 if (!it) return; 60 var duplicate = out.some(function (x) { return sameItem(x, it); }); 61 if (!duplicate) out.push(it); 62 }); 63 return out; 64 } 65 function setItems(a) { try { localStorage.setItem(LS_ITEMS, JSON.stringify(compactItems(a).slice(0, MAX))); } catch (e) {} } 66 function txt(sel, root) { var e = (root || document).querySelector(sel); return e ? (e.textContent || '').replace(/\s+/g, ' ').trim() : ''; } 67 function cleanPath(u) { 68 try { return new URL(u || location.pathname, location.origin).pathname.replace(/\/+$/, '/') || '/'; } 69 catch (e) { return String(u || '').split('?')[0].replace(/\/+$/, '/') || '/'; } 70 } 71 function readImg(el) { 72 if (!el) return ''; 73 var vals = [el.currentSrc, el.getAttribute('src'), el.getAttribute('data-src'), el.getAttribute('data-lazy-src')]; 74 for (var i = 0; i < vals.length; i++) { 75 var u = vals[i] || ''; 76 if (u && !/^(data:|about:)/.test(u) && !/spacer/.test(u)) return u; 77 } 78 return ''; 79 } 80 function okThumb(u) { return u && /\/thumbnails\/\d+\/\d+\//.test(u) && !/spacer|data:/.test(u); } 81 function rfRvStripWebp(u){ return (u||'').replace('/images/ab__webp/thumbnails/','/images/thumbnails/').replace(/_(jpe?g|png)\.webp(\?.*)?$/i,function(_,e,q){return '.'+e.toLowerCase()+(q||'');}); } 82 function normalizeThumb340(u) { return rfRvStripWebp(u).replace(/\/thumbn
82ails\/\d+\/\d+\//,'/thumbnails/340/340/'); } 83 function collectThumbs(sel, limit) { 84 var out = []; 85 [].forEach.call(document.querySelectorAll(sel), function (g) { 86 var raw = readImg(g); 87 if (okThumb(raw) && out.indexOf(raw) < 0) out.push(raw); 88 }); 89 return out.slice(0, limit || 6); 90 } 91 function metaImage() { 92 var m = document.querySelector('meta[property="og:image"], meta[name="twitter:image"]'); 93 var u = m ? (m.getAttribute('content') || '') : ''; 94 return okThumb(u) ? normalizeThumb340(u) : ''; 95 } 96 function categoryThumbMap() { 97 var map = {}; 98 [].forEach.call(document.querySelectorAll('.ty-subcategories__item a, .subcategories a'), function (a) { 99 var img = a.querySelector('img'); 100 var u = readImg(img); 101 if (okThumb(u)) map[cleanPath(a.getAttribute('href') || '')] = normalizeThumb340(u); 102 }); 103 return map; 104 } 105 function categoryImageForPath(path) { 106 var own = metaImage(); 107 if (own) return own; 108 var hit = categoryThumbMap()[cleanPath(path)]; 109 if (hit) return hit; 110 return ''; 111 } 112 113 function isProductDetailPage() { 114 return !!document.querySelector('.ut2-pb__img-wrapper, .ty-product-img'); 115 } 116 function productIdFromReturnUrl(v) { 117 var m = String(v || '').match(/[?&]product_id=(\d+)/); 118 return m ? m[1] : ''; 119 } 120 function currentProductId() { 121 if (!isProductDetailPage()) return ''; 122 var f = document.querySelector('form[name^="product_form_"]'); 123 if (f) { var m2 = f.getAttribute('name').match(/product_form_(\d+)/); if (m2) return m2[1]; } 124 var ret = document.querySelector('input[name="redirect_url"][value*="dispatch=products.view"], input[name="return_url"][value*="dispatch=products.view"]'); 125 return productIdFromReturnUrl(ret ? ret.getAttribute('value') : ''); 126 } 127 function currentCategoryId() { 128 var el = document.querySelector('#pagination_contents[data-rf-fast-category-id]') || document.querySelector('[data-rf-fast-category-id]'); 129 var id = el ? (el.getAttribute('data-rf-fast-category-id') || '') : ''; 130 return /^\d+$/.test(id) ? id : ''; 131 } 132 function currentItem() { 133 var pid = currentProductId(); 134 if (pid) return { item_type: 'product', item_id: pid, product_id: pid, category_id: '' }; 135 var cid = currentCategoryId(); 136 if (cid) return { item_type: 'category', item_id: cid, product_id: '', category_id: cid }; 137 return null; 138 } 139 function itemType(it) { 140 if (!it) return ''; 141 var t = String(it.item_type || it.type || '').toLowerCase(); 142 if (t === 'product' || t === 'category') return t; 143 if (it.category_id && !it.product_id) return 'category'; 144 if (it.product_id) return 'product'; 145 return ''; 146 } 147 function itemId(it) { 148 if (!it) return ''; 149 var t = itemType(it); 150 if (t === 'category') return String(it.item_id || it.category_id || ''); 151 if (t === 'product') return String(it.item_id || it.product_id || ''); 152 return String(it.item_id || it.product_id || it.category_id || ''); 153 } 154 function itemKey(it) { 155 var id = itemId(it); 156 return id ? itemType(it) + ':' + id : ''; 157 } 158 function sameItem(a, b) { 159 var ak = itemKey(a), bk = itemKey(b); 160 if (ak && bk && ak === bk) return true; 161 if (a && b && a.url && b.url && cleanPath(a.url) === cleanPath(b.url)) return true; 162 return false; 163 } 164 function normalizeItemCard(it, map) { 165 if (!it) return it; 166 if (itemType(it) !== 'category') return it; 167 var one = it.img || (it.imgs && it.imgs.length ? it.imgs[0] : '') || ''; 168 if (!one && it.url && map) one = map[cleanPath(it.url)] || ''; 169 one = one ? normalizeThumb340(one) : ''; 170 it.item_type = it.type = 'category'; 171 it.product_id = it.product_id || 0; 172 it.category_id = it.category_id || it.item_id || 0; 173 it.img = one; 174 it.imgs = one ? [one] : []; 175 it.price = ''; 176 it.old_price = ''; 177 it.sold = ''; 178 return it; 179 } 180 function repairStoredImages(items) { 181 var map = categoryThumbMap(), changed = false; 182 items.forEach(function (it) { 183 if (!it) return; 184 var beforeType = it.item_type || it.type || ''; 185 var beforeImg = it.img || ''; 186 var beforeImgs = it.imgs ? it.imgs.join('|') : ''; 187 var beforePrice = (it.price || '') + '|' + (it.old_price || '') + '|' + (it.sold || ''); 188 if (!itemType(it) && !it.product_id && it.url && map[cleanPath(it.url)]) it.item_type = it.type = 'category'; 189 normalizeItemCard(it, map); 190 var afterImgs = it.imgs ? it.imgs.join('|') : ''; 191 if (beforeType !== (it.item_type || it.type || '') || beforeImg !== (it.img || '') || beforeImgs !== afterImgs || beforePrice !== ((it.price || '') + '|' + (it.old_price || '') + '|' + (it.sold || ''))) changed = true; 192 }); 193 if (changed) setItems(items); 194 return items; 195 } 196 197 function hasRvImage(it) {
198 return !!(it && (it.img || (it.imgs && it.imgs.length))); 199 } 200 function isServiceRvUrl(u) { 201 var p = cleanPath(u || ''); 202 return /^\/(?:login|cart|checkout|profiles|orders|auth|wishlist|compare|index\.php)(?:\/|$)/i.test(p); 203 } 204 function isRenderableRvItem(it) { 205 var t = itemType(it), id = itemId(it); 206 if (!t || !id || !it || !it.url || !it.name || !hasRvImage(it) || isServiceRvUrl(it.url)) return false; 207 if (t === 'product' && !(it.product_id || it.item_id)) return false; 208 if (t === 'category' && !(it.category_id || it.item_id)) return false; 209 return true; 210 } 211 212 function scrapeCurrent() { 213 var item = currentItem(); 214 if (!item || !item.item_id) return null; 215 var isCategory = item.item_type === 'category'; 216 var imgs = isCategory 217 ? [categoryImageForPath(location.pathname)].filter(Boolean) 218 : collectThumbs('.ut2-pb__img-wrapper img, .ty-product-img img', 6); 219 if (!imgs.length) imgs = [metaImage()].filter(Boolean); 220 imgs = imgs.map(function (u) { return isCategory ? normalizeThumb340(u) : u; }).filter(function (u, i, a) { return u && a.indexOf(u) === i; }).slice(0, isCategory ? 1 : 6); 221 var imgUrl = imgs[0] || ''; 222 var soldM = (document.body.textContent || '').match(/ÐÑодано\s+[^\s,<]+(\s*(ÑÑÑ|млн|ÑÑÑÑÑ)?\.?)?/); 223 return normalizeItemCard({ 224 item_type: item.item_type, 225 type: item.item_type, 226 item_id: item.item_id, 227 product_id: item.product_id || 0, 228 category_id: item.category_id || 0, 229 url: location.pathname, 230 name: txt('h1'), 231 price: isCategory ? '' : priceText('.ut2-pb__price-actual .ty-price-num, .ut2-pb__price-wrap .ty-price-num, .ty-price-num'), 232 old_price: isCategory ? '' : priceText('.ut2-pb__price-old .ty-price-num, del .ty-price-num'), 233 img: imgUrl, 234 imgs: imgs, 235 sold: isCategory ? '' : (soldM ? soldM[0].trim() : ''), 236 ts: Date.now() 237 }); 238 } 239 240 function sid() { 241 var s = null, now = Date.now(); 242 try { var raw = localStorage.getItem('rf_sid'); if (raw) { var o = JSON.parse(raw); if (o.t && now - o.t < 1800000) s = o.id; } } catch (e) {} 243 if (!s) s = 's_' + now.toString(36) + Math.random().toString(36).slice(2, 8); 244 try { localStorage.setItem('rf_sid', JSON.stringify({ id: s, t: now })); } catch (e) {} 245 return s; 246 } 247 function capture() { 248 var pd = scrapeCurrent(); 249 if (!pd || !pd.item_id) return; 250 var items = repairStoredImages(getItems()).filter(function (x) { return !sameItem(x, pd); }); 251 var referrer_id = items.length ? (itemId(items[0]) || '') : ''; 252 var referrer_type = items.length ? itemType(items[0]) : ''; 253 var session_id = sid(); 254 items.unshift(pd); 255 setItems(items); 256 var body = 'uid=' + encodeURIComponent(uid()) + '&item_type=' + encodeURIComponent(pd.item_type) + '&item_id=' + encodeURIComponent(pd.item_id) + '&product_id=' + encodeURIComponent(pd.product_id || '') + '&category_id=' + encodeURIComponent(pd.category_id || '') + '&session_id=' + encodeURIComponent(session_id) + '&referrer_id=' + encodeURIComponent(referrer_id) + '&referrer_type=' + encodeURIComponent(referrer_type) + '&card=' + encodeURIComponent(JSON.stringify(pd)) + '&tk=' + encodeURIComponent(window.rfTk || ''); 257 try { 258 if (navigator.sendBeacon) { 259 navigator.sendBeacon(API + '.track', new Blob([body], { type: 'application/x-www-form-urlencoded' })); 260 } else { 261 fetch(API + '.track', { method: 'POST', body: body, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, keepalive: true }); 262 } 263 } catch (e) {} 264 } 265 266 function esc(s) { return String(s || '').replace(/"/g, '"').replace(/</g, '<'); } 267 // rf_rv-каÑÑоÑки могÑÑ Ñ ÑаниÑÑ webp-URL или ÑÑаÑÑй ÑÐ°Ð·Ð¼ÐµÑ Ð¼Ð¸Ð½Ð¸Ð°ÑÑÑÑ. ÐÑиводим 268 // к ÑÑабилÑÐ½Ð¾Ð¼Ñ 340 JPG URL; nginx Ñам оÑдаÑÑ WebP по Accept, еÑли Ñайл еÑÑÑ. 269 function card340(u){ return normalizeThumb340(u); } 270 function card480(u){ return card340(u); } 271 // If a rare 340 thumbnail is missing, fall back to the original stored JPG URL. 272 function attachFallback(root) { 273 [].forEach.call(root.querySelectorAll('img[data-rvfull]'), function (im) { 274 if (im._rvfb) return; im._rvfb = 1; 275 im.addEventListener('error', function () { 276 var f = im.getAttribute('data-rvfull'); 277 if (f && im.src.indexOf(f) < 0) { im.removeAttribute('data-rvfull'); im.src = f; } 278 }); 279 }); 280 } 281 function cardHtml(it) { 282 if (!it || !it.name) return ''; 283 var isCategory = itemType(it) === 'category'; 284 if (isCategory) normalizeItemCard(it); 285 var imgs = (it.imgs && it.imgs.length) ? it.imgs : (it.img ? [it.img] : []); 286 if (isCategory) imgs = imgs.slice(0, 1); 287 var mainFull = imgs[0] || it.img || ''; 288 var main = card480(mainFull); 289 // hover-gallery: invisible hover strips + indicator dots (theme's delegated JS binds by class) 290 var gallery = ''; 291 if (!isCategory && imgs.length > 1) { 292 var strips = '<div class="item"></div>'; 293 var dots = '<span class="active"></span>'; 294 for (var i = 1; i < imgs.length; i++) {
295 strips += '<div class="item" data-ca-product-additional-image-src="' + esc(imgs[i]) + '"></div>'; 296 dots += '<span></span>'; 297 } 298 gallery = '<div class="cm-ab-hover-gallery abt__ut2_hover_gallery lines">' + strips + 299 '<div class="abt__ut2_hover_gallery_indicators">' + dots + '</div></div>'; 300 } 301 // â Ðдна Ñена Â«Ð¾Ñ N â½Â» (ÑеÑение владелÑÑа 25.09.2026) â Ñак же, как в обÑем ÑбоÑÑике 302 // cards_render.js (layout recently_viewed); ÑÑаÑÑÑ ÑÐµÐ½Ñ Ð² ÑÑом блоке не вÑводим. 303 var sold = (!isCategory && it.sold) ? '<span class="rf-rv__sold">' + esc(it.sold) + '</span>' : ''; 304 var image = main ? '<img class="ty-pict img-ab-hover-gallery" src="' + esc(main) + '" data-rvfull="' + esc(rfRvStripWebp(mainFull)) + '" alt="' + esc(it.name) + '">' : '<span class="rf-rv__image-placeholder"></span>'; 305 // â Ðлок ÑÐµÐ½Ñ â вÑегда (кÑоме каÑегоÑий): Ð½ÐµÑ ÑÐµÐ½Ñ Ð½Ð¸ Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ ваÑианÑа â Â«Ð½ÐµÑ ÑенÑ», 306 // «ÐÑодано» пÑи ÑÑом оÑÑаÑÑÑÑ. Цена в Ñнимке иÑÑоÑии â ÑÑÑока, поÑÑÐ¾Ð¼Ñ Ð¿Ñиводим к ÑиÑлÑ. 307 var pnum = parseFloat(String(it.price == null ? '' : it.price).replace(/[^\d.,-]/g, '').replace(',', '.')) || 0; 308 var price = isCategory ? '' : '<div class="ut2-gl__price-wrap">' + (pnum > 0 309 ? '<span class="ut2-gl__price"><span class="ty-price"><span class="rf-rv__from">оÑ</span> <span class="ty-price-num">' + esc(it.price) + '</span> â½</span></span>' 310 : '<span class="rf-rv__noprice">Ð½ÐµÑ ÑенÑ</span>') + sold + '</div>'; 311 var itemClass = isCategory ? 'category' : 'product'; 312 return '' + 313 '<div class="ut2-gl__item rf-rv__item rf-rv__item--' + itemClass + '">' + 314 '<div class="ut2-gl__body">' + 315 '<div class="ut2-gl__image">' + 316 '<a href="' + esc(it.url) + '">' + 317 image + 318 gallery + 319 '</a>' + 320 '</div>' + 321 '<div class="ut2-gl__content">' + 322 '<div class="ut2-gl__name"><a class="product-title" href="' + esc(it.url) + '">' + esc(it.name) + '</a></div>' + 323 price + 324 '</div>' + 325 '</div>' + 326 '</div>'; 327 } 328 329 // Свежие каÑÑоÑки ÑоваÑов Ñ ÑеÑвеÑа (обÑий ProductCard) и конÑекÑÑ ÐºÐ¾Ð¼Ð¿Ð°ÐºÑного пÑоÑилÑ. 330 // â ÐÑÑоÑÐ¸Ñ (localStorage и ES) даÑÑ ÑолÑко ÑпиÑок и поÑÑдок: лежаÑие в ней Ð¿Ð¾Ð»Ñ ÐºÐ°ÑÑоÑки â 331 // Ñнимок на Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð¿ÑоÑмоÑÑа и ÑÑÑаÑеваÑÑ. Ðока ÑеÑÐ²ÐµÑ Ð¸Ñ Ð½Ðµ пÑиÑлал, ÑабоÑÐ°ÐµÑ Ð¿Ñежний пÑÑÑ. 332 var rvCards = null, rvCtx = null; 333 334 function rvUseCommon() { 335 return !!(rvCards && rvCards.length && window.rfCards && typeof window.rfCards.render === 'function'); 336 } 337 338 function renderItems(mount, items) { 339 var cats = [], products = []; 340 items.forEach(function (x) { 341 if (itemType(x) === 'category') cats.push(normalizeItemCard(x)); 342 else if (itemType(x) === 'product') products.push(x); 343 }); 344 cats = cats.filter(isRenderableRvItem).slice(0, 12); 345 products = products.filter(isRenderableRvItem).slice(0, 12); 346 var catGrid = mount.querySelector('.rf-rv__grid--categories'); 347 var productGrid = mount.querySelector('.rf-rv__grid--products'); 348 var catSection = mount.querySelector('.rf-rv__section--categories'); 349 var productSection = mount.querySelector('.rf-rv__section--products'); 350 if (catGrid) { 351 catGrid.innerHTML = cats.map(cardHtml).join(''); 352 if (catSection) catSection.style.display = cats.length ? '' : 'none'; 353 attachFallback(catGrid); 354 } 355 if (productGrid) { 356 if (rvUseCommon()) { 357 // обÑий ÑбоÑÑик виÑÑинÑ, компакÑнÑй пÑоÑилÑ: Ñвоей вÑÑÑÑки Ñ Ð±Ð»Ð¾ÐºÐ° болÑÑе Ð½ÐµÑ 358 productGrid.innerHTML = window.rfCards.render(rvCards, rvCtx || {}, {}); 359 if (productSection) productSection.style.display = rvCards.length ? '' : 'none'; 360 products = rvCards; 361 } else { 362 productGrid.innerHTML = products.map(cardHtml).join(''); 363 if (productSection) productSection.style.display = products.length ? '' : 'none'; 364 attachFallback(productGrid); 365 } 366 } 367 mount.style.display = (cats.length || products.length) ? '' : 'none'; 368 } 369 370 function findContainer() { 371 // old grid_tab widget wrapper (contains the "Ðедавно пÑоÑмоÑÑеннÑе" tab) 372 var tab = document.querySelector('[id^="abt__ut2_grid_tab_"]'); 373 if (tab) { 374 var box = tab.closest('.ty-tabs, [class*="tabs"], [class*="block"]') || tab.parentElement; 375 var h = box ? box.parentElement || box : null; 376 if (h) { h.setAttribute('data-rv-legacy', '1'); return h; } 377 } 378 var main = document.querySelector('#product_detail_page'); 379 if (main) { 380 var mainGrid = main.closest('.main-content-grid') || main.parentElement; 381 if (mainGrid) { mainGrid.setAttribute('data-rv-append', '1'); return mainGrid; } 382 } 383 // legacy block disabled -> anchor before footer (do not hide it) 384 return document.querySelector('.ty-footer-grid, .ty-footer, #tygh_footer') || null; 385 } 386 387 function render() { 388 var host = findContainer(); 389 if (!host) return; 390 var current = currentItem(); 391 var items = repairStoredImages(getItems()).filter(function (x) { return !sameItem(x, current); }); 392 if (!items.length) return; // nothing yet â leave old block as-is 393 // build hybrid block, hide old widget 394 var mount = document.getElementById('rf_rv_hybrid'); 395 if (!mount) { 396 mount = document.createElement('div'); 397 mount.id = 'rf_rv_hybrid'; 398 mount.className = 'rf-rv'; 399 if (host.getAttribute && host.getAttribute('data-rv-append')) host.appendChild(mount); 400 else host.parentNode.insertBefore(mount, host); 401 } 402 if (!mount.querySelector('.rf-rv__grid--products')) { 403 mount.innerHTML = '<section class="rf-rv__section rf-rv__section--categories"><h2 class="rf-rv__title ty-block__title">ÐÑоÑмоÑÑеннÑе каÑегоÑии</h2><div class="grid-list ut2-gl rf-rv__grid rf-rv__grid--categories"></div></section><section class="rf-rv__section rf-rv__section--products"><h2 class="rf-rv__title ty-block__title">ÐÑоÑмоÑÑеннÑе ÑоваÑÑ</h2><div class="grid-list ut2-gl rf-rv__grid rf-rv__grid--products"></div></section>'; 404 } 405 if (host.getAttribute && host.getAttribute('data-rv-legacy')) host.style.display = 'none'; 406 renderItems(mount, items); 407 // background refresh from ES (fresh price/img) 408 var t0 = performance.now(); 409 var rvWant = ''; 410 if (window.rfCards && typeof window.rfCards.render === 'function') { 411 // Ñвой локалÑнÑй ÑпиÑок оÑдаÑм как поÑÑдок и ÑоÑÑав; каÑÑоÑки пÑидÑÑ Ñвежими Ñ ÑеÑвеÑа 412 var rvIds = [];
413 try { 414 getItems().forEach(function (x) { 415 var pid = parseInt(x.product_id || x.item_id || 0, 10); 416 if (itemType(x) === 'product' && pid && rvIds.indexOf(pid) === -1) { rvIds.push(pid); } 417 }); 418 } catch (e) {} 419 rvWant = '&rf_cards=1' + (rvIds.length ? '&ids=' + encodeURIComponent(rvIds.slice(0, 24).join(',')) : ''); 420 } 421 fetch(API + '.list' + rvWant + '&uid=' + encodeURIComponent(uid()) + '&limit=12&exclude_type=' + encodeURIComponent(current ? current.item_type : '') + '&exclude_item_id=' + encodeURIComponent(current ? current.item_id : ''), { credentials: 'same-origin' }) 422 .then(function (r) { return r.json(); }) 423 .then(function (res) { 424 if (window.console) console.log('[rf_rv] ES refresh ' + Math.round(performance.now() - t0) + 'ms, items=' + (res.items ? res.items.length : 0)); 425 if (res && res.cards && res.cards.length && res.render_context) { 426 rvCards = res.cards; rvCtx = res.render_context; 427 } 428 if (res && res.ok && res.items && res.items.length) { 429 var fresh = res.items.map(function (h) { var c = h.card || {}; c.item_type = c.item_type || h.item_type || ''; c.type = c.type || c.item_type; c.item_id = c.item_id || h.item_id || h.product_id || h.category_id; c.product_id = c.product_id || h.product_id || 0; c.category_id = c.category_id || h.category_id || 0; return normalizeItemCard(c); }).filter(isRenderableRvItem); 430 if (fresh.length) { 431 // MERGE (do not clobber): refresh LS items with ES data, keep LS order, append ES-only 432 var esById = {}; fresh.forEach(function (c) { esById[itemKey(c)] = c; }); 433 var ls = repairStoredImages(getItems()); 434 var merged = ls.map(function (x) { 435 var e = esById[itemKey(x)] || fresh.filter(function (c) { return sameItem(c, x); })[0]; 436 if (!e) return x; 437 if ((!e.imgs || !e.imgs.length) && x.imgs && x.imgs.length) e.imgs = x.imgs; 438 if (!e.img && x.img) e.img = x.img; 439 if (!e.item_type && x.item_type) e.item_type = e.type = x.item_type; 440 if (!e.item_id && x.item_id) e.item_id = x.item_id; 441 if (!e.category_id && x.category_id) e.category_id = x.category_id; 442 return e; 443 }); 444 fresh.forEach(function (c) { 445 var seen = merged.some(function (x) { return sameItem(x, c); }); 446 if (!seen) merged.push(c); 447 }); 448 merged = repairStoredImages(merged); 449 setItems(merged); 450 var vis = merged.filter(function (x) { return !sameItem(x, current); }); 451 renderItems(mount, vis); 452 } 453 } 454 }).catch(function () {}); 455 } 456 457 function run() { try { capture(); render(); } catch (e) { if (window.console) console.warn('[rf_rv]', e); } } 458 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', run); else run(); 459 460 // dwell: active foreground time on the product/category page, sent once on exit 461 (function () { 462 var cur = currentItem(); if (!cur || !cur.item_id) return; 463 var start = Date.now(), acc = 0, active = true, sent = false; 464 function flush() { if (active) { acc += Date.now() - start; start = Date.now(); } } 465 function sendDwell() { 466 flush(); if (sent || acc < 500) return; sent = true; 467 var body = 'uid=' + encodeURIComponent(uid()) + '&item_type=' + encodeURIComponent(cur.item_type) + '&item_id=' + encodeURIComponent(cur.item_id) + '&product_id=' + encodeURIComponent(cur.product_id || '') + '&category_id=' + encodeURIComponent(cur.category_id || '') + '&tk=' + encodeURIComponent(window.rfTk || '') + '&dwell_ms=' + acc;
468 try { navigator.sendBeacon(API + '.track', new Blob([body], { type: 'application/x-www-form-urlencoded' })); } catch (e) {} 469 } 470 document.addEventListener('visibilitychange', function () { 471 if (document.hidden) { flush(); active = false; sendDwell(); } else { start = Date.now(); active = true; } 472 }); 473 window.addEventListener('pagehide', sendDwell); 474 })(); 475})(); 476 477(function () { 478 if (window.__kitWishAjax) { return; } 479 window.__kitWishAjax = 1; 480 481 var enc = encodeURIComponent; 482 483 function getHash() { 484 var el = document.querySelector('input[name="security_hash"]'); 485 if (el && el.value) { return Promise.resolve(el.value); } 486 return fetch('/index.php?dispatch=my_changes_csrf.refresh', { credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest' } }) 487 .then(function (r) { return r.json(); }) 488 .then(function (j) { return j.security_hash || ''; }) 489 .catch(function () { return ''; }); 490 } 491 492 /* СÑÑÑÑик в Ñапке: ÑеÑÐ²ÐµÑ Ð½Ðµ ÑендеÑÐ¸Ñ .count пÑи нÑле â ÑоздаÑм/ÑдалÑем Ñами. */ 493 function bump(d) { 494 [].forEach.call(document.querySelectorAll('[id="abt__ut2_wishlist_count"]'), function (box) { 495 var wc = box.querySelector('.count'), link = box.querySelector('a'); 496 var n = Math.max(0, (wc ? parseInt(wc.textContent, 10) || 0 : 0) + d); 497 var sticky = !!(link && (link.className || '').indexOf('sticky-panel') > -1); 498 if (n > 0) { 499 if (!wc) { 500 var icon = box.querySelector('i'); 501 wc = document.createElement(sticky ? 'em' : 'span'); 502 wc.className = 'count'; 503 if (sticky && icon) { icon.appendChild(wc); } 504 else if (icon && icon.parentNode) { icon.parentNode.insertBefore(wc, icon.nextSibling); } 505 else if (link) { link.appendChild(wc); } 506 else { return; } 507 } 508 wc.textContent = String(n); 509 } else if (wc) { 510 wc.parentNode.removeChild(wc); 511 } 512 }); 513 } 514 515 function toast(msg, kind) { if (window.kitToastCo) { window.kitToastCo(msg, kind); } } 516 517 /* СеÑдеÑки виÑÑинÑ: каÑалог/ленÑа â `a.ut2-add-to-wish` Ñ i
517d `button_wishlist_<id>`, 518 каÑÑоÑка ÑоваÑа â `button.rf-fav` (id Ñ Ð½ÐµÑ Ð½ÐµÑ, ÑÐ¾Ð²Ð°Ñ Ð±ÐµÑÑм из dispatch-имени). 519 Ðбе ведÑÑ ÑÐµÐ±Ñ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ð¾: клик пеÑеклÑÑÐ°ÐµÑ Ð¸Ð·Ð±Ñанное, клаÑÑ `active` â залиÑое ÑеÑдÑе. */ 520 var WISH_SEL = '.ut2-add-to-wish, .rf-fav'; 521 522 function pidOf(el) { 523 var m = String(el.id || '').match(/button_wishlist_(\d+)/); 524 if (m) { return m[1]; } 525 m = String(el.getAttribute('data-ca-dispatch') || el.getAttribute('name') || '').match(/wishlist\.add\.\.(\d+)/); 526 return m ? m[1] : ''; 527 } 528 529 /* ===== подÑвеÑка Ñже добавленнÑÑ (23.08.2026) ===== 530 ÐаÑÑоÑки ленÑÑ ÑиÑÑÐµÑ HomeFeed, ÐµÑ HTML кÑÑиÑÑеÑÑÑ Ð¾Ð±Ñим кÑÑем на вÑÐµÑ â оÑмеÑÐºÑ 531 «в избÑанном» ÑеÑÐ²ÐµÑ ÑÑда положиÑÑ Ð½Ðµ можеÑ. ÐабиÑаем ÑпиÑок id и кÑаÑим Ñами; 532 пеÑвÑÑ ÑÑÑаниÑÑ Ð»Ð¸ÑÑинга ÑеÑÐ²ÐµÑ Ð¸ Ñак оÑдаÑÑ Ñ ÐºÐ»Ð°ÑÑом active, ей ÑÑо не меÑаеÑ. */ 533 var wishIds = null; 534 535 function paint(root) { 536 if (!wishIds) { return; } 537 [].forEach.call((root || document).querySelectorAll(WISH_SEL), function (el) { 538 var pid = pidOf(el); 539 /* wish_ids â полнÑй ÑеÑÑионнÑй ÑпиÑок, поÑÑÐ¾Ð¼Ñ ÐºÑаÑим Ð ÐÐÐ ÑÑоÑонÑ: 540 каÑÑоÑка ÑоваÑа пÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ð¸Ð· обÑего кÑÑа блока и Ñвоего ÑоÑÑоÑÐ½Ð¸Ñ Ð½Ðµ Ð·Ð½Ð°ÐµÑ */ 541 if (pid) { el.classList.toggle('active', !!wishIds[pid]); } 542 }); 543 } 544 545 function loadWishIds() { 546 return fetch('/index.php?dispatch=my_changes.wish_ids', { credentials: 'same-origin', cache: 'no-store', headers: { 'X-Requested-With': 'XMLHttpRequest' } }) 547 .then(function (r) { return r.json(); }) 548 .then(function (j) { 549 wishIds = {}; 550 (j && j.ids || []).forEach(function (id) { wishIds[String(id)] = 1; }); 551 paint(); 552 }) 553 .catch(function () {}); 554 } 555 556 loadWishIds(); 557 558 /* каÑÑоÑки, догÑÑженнÑе ленÑой/пагинаÑией, кÑаÑим по меÑе поÑÐ²Ð»ÐµÐ½Ð¸Ñ */ 559 new MutationObserver(function (muts) { 560 for (var i = 0; i < muts.length; i++) { 561 var added = muts[i].addedNodes; 562 for (var j = 0; j < added.length; j++) { 563 var n = added[j]; 564 if (n.nodeType === 1 && n.querySelector && n.querySelector(WISH_SEL)) { paint(n); } 565 } 566 } 567 }).observe(document.body, { childList: true, subtree: true }); 568 569 document.addEventListener('click', function (e) { 570 var b = e.target && e.target.closest ? e.target.closest(WISH_SEL) : null; 571 if (!b) { return; } 572 var pid = pidOf(b); 573 if (!pid) { return; } // не наÑа кнопка â оÑдаÑм ÑÑаÑÐ½Ð¾Ð¼Ñ Ð¾Ð±ÑабоÑÑÐ¸ÐºÑ 574 575 e.preventDefault(); 576 e.stopPropagation(); 577 578 if (b.getAttribute('data-kit-busy')) { return; } 579 b.setAttribute('data-kit-busy', '1'); 580 var free = function () { b.removeAttribute('data-kit-busy'); }; 581 582 var wasOn = b.classList.contains('active'); 583 // опÑимиÑÑиÑно: ÑеÑдеÑко и ÑÑÑÑÑик ÑеагиÑÑÑÑ ÑÑазÑ, ÑеÑÑ Ð´Ð¾Ð³Ð¾Ð½ÑÐµÑ 584 b.classList.toggle('active', !wasOn); 585 bump(wasOn ? -1 : 1); 586 toast(wasOn ? 'Ð¢Ð¾Ð²Ð°Ñ ÑдалÑн из избÑанного' : 'Ð¢Ð¾Ð²Ð°Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½ в избÑанное', wasOn ? 'remove' : ''); 587 588 getHash().then(function (h) { 589 var body; 590 if (wasOn) { 591 /* Ñдаление â ÑÑаÑнÑм dispatch по product_id Ñ force: клÑÑ ÑпиÑка (cart_id) 592 клиенÑÑ Ð½ÐµÐ¸Ð·Ð²ÐµÑÑен, а force-ваÑÐ¸Ð°Ð½Ñ Ð¿ÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ Ð¸Ð¼ÐµÐ½Ð½Ð¾ product_id */ 593 body = 'security_hash=' + enc(h) + '&redirect_url=%2F&' + enc('dispatch[wishlist.delete.force.' + pid + ']') + '=1'; 594 return fetch('/index.php', { 595 method: 'POST', credentials: 'same-origin', 596 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' }, 597 body: body 598 }); 599 } 600 body = 'security_hash=' + enc(h) + '&product_data[' + pid + '][product_id]=' + pid + '&product_data[' + pid + '][amount]=1'; 601 return fetch('/index.php?dispatch=wishlist.add', { 602 method: 'POST', credentials: 'same-origin', 603 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' }, 604 body: body 605 }); 606 }).then(function () { 607 // вÑе кнопки ÑÑого ÑоваÑа на ÑÑÑаниÑе (каÑÑоÑка Ð¼Ð¾Ð¶ÐµÑ Ð²ÑÑÑеÑаÑÑÑÑ Ð´Ð²Ð°Ð¶Ð´Ñ) 608 [].forEach.call(document.querySelectorAll(WISH_SEL), function (el) { 609 if (pidOf(el) === pid) { el.classList.toggle('active', !wasOn); } 610 }); 611 if (wishIds) { if (wasOn) { delete wishIds[pid]; } else { wishIds[pid] = 1; } } 612 free(); 613 }, function () { 614 // ÑеÑÑ Ð½Ðµ оÑвеÑила â оÑкаÑÑваем опÑимиÑÑиÑное ÑоÑÑоÑние 615 b.classList.toggle('active', wasOn); 616 bump(wasOn ? 1 : -1); 617 toast('Ðе ÑдалоÑÑ Ð¸Ð·Ð¼ÐµÐ½Ð¸ÑÑ Ð¸Ð·Ð±Ñанное', 'error'); 618 free(); 619 }); 620 }, true); 621})();
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.