1// // СкÑÐ¸Ð¿Ñ Ð´Ð»Ñ Ð¿Ð¾Ð´ÑÑÐ³Ð¸Ð²Ð°Ð½Ð¸Ñ ÑпиÑка гоÑодов Ñ ÑаблиÑÑ 2// var MAX_ROWS = 500; 3// var file = "https://docs.google.com/spreadsheets/d/1nYkPC8SeAaSl91JUWrhjXUbVldpieGHLBZIswg-Ewh0"; 4// var query = "/gviz/tq?gid=0"; 5// var url = file + query; 6 7// regions = []; 8 9// var google = { 10// visualization: { Query: { setResponse: function () {} } }, 11// }; 12 13// function updatePage(data) { 14// if (!data || !data.table) { 15// console.log("No data table."); 16// return; 17// } 18 19// var table = data.table; 20 21// if (!table.rows) { 22// console.log("No table rows."); 23// return; 24// } 25 26// var row_count = table.rows.length; 27// var cols = Math.ceil(row_count / MAX_ROWS); 28 29// for (var row of table.rows) { 30// if (row.c && row.c[0] && row.c[1]) { 31// var name_human = row.c[0]; 32// var name_search = row.c[1]; 33// var enabled = row.c[2]; 34// var link = row.c[3]; 35// var region_id = row.c[6]; 36 37// if ( 38// !name_human.v || 39// !name_human.v.length || 40// !name_search.v || 41// !name_search.v.length || 42// !link.v || 43// !link.v.length 44// ) { 45// continue; 46// } 47 48// regions.push({ 49// name_human: name_human.v, 50// name_search: name_search.v, 51// link: link.v, 52// enabled: enabled.v, 53// region_id: region_id.v, 54// }); 55// } 56// } 57 58// drawRegions( 59// regions 60// .sort((a, b) => a.name_human.localeCompare(b.name_human)) 61// .filter((x) => x.enabled) 62// ); 63// } 64 65// function drawRegions(regions_arr) { 66// var trs = []; 67 68// $(".city_list tbody").empty(); 69 70// for (var i = 0; i < MAX_ROWS; i++) { 71// var tr = $("<tr></tr>"); 72// $(".city_list tbody").append(tr); 73// trs.push(tr); 74// } 75 76// var letter = ""; 77// while (regions_arr.length) { 78// for (var tr of trs) { 79// if (!regions_arr.length) { 80// break; 81// } 82 83// if (letter !== regions_arr[0].name_human[0]) { 84// letter = regions_arr[0].name_human[0]; 85// tr.append(` 86// <td> 87// <p class="region_title">${letter}</p> 88// </td>`); 89// continue; 90// } 91 92// var next_region = regions_arr.shift(); 93 94// if (next_region) { 95// tr.append(` 96// <td> 97// <a class="region_item region_link" href='${next_region.link}' id='${next_region.region_id}'>${next_region.name_human}</a> 98// </td>`); 99// } 100// } 101// } 102// } 103 104// google.visualization.Query.setResponse = function (data) { 105// updatePage(data); 106// }; 107 108// $().ready(function () { 109// $.ajax({ 110// url: url, 111// dataType: "jsonp", 112// }); 113 114// // ÐЫÐÐÐ ÐÐТ РТÐРЧÐСÐÐ ÐÐÐÐÐÐÐ ÐÐÐЫРСÐÐÐÐ 115// // $('#region_search').on('input', function(e) { 116// // if (regions && regions.length && e.target.value && e.target.value.length > 1) { 117// // var regions_filtered = regions.filter(function(x) { 118// // return x.name_search.toLowerCase().includes(e.target.value.toLowerCase()); 119// // }) 120// // drawRegions(regions_filtered); 121// // } else if (!e.target.value) { 122// // drawRegions(regions.sort((a, b) => a.name_human.localeCompare(b.name_human)).filter(x => x.enabled)) 123// // } 124// // }) 125 126 127// // ÐÐ ÐЫÐÐÐ ÐÐТ ÐÐÐÐÐÐÐ ÐÐÐЫÐ, ÐÐÐТ С ÐÐЧÐÐРСÐÐÐÐ 128// $('.popup-select-city__input').on('input', function(e) { 129// if (regions && regions.length && e.target.value && e.target.value.length > 1) { 130// var regions_filtered = regions.filter(function(x) { 131// return x.name_search.toLowerCase().startsWith(e.target.value.toLowerCase()); 132// }); 133// drawRegions(regions_filtered); 134// } else if (!e.target.value) { 135// drawRegions(regions.sort((a, b) => a.name_human.localeCompare(b.name_human)).filter(x => x.enabled)); 136// } 137// }); 138// }); 139 140 141 142 143 144 145 const RegionManager = { 146 cfg: {
147 url: "/wp-content/uploads/select-region/regions.csv", 148 sheet: { id: "1nYkPC8SeAaSl91JUWrhjXUbVldpieGHLBZIswg-Ewh0", gid: "0" }, 149 batch: 40, 150 key: "regions_v5", // Ðбновил веÑÑÐ¸Ñ ÐºÐµÑа 151 cookie: "regions_v", 152 domain: ".mts-home.online", 153 ttl: 7200000, 154 }, 155 state: { 156 all: [], 157 active: [], 158 ltr: "", 159 loading: false, 160 raf: 0, 161 searchRaw: "", 162 $table: null, 163 isFirstBatch: true 164 }, 165 166 init() { 167 this.state.$table = $(".city_list tbody"); 168 this.bind(); 169 170 const cache = this.loadCache(); 171 const cookieV = this.getCookie(this.cfg.cookie); 172 173 if (cache) { 174 this.state.all = cache.all; 175 this.state.active = cache.active; 176 this.render(this.state.active); 177 178 if (cache.v !== cookieV) { 179 console.log("[Regions] Update bg"); 180 this.fetchCsv(true); 181 } 182 } else { 183 this.renderMsg("ÐагÑÑзка ÑпиÑка..."); 184 this.fetchCsv(false); 185 } 186 }, 187 188 async fetchCsv(isBg) { 189 if (this.state.loading) return; 190 this.state.loading = true; 191 this.state.isFirstBatch = true; 192 193 this.state.all = []; 194 this.state.active = []; 195 196 try { 197 const resp = await fetch(this.cfg.url); 198 199 // ÐÑли ÑеÑÐ²ÐµÑ Ð²ÐµÑнÑл 404 или 500 â ÑÑÐ°Ð·Ñ Ð¾Ñибка Ñ Ð½ÑжнÑм ÑекÑÑом 200 if (!resp.ok) throw new Error("CSV not found"); 201 202 const reader = resp.body.getReader(); 203 const dec = new TextDecoder("utf-8"); 204 let buf = ""; 205 let batch = []; 206 207 while (true) { 208 const { done, value } = await reader.read(); 209 if (done) break; 210 211 buf += dec.decode(value, { stream: true }); 212 const lines = buf.split(/\r?\n/); 213 buf = lines.pop(); 214 215 for (const line of lines) { 216 if (!line.trim()) continue; 217 const row = this.parse(line); 218 if (!row) continue; 219 220 this.state.all.push(row); 221 222 if (row.en) { 223 this.state.active.push(row); 224 batch.push(row); 225 } 226 } 227 228 if (!isBg && !this.state.searchRaw && batch.length >= this.cfg.batch) { 229 this.flushBatch(batch); 230 batch = []; 231 } 232 } 233 234 if (buf.trim()) { 235 const row = this.parse(buf); 236 if (row) { 237 this.state.all.push(row); 238 if (row.en) { 239 this.state.active.push(row); 240 batch.push(row); 241 } 242 } 243 } 244 245 // === ÐÐÐÐЫРФÐÐС === 246 if (this.state.all.length === 0) { 247 throw new Error("CSV is empty"); 248 } 249 250 if (!isBg && !this.state.searchRaw && batch.length) { 251 this.flushBatch(batch); 252 } 253 254 // ÐÑвод об ÑÑпеÑной загÑÑзке из CSV 255 console.log(`[regions] Loaded from CSV: ${this.state.all.length} rows`); 256 257 this.finishLoad(true); 258 259 } catch (e) { 260 // ÐÑвод оÑибки Ð´Ð»Ñ Fallback-ÑÑенаÑÐ¸Ñ 261 console.warn(`[regions] CSV fallback triggered: ${e.message}`); 262 await this.fetchGoogle(); 263 } 264 }, 265 266 flushBatch(batch) { 267 if (this.state.isFirstBatch) { 268 this.state.$table.empty(); 269 this.state.ltr = ""; 270 this.state.isFirstBatch = false; 271 } 272 this.appendDraw(batch); 273 }, 274 275 finishLoad(success) { 276 this.state.loading = false; 277 if (this.state.all.length > 0) {
278 this.saveCache(); 279 } 280 281 if (this.state.searchRaw.length >= 2) { 282 this.runSearch(this.state.searchRaw); 283 } else if (!this.state.$table.children().length) { 284 this.render(this.state.active); 285 } 286 }, 287 288 parse(line) { 289 const cols = line.split(','); 290 if (cols.length < 4) return null; 291 292 const clean = v => (v || "").replace(/^"|"$/g, "").trim(); 293 const enVal = clean(cols[2]).toLowerCase(); 294 295 return { 296 n: clean(cols[0]), 297 s: clean(cols[1]).toLowerCase(), 298 en: ["1", "true", "yes"].includes(enVal), 299 l: clean(cols[3]), 300 id: clean(cols[4]) 301 }; 302 }, 303 304 render(list) { 305 this.state.$table.empty(); 306 this.state.ltr = ""; 307 308 if (!list.length) { 309 const msg = this.state.loading ? "ÐÑем..." : "ÐиÑего не найдено"; 310 this.renderMsg(msg); 311 return; 312 } 313 this.appendDraw(list); 314 }, 315 316 appendDraw(list) { 317 const html = []; 318 for (const r of list) { 319 const l = r.n[0].toUpperCase(); 320 if (l !== this.state.ltr) { 321 this.state.ltr = l; 322 html.push(`<tr><td class="region_title">${l}</td></tr>`); 323 } 324 html.push(`<tr><td><a class="region_item region_link" href="${r.l}" id="${r.id}">${r.n}</a></td></tr>`); 325 } 326 this.state.$table.append(html.join("")); 327 }, 328 329 renderMsg(txt) { 330 this.state.$table.html(`<tr><td class="region_title">${txt}</td></tr>`); 331 }, 332 333 bind() { 334 const handle = (e, method) => { 335 const q = e.target.value.trim().toLowerCase(); 336 this.state.searchRaw = q; 337 338 if (this.state.raf) cancelAnimationFrame(this.state.raf); 339 340 this.state.raf = requestAnimationFrame(() => { 341 if (q.length < 2) { 342 this.render(this.state.active); 343 return; 344 } 345 this.runSearch(q, method); 346 }); 347 }; 348 349 $('#region_search').on('input', e => handle(e, 'includes')); 350 $('.popup-select-city__input').on('input', e => handle(e, 'startsWith')); 351 }, 352 353 runSearch(q, method = 'includes') { 354 const source = this.state.all; 355 const filtered = source.filter(i => { 356 return method === 'startsWith' ? i.s.startsWith(q) : i.s.includes(q); 357 }); 358 this.render(filtered); 359 }, 360 361 async fetchGoogle() { 362 if (this.state.all.length === 0) { 363 this.renderMsg("ÐагÑÑзка из ÑезеÑва..."); 364 } 365 366 const url = `https://docs.google.com/spreadsheets/d/${this.cfg.sheet.id}/gviz/tq?gid=${this.cfg.sheet.gid}&tq=select%20*&tqx=out:json`; 367 try { 368 const txt = await (await fetch(url)).text(); 369 const json = JSON.parse(txt.match(/setResponse\(([\s\S]*)\);/)[1]); 370 const rows = json.table.rows || []; 371 372 let tempAll = []; 373 374 rows.forEach(r => { 375 const item = { 376 n: r.c[0]?.v, 377 s: (r.c[1]?.v||"").toLowerCase(), 378 en: !!r.c[2]?.v, 379 l: r.c[3]?.v, 380 id: r.c[6]?.v 381 }; 382 if (item.n && item.l) { 383 tempAll.push(item); 384 } 385 }); 386 387 tempAll.sort((a, b) => a.n.localeCompare(b.n)); 388 389 this.state.all = tempAll; 390 this.state.active = tempAll.filter(x => x.en); 391 392 // ÐÑвод об ÑÑпеÑной загÑÑзке из Google 393 console.log(`[regions] Loaded from Google: ${this.state.all.length} rows`); 394 395 this.finishLoad(true); 396 397 if (this.state.searchRaw.length >= 2) this.runSearch(this.state.searchRaw); 398 else this.render(this.state.active); 399 400 } catch(e) { 401 console.error(e); 402 this.renderMsg("ÐÑибка загÑÑзки"); 403 } 404 }, 405 406 saveCache() { 407 try { 408 localStorage.setItem(this.cfg.key, JSON.stringify({ 409 v: ""+Date.now(), 410 all: this.state.all, 411 active: this.state.active 412 })); 413 document.cookie = `${this.cfg.cookie}=${Date.now()}; path=/; domain=${this.cfg.domain}; max-age=86400; SameSite=Lax`; 414 } catch(e){} 415 }, 416 417 loadCache() { 418 try { 419 const d = JSON.parse(localStorage.getItem(this.cfg.key)); 420 if (!d || Date.now() - parseInt(d.v) > this.cfg.ttl) return null; 421 return d; 422 } catch(e) { return null; } 423 }, 424 425 getCookie(n) { const m = document.cookie.match(new RegExp(`(^| )${n}=([^;]+)`)); return m ? m[2] : null; } 426}; 427 428$(() => RegionManager.init());
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.