1/* Ðоведение каÑалога: поиÑк, оÑбоÑ, ÑоÑÑиÑовка и подгÑÑзка поÑÑий. 2 3 РмакеÑе оÑÐ±Ð¾Ñ Ð±Ñл клиенÑÑким â вÑе набоÑÑ Ð»ÐµÐ¶Ð°Ð»Ð¸ в ÑазмеÑке. Ðа ÑайÑе вÑдаÑÑ ÑÑиÑÐ°ÐµÑ ÑеÑвеÑ: 4 оÑÐ±Ð¾Ñ Ð¸ ÑоÑÑиÑовка менÑÑÑ Ð¿Ð°ÑамеÑÑÑ Ð°Ð´ÑеÑа, а ÑÑÑока поиÑка, «ÐоказаÑÑ ÐµÑÑ» и пеÑеÑÑÑÑ Ð¿Ð¾Ð´ 5 оÑкÑÑÑÑм окном оÑбоÑа подÑÑгиваÑÑ Ð²ÑдаÑÑ Ñем же компоненÑом. УÑÑÑойÑÑво панели оÑбоÑа Ñеликом â 6 docs/reference/45-catalog-filters.md. 7 8 ÐоÑÑÐ¸Ñ Ð¸ поиÑк Ñ Ð¾Ð´ÑÑ Ð¿Ð¾ Ð¾Ð´Ð½Ð¾Ð¼Ñ Ð°Ð´ÑеÑÑ â ÑекÑÑÐ°Ñ ÑÑÑаниÑа Ñ `ajax=items`. ÐнаÑиÑ, вÑдаÑа 9 ÑÑÑоки поиÑка и вÑдаÑа по адÑеÑÑ `/katalog/?q=â¦` ÑовпадаÑÑ Ð¿Ð¾ поÑÑÑоениÑ: ÑÑо один и ÑÐ¾Ñ Ð¶Ðµ 10 оÑÐ²ÐµÑ Ð¾Ð´Ð½Ð¾Ð³Ð¾ и Ñого же компоненÑа, а не два Ð¿Ð¾Ñ Ð¾Ð¶Ð¸Ñ Ð¾ÑбоÑа. ÐÑÐµÑ Ð¿Ñи ÑÑом клаÑÑ CatalogSearch â 11 ÑÐ¾Ñ Ð¶Ðµ, коÑоÑÑм оÑвеÑÐ°ÐµÑ Ð¾ÐºÐ½Ð¾ по лÑпе. 12 13 ЧиÑла взÑÑÑ Ñ Ð¾ÐºÐ½Ð° поиÑка и не вÑдÑÐ¼Ð°Ð½Ñ Ð·Ð°Ð½Ð¾Ð²Ð¾ (docs/reference/03-search.md): 14 2 знака коÑоÑе запÑÐ¾Ñ Ð½Ðµ даÑÑ Ð½Ð¸ одной оÑновÑ, и поиÑк пеÑÐµÑ Ð¾Ð´Ð¸Ñ Ð½Ð° подÑÑÑокÑ; 15 200 Ð¼Ñ Ð¿Ñи обÑÑной ÑкоÑоÑÑи набоÑа Ð¼ÐµÐ¶Ð´Ñ Ð½Ð°Ð¶Ð°ÑиÑми 160â250 Ð¼Ñ â запÑÐ¾Ñ ÑÑ Ð¾Ð´Ð¸Ñ Ñаз на Ñлово; 16 50 оÑвеÑов кÑÑ Ð½Ð° ÑеанÑ: ÑÑиÑание поÑледнего знака и возвÑÐ°Ñ Ðº пÑÐµÐ¶Ð½ÐµÐ¼Ñ Ð·Ð°Ð¿ÑоÑÑ Ð½Ðµ ÑоздаÑÑ Ð·Ð°Ð¿ÑоÑа. 17 18 СкÑÐ¸Ð¿Ñ Ð¿Ð¾Ð´ÐºÐ»ÑÑаÑÑ Ð´Ð²Ðµ ÑÑÑаниÑÑ â каÑалог и Ñаздел. СÑÑока на Ð½Ð¸Ñ Ð¾Ð´Ð½Ð° и Ñа же: Ñаздел 19 оÑвеÑÐ°ÐµÑ Ñем же поÑÑионнÑм оÑвеÑом, ÑолÑко ÑÑзив вÑдаÑÑ Ñвоим ÑоÑÑавом. ÐÑÑ, ÑÑо каÑаеÑÑÑ 20 ÑÑÑоки, вклÑÑаеÑÑÑ Ð¿Ñи налиÑии полÑ: в Ñазделе без набоÑов его Ð½ÐµÑ Ð²Ð¾Ð²Ñе. */ 21(function () { 22 var form = document.querySelector('[data-catalog-filters]'); 23 var grid = document.getElementById('grid'); 24 25 /* ââ Ðкно оÑбоÑа живÑÑ ÑолÑко на Ñзком ÑкÑане ââââââââââââââââââââââââââââââ */ 26 /* Ðа ÑиÑоком Ð¿Ð°Ð½ÐµÐ»Ñ ÑÑÐ¾Ð¸Ñ ÐºÐ¾Ð»Ð¾Ð½ÐºÐ¾Ð¹ Ñлева пÑÑмо в поÑоке: окно и его обÑÑÑки ÑÑановÑÑÑÑ 27 display:contents и Ñвоей коÑобки не ÑиÑÑÑÑ. ÐÑаниÑÑ Ð¼Ð¾Ð¶Ð½Ð¾ пеÑеÑеÑÑ Ñ Ð¾ÑкÑÑÑÑм окном â 28 планÑÐµÑ Ð¸Ð· поÑÑÑеÑа в ландÑаÑÑ Ð´Ð°ÑÑ Ñовно 1024 px. Тогда Ð¿Ð°Ð½ÐµÐ»Ñ ÑÑ Ð¾Ð´Ð¸Ñ Ð² поÑок, а окно 29 оÑÑаÑÑÑÑ Ð¼Ð¾Ð´Ð°Ð»ÑнÑм: ÑÑÑаниÑа под ним недоÑÑÑпна, а закÑÑÑÑ ÐµÑ Ð½ÐµÑем â кÑеÑÑик ÑпÑÑÑан 30 Ñем же lg. ÐакÑÑваем Ñами на пеÑÐµÑ Ð¾Ð´Ðµ ÑеÑез гÑаниÑÑ. */ 31 var panel = document.getElementById('filters-panel'); 32 var wide = window.matchMedia('(min-width: 64rem)'); 33 function keepInFlow() { if (panel && panel.open && wide.matches) panel.close(); } 34 keepInFlow(); 35 if (wide.addEventListener) wide.addEventListener('change', keepInFlow); 36 37 if (!grid) return; 38 39 var q = document.getElementById('q'); 40 var reduce = window.matchMedia('(prefers-reduced-motion: reduce)'); 41 42 /* ÐÑбоÑ, ÑоÑÑиÑовка и запÑÐ¾Ñ Ð¶Ð¸Ð²ÑÑ Ð² адÑеÑе ÑÑÑаниÑÑ: он и еÑÑÑ ÑоÑÑоÑние вÑдаÑи. 43 СоÑÑиÑовка, поÑÑиÑ, оÑÐ±Ð¾Ñ Ð¸ поиÑк ÑобиÑаÑÑ Ñвой адÑÐµÑ Ð¾ÑÑÑда, поÑÑÐ¾Ð¼Ñ Ð½Ð¸ одно ÑÑловие 44 не ÑеÑÑеÑÑÑ Ð¿Ð¾ доÑоге. */ 45 function params() { 46 var address = new URLSearchParams(window.location.search); 47 address.delete('ajax'); 48 address.delete('page'); 49 return address; 50 } 51 52 /* СÑÑока запÑоÑа из ÑобÑанного адÑеÑа. ÐапÑÑÑе оÑÑаÑÑÑÑ Ð·Ð°Ð¿ÑÑÑми: неÑколÑко оÑмеÑеннÑÑ 53 пÑнкÑов одной гÑÑÐ¿Ð¿Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ Ð¶Ð´ÑÑ ÑпиÑком ÑеÑез запÑÑÑÑ (pickList), а повÑÐ¾Ñ ÐºÐ»ÑÑа 54 PHP не ÑкладÑÐ²Ð°ÐµÑ â из «price=free&price=le100» до оÑбоÑа Ð´Ð¾Ñ Ð¾Ð´Ð¸Ñ ÑолÑко поÑледнее, 55 и вÑоÑой оÑмеÑеннÑй пÑÐ½ÐºÑ Ð¿Ñопадал молÑа. ÐапÑÑÐ°Ñ Ð² ÑÑÑоке запÑоÑа законна, и ÑеÑÐ²ÐµÑ 56 полÑÑÐ°ÐµÑ ÐµÑ Ñой же, ÑÑо и в виде %2C. */ 57 function query(address) { 58 return address.toString().replace(/%2C/g, ','); 59 } 60 61 // адÑÐµÑ ÑÑÑаниÑÑ Ñ ÑобÑанной ÑÑÑокой запÑоÑа â им Ñ Ð¾Ð´ÑÑ Ð¸ пеÑÐµÑ Ð¾Ð´Ñ, и обÑаÑÐµÐ½Ð¸Ñ Ð·Ð° вÑдаÑей 62 function link(address) { 63 var search = query(address); 64 return window.location.pathname + (search ? '?' + search : ''); 65 } 66 67 /* ââ СоÑÑиÑовка âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 68 [].slice.call(document.querySelectorAll('[data-sort]')).forEac
68h(function (btn) { 69 btn.addEventListener('click', function () { 70 var address = params(); 71 address.set('sort', btn.dataset.sort); 72 window.location.href = link(address); 73 }); 74 }); 75 76 /* ââ СÑÑÑÑики вÑдаÑи ââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 77 var moreWrap = document.querySelector('[data-more-wrap]'); 78 var moreBtn = document.querySelector('[data-more]'); 79 var moreCount = document.querySelector('[data-more-count]'); 80 var moreWord = document.querySelector('[data-more-word]'); 81 var shown = document.querySelector('[data-shown]'); 82 var shownBtn = document.querySelector('[data-shown-btn]'); 83 var empty = document.getElementById('empty'); 84 var emptyNote = document.querySelector('[data-empty]'); 85 /* ÐÑÑ Ð¾Ð´ в обÑий поиÑк из пÑÑÑой вÑдаÑи â ÑолÑко Ñ Ñаздела: Ð½Ð°Ð±Ð¾Ñ Ð¼Ð¾Ð³ лежаÑÑ Ð² дÑÑгом. 86 РкаÑалоге иÑкаÑÑ ÑиÑе Ñже негде, и ÑÑÑоки Ñам неÑ. */ 87 var searchAll = document.querySelector('[data-search-all]'); 88 var searchAllLink = document.querySelector('[data-search-all-link]'); 89 90 function plural(n, one, few, many) { 91 var t = n % 10, h = n % 100; 92 if (h >= 11 && h <= 14) return many; 93 if (t === 1) return one; 94 if (t >= 2 && t <= 4) return few; 95 return many; 96 } 97 98 /* ÐÑÐ²ÐµÑ Ð¿ÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ñо ÑÑÑÑом пеÑвой ÑÑÑокой (Ñаблон items.php): ÑколÑко найдено, ÑколÑко 99 показано, ÑколÑко оÑÑалоÑÑ Ð¸ ÑÑо пиÑаÑÑ Ð¿Ñи пÑÑÑой вÑдаÑе. ЧиÑла беÑÑм оÑÑÑда, а не 100 ÑÑиÑаем заново â инаÑе пÑавила поÑÑии пÑиÑлоÑÑ Ð±Ñ Ð´ÐµÑжаÑÑ Ð² двÑÑ Ð¼ÐµÑÑÐ°Ñ . */ 101 function parse(html) { 102 var box = document.createElement('div'); 103 box.innerHTML = html.trim(); 104 105 var meta = box.querySelector('[data-catalog-meta]'); 106 if (meta) box.removeChild(meta); 107 108 return { meta: meta ? meta.dataset : null, cards: box }; 109 } 110 111 function counters(meta) { 112 if (!meta) return; 113 114 var found = parseInt(meta.found, 10); 115 var left = parseInt(meta.left, 10); 116 117 if (shown) shown.textContent = meta.shown; 118 if (moreWrap) moreWrap.hidden = left === 0; 119 if (moreCount) moreCount.textContent = left; 120 if (moreWord) moreWord.textContent = plural(left, 'набоÑ', 'набоÑа', 'набоÑов'); 121 if (moreBtn) moreBtn.dataset.page = meta.page; 122 if (empty) empty.hidden = found > 0; 123 if (emptyNote && meta.empty) emptyNote.textContent = meta.empty; 124 125 // адÑÐµÑ Ð¾Ð±Ñего поиÑка пÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ñ Ð¾ÑвеÑом: пÑÑÑой â знаÑÐ¸Ñ Ð²ÐµÑÑи некÑда (запÑоÑа неÑ) 126 if (searchAll && meta.all !== undefined) { 127 searchAll.hidden = meta.all === ''; 128 if (searchAllLink && meta.all !== '') searchAllLink.href = meta.all; 129 } 130 131 if (shownBtn) { 132 shownBtn.textContent = found; 133 // Ñлово ÑÑÐ¾Ð¸Ñ Ð² кнопке ÑекÑÑом, а не оÑделÑнÑм ÑлеменÑом â Ñак же, как в макеÑе 134 if (shownBtn.nextSibling) { 135 shownBtn.nextSibling.textContent = ' ' + plural(found, 'набоÑ', 'набоÑа', 'набоÑов'); 136 } 137 } 138 } 139 140 /* ÐÑÐ²ÐµÑ Ð·Ð°Ð¼ÐµÐ½ÑÐµÑ ÑеÑÐºÑ Ñеликом: каÑÑоÑки пеÑвой поÑÑии нового оÑбоÑа. ÐÑежние ÑнимаÑÑÑÑ, 141 инаÑе к вÑдаÑе по новÑм ÑÑловиÑм пÑипиÑалиÑÑ Ð±Ñ Ð½Ð°Ð±Ð¾ÑÑ, под Ð½Ð¸Ñ Ð½Ðµ подоÑедÑие. */ 142 function paint(html) { 143 var answer = parse(html); 144 grid.innerHTML = ''; 145 while (answer.cards.firstElementChild) grid.appendChild(answer.cards.firstElementChild); 146 counters(answer.meta); 147 } 148 149 /* ÐдÑÐµÑ Ð¾ÑÑÐ°Ð¶Ð°ÐµÑ ÑоÑÑоÑние вÑдаÑи: ÑÑÑÐ»ÐºÑ Ð¼Ð¾Ð¶Ð½Ð¾ оÑдаÑÑ, а обновление ÑÑÑаниÑÑ Ð²Ð¾Ð·Ð²ÑаÑаеÑ
150 Ñовно Ñо, ÑÑо бÑло на ÑкÑане. replaceState, а не pushState: ни Ð½Ð°Ð±Ð¾Ñ ÑÑазÑ, ни пеÑÐµÐ±Ð¾Ñ 151 Ñлажков не Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¾ÑÑавлÑÑÑ Ð¿Ð¾ запиÑи иÑÑоÑии на каждое нажаÑие. */ 152 function remember(address) { 153 history.replaceState(null, '', link(address)); 154 } 155 156 /* ââ ÐÑÐ±Ð¾Ñ ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 157 /* Ðа ÑиÑоком ÑкÑане Ð¿Ð°Ð½ÐµÐ»Ñ ÑÑÐ¾Ð¸Ñ ÐºÐ¾Ð»Ð¾Ð½ÐºÐ¾Ð¹ Ñлева, ÑÑдом Ñ Ð²ÑдаÑей: Ñам оÑмеÑка пÑименÑеÑÑÑ 158 ÑÑазÑ, ÑÑÑаниÑей â Ñем же ÑпоÑобом, ÑÑо и вÑÐ±Ð¾Ñ ÑоÑÑиÑовки. ÐдаÑÑ Ð½ÐµÑего, лиÑний Ñаг 159 «пÑимениÑÑ» бÑл Ð±Ñ ÑабоÑой на пÑÑÑом меÑÑе. 160 161 Ðа ÑелеÑоне Ð¿Ð°Ð½ÐµÐ»Ñ â окно повеÑÑ Ð²ÑдаÑи, и Ñа же немедленноÑÑÑ Ð·Ð°ÐºÑÑвала Ð±Ñ ÐµÐ³Ð¾ поÑле 162 каждой галоÑки: набÑаÑÑ Ð´Ð²Ð° ÑÑÐ»Ð¾Ð²Ð¸Ñ Ð±Ñло нелÑзÑ, окно пÑÐ¸Ñ Ð¾Ð´Ð¸Ð»Ð¾ÑÑ Ð²ÑзÑваÑÑ Ð·Ð°Ð½Ð¾Ð²Ð¾. 163 ÐоÑÑÐ¾Ð¼Ñ Ð¿Ð¾ÐºÐ° Ð¿Ð°Ð½ÐµÐ»Ñ Ð¾ÑкÑÑÑа окном, оÑмеÑка не пеÑезагÑÑÐ¶Ð°ÐµÑ ÑÑÑаниÑÑ, а пеÑеÑÑиÑÑÐ²Ð°ÐµÑ 164 вÑдаÑÑ Ð¿Ð¾Ð´ окном Ñем же оÑвеÑом `ajax=items`, коÑоÑÑм оÑвеÑÐ°ÐµÑ ÑÑÑока поиÑка. СÑÑÑ 165 пÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ñ Ð¾ÑвеÑом, поÑÑÐ¾Ð¼Ñ Â«ÐоказаÑÑ N набоÑов» назÑÐ²Ð°ÐµÑ Ñо, ÑÑо Ñеловек ÑвидиÑ, 166 закÑÑв окно, а не Ñо, ÑÑо бÑло до оÑбоÑа. 167 168 УÑловие Â«Ð¿Ð°Ð½ÐµÐ»Ñ Ð¾ÑкÑÑÑа окном» â ÑÑо `panel.open`: на ÑиÑоком ÑкÑане окно закÑÑÑо вÑегда 169 (keepInFlow вÑÑе), а Ð¿Ð°Ð½ÐµÐ»Ñ Ð²Ð¸Ð´Ð½Ð° за ÑÑÑÑ `lg:contents`. */ 170 171 var boxes = form ? [].slice.call(form.querySelectorAll('input[type="checkbox"]')) : []; 172 var filtersCount = document.querySelector('[data-filters-count]'); 173 var showBtn = shownBtn ? shownBtn.closest('button') : null; 174 var pick = null; 175 var pickTurn = 0; 176 177 // ÑÑÑока поиÑка ÑÑÐ¾Ð¸Ñ Ð²Ð½Ðµ ÑоÑÐ¼Ñ Ð¾ÑбоÑа, но в Ñом же адÑеÑе: пÑиводим ÐµÑ Ñак же, как поиÑк 178 function typed() { 179 return q ? q.value.replace(/\s+/g, ' ').trim() : ''; 180 } 181 182 /* ÐдÑÐµÑ Ð²ÑдаÑи по нÑнеÑÐ½ÐµÐ¼Ñ ÑоÑÑоÑÐ½Ð¸Ñ Ð¿Ð°Ð½ÐµÐ»Ð¸. СоÑÑиÑовка и вÑÑ, Ñего Ð¿Ð°Ð½ÐµÐ»Ñ Ð½Ðµ каÑаеÑÑÑ, 183 беÑÑÑÑÑ Ð¸Ð· адÑеÑа ÑÑÑаниÑÑ; оÑÐ±Ð¾Ñ â из ÑÐ°Ð¼Ð¸Ñ Ð¿Ð¾Ð»ÐµÐ¹, а не из адÑеÑа: в окне он мог 184 измениÑÑÑÑ Ð¸ еÑÑ Ð½Ðµ пÑимениÑÑÑÑ. ÐÐ¾Ð¼ÐµÑ Ð¿Ð¾ÑÑии params() ÑÐ½Ð¸Ð¼Ð°ÐµÑ â оÑÐ±Ð¾Ñ Ð½Ð°ÑÐ¸Ð½Ð°ÐµÑ ÑÑÑÑ 185 заново. */ 186 function chosen() { 187 var address = params(); 188 189 ['price', 'vol', 'rev'].forEach(function (name) { address.delete(name); }); 190 191 var groups = {}; 192 boxes.forEach(function (box) { 193 if (!box.checked) return; 194 (groups[box.name] = groups[box.name] || []).push(box.value); 195 }); 196 Object.keys(groups).forEach(function (name) { address.set(name, groups[name].join(',')); }); 197 198 var value = typed(); 199 if (value === '') address.delete('q'); else address.set('q', value); 200 201 return address; 202 } 203 204 // ЧиÑло в кÑÑжке Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¸ «ФилÑÑÑÑ»: на ÑелеÑоне Ð¿Ð°Ð½ÐµÐ»Ñ ÑпÑÑÑана, и кÑÑжок â единÑÑвеннÑй 205 // пÑизнак, ÑÑо Ñеловек ÑÑо-Ñо оÑмеÑил. СÑиÑÐ°ÐµÑ Ð¾Ð½ именно оÑмеÑенное, а не ÑÑженноÑÑÑ 206 // вÑдаÑи: Ñлажок ÑÑÐ¾Ð¸Ñ Ñ Ñого мгновениÑ, как его нажали, еÑÑ Ð´Ð¾ оÑвеÑа ÑеÑвеÑа. 207 function badge() { 208 if (!filtersCount) return; 209 210 var on = boxes.filter(function (box) { return box.checked; }).length; 211 filtersCount.textContent = on;
212 filtersCount.hidden = on === 0; 213 } 214 215 /* ÐеÑеÑÑÑÑ Ð²ÑдаÑи под оÑкÑÑÑÑм окном. ÐÑежнее обÑаÑение оÑменÑеÑÑÑ, а Ð½Ð¾Ð¼ÐµÑ Ð¾Ð±ÑаÑÐµÐ½Ð¸Ñ 216 оÑлиÑÐ°ÐµÑ Ð¾ÑÐ²ÐµÑ Ð½Ð° оÑмеÑенное ÑейÑÐ°Ñ Ð¾Ñ Ð¾Ð¿Ð¾Ð·Ð´Ð°Ð²Ñего â Ñе же две меÑÑ, ÑÑо Ñ ÑÑÑоки 217 поиÑка, и по Ñой же пÑиÑине: оÑÐ¼ÐµÐ½Ñ Ð¼Ð°Ð»Ð¾, оÑпÑавленнÑй до Ð½ÐµÑ Ð¾ÑÐ²ÐµÑ Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑийÑи 218 поÑле нового. */ 219 function recount() { 220 if (pick) { pick.abort(); pick = null; } 221 var ticket = ++pickTurn; 222 223 var address = chosen(); 224 var request = new URLSearchParams(address); 225 request.set('ajax', 'items'); 226 227 pick = window.AbortController ? new AbortController() : null; 228 if (showBtn) showBtn.setAttribute('aria-busy', 'true'); 229 230 window.fetch(link(request), { 231 headers: { 'X-Requested-With': 'fetch' }, 232 credentials: 'same-origin', 233 signal: pick ? pick.signal : undefined 234 }).then(function (response) { 235 return response.ok ? response.text() : null; 236 }).then(function (html) { 237 if (ticket !== pickTurn) return; 238 239 // помеÑÐºÑ ÑÐ½Ð¸Ð¼Ð°ÐµÑ Ñо обÑаÑение, коÑоÑое ÐµÑ Ð¿Ð¾ÑÑавило, â и когда оÑÐ²ÐµÑ Ð¿ÑиÑÑл, 240 // и когда ÑеÑÐ²ÐµÑ Ð¾Ñказал: инаÑе на оÑказе она оÑÑалаÑÑ Ð±Ñ Ð²Ð¸ÑеÑÑ Ð½Ð°Ð²Ñегда 241 if (showBtn) showBtn.removeAttribute('aria-busy'); 242 if (html === null || html === undefined) return; 243 244 /* ÐдÑÐµÑ Ð¿ÐµÑепиÑÑваеÑÑÑ Ð²Ð¼ÐµÑÑе Ñ Ð²ÑдаÑей, а не до неÑ. ÐдÑÐµÑ â ÑоÑÑоÑние вÑдаÑи; 245 еÑли обÑаÑение не доÑло, он обеÑал Ð±Ñ Ð¾ÑбоÑ, коÑоÑого на ÑкÑане неÑ, а ÑÑÑлка 246 вела Ð±Ñ Ð½Ðµ ÑÑда, кÑда ÑмоÑÑел Ñеловек. ÐÑмеÑеннÑй Ñлажок в ÑÑом ÑлÑÑае оÑÑаÑÑÑÑ 247 оÑмеÑеннÑм â ÑÑо пÑоÑÑба, коÑоÑÐ°Ñ ÐµÑÑ Ð½Ðµ иÑполнилаÑÑ, и ÑнимаÑÑ ÐµÑ Ð·Ð° Ñеловека 248 Ð¼Ñ Ð½Ðµ впÑаве. */ 249 remember(address); 250 paint(html); 251 }).catch(function () { 252 /* ÐÑменÑнное обÑаÑение и обоÑÐ²Ð°Ð½Ð½Ð°Ñ ÑеÑÑ Ð²ÑглÑдÑÑ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ð¾: и вÑдаÑа, и адÑеÑ, 253 и ÑиÑло в кнопке оÑÑаÑÑÑÑ Ð¿Ñежними â Ñо еÑÑÑ ÑÑ Ð¾Ð´ÑÑÑÑ Ð¼ÐµÐ¶Ð´Ñ Ñобой. ÐомеÑÐºÑ 254 ÑÐ½Ð¸Ð¼Ð°ÐµÑ Ñо обÑаÑение, коÑоÑое ÐµÑ Ð¿Ð¾ÑÑавило. */ 255 if (ticket === pickTurn && showBtn) showBtn.removeAttribute('aria-busy'); 256 }); 257 } 258 259 function apply() { 260 badge(); 261 262 if (panel && panel.open && window.fetch) { 263 recount(); 264 return; 265 } 266 267 window.location.href = link(chosen()); 268 } 269 270 boxes.forEach(function (box) { box.addEventListener('change', apply); }); 271 272 /* СбÑоÑ. Ðнопок на ÑÑÑаниÑе две, и ÑабоÑа Ñ Ð½Ð¸Ñ ÑазнаÑ. 273 274 «СбÑоÑиÑÑ ÑилÑÑÑÑ» в панели ÑÐ½Ð¸Ð¼Ð°ÐµÑ Ð¾ÑмеÑки â и ÑолÑко Ð¸Ñ : ÑÑÑока поиÑка и ÑоÑÑиÑовка 275 ÑÑоÑÑ Ð²Ð½Ðµ панели, и ÑÑиÑаÑÑ Ð¸Ñ ÑÑой кнопкой знаÑило Ð±Ñ Ð¾ÑменÑÑÑ Ñо, Ñего Ñеловек 276 не оÑменÑл. РанÑÑе кнопка бÑла `type="reset"`, и не ÑабоÑала нигде: обÑÑнÑй ÑбÑÐ¾Ñ 277 возвÑаÑÐ°ÐµÑ Ð¿Ð¾Ð»Ñ Ðº напеÑаÑÐ°Ð½Ð½Ð¾Ð¼Ñ ÑеÑвеÑом, а ÑеÑÐ²ÐµÑ Ð¿ÐµÑаÑÐ°ÐµÑ Ð¸Ñ Ð¾ÑмеÑеннÑми по адÑеÑÑ â 278 галоÑки ÑнималиÑÑ Ð¸ ÑÑÑ Ð¶Ðµ возвÑаÑалиÑÑ. 279 280 «ÐоказаÑÑ Ð²Ñе N набоÑов» в пÑÑÑой вÑдаÑе ÑÐ½Ð¸Ð¼Ð°ÐµÑ Ð¸ оÑмеÑки, и запÑоÑ: обеÑание кнопки â 281 веÑÑ ÐºÐ°Ñалог. СоÑÑиÑÐ¾Ð²ÐºÑ Ð¾Ð½Ð° ÑÐ¾Ñ ÑанÑÐµÑ â ÑÑо поÑÑдок вÑдаÑи, а не ÑÑловие оÑбоÑа. */ 282 [].slice.call(document.querySelectorAll('[data-reset-filters]')).forEac
282h(function (btn) { 283 btn.addEventListener('click', function () { 284 boxes.forEach(function (box) { box.checked = false; }); 285 apply(); 286 }); 287 }); 288 289 [].slice.call(document.querySelectorAll('[data-reset-all]')).forEach(function (btn) { 290 btn.addEventListener('click', function () { 291 boxes.forEach(function (box) { box.checked = false; }); 292 if (q) q.value = ''; 293 apply(); 294 }); 295 }); 296 297 /* «ÐоказаÑÑ N набоÑов» закÑÑÐ²Ð°ÐµÑ Ð¾ÐºÐ½Ð¾ ÑазмеÑкой (command="close") â вÑдаÑа под ним Ñже 298 пеÑеÑÑиÑана, пÑименÑÑÑ Ð½ÐµÑего. ÐÑÑаÑÑÑÑ Ð¿Ð¾Ð´Ð²ÐµÑÑи к ней глаза: ÑÑÑаниÑа оÑÑалаÑÑ Ñам, 299 где Ñеловек нажал «ФилÑÑÑÑ», и без пÑокÑÑÑки он не ÑвидиÑ, ÑÑо изменилоÑÑ. ÐÑокÑÑÑка 300 идÑÑ Ð¿Ð¾ ÑобÑÑÐ¸Ñ close, а не ÑÑазÑ: пока окно оÑкÑÑÑо, ÑÑÑаниÑа под ним не пÑокÑÑÑиваеÑÑÑ. */ 301 var toResults = false; 302 303 if (showBtn && panel) { 304 showBtn.addEventListener('click', function () { toResults = true; }); 305 panel.addEventListener('close', function () { 306 if (!toResults) return; 307 toResults = false; 308 309 var header = document.querySelector('header'); 310 var gap = (header ? header.getBoundingClientRect().height : 0) + 16; 311 var top = grid.getBoundingClientRect().top + window.scrollY - gap; 312 313 window.scrollTo({ top: Math.max(0, top), behavior: reduce.matches ? 'auto' : 'smooth' }); 314 }); 315 } 316 317 /* ââ ÐодгÑÑзка поÑÑии âââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 318 if (moreBtn) { 319 moreBtn.addEventListener('click', function () { 320 var next = parseInt(moreBtn.dataset.page || '1', 10) + 1; 321 var request = params(); 322 request.set('page', String(next)); 323 request.set('ajax', 'items'); 324 325 moreBtn.disabled = true; 326 fetch(link(request), { headers: { 'X-Requested-With': 'fetch' } }) 327 .then(function (response) { return response.text(); }) 328 .then(function (html) { 329 var answer = parse(html); 330 while (answer.cards.firstElementChild) grid.appendChild(answer.cards.firstElementChild); 331 counters(answer.meta); 332 }) 333 .catch(function () { 334 // подгÑÑзка не ÑдалаÑÑ â оÑÑавлÑем ÐºÐ½Ð¾Ð¿ÐºÑ ÑабоÑей, ÑÑÐ¾Ð±Ñ Ð¼Ð¾Ð¶Ð½Ð¾ бÑло повÑоÑиÑÑ 335 }) 336 .finally(function () { moreBtn.disabled = false; }); 337 }); 338 } 339 340 if (!q) return; 341 342 /* ââ ÐÑпа в Ñапке âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 343 /* Там, где поиÑк Ñже ÑÑÐ¾Ð¸Ñ Ð½Ð° ÑÑÑаниÑе, лÑпа не оÑкÑÑÐ²Ð°ÐµÑ Ð¾ÐºÐ½Ð¾, а ÑÑÐ°Ð²Ð¸Ñ ÐºÑÑÑÐ¾Ñ Ð² ÑÑÑÐ¾ÐºÑ 344 и Ð¿Ð¾Ð´Ð²Ð¾Ð´Ð¸Ñ ÐµÑ Ðº глазам (макеÑÑ design/catalog.html и design/category.html). Ðнопка 345 в Ñапке одна на вÑе ÑÑÑаниÑÑ, поÑÑÐ¾Ð¼Ñ Ð¿ÐµÑеклÑÑаем ÐµÑ Ð·Ð´ÐµÑÑ, на ÑÑÑаниÑе Ñо ÑÑÑокой: 346 Ñнимаем command и commandfor â без Ð½Ð¸Ñ Ð¾ÐºÐ½Ð¾ не оÑкÑÐ¾ÐµÑ Ð½Ð¸ вÑÑÑоеннÑй вÑзов, ни его 347 подмена в @tailwindplus/elements. Ðез ÑкÑипÑа кнопка оÑÑаÑÑÑÑ Ð¿Ñежней и оÑкÑÑÐ²Ð°ÐµÑ Ð¾ÐºÐ½Ð¾. */ 348 var lens = document.querySelector('header [commandfor="search-dialog"]'); 349 if (lens) { 350 lens.removeAttribute('command'); 351 lens.removeAttribute('commandfor'); 352 lens.setAttribute('data-focus-search', ''); 353 354 // название говоÑÐ¸Ñ Ð¾ дейÑÑвии, а не об Ð¾Ñ Ð²Ð°Ñе: в каÑалоге ÑÑÑока иÑÐµÑ Ð¿Ð¾ каÑалогÑ, 355 // в Ñазделе â по ÑазделÑ, а кнопка на Ð¾Ð±ÐµÐ¸Ñ ÑÑÑаниÑÐ°Ñ Ð´ÐµÐ»Ð°ÐµÑ Ð¾Ð´Ð½Ð¾ и Ñо же 356 var name = lens.querySelector('.sr-only');
357 if (name) name.textContent = 'ÐеÑейÑи к ÑÑÑоке поиÑка'; 358 359 lens.addEventListener('click', function () { 360 // ведÑм ÑÑÑаниÑÑ Ñак, ÑÑÐ¾Ð±Ñ ÑÑÑока вÑÑала ÑÑÐ°Ð·Ñ Ð¿Ð¾Ð´ Ñапкой: одного кÑÑÑоÑа в поле 361 // мало â движение показÑваеÑ, ÑÑо нажаÑие ÑÑабоÑало 362 var header = document.querySelector('header'); 363 var gap = (header ? header.getBoundingClientRect().height : 0) + 16; 364 var top = q.getBoundingClientRect().top + window.scrollY - gap; 365 366 window.scrollTo({ top: Math.max(0, top), behavior: reduce.matches ? 'auto' : 'smooth' }); 367 q.focus({ preventScroll: true }); 368 369 // еÑли ÑÑÑока и Ñак бÑла на ÑкÑане, пÑокÑÑÑки не бÑÐ´ÐµÑ â подÑвеÑиваем поле 370 q.style.backgroundColor = 'var(--color-accent-soft)'; 371 setTimeout(function () { q.style.backgroundColor = ''; }, 900); 372 }); 373 } 374 375 /* ââ СÑÑока поиÑка ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 376 /* Ðез fetch ÑÑÑока оÑÑаÑÑÑÑ Ð¾Ð±ÑÑной ÑоÑмой: Enter оÑпÑавлÑÐµÑ ÐµÑ Ð¸ ÑÑÑаниÑа 377 пеÑезагÑÑжаеÑÑÑ â вÑдаÑа Ñа же, пÑоÑÑо не на леÑÑ. */ 378 if (!window.fetch) return; 379 380 var MIN = 2, DELAY = 200, KEEP = 50; 381 var clear = document.querySelector('[data-clear]'); 382 var cache = new Map(); 383 var timer = null; 384 var flight = null; 385 var turn = 0; 386 387 // запÑÐ¾Ñ ÐºÐ¾ÑоÑе двÑÑ Ð·Ð½Ð°ÐºÐ¾Ð² поиÑк не ÑазбиÑаеÑ: Ð´Ð»Ñ ÑеÑвеÑа он Ñавен пÑÑÑомÑ, 388 // и каÑалог оÑÑаÑÑÑÑ Ñеликом â как окно в ÑÑом же ÑоÑÑоÑнии показÑÐ²Ð°ÐµÑ Â«Ð§Ð°Ñе вÑего покÑпаÑÑ» 389 function asked(value) { 390 return value.length >= MIN ? value : ''; 391 } 392 393 function ask(value) { 394 // не `query`: Ñак назÑваеÑÑÑ ÑбоÑка ÑÑÑоки запÑоÑа вÑÑе, и обÑаÑение к ней оÑÑÑда 395 // молÑа полÑÑило Ð±Ñ ÑÑÑÐ¾ÐºÑ Ð²Ð¼ÐµÑÑо ÑÑнкÑии 396 var asking = asked(value); 397 398 /* ÐдÑÐµÑ Ð¾ÑÑÐ°Ð¶Ð°ÐµÑ Ð½Ð°Ð±Ñанное: ÑÑÑÐ»ÐºÑ Ð¼Ð¾Ð¶Ð½Ð¾ оÑдаÑÑ, а обновление ÑÑÑаниÑÑ Ð²Ð¾Ð·Ð²ÑаÑÐ°ÐµÑ ÑÑ Ð¶Ðµ 399 вÑдаÑÑ â коÑоÑкий запÑÐ¾Ñ ÑеÑÐ²ÐµÑ Ð½Ðµ ÑазбиÑÐ°ÐµÑ ÑоÑно Ñак же, как не оÑпÑавлÑÐµÑ ÐµÐ³Ð¾ ÑкÑипÑ. 400 ÐÑмеÑеннÑе пÑнкÑÑ Ð¾ÑбоÑа пÑÐ¸Ñ Ð¾Ð´ÑÑ Ð¸Ð· адÑеÑа и оÑÑаÑÑÑÑ Ð² нÑм: поиÑк Ð¸Ñ Ð½Ðµ ÑеÑÑеÑ. */ 401 var address = params(); 402 if (value === '') address.delete('q'); else address.set('q', value); 403 remember(address); 404 405 var request = params(); 406 if (asking === '') request.delete('q'); else request.set('q', asking); 407 request.set('ajax', 'items'); 408 409 /* ÐлÑÑ ÐºÑÑа â веÑÑ Ð°Ð´ÑÐµÑ Ð²ÑдаÑи, а не одна ÑÑаза. Ðока оÑÐ±Ð¾Ñ Ð¼ÐµÐ½ÑлÑÑ ÑолÑко 410 пеÑезагÑÑзкой ÑÑÑаниÑÑ, ÑÑÐ°Ð·Ñ Ñ Ð²Ð°Ñало: кÑÑ ÑмиÑал вмеÑÑе Ñо ÑÑÑаниÑей. ТепеÑÑ Ð¾ÐºÐ½Ð¾ 411 на ÑелеÑоне менÑÐµÑ Ð¾ÑÐ±Ð¾Ñ Ð±ÐµÐ· пеÑезагÑÑзки, и оÑвеÑ, ÑложеннÑй до оÑбоÑа, по одной 412 ÑÑазе вÑдавалÑÑ Ð±Ñ Ð¿Ð¾Ñле него â адÑÐµÑ Ð¾Ð±ÐµÑал Ð±Ñ Ð´Ð²Ð° набоÑа, а в ÑеÑке лежали Ð±Ñ 413 воÑемÑ, и ÑÑÑлка показала Ð±Ñ Ð½Ðµ Ñо, ÑÑо Ñеловек видел. 414 415 РегиÑÑÑ ÑнимаеÑÑÑ Ñ Ñамой ÑÑазÑ, до ÑбоÑки клÑÑа. Со вÑего адÑеÑа его ÑнимаÑÑ Ð½ÐµÐ»ÑзÑ: 416 в ÑобÑанном адÑеÑе ÑÑаза пÑоÑенÑно-кодиÑована, и ÑвÑÑÑка задела Ð±Ñ ÑеÑÑнадÑаÑеÑиÑнÑе 417 ÑиÑÑÑ, а не бÑквÑ, â «ÐÑамоÑÑ» и «гÑамоÑÑ» ÑазоÑлиÑÑ Ð±Ñ Ð½Ð° %D0%93 и %D0%B3 и ÑÑали 418 двÑÐ¼Ñ Ð¾Ð±ÑаÑениÑми вмеÑÑо одного. ÐÑÑалÑнÑе знаÑÐµÐ½Ð¸Ñ Ð°Ð´ÑеÑа и Ñак в нижнем ÑегиÑÑÑе. */ 419 var keyed = new URLSearchParams(request); 420 if (asking !== '') keyed.set('q', asking.toLocaleLowerCase()); 421 var key = query(keyed); 422 423 // ÐÑежний оÑÐ²ÐµÑ Ð½Ðµ нÑжен ни пÑи попадании в кÑÑ, ни пÑи новом обÑаÑении: инаÑе оÑÐ²ÐµÑ 424 // на Â«Ð´ÐµÐ½Ñ ÐºÂ» пÑиÑÑл Ð±Ñ Ð¿Ð¾Ñле оÑвеÑа на Â«Ð´ÐµÐ½Ñ ÐºÐ¾ÑмонавÑики». ÐÐ¾Ð¼ÐµÑ Ð¾Ð±ÑаÑÐµÐ½Ð¸Ñ ÑаÑÑÑÑ 425 // на каждÑй запÑÐ¾Ñ â по Ð½ÐµÐ¼Ñ Ð¾ÑлиÑаеÑÑÑ Ð¾ÑÐ²ÐµÑ Ð½Ð° набÑанное ÑейÑÐ°Ñ Ð¾Ñ Ð¾Ð¿Ð¾Ð·Ð´Ð°Ð²Ñего. 426 if (flight) { flight.abort(); flight = null; } 427 var ticket = ++turn; 428 429 if (cache.has(key)) { 430 q.removeAttribute('aria-busy'); 431 paint(cache.get(key)); 432 return; 433 } 434 435 flight = window.AbortController ? new AbortController() : null; 436 q.setAttribute('aria-busy', 'true'); 437 438 window.fetch(link(request), { 439 headers: { 'X-Requested-With': 'fetch' }, 440 credentials: 'same-origin', 441 signal: flight ? flight.signal : undefined 442 }).then(function (response) { 443 return response.ok ? response.text() : null; 444 }).then(function (html) { 445 // оÑÐ²ÐµÑ Ð³Ð¾Ð´ÐµÐ½ и опоздавÑий: набÑанное к Ð½ÐµÐ¼Ñ ÐµÑÑ Ð²ÐµÑнÑÑÑÑ ÑÑиÑанием знака 446 if (html !== null && html !== undefined) { 447 if (cache.size >= KEEP) cache.delete(cache.keys().next().value); 448 cache.set(key, html); 449 } 450 451 if (ticket !== turn) return; 452 453 // помеÑÐºÑ ÑÐ½Ð¸Ð¼Ð°ÐµÑ Ñо обÑаÑение, коÑоÑое ÐµÑ Ð¿Ð¾ÑÑавило, â и на оÑказе ÑеÑвеÑа Ñоже: 454 // пÑи !response.ok ÑÑда пÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ð¿ÑÑÑой оÑвеÑ, а не оÑказ пÑомиÑа, и catch молÑÐ¸Ñ 455 q.removeAttribute('aria-busy'); 456 if (html === null || html === undefined) return; 457 458 paint(html); 459 }).catch(function () { 460 /* ÐÑменÑннÑй запÑÐ¾Ñ Ð¸ обоÑÐ²Ð°Ð½Ð½Ð°Ñ ÑеÑÑ Ð²ÑглÑдÑÑ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ð¾: в Ð¾Ð±Ð¾Ð¸Ñ ÑлÑÑаÑÑ 461 показанное оÑÑаÑÑÑÑ Ð½Ð° меÑÑе. ÐомеÑÐºÑ ÑÐ½Ð¸Ð¼Ð°ÐµÑ Ñо обÑаÑение, коÑоÑое ÐµÑ Ð¿Ð¾ÑÑавило. */ 462 if (ticket === turn) q.removeAttribute('aria-busy'); 463 }); 464 } 465 466 function stop() { 467 if (timer) { clearTimeout(timer); timer = null; } 468 } 469 470 function schedule() { 471 if (clear) clear.hidden = q.value === ''; 472 stop(); 473 timer = setTimeout(function () { timer = null; ask(typed()); }, DELAY); 474 } 475 476 q.addEventListener('input', schedule); 477 // Escape и кÑеÑÑик Ñамого Ð¿Ð¾Ð»Ñ ÑÑиÑаÑÑ ÑÑÑÐ¾ÐºÑ ÑобÑÑием search, а не input 478 q.addEventListener('search', schedule); 479 480 if (clear) { 481 clear.hidden = q.value === ''; 482 clear.addEventListener('click', function () { 483 q.value = ''; 484 clear.hidden = true; 485 q.focus(); 486 stop(); 487 ask(''); 488 }); 489 } 490 491 /* Enter оÑпÑавлÑÐµÑ ÑоÑÐ¼Ñ Ð¸ пеÑезагÑÑÐ¶Ð°ÐµÑ ÑÑÑаниÑÑ â Ñем же оÑбоÑом и Ñ Ñем же иÑогом, 492 ÑÑо Ñже на ÑкÑане. Ðез ÑкÑипÑа ÑÑо единÑÑвеннÑй ÑпоÑоб иÑкаÑÑ, поÑÑÐ¾Ð¼Ñ ÑоÑма оÑÑаÑÑÑÑ 493 как еÑÑÑ; Ñо ÑкÑипÑом оÑпÑÐ°Ð²ÐºÑ Ð¿ÐµÑÐµÑ Ð²Ð°ÑÑваем и вÑполнÑем оÑложеннÑй запÑÐ¾Ñ Ð´Ð¾ÑÑоÑно. */ 494 if (form) { 495 form.addEventListener('submit', function (event) { 496 event.preventDefault(); 497 stop(); 498 ask(typed()); 499 }); 500 } 501})();
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.