1/*! 2 * ÐамелиÑ-Ðед â ÑÐ±Ð¾Ñ Ð¸ÑÑоÑника обÑаÑÐµÐ½Ð¸Ñ Ð¸ дÑÐ±Ð»Ñ Ð·Ð°Ñвок в аналиÑикÑ. 3 * Vanilla JS, без завиÑимоÑÑей (на kamelia-med.ru jQuery 1.8.3 â намеÑенно не иÑполÑзÑем). 4 * ÐеÑÑÐ¸Ñ 1.0 (2026-07-25) 5 * 6 * ЧÑо делаеÑ: 7 * 1) пÑи Ð²Ñ Ð¾Ð´Ðµ на ÑÐ°Ð¹Ñ Ð·Ð°Ð¿Ð¾Ð¼Ð¸Ð½Ð°ÐµÑ Ð¸ÑÑоÑник (utm_*, yclid, gclid, ÑеÑеÑеÑ, ÑÑÑаниÑа Ð²Ñ Ð¾Ð´Ð°); 8 * 2) Ñ ÑÐ°Ð½Ð¸Ñ ÐÐРкаÑаниÑ: пеÑвое (first) и поÑледнее (last), 90 дней; 9 * 3) беÑÑÑ ClientID ЯндекÑ.ÐеÑÑики â вÑоÑой клÑÑ Ñклейки помимо ÑелеÑона; 10 * 4) подÑÑавлÑÐµÑ Ð²ÑÑ ÑÑо ÑкÑÑÑÑми полÑми в ÑоÑÐ¼Ñ ÑайÑа (ÑÑ Ð¾Ð´Ð¸Ñ Ð² Ñодное пиÑÑмо/админкÑ); 11 * 5) пÑи оÑпÑавке ÑоÑÐ¼Ñ ÑлÑÑ ÐÐÐÐЮ заÑвки на пÑиÑмник (sendBeacon â не блокиÑÑÐµÑ Ð¾ÑпÑавкÑ). 12 * 13 * Ð Ð¾Ð´Ð½Ð°Ñ Ð»Ð¾Ð³Ð¸ÐºÐ° ÑоÑм не менÑеÑÑÑ. ÐÑÐ±Ð°Ñ Ð¾Ñибка внÑÑÑи ÑкÑипÑа не должна ломаÑÑ Ð¾ÑпÑÐ°Ð²ÐºÑ â 14 * поÑÑÐ¾Ð¼Ñ Ð²ÑÑ Ð² try/catch. 15 * 16 * ÐодклÑÑение (пеÑед </body>): 17 * <script>window.KM_METRIKA_ID=21948439; window.KM_ENDPOINT='/km-lead.php';</script> 18 * <script src="/km_track.js" defer></script> 19 */ 20(function () { 21 'use strict'; 22 23 var CFG = { 24 key: 'km_attr', // клÑÑ Ð² localStorage 25 ttlDays: 90, // окно аÑÑибÑÑии пÑоекÑа 26 metrikaId: window.KM_METRIKA_ID || null, // 21948439 (.ru) / 98391964 (.ÑÑ) 27 endpoint: window.KM_ENDPOINT || '/km-lead.php', // пÑиÑмник на ÑÑом же домене 28 site: window.KM_SITE || location.hostname, 29 debug: !!window.KM_DEBUG 30 }; 31 32 function log() { if (CFG.debug && window.console) console.log.apply(console, ['[km_track]'].concat([].slice.call(arguments))); } 33 34 // ---------- Ñ ÑанилиÑе (localStorage Ñ Ð¾ÑкаÑом на cookie) ---------- 35 function storeGet() { 36 try { 37 var raw = localStorage.getItem(CFG.key); 38 if (!raw) { 39 var m = document.cookie.match(/(?:^|;\s*)km_attr=([^;]+)/); 40 raw = m ? decodeURIComponent(m[1]) : null; 41 } 42 if (!raw) return null; 43 var data = JSON.parse(raw); 44 if (data.exp && data.exp < Date.now()) return null; // пÑоÑÑÑ Ð»Ð¾ 45 return data; 46 } catch (e) { return null; } 47 } 48 49 function storeSet(data) { 50 try { 51 data.exp = Date.now() + CFG.ttlDays * 864e5; 52 var raw = JSON.stringify(data); 53 try { localStorage.setItem(CFG.key, raw); } catch (e) {} 54 document.cookie = 'km_attr=' + encodeURIComponent(raw) + 55 ';path=/;max-age=' + (CFG.ttlDays * 86400) + ';SameSite=Lax'; 56 } catch (e) {} 57 } 58 59 // ---------- ÑÐ°Ð·Ð±Ð¾Ñ Ð¸ÑÑоÑника ---------- 60 function qs() { 61 var out = {}, s = location.search.replace(/^\?/, ''); 62 if (!s) return out; 63 s.split('&').forEach(function (pair) { 64 if (!pair) return; 65 var i = pair.indexOf('='), k = i < 0 ? pair : pair.slice(0, i); 66 var v = i < 0 ? '' : pair.slice(i + 1); 67 try { out[decodeURIComponent(k)] = decodeURIComponent(v.replace(/\+/g, ' ')); } catch (e) { out[k] = v; } 68 }); 69 return out; 70 } 71 72 /** ТекÑÑее каÑание: меÑки из URL, инаÑе ÑеÑеÑеÑ, инаÑе пÑÑмой Ð·Ð°Ñ Ð¾Ð´. */ 73 function currentTouch() { 74 var p = qs(), t = { ts: new Date().toISOString(), landing: location.href.slice(0, 500) }; 75 ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].forEach(function (k) { 76 if (p[k]) t[k] = String(p[k]).slice(0, 255); 77 }); 78 // клик-иденÑиÑикаÑоÑÑ ÑекламнÑÑ ÑиÑÑем 79 ['yclid', 'gclid', 'ysclid', 'fbclid', 'erid'].forEach(function (k) { if (p[k]) t[k] = String(p[k]).slice(0, 255); }); 80 81 var ref = document.referrer || ''; 82 if (ref) t.referrer = ref.slice(0, 500); 83 84 if (!t.utm_source) {
85 if (t.yclid) { t.utm_source = 'yandex'; t.utm_medium = 'cpc'; } 86 else if (t.gclid) { t.utm_source = 'google'; t.utm_medium = 'cpc'; } 87 else if (ref) { 88 var host = ''; 89 try { host = ref.split('/')[2] || ''; } catch (e) {} 90 if (host && host.indexOf(location.hostname) === -1) { 91 t.utm_source = host.replace(/^www\./, ''); 92 // поиÑковики оÑделÑем Ð¾Ñ Ð¾Ð±ÑÑнÑÑ ÑÑÑлок â инаÑе оÑганика ÑÐµÐ´ÐµÑ Ð² referral 93 t.utm_medium = /(yandex|google|mail|bing|rambler|duckduckgo)\./i.test(host) ? 'organic' : 'referral'; 94 } else { t.utm_source = 'direct'; t.utm_medium = 'none'; } 95 } else { t.utm_source = 'direct'; t.utm_medium = 'none'; } 96 } 97 return t; 98 } 99 100 /** ÐнаÑимое каÑание = еÑÑÑ Ð¼ÐµÑки/клик-ид или внеÑний ÑеÑеÑеÑ. ÐÑÑмой Ð·Ð°Ñ Ð¾Ð´ не пеÑеÑиÑÐ°ÐµÑ Ð¸ÑÑоÑиÑ. */ 101 function isMeaningful(t) { 102 return !!(t.utm_campaign || t.yclid || t.gclid || 103 (t.utm_source && t.utm_source !== 'direct')); 104 } 105 106 function ensureAttr() { 107 var data = storeGet() || {}; 108 var t = currentTouch(); 109 if (!data.first) { data.first = t; data.touches = 1; } 110 else if (isMeaningful(t)) { 111 // новое знаÑимое каÑание: обновлÑем last, копим ÑепоÑÐºÑ 112 var prev = data.last || data.first; 113 if (prev.utm_source !== t.utm_source || prev.utm_campaign !== t.utm_campaign) { 114 data.last = t; 115 data.touches = (data.touches || 1) + 1; 116 data.chain = ((data.chain || data.first.utm_source || '') + '>' + t.utm_source).slice(-200); 117 } 118 } 119 if (!data.last) data.last = data.first; 120 data.site = CFG.site; 121 storeSet(data); 122 log('attr', data); 123 return data; 124 } 125 126 // ---------- ClientID ÐеÑÑики ---------- 127 function withClientId(cb) { 128 var id = CFG.metrikaId; 129 if (!id || typeof window.ym !== 'function') { cb(''); return; } 130 var done = false; 131 var finish = function (v) { if (!done) { done = true; cb(v || ''); } }; 132 try { window.ym(id, 'getClientID', finish); } catch (e) { finish(''); } 133 setTimeout(function () { finish(''); }, 1000); // ÐеÑÑика могла не загÑÑзиÑÑÑÑ â не ждÑм веÑно 134 } 135 136 // ---------- ÑабоÑа Ñ ÑоÑмами ---------- 137 var FIELDS = ['km_utm_source', 'km_utm_medium', 'km_utm_campaign', 'km_utm_term', 138 'km_utm_content', 'km_source_first', 'km_ym_cid', 'km_yclid', 'km_landing', 'km_referrer']; 139 140 function setHidden(form, name, value) { 141 if (!value) return; 142 var el = form.querySelector('input[name="' + name + '"]'); 143 if (!el) { 144 el = document.createElement('input'); 145 el.type = 'hidden'; 146 el.name = name; 147 form.appendChild(el); 148 }
149 el.value = String(value).slice(0, 500); 150 } 151 152 /** СкÑÑÑÑе Ð¿Ð¾Ð»Ñ Ñ Ð¸ÑÑоÑником â ÑÑ Ð¾Ð´ÑÑ ÑÑда же, кÑда ÑоÑма (в пиÑÑмо/Ð°Ð´Ð¼Ð¸Ð½ÐºÑ ÑайÑа). */ 153 function fillForm(form, attr, ymCid) { 154 try { 155 var L = attr.last || {}, F = attr.first || {}; 156 setHidden(form, 'km_utm_source', L.utm_source); 157 setHidden(form, 'km_utm_medium', L.utm_medium); 158 setHidden(form, 'km_utm_campaign', L.utm_campaign); 159 setHidden(form, 'km_utm_term', L.utm_term); 160 setHidden(form, 'km_utm_content', L.utm_content); 161 setHidden(form, 'km_source_first', F.utm_source); 162 setHidden(form, 'km_yclid', L.yclid || L.gclid); 163 setHidden(form, 'km_landing', F.landing); 164 setHidden(form, 'km_referrer', F.referrer); 165 setHidden(form, 'km_ym_cid', ymCid); 166 } catch (e) { log('fillForm error', e); } 167 } 168 169 /** ÐоÑÑаÑм из ÑоÑÐ¼Ñ ÐºÐ¾Ð½ÑакÑÑ. Ðмена полей ÑазнÑе: Drupal submitted[...], Joomla cf_phone. */ 170 // ÐовÑÑки Ð´Ð»Ñ Ð±Ð¾Ñов (honeypot): ÑкÑÑÑое поле, коÑоÑое Ñеловек не Ð²Ð¸Ð´Ð¸Ñ Ð¸ не 171 // заполнÑеÑ, а Ð±Ð¾Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÑеÑ. CMS ÑакÑÑ Ð·Ð°ÑÐ²ÐºÑ Ð¾ÑклонÑÐµÑ â и Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ Ñоже, 172 // инаÑе Ñпам, оÑбиÑÑй ÑайÑом, вÑÑ Ñавно попадÑÑ Ð² 1С админиÑÑÑаÑоÑÑ. 173 // Ðа камелиÑ-мед.ÑÑ (Convert Forms) ÑÑо поле «eMaiLaddress». 174 function isHidden(el) { 175 try { 176 if (el.type === 'hidden') return true; 177 if (el.offsetParent === null && el.type !== 'hidden') return true; 178 var s = window.getComputedStyle ? window.getComputedStyle(el) : null; 179 if (s && (s.display === 'none' || s.visibility === 'hidden' || parseFloat(s.opacity) === 0)) return true; 180 } catch (e) {} 181 return false; 182 } 183 184 function honeypotFilled(form) { 185 try { 186 // ТÐÐЬÐÐ ÑекÑÑовÑе полÑ. ЧекбокÑÑ Ð¸ Ñадио ÑÑда попадаÑÑ Ð½Ðµ должнÑ: 187 // Ð¸Ñ ÑплоÑÑ Ð¸ ÑÑдом пÑÑÑÑÑ Ñади каÑÑомной ÑÑилизаÑии â на kamelia-med.ru 188 // Ñак ÑкÑÑÑ ÑÐµÐºÐ±Ð¾ÐºÑ ÑоглаÑÐ¸Ñ Ð½Ð° обÑабоÑÐºÑ ÐÐн, и он вÑегда заполнен. 189 // ÐÑовеÑка «лÑбое ÑкÑÑÑое непÑÑÑое = боÑ» блокиÑовала Ð±Ñ ÐÐÐÐУЮ заÑвкÑ. 190 var TEXTY = { text: 1, email: 1, tel: 1, url: 1, search: 1, number: 1, textarea: 1 }; 191 // РТÐÐЬÐÐ ÐÐÐЯ-ÐÐÐУШÐÐ ÐÐ ÐÐÐÐÐ. ÐвÑиÑÑика «лÑбое ÑкÑÑÑое ÑекÑÑовое 192 // заполнено = боÑ» Ð´Ð²Ð°Ð¶Ð´Ñ Ð±Ð¸Ð»Ð° по живÑм полÑм: 08.08 â по ÑекбокÑÑ 193 // ÑоглаÑÐ¸Ñ Ð½Ð° ÐÐн, 10.08 â по textarea «message», коÑоÑÐ°Ñ Ð»ÐµÐ¶Ð¸Ñ Ð² 194 // ÑвÑÑнÑÑом блоке «УказаÑÑ Ð±Ð¾Ð»ÑÑе инÑоÑмаÑии» и поÑÐ¾Ð¼Ñ ÑÑиÑаеÑÑÑ 195 // ÑкÑÑÑой. Ðо вÑоÑом ÑлÑÑае заÑвка паÑиенÑа, заполнивÑего комменÑаÑий, 196 // молÑа не оÑпÑавлÑлаÑÑ Ð²Ð¾Ð¾Ð±Ñе. ÐовÑÑки на наÑÐ¸Ñ ÑайÑÐ°Ñ Ð¸Ð·Ð²ÐµÑÑÐ½Ñ 197 // поимÑнно (hp_field Ñ Drupal, eMaiLaddress Ñ Convert Forms на .ÑÑ), 198 // поÑÑÐ¾Ð¼Ñ Ð¿ÑовеÑÑем Ð¸Ñ , а не вÑÑ Ð¿Ð¾Ð´ÑÑд: пÑопÑÑÑиÑÑ Ð±Ð¾Ñа деÑевле, 199 // Ñем поÑеÑÑÑÑ Ð¿Ð°ÑиенÑа â Ñпам вÑÑ Ñавно оÑÑÐµÐµÑ CMS. 200 var TRAP = /^(hp_field|honeypot|hp|winnie|nickname|not_?fill|emailaddress|e_?mail_?address|url|website|company_?url)$/i; 201 var els = form.querySelectorAll('input, textarea'); 202 for (var i = 0; i < els.length; i++) { 203 var el = els[i]; 204 var type = (el.tagName.toLowerCase() === 'textarea') ? 'textarea' : (el.type || 'text'); 205 if (!TEXTY[type]) continue; // ÑекбокÑÑ, radio, hidden â мимо 206 if (!TRAP.test((el.name || '').trim())) continue; // не ловÑÑка â не ÑÑогаем 207 if (!isHidden(el)) continue; 208 if ((el.value || '').trim() !== '') return true; // ловÑÑка ÑкÑÑÑа и заполнена = Ð±Ð¾Ñ 209 } 210 } catch (e) {} 211 return false;
212 } 213 214 // ÐодпиÑи полей Ð´Ð»Ñ ÐºÐ°ÑÑоÑки 1С. ÐдминиÑÑÑаÑÐ¾Ñ ÑиÑÐ°ÐµÑ ÐºÐ¾Ð¼Ð¼ÐµÐ½ÑаÑий обÑаÑÐµÐ½Ð¸Ñ 215 // глазами, и «clinic: 93» ÐµÐ¼Ñ Ð½Ð¸Ñего не говоÑÐ¸Ñ â нÑжно «Ðлиника: РоÑоÑнаÑ». 216 // ÐÐ¼Ñ Ð¿Ð¾Ð»Ñ Ñ Drupal пÑÐ¸Ñ Ð¾Ð´Ð¸Ñ ÐºÐ°Ðº «submitted[clinic]», поÑÑÐ¾Ð¼Ñ ÑнаÑала 217 // вÑнимаем поÑледнÑÑ ÑаÑÑÑ. Ðезнакомое поле оÑÑавлÑем как еÑÑÑ: пÑÑÑÑ Ð»ÑÑÑе 218 // бÑÐ´ÐµÑ ÑÐµÑ Ð½Ð¸ÑеÑкое имÑ, Ñем поле молÑа пÑопадÑÑ. 219 var FIELD_LABELS = { 220 clinic: 'Ðлиника', branch: 'Филиал', filial: 'Филиал', 221 service: 'УÑлÑга', services: 'УÑлÑга', usluga: 'УÑлÑга', 222 doctor: 'ÐÑаÑ', vrach: 'ÐÑаÑ', specialist: 'СпеÑиалиÑÑ', 223 speciality: 'ÐапÑавление', napravlenie: 'ÐапÑавление', 224 date: 'ÐаÑа', data: 'ÐаÑа', time: 'ÐÑемÑ', vremya: 'ÐÑемÑ', 225 date_and_time: 'Ðелаемое вÑемÑ', time_interval: 'Удобное вÑемÑ', 226 promo: 'ÐкÑиÑ', akciya: 'ÐкÑиÑ', question: 'ÐопÑоÑ' 227 }; 228 function prettyField(name) { 229 var key = String(name).replace(/^.*\[/, '').replace(/\].*$/, '').toLowerCase(); 230 if (FIELD_LABELS[key]) return FIELD_LABELS[key]; 231 return String(name).slice(0, 60); 232 } 233 234 function readForm(form) { 235 var out = { fields: {} }; 236 var els = form.querySelectorAll('input, textarea, select'); 237 for (var i = 0; i < els.length; i++) { 238 var el = els[i]; 239 var name = (el.name || '').toLowerCase(); 240 var val = (el.value || '').trim(); 241 // У вÑпадаÑÑÐ¸Ñ ÑпиÑков value â ÑÑо внÑÑÑенний id ÑпÑавоÑника Drupal, 242 // и в каÑÑоÑке 1С админиÑÑÑаÑÐ¾Ñ Ð²Ð¸Ð´ÐµÐ» «clinic: 93; service: 18; 243 // doctor: 3736» вмеÑÑо названий. ÐеÑÑм подпиÑÑ Ð²ÑбÑанного пÑнкÑа: 244 // она и еÑÑÑ Ñо, ÑÑо Ñеловек ÑеалÑно вÑбÑал. 245 if (el.tagName === 'SELECT') { 246 try { 247 var opt = el.options[el.selectedIndex]; 248 var optText = opt ? (opt.text || '').trim() : ''; 249 // «- ÐÑбеÑиÑе -» и подобнÑе заглÑÑки â не оÑÐ²ÐµÑ Ð¿Ð¾Ð»ÑзоваÑÐµÐ»Ñ 250 if (optText && !/^[-ââ\s]*(вÑбеÑиÑе|не вÑбÑано|select|â|-)\s*[-ââ\s]*$/i.test(optText)) { 251 // в иеÑаÑÑ Ð¸ÑеÑÐºÐ¸Ñ ÑпиÑÐºÐ°Ñ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð¾ÑÑÑ ÑиÑÑÑÑ Ð´ÐµÑиÑами 252 // («--ÐеÑение каÑиеÑа») â админиÑÑÑаÑоÑÑ ÑÑо ÑиÑаÑÑ Ð½ÐµÐ·Ð°Ñем 253 val = optText.replace(/^[-ââ\s]+/, ''); 254 } else if (optText) { 255 continue; 256 } 257 } catch (e) {} 258 } else if (el.type === 'radio' || el.type === 'checkbox') { 259 // ÐеÑеклÑÑаÑели â Ñа же беда, ÑÑо Ñ ÑпиÑков: value ÑÑо id ÑпÑавоÑника 260 // («clinic: 92»). ÐодпиÑÑ Drupal кладÑÑ Ð² оÑделÑнÑй label[for], а не 261 // обоÑаÑÐ¸Ð²Ð°ÐµÑ Ð¿Ð¾Ð»Ðµ, поÑÑÐ¾Ð¼Ñ ÑнаÑала иÑем по id и ÑолÑко поÑом 262 // ÑмоÑÑим на обÑÑÑÐºÑ Ð¸ ÑоÑеда. 263 try { 264 var lbl = el.id ? document.querySelector('label[for="' + el.id + '"]') : null; 265 if (!lbl) lbl = el.closest('label'); 266 if (!lbl && el.nextElementSibling && el.nextElementSibling.tagName === 'LABEL') { 267 lbl = el.nextElementSibling; 268 } 269 var lblText = lbl ? (lbl.innerText || lbl.textContent || '').trim() : ''; 270 if (lblText) val = lblText.slice(0, 120); 271 } catch (e) {} 272 } 273 if (!name || !val) continue; 274 if (el.type === 'hidden' && name.indexOf('km_') !== 0) continue; // ÑлÑжебнÑе Ð¿Ð¾Ð»Ñ CMS не ÑаÑим 275 if (el.type === 'checkbox' && !el.checked) continue; 276 if (el.type === 'radio' && !el.checked) continue; 277 // СлÑжебное в комменÑаÑий не ÑаÑим: в 1С Ñ Ð¾Ð±ÑаÑÐµÐ½Ð¸Ñ Ð¾Ð½Ð¾ вÑглÑÐ´Ð¸Ñ Ð¼ÑÑоÑом 278 // («agree: 1; op: ÐеÑезвониÑе мне» вмеÑÑо Ñлов паÑиенÑа) â поймано на живой 279 // ÑоÑме 08.08.2026. ÐнаÑимÑе Ð¿Ð¾Ð»Ñ (ÑообÑение, ÑÑлÑга, Ñилиал) пÑÐ¾Ñ Ð¾Ð´ÑÑ. 280 if (/pass|token|captcha|form_build|form_id|form_mode|honeypot|h
280p_field|not.?fill|^agree$|^op$|^submit$|consent|privacy|^source\[|time_interval|^sy_|^l10n$|^reqenc$|^searchid$/i.test(name)) continue; 281 282 if (!out.phone && (/phone|tel|mobil|ÑелеÑон/i.test(name) || el.type === 'tel')) out.phone = val; 283 else if (!out.email && (/mail/i.test(name) || el.type === 'email')) out.email = val; 284 else if (!out.name && /(^|\[)(name|fio|имÑ)/i.test(name) && !/username|form/i.test(name)) out.name = val; 285 else if (!out.comment && /(comment|message|text|вопÑоÑ|комменÑ)/i.test(name)) out.comment = val; 286 else if (name.indexOf('km_') !== 0) out.fields[prettyField(name)] = val.slice(0, 200); 287 } 288 // ÐапаÑной ваÑианÑ: ÑелеÑон по маÑке, еÑли поле названо непÑивÑÑно. 289 // 290 // ÐÐÐÐÐ, Ñем он бÑл опаÑен. РанÑÑе здеÑÑ Ð¿ÐµÑебиÑалиÑÑ ÐСРÑлеменÑÑ ÑоÑмÑ, 291 // вклÑÑÐ°Ñ ÑкÑÑÑÑе, и пеÑвое знаÑение из 10+ ÑиÑÑ Ð¾Ð±ÑÑвлÑлоÑÑ ÑелеÑоном. 292 // Рв ÑкÑÑÑÑÑ Ð¿Ð¾Ð»ÑÑ Ð»ÐµÐ¶Ð°Ñ utm_content (id обÑÑÐ²Ð»ÐµÐ½Ð¸Ñ ÐиÑекÑа), yclid и 293 // пÑоÑие ÑиÑла Ñовно Ñой же длинÑ. Ðогда Ñеловек кликал ÐºÐ½Ð¾Ð¿ÐºÑ Ð½Ð° пÑÑÑой 294 // ÑоÑме, ÑÑÐµÐºÐµÑ (он ÑÑабаÑÑÐ²Ð°ÐµÑ Ð½Ð° mousedown, до валидаÑии бÑаÑзеÑа) 295 // оÑпÑавлÑл в 1С заÑвкÑ, коÑоÑой не бÑло, Ñ Â«Ð½Ð¾Ð¼ÐµÑом» вида 296 // +7 (5782) 659870 â ÑÑо бÑл id обÑÑвлениÑ. Ðа 08â10.08 Ñак ÑÑло
297 // 9 ÑикÑивнÑÑ Ð¾Ð±ÑаÑений из 40 â ÑеÑвеÑÑÑ Ð¿Ð¾Ñока, и админиÑÑÑаÑÐ¾Ñ 298 // ÑÑаÑил на Ð½Ð¸Ñ Ð²ÑемÑ, пÑÑаÑÑÑ Ð´Ð¾Ð·Ð²Ð¾Ð½Ð¸ÑÑÑÑ. 299 // 300 // ÐоÑÑомÑ: ÑолÑко видимÑе Ð¿Ð¾Ð»Ñ Ð²Ð²Ð¾Ð´Ð°, ÑÐ¾Ñ Ð¶Ðµ ÑилÑÑÑ ÑлÑжебнÑÑ Ð¸Ð¼Ñн, ÑÑо и 301 // вÑÑе, и пÑовеÑка на ÑеалÑнÑй ÑоÑÑийÑкий номеÑ. ÐÑли валидного ÑелеÑона 302 // Ð½ÐµÑ â оÑÑавлÑем пÑÑÑо: заÑвка без конÑакÑов не оÑпÑавиÑÑÑ Ð²Ð¾Ð²Ñе, и ÑÑо 303 // ÑеÑÑнее, Ñем вÑдÑмаÑÑ Ð½Ð¾Ð¼ÐµÑ. 304 if (!out.phone) { 305 for (var j = 0; j < els.length; j++) { 306 var fb = els[j]; 307 if (fb.type === 'hidden' || fb.type === 'checkbox' || fb.type === 'radio') continue; 308 var fbName = (fb.name || '').toLowerCase(); 309 if (/pass|token|captcha|form_build|form_id|form_mode|honeypot|hp_field|not.?fill|consent|privacy|^sy_|^l10n$|^reqenc$|^searchid$/i.test(fbName)) continue; 310 var v = (fb.value || '').trim(); 311 if (!/^[\d\s()+-]{10,20}$/.test(v)) continue; 312 var dg = v.replace(/\D/g, ''); 313 if (dg.length < 10 || dg.length > 11) continue; 314 // код Ñегиона/опеÑаÑоÑа в РоÑÑии наÑинаеÑÑÑ Ð½Ð° 3, 4, 8 или 9; 315 // id обÑÑÐ²Ð»ÐµÐ½Ð¸Ñ (5782659870) на ÑÑом и оÑÑекаеÑÑÑ 316 if ('3489'.indexOf(dg.slice(-10).charAt(0)) < 0) continue; 317 out.phone = v; 318 break; 319 } 320 } 321 return out; 322 } 323 324 // ЧеловеÑеÑкое название ÑоÑÐ¼Ñ Ð´Ð»Ñ ÐºÐ°ÑÑоÑки в 1С: админиÑÑÑаÑоÑÑ Â«eforms-callme-form» 325 // ни о ÑÑм не говоÑиÑ, а «ÐбÑаÑнÑй звонок» ÑÑÐ°Ð·Ñ Ð¾Ð±ÑÑÑнÑеÑ, оÑкÑда заÑвка. 326 // ÐеÑÑм Ñо ÑÑÑаниÑÑ, а не из ÑловаÑÑ â Ñогда новÑе ÑоÑÐ¼Ñ Ð¿Ð¾Ð´Ð¿Ð¸ÑÑваÑÑÑÑ Ñами. 327 function formTitle(form) { 328 // «ÐÑпÑавиÑÑ» и «Ðл в каÑÑоÑке 1С ниÑего не обÑÑÑнÑÑÑ â еÑли кнопка подпиÑана 329 // Ñак, пÑодолжаем иÑкаÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²Ð¾Ðº вÑÑе по деÑÐµÐ²Ñ (на камелиÑ-мед.ÑÑ ÑоÑма 330 // Ð»ÐµÐ¶Ð¸Ñ Ð² модалке UIkit, и оÑмÑÑленное название ÑолÑко Ñам). 331 var GENERIC = /^(оÑпÑавиÑÑ|оÑпÑавка|ок|ok|submit|send|гоÑово|далее)$/i; 332 var SEL = 'legend, .form-title, .modal-title, .uk-modal-title, h1, h2, h3, h4, h5'; 333 var t = ''; 334 try { 335 var h = form.querySelector(SEL); 336 if (!h && form.closest) { 337 var box = form.closest('.modal, .popup, .modal-content, .uk-modal, .uk-modal-dialog, section, .block, .b-form'); 338 if (box) h = box.querySelector(SEL); 339 } 340 t = (h ? (h.textContent || '') : '').replace(/\s+/g, ' ').trim(); 341 if (!t || GENERIC.test(t)) { 342 var btn = form.querySelector('button[type="submit"], input[type="submit"], button'); 343 var bt = (btn ? (btn.textContent || btn.value || '') : '').replace(/\s+/g, ' ').trim(); 344 // беÑÑм ÑекÑÑ ÐºÐ½Ð¾Ð¿ÐºÐ¸, ÑолÑко еÑли он ÑÑо-Ñо говоÑиÑ; инаÑе оÑÑавлÑем заголовок 345 if (bt && !GENERIC.test(bt)) t = bt; 346 else if (!t) t = bt; 347 } 348 } catch (e) {} 349 return t.replace(/\s+/g, ' ').trim().slice(0, 60); 350 } 351 352 function sendCopy(form, attr, ymCid) { 353 try { 354 if (honeypotFilled(form)) { log('заполнена ловÑÑка Ð´Ð»Ñ Ð±Ð¾Ñов â не ÑлÑм'); return; } 355 var data = readForm(form); 356 if (!data.phone && !data.email) { log('Ð½ÐµÑ ÐºÐ¾Ð½ÑакÑов â не ÑлÑм'); return; } 357 358 var L = attr.last || {}, F = attr.first || {}; 359 var payload = { 360 v: 1, 361 site: CFG.site, 362 page: location.href.slice(0, 500), 363 form_id: form.getAttribute('id') || form.getAttribute('name') || '', 364 form_title: formTitle(form), 365 name: data.name || '', 366 phone: data.phone || '', 367 email: data.email || '', 368 comment: data.comment || '', 369 extra: data.fields, 370 utm_source: L.utm_source || '', utm_medium: L.utm_medium || '', 371 utm_campaign: L.utm_campaign || '', utm_term: L.utm_term || '',
372 utm_content: L.utm_content || '', 373 yclid: L.yclid || '', gclid: L.gclid || '', 374 first_source: F.utm_source || '', first_campaign: F.utm_campaign || '', 375 chain: attr.chain || '', touches: attr.touches || 1, 376 landing: F.landing || '', referrer: F.referrer || '', 377 ym_cid: ymCid || '', 378 ts: new Date().toISOString() 379 }; 380 var body = JSON.stringify(payload); 381 var ok = false; 382 // sendBeacon пеÑÐµÐ¶Ð¸Ð²Ð°ÐµÑ Ð¿ÐµÑÐµÑ Ð¾Ð´/ÑедиÑÐµÐºÑ Ð¿Ð¾Ñле ÑабмиÑа â Ñо, ÑÑо нÑжно 383 if (navigator.sendBeacon) { 384 try { ok = navigator.sendBeacon(CFG.endpoint, new Blob([body], { type: 'text/plain;charset=UTF-8' })); } catch (e) {} 385 } 386 if (!ok) { 387 try { 388 var x = new XMLHttpRequest(); 389 x.open('POST', CFG.endpoint, true); 390 x.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8'); 391 x.send(body); 392 } catch (e) { log('send error', e); } 393 } 394 log('ÐºÐ¾Ð¿Ð¸Ñ Ð¾ÑпÑавлена', payload); 395 } catch (e) { log('sendCopy error', e); } 396 } 397 398 // ---------- запÑÑк ---------- 399 function init() { 400 var attr = ensureAttr(); 401 withClientId(function (ymCid) { 402 try { if (ymCid) { attr.ym_cid = ymCid; storeSet(attr); } } catch (e) {} 403 404 var fill = function () { 405 var forms = document.querySelectorAll('form'); 406 for (var i = 0; i < forms.length; i++) { 407 if (forms[i].getAttribute('data-km') === '1') continue; 408 if (forms[i].querySelector('input[type="search"]') && forms[i].querySelectorAll('input').length < 3) continue; // поиÑк пÑопÑÑкаем 409 forms[i].setAttribute('data-km', '1'); 410 fillForm(forms[i], attr, ymCid); 411 } 412 }; 413 fill(); 414 415 // ÑоÑÐ¼Ñ Ð¼Ð¾Ð³ÑÑ Ð¿Ð¾ÑвлÑÑÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ (модалки, поп-апÑ) 416 try { 417 new MutationObserver(function () { fill(); }) 418 .observe(document.documentElement, { childList: true, subtree: true }); 419 } catch (e) {} 420 421 // Ðдна оÑпÑавка на ÑоÑмÑ, из какого Ð±Ñ Ð¾Ð±ÑабоÑÑика ни пÑиÑли. 422 function handoff(form) { 423 if (!form || form.tagName !== 'FORM') return; 424 if (form.getAttribute('data-km-sent') === '1') return; 425 form.setAttribute('data-km-sent', '1'); 426 setTimeout(function () { form.removeAttribute('data-km-sent'); }, 3000); 427 fillForm(form, attr, ymCid); 428 sendCopy(form, attr, ymCid); 429 } 430 431 // (1) наÑивнÑй submit, capture â ÑÑабаÑÑваем ÑанÑÑе обÑабоÑÑиков CMS 432 document.addEventListener('submit', function (ev) { 433 try { handoff(ev.target); } catch (e) { log('submit handler error', e); } 434 }, true); 435 436 // (2) клик по кнопке оÑпÑавки. 437 // ÐбÑзаÑелÑно: ÑоÑÐ¼Ñ Drupal на ÑÑом ÑайÑе ÑÑ Ð¾Ð´ÑÑ ÑеÑез AJAX (use-ajax, 438 // ajax-eforms-callme-form), а jQuery-ÑÑÐ¸Ð³Ð³ÐµÑ submit не вÑплÑÐ²Ð°ÐµÑ Ð´Ð¾ document 439 // наÑивнÑм ÑобÑÑием â на одном обÑабоÑÑике submit заÑвки ÑеÑÑлиÑÑ Ð±Ñ Ð¼Ð¾Ð»Ñа. 440 // Тем же ÑпоÑобом Ð»Ð¾Ð²Ð¸Ñ ÑоÑÐ¼Ñ Ð²Ð¸Ð´Ð¶ÐµÑ Ðанго (mousedown по form .btn). 441 var onPress = function (ev) { 442 try { 443 var el = ev.target; 444 for (var d = 0; el && d < 5; d++, el = el.parentNode) { 445 if (!el.tagName) continue; 446 var tag = el.tagName.toLowerCase(); 447 var type = (el.getAttribute && (el.getAttribute('type') || '')).toLowerCase(); 448 var isBtn = tag === 'button' || (tag === 'input' && (type === 'submit' || type === 'button')) 449 || (el.className && /\bbtn\b|submit/i.test(el.className)); 450 if (!isBtn) continue; 451 var form = el.form || (el.closest && el.closest('form')); 452 if (form) { handoff(form); } 453 return; 454 } 455 } catch (e) { log('press handler error', e); } 456 }; 457 document.addEventListener('mousedown', onPress, true); 458 document.addEventListener('touchstart', onPress, true); 459 }); 460 } 461 462 try { 463 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); 464 else init(); 465 } catch (e) { /* молÑа: ÑкÑÐ¸Ð¿Ñ Ð½Ðµ должен ломаÑÑ ÑÐ°Ð¹Ñ */ } 466})(); 467 468/* Ðвиз «ÐмпланÑаÑиÑ», подклÑÑение. Ðобавлено 28.08.2026. 469 ÐивÑÑ Ð·Ð´ÐµÑÑ, а не в counters.inc: ÑÐ¾Ñ Ð»ÐµÐ¶Ð¸Ñ Ð² Ñеме, а ÑÐµÐ¼Ñ Ð¿ÐµÑепиÑÑÐ²Ð°ÐµÑ 470 деплой ЦеÑеÑÑ Ñеликом (пÑовеÑено: 25.08 15:35 пеÑепиÑÐ°Ð½Ñ themes/, modules/, 471 sites/, а ÑÐ°Ð¹Ð»Ñ Ð² коÑне ÑÑелели). Ðбе ÑÑÑоки в try/catch â Ñбой квиза 472 не должен задеÑÑ Ð¾ÑпÑÐ°Ð²ÐºÑ Ð·Ð°Ñвок. ÐÑÑÑ Ð¿ÑовеÑÑеÑÑÑ, ÑÑÐ¾Ð±Ñ ÑÐ°Ð¹Ð»Ñ 473 не гÑÑзилиÑÑ Ð½Ð° оÑÑалÑнÑÑ ÑÑÑаниÑÐ°Ñ ÑайÑа. */ 474try { if (location.pathname.indexOf('/implantaciya-zubov') === 0) { var kmqL = document.createElement('link'); kmqL.rel = 'stylesheet'; kmqL.href = '/kviz.css?v=1'; document.head.appendChild(kmqL); } } catch (e) {} 475try { if (location.pathname.indexOf('/implantaciya-zubov') === 0) { var kmqS = document.createElement('script'); kmqS.src = '/kviz.js?v=2'; document.head.appendChild(kmqS); } } catch (e) {}
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.