1/** 2 * ÐÐÐÐÐÐЬÐÐ«Ð ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ Ð²ÑбоÑа гоÑода (GraphModal). 3 * ÐÑкÑÑÑие: CitySelect.open({ current: '<code>' }) â current Ð´Ð»Ñ Ð¿Ð¾Ð´ÑвеÑки. 4 * ÐоÑле вÑбоÑа: Ñегион пиÑеÑÑÑ Ð² ÑеÑÑÐ¸Ñ (delivery_region.php?action=set) 5 * и ÑлÑÑÑÑ ÑобÑÑие window 'cityselect:chosen' (detail: {code, name}). 6 * ÐодпиÑÑики Ñами ÑеагиÑÑÑÑ (каÑÑоÑка â пеÑеÑÑÑÑ Ð±Ð»Ð¾ÐºÐ° «Ðак забÑаÑÑ?»). 7 */ 8(function () { 9 'use strict'; 10 11 var LAT_KBD = 'qwertyuiop[]asdfghjkl;\'zxcvbnm,.'; 12 var CYR_KBD = 'йÑÑкенгÑÑÐ·Ñ ÑÑÑвапÑолджÑÑÑÑмиÑÑбÑ'; 13 var layoutMap = (function () { 14 var m = {}; 15 for (var i = 0; i < LAT_KBD.length; i++) { 16 m[LAT_KBD[i]] = CYR_KBD[i]; 17 m[CYR_KBD[i]] = LAT_KBD[i]; 18 m[LAT_KBD[i].toUpperCase()] = CYR_KBD[i].toUpperCase(); 19 m[CYR_KBD[i].toUpperCase()] = LAT_KBD[i].toUpperCase(); 20 } 21 return m; 22 })(); 23 24 function swapLayout(s) { 25 var out = ''; 26 for (var i = 0; i < s.length; i++) out += layoutMap[s[i]] || s[i]; 27 return out; 28 } 29 30 var POPULAR = ['ÐоÑква', 'СанкÑ-ÐеÑеÑбÑÑг', 'ÐовоÑибиÑÑк', 'ÐкаÑеÑинбÑÑг', 'ÐазанÑ', 'ÐÑаÑноÑÑÑк']; 31 32 var cities = null; // ÑпÑавоÑник, в памÑÑи до пеÑезагÑÑзки 33 var fuse = null; 34 var currentCode = ''; 35 36 window.CitySelect = { 37 open: function (opts) { 38 opts = opts || {}; 39 currentCode = opts.current || ''; 40 41 var input = searchInput(); 42 if (input) input.value = ''; 43 44 if (window.ModalManager && typeof window.ModalManager.open === 'function') { 45 window.ModalManager.open('city-select'); 46 } 47 ensureData(); 48 } 49 }; 50 51 /* ---------- DOM ---------- */ 52 53 function modalBox() { return document.querySelector('[data-graph-target="city-select"]'); } 54 function listBox() { var m = modalBox(); return m ? m.querySelector('.city-select__list') : null; } 55 function searchInput() { var m = modalBox(); return m ? m.querySelector('.city-select__search input') : null; } 56 57 /* ---------- ÐÐÐÐЫР(1 запÑÐ¾Ñ Ð½Ð° ÑÑÑаниÑÑ) ---------- */ 58 59 function ensureData() { 60 if (cities && cities.length) { renderList(cities, false); return; } 61 62 var box = listBox(); 63 if (box) box.innerHTML = '<div class="city-select__empty">ÐагÑÑзкаâ¦</div>'; 64 65 fetch('/local/ajax/delivery_cities.php', { credentials: 'same-origin' }) 66 .then(function (r) { return r.json(); }) 67 .then(function (res) { 68 var data = (res && res.success && Array.isArray(res.data)) ? res.data : []; 69 if (!data.length) { 70 if (box) box.innerHTML = '<div class="city-select__empty">Ðе ÑдалоÑÑ Ð·Ð°Ð³ÑÑзиÑÑ ÑпиÑок гоÑодов</div>'; 71 return; 72 } 73 cities = data; 74 initFuse(); 75 renderList(cities, false); 76 }) 77 .catch(function () { 78 if (box) box.innerHTML = '<div class="city-select__empty">Ðе ÑдалоÑÑ Ð·Ð°Ð³ÑÑзиÑÑ ÑпиÑок гоÑодов</div>'; 79 }); 80 } 81 82 function initFuse() { 83 fuse = null; 84 if (typeof Fuse !== 'undefined' && cities && cities.length) { 85 fuse = new Fuse(cities, { 86 keys: ['name'], 87 threshold: 0.35, 88 ignoreLocation: true, 89 minMatchCharLength: 2 90 }); 91 } 92 } 93 94 /* ---------- Ð ÐÐÐÐÐ ---------- */ 95 96 function renderList(list, isSearch) { 97 var box = listBox(); 98 if (!box) return; 99 if (!list || !list.length) { 100 box.innerHTML = '<div class="city-select__empty">ÐиÑего не найдено</div>'; 101 return; 102 } 103 104 var html = ''; 105 106 // попÑлÑÑнÑе â ÑолÑко в полном ÑпиÑке 107 if (!isSearch) { 108 var popular = []; 109 POPULAR.forEach(function (name) { 110 for (var i = 0; i < list.length; i++) { 111 if (list[i].name === name) { popular.push(list[i]); break; } 112 } 113 }); 114 if (popular.length) { 115 html += '<div class="city-select__popular">'; 116 popular.forEach(function (c) { html += cityBtn(c, true); }); 117 html += '</div>'; 118 } 119 } 120 121 var sorted = list.slice().sort(function (a, b) { return a.name.localeCompare(b.name, 'ru'); }); 122 123 // гÑÑÐ¿Ð¿Ñ Ð¿Ð¾ бÑкве (в поиÑке бÑÐºÐ²Ñ Ð½Ðµ ÑÑавим) 124 var groups = []; 125 var last = null; 126 sorted.forEach(function (c) { 127 var letter = isSearch ? '' : c.name.charAt(0).toUpperCase(); 128 if (letter !== last) { groups.push({ letter: letter, items: [] }); last = letter; } 129 groups[groups.length - 1].items.push(c); 130 }); 131 132 html += '<div class="city-select__cols">'; 133 groups.forEach(function (g) { 134 html += '<div class="city-select__group">'; 135 if (g.letter) html += '<span class="city-select__letter">
135' + escapeHtml(g.letter) + '</span>'; 136 g.items.forEach(function (c) { html += cityBtn(c, false); }); 137 html += '</div>'; 138 }); 139 html += '</div>'; 140 141 box.innerHTML = html; 142 } 143 144 function cityBtn(c, popular) { 145 var active = (c.code === currentCode) ? ' city-select__item--active' : ''; 146 var pop = popular ? ' city-select__item--popular' : ''; 147 return '<button type="button" class="city-select__item' + active + pop + 148 '" data-city-code="' + escapeHtml(c.code) + 149 '" data-city-name="' + escapeHtml(c.name) + '">' + escapeHtml(c.name) + '</button>'; 150 } 151 152 /* ---------- СÐÐЫТÐЯ ---------- */ 153 154 function bindEvents() { 155 var input = searchInput(); 156 if (input) { 157 input.addEventListener('input', function () { 158 if (!cities) return; 159 var q = this.value.trim(); 160 if (q === '') { renderList(cities, false); return; } 161 162 // иÑем в Ð¾Ð±ÐµÐ¸Ñ ÑаÑÐºÐ»Ð°Ð´ÐºÐ°Ñ 163 var variants = [q]; 164 var alt = swapLayout(q); 165 if (alt !== q) variants.push(alt); 166 167 var res = []; 168 var seen = {};
169 variants.forEach(function (v) { 170 var part; 171 if (fuse) { 172 part = fuse.search(v).map(function (r) { return r.item; }); 173 } else { 174 var vl = v.toLowerCase(); 175 part = cities.filter(function (c) { return c.name.toLowerCase().indexOf(vl) !== -1; }); 176 } 177 part.forEach(function (c) { 178 if (!seen[c.code]) { seen[c.code] = 1; res.push(c); } 179 }); 180 }); 181 renderList(res, true); 182 }); 183 } 184 185 var list = listBox(); 186 if (list) { 187 list.addEventListener('click', function (e) { 188 var btn = e.target.closest('.city-select__item'); 189 if (!btn) return; 190 choose(btn.getAttribute('data-city-code'), btn.getAttribute('data-city-name')); 191 }); 192 } 193 } 194 195 /* ---------- ÐÐÐÐЫРÐЫÐÐÐ ÐÐÐ ÐÐÐ ---------- */ 196 197 function choose(code, name) { 198 if (!code) return; 199 200 // закÑÑваем ÑолÑко ÑеÑез GraphModal 201 if (window.ModalManager && typeof window.ModalManager.close === 'function') { 202 window.ModalManager.close(); 203 } 204 205 // запоминаем Ñегион, ÐÐТÐÐ ÑведомлÑем подпиÑÑиков 206 fetch('/local/ajax/delivery_region.php?action=set&code=' + encodeURIComponent(code) + 207 '&name=' + encodeURIComponent(name), { credentials: 'same-origin' }) 208 .catch(function () {}) 209 .then(function () { 210 try { 211 window.dispatchEvent(new CustomEvent('cityselect:chosen', { detail: { code: code, name: name } })); 212 } catch (e) {} 213 }); 214 } 215 216 /* ---------- СÐУÐÐÐÐЫР---------- */ 217 218 function escapeHtml(str) { 219 var div = document.createElement('div'); 220 div.textContent = (str == null ? '' : String(str)); 221 return div.innerHTML; 222 } 223 224 if (document.readyState === 'loading') { 225 document.addEventListener('DOMContentLoaded', bindEvents); 226 } else { 227 bindEvents(); 228 } 229})();
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.