1// // СкÑÐ¸Ð¿Ñ Ð´Ð»Ñ Ð¿Ð¾Ð´ÑÑÐ³Ð¸Ð²Ð°Ð½Ð¸Ñ ÑпиÑка гоÑодов Ñ ÑаблиÑÑ 2// var MAX_ROWS = 400; 3// var file = "https://docs.google.com/spreadsheets/d/15eDJMvlEKcnKe36Q1SeT3B8pA1RkmFJu2uHVd7aM4Vc"; 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 const RegionManager = { 145 cfg: {
146 url: "/wp-content/uploads/select-region/regions.csv", 147 sheet: { id: "15eDJMvlEKcnKe36Q1SeT3B8pA1RkmFJu2uHVd7aM4Vc", gid: "0" }, 148 batch: 40, 149 key: "regions_v5", // Ðбновил веÑÑÐ¸Ñ ÐºÐµÑа 150 cookie: "regions_v", 151 domain: ".beeline-ru.online", 152 ttl: 7200000, 153 }, 154 state: { 155 all: [], 156 active: [], 157 ltr: "", 158 loading: false, 159 raf: 0, 160 searchRaw: "", 161 $table: null, 162 isFirstBatch: true 163 }, 164 165 init() { 166 this.state.$table = $(".city_list tbody"); 167 this.bind(); 168 169 const cache = this.loadCache(); 170 const cookieV = this.getCookie(this.cfg.cookie); 171 172 if (cache) { 173 this.state.all = cache.all; 174 this.state.active = cache.active; 175 this.render(this.state.active); 176 177 if (cache.v !== cookieV) { 178 console.log("[Regions] Update bg"); 179 this.fetchCsv(true); 180 } 181 } else { 182 this.renderMsg("ÐагÑÑзка ÑпиÑка..."); 183 this.fetchCsv(false); 184 } 185 }, 186 187 async fetchCsv(isBg) { 188 if (this.state.loading) return; 189 this.state.loading = true; 190 this.state.isFirstBatch = true; 191 192 this.state.all = []; 193 this.state.active = []; 194 195 try { 196 const resp = await fetch(this.cfg.url); 197 198 // ÐÑли ÑеÑÐ²ÐµÑ Ð²ÐµÑнÑл 404 или 500 â ÑÑÐ°Ð·Ñ Ð¾Ñибка Ñ Ð½ÑжнÑм ÑекÑÑом 199 if (!resp.ok) throw new Error("CSV not found"); 200 201 const reader = resp.body.getReader(); 202 const dec = new TextDecoder("utf-8"); 203 let buf = ""; 204 let batch = []; 205 206 while (true) { 207 const { done, value } = await reader.read(); 208 if (done) break; 209 210 buf += dec.decode(value, { stream: true }); 211 const lines = buf.split(/\r?\n/); 212 buf = lines.pop(); 213 214 for (const line of lines) { 215 if (!line.trim()) continue; 216 const row = this.parse(line); 217 if (!row) continue; 218 219 this.state.all.push(row); 220 221 if (row.en) { 222 this.state.active.push(row); 223 batch.push(row); 224 } 225 } 226 227 if (!isBg && !this.state.searchRaw && batch.length >= this.cfg.batch) { 228 this.flushBatch(batch); 229 batch = []; 230 } 231 } 232 233 if (buf.trim()) { 234 const row = this.parse(buf); 235 if (row) { 236 this.state.all.push(row); 237 if (row.en) { 238 this.state.active.push(row); 239 batch.push(row); 240 } 241 } 242 } 243 244 // === ÐÐÐÐЫРФÐÐС === 245 if (this.state.all.length === 0) { 246 throw new Error("CSV is empty"); 247 } 248 249 if (!isBg && !this.state.searchRaw && batch.length) { 250 this.flushBatch(batch); 251 } 252 253 // ÐÑвод об ÑÑпеÑной загÑÑзке из CSV 254 console.log(`[regions] Loaded from CSV: ${this.state.all.length} rows`); 255 256 this.finishLoad(true); 257 258 } catch (e) { 259 // ÐÑвод оÑибки Ð´Ð»Ñ Fallback-ÑÑенаÑÐ¸Ñ 260 console.warn(`[regions] CSV fallback triggered: ${e.message}`); 261 await this.fetchGoogle(); 262 } 263 }, 264 265 flushBatch(batch) { 266 if (this.state.isFirstBatch) { 267 this.state.$table.empty(); 268 this.state.ltr = ""; 269 this.state.isFirstBatch = false; 270 } 271 this.appendDraw(batch); 272 }, 273 274 finishLoad(success) { 275 this.state.loading = false; 276 if (this.state.all.length > 0) {
277 this.saveCache(); 278 } 279 280 if (this.state.searchRaw.length >= 2) { 281 this.runSearch(this.state.searchRaw); 282 } else if (!this.state.$table.children().length) { 283 this.render(this.state.active); 284 } 285 }, 286 287 parse(line) { 288 const cols = line.split(','); 289 if (cols.length < 4) return null; 290 291 const clean = v => (v || "").replace(/^"|"$/g, "").trim(); 292 const enVal = clean(cols[2]).toLowerCase(); 293 294 return { 295 n: clean(cols[0]), 296 s: clean(cols[1]).toLowerCase(), 297 en: ["1", "true", "yes"].includes(enVal), 298 l: clean(cols[3]), 299 id: clean(cols[4]) 300 }; 301 }, 302 303 render(list) { 304 this.state.$table.empty(); 305 this.state.ltr = ""; 306 307 if (!list.length) { 308 const msg = this.state.loading ? "ÐÑем..." : "ÐиÑего не найдено"; 309 this.renderMsg(msg); 310 return; 311 } 312 this.appendDraw(list); 313 }, 314 315 appendDraw(list) { 316 const html = []; 317 for (const r of list) { 318 const l = r.n[0].toUpperCase(); 319 if (l !== this.state.ltr) { 320 this.state.ltr = l; 321 html.push(`<tr><td class="region_title">${l}</td></tr>`); 322 } 323 html.push(`<tr><td><a class="region_item region_link" href="${r.l}" id="${r.id}">${r.n}</a></td></tr>`); 324 } 325 this.state.$table.append(html.join("")); 326 }, 327 328 renderMsg(txt) { 329 this.state.$table.html(`<tr><td class="region_title">${txt}</td></tr>`); 330 }, 331 332 bind() { 333 const handle = (e, method) => { 334 const q = e.target.value.trim().toLowerCase(); 335 this.state.searchRaw = q; 336 337 if (this.state.raf) cancelAnimationFrame(this.state.raf); 338 339 this.state.raf = requestAnimationFrame(() => { 340 if (q.length < 2) { 341 this.render(this.state.active); 342 return; 343 } 344 this.runSearch(q, method); 345 }); 346 }; 347 348 $('#region_search').on('input', e => handle(e, 'includes')); 349 $('.popup-select-city__input').on('input', e => handle(e, 'startsWith')); 350 }, 351 352 runSearch(q, method = 'includes') { 353 const source = this.state.all; 354 const filtered = source.filter(i => { 355 return method === 'startsWith' ? i.s.startsWith(q) : i.s.includes(q); 356 }); 357 this.render(filtered); 358 }, 359 360 async fetchGoogle() { 361 if (this.state.all.length === 0) { 362 this.renderMsg("ÐагÑÑзка из ÑезеÑва..."); 363 } 364 365 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`; 366 try { 367 const txt = await (await fetch(url)).text(); 368 const json = JSON.parse(txt.match(/setResponse\(([\s\S]*)\);/)[1]); 369 const rows = json.table.rows || []; 370 371 let tempAll = []; 372 373 rows.forEach(r => { 374 const item = { 375 n: r.c[0]?.v, 376 s: (r.c[1]?.v||"").toLowerCase(), 377 en: !!r.c[2]?.v, 378 l: r.c[3]?.v, 379 id: r.c[6]?.v 380 }; 381 if (item.n && item.l) { 382 tempAll.push(item); 383 } 384 }); 385 386 tempAll.sort((a, b) => a.n.localeCompare(b.n)); 387 388 this.state.all = tempAll; 389 this.state.active = tempAll.filter(x => x.en); 390 391 // ÐÑвод об ÑÑпеÑной загÑÑзке из Google 392 console.log(`[regions] Loaded from Google: ${this.state.all.length} rows`); 393 394 this.finishLoad(true); 395 396 if (this.state.searchRaw.length >= 2) this.runSearch(this.state.searchRaw); 397 else this.render(this.state.active); 398 399 } catch(e) { 400 console.error(e); 401 this.renderMsg("ÐÑибка загÑÑзки"); 402 } 403 }, 404 405 saveCache() { 406 try { 407 localStorage.setItem(this.cfg.key, JSON.stringify({ 408 v: ""+Date.now(), 409 all: this.state.all, 410 active: this.state.active 411 })); 412 document.cookie = `${this.cfg.cookie}=${Date.now()}; path=/; domain=${this.cfg.domain}; max-age=86400; SameSite=Lax`; 413 } catch(e){} 414 }, 415 416 loadCache() { 417 try { 418 const d = JSON.parse(localStorage.getItem(this.cfg.key)); 419 if (!d || Date.now() - parseInt(d.v) > this.cfg.ttl) return null; 420 return d; 421 } catch(e) { return null; } 422 }, 423 424 getCookie(n) { const m = document.cookie.match(new RegExp(`(^| )${n}=([^;]+)`)); return m ? m[2] : null; } 425}; 426 427$(() => 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.