1(function () { 2 'use strict'; 3 4 var root = document.getElementById('ox8bk-root'); 5 if (!root) { return; } 6 7 var grid = document.getElementById('ox8bk-grid'); 8 // Ðлок ÑилÑÑÑов/ÑоÑÑиÑовки Ð¸Ð¼ÐµÐµÑ ÑмÑÑл ÑолÑко в каÑалоге. Ðа каÑÑоÑке вÑаÑа гÑида неÑ, 9 // и без ÑÑого вÑÑ Ð¾Ð´Ð° apply() ÑпадÑÑ Ð½Ð° оÑÑÑÑÑÑвÑÑÑем поле поиÑка. 10 if (!grid) { return; } 11 var counter = document.getElementById('ox8bk-counter-value'); 12 var emptyBox = document.getElementById('ox8bk-empty'); 13 var fallbackBox = document.getElementById('ox8bk-fallback'); 14 var fallbackGrid = document.getElementById('ox8bk-fallback-grid'); 15 var searchInput = document.getElementById('ox8bk-search'); 16 var sortSelect = document.getElementById('ox8bk-sort'); 17 var filterForm = document.getElementById('ox8bk-filter-form'); 18 var aside = document.getElementById('ox8bk-filter'); 19 var toggle = document.getElementById('ox8bk-filter-toggle'); 20 var closeBtn = document.getElementById('ox8bk-filter-close'); 21 var applyBtn = document.getElementById('ox8bk-filter-apply'); 22 var resetBtn = document.getElementById('ox8bk-reset'); 23 24 var THRESHOLD = 5; 25 var cards = grid ? [].slice.call(grid.querySelectorAll('.ox8bk-card')) : []; 26 var timer = null; 27 var fbTimer = null; 28 29 function norm(v) { 30 return String(v || '').toLowerCase().replace(/Ñ/g, 'е').replace(/\s+/g, ' ').trim(); 31 } 32 33 34 function soonLimit() { 35 var d = new Date(); 36 d.setDate(d.getDate() + 1); 37 var m = String(d.getMonth() + 1); 38 var day = String(d.getDate()); 39 if (m.length < 2) { m = '0' + m; } 40 if (day.length < 2) { day = '0' + day; } 41 return d.getFullYear() + '-' + m + '-' + day + ' 23:59'; 42 } 43 44 function checked(name) { 45 if (!filterForm) { return []; } 46 var list = [].slice.call(filterForm.querySelectorAll('input[name="' + name + '"]:checked')); 47 return list.map(function (el) { return el.value; }); 48 } 49 50 // ÐеÑево «взÑоÑÐ»Ð°Ñ â [деÑÑкие]»: ÑÑÑока ÑаÑеÑа â ÑÑо Ñема, а не один id. 51 var TREE = (function () { 52 try { return JSON.parse(root.dataset.specTree || '{}') || {}; } catch (e) { return {}; } 53 })(); 54 55 function ageMode() { 56 var picked = checked('age'); 57 // ÐÑмеÑÐµÐ½Ñ Ð¾Ð±Ð° или ни одного â одно и Ñо же: огÑаниÑÐµÐ½Ð¸Ñ Ð½ÐµÑ. 58 return picked.length === 1 ? picked[0] : ''; 59 } 60 61 function state() { 62 return { 63 spec: checked('spec'), 64 branch: checked('branch'), 65 avail: checked('avail').length > 0, 66 age: ageMode(), 67 q: norm(searchInput ? searchInput.value : ''), 68 sort: sortSelect ? sortSelect.value : root.dataset.sort 69 }; 70 } 71 72 function targetSpecIds(st) { 73 var out = []; 74 st.spec.forEach(function (id) { 75 var kids = (TREE[id] || []).map(String); 76 if (st.age === 'child') { 77 out = out.concat(kids); 78 } else if (st.age === 'adult') { 79 out.push(id); 80 } else { 81 out.push(id); 82 out = out.concat(kids); 83 } 84 }); 85 return out; 86 } 87 88 function matches(card, st, limit) { 89 if (st.spec.length) { 90 var targets = targetSpecIds(st); 91 // У ÑÐµÐ¼Ñ Ð½ÐµÑ Ð²ÐµÑÑии Ð´Ð»Ñ ÑÑой аÑдиÑоÑии (напÑÐ¸Ð¼ÐµÑ Â«ÐаÑдиолог + ÐеÑÑм») â показÑваÑÑ Ð½ÐµÐºÐ¾Ð³Ð¾. 92 if (!targets.length) { return false; } 93 var own = (card.dataset.spec || '').split(','); 94 var hit = targets.some(function (id) { return own.indexOf(id) !== -1; }); 95 if (!hit) { return false; } 96 } else if (st.age) { 97 // «ÐÐ¾Ð¼Ñ Ð¿ÑиÑм» без ÑÐµÐ¼Ñ â ÑамоÑÑоÑÑелÑнÑй ÑилÑÑÑ: «покажиÑе вÑÐµÑ Ð´ÐµÑÑÐºÐ¸Ñ Ð²ÑаÑей».
98 var pool = st.age === 'child' ? (card.dataset.specChild || '') : (card.dataset.specAdult || ''); 99 if (pool === '') { return false; } 100 } 101 if (st.branch.length && st.branch.indexOf(card.dataset.branch) === -1) { return false; } 102 if (st.avail) { 103 var first = card.dataset.first || ''; 104 if (!first || first > limit) { return false; } 105 } 106 if (st.q && (card.dataset.search || '').indexOf(st.q) === -1) { return false; } 107 return true; 108 } 109 110 function sortKey(card, mode, limit) { 111 var first = card.dataset.first || '9999-12-31 23:59'; 112 var reviews = parseInt(card.dataset.reviews || '0', 10); 113 var price = parseInt(card.dataset.price || '0', 10) || 999999; 114 var group = card.dataset.first ? 0 : 2; 115 if (mode === 'nearest_popular' && group === 0) { 116 group = first <= limit ? 0 : 1; 117 } 118 return { group: group, first: first, reviews: reviews, price: price, id: parseInt(card.dataset.id || '0', 10) }; 119 } 120 121 function compare(a, b, mode, limit) { 122 var ka = sortKey(a, mode, limit); 123 var kb = sortKey(b, mode, limit); 124 if (ka.group !== kb.group) { return ka.group - kb.group; } 125 if (mode === 'popular') { 126 if (ka.reviews !== kb.reviews) { return kb.reviews - ka.reviews; } 127 if (ka.first !== kb.first) { return ka.first < kb.first ? -1 : 1; } 128 return ka.id - kb.id; 129 } 130 if (mode === 'price') { 131 if (ka.price !== kb.price) { return ka.price - kb.price; } 132 if (ka.first !== kb.first) { return ka.first < kb.first ? -1 : 1; } 133 return ka.id - kb.id; 134 } 135 if (mode === 'nearest_popular' && ka.group === 0) { 136 if (ka.reviews !== kb.reviews) { return kb.reviews - ka.reviews; } 137 if (ka.first !== kb.first) { return ka.first < kb.first ? -1 : 1; } 138 return ka.id - kb.id; 139 } 140 if (ka.first !== kb.first) { return ka.first < kb.first ? -1 : 1; } 141 if (ka.reviews !== kb.reviews) { return kb.reviews - ka.reviews; } 142 return ka.id - kb.id; 143 } 144 145 function syncUrl(st) { 146 if (!window.history || !window.history.replaceState) { return; } 147 var params = new URLSearchParams(); 148 if (st.spec.length) { params.set('spec', st.spec.join(',')); } 149 if (st.age) { params.set('age', st.age); } 150 if (st.branch.length) { params.set('branch', st.branch.join(',')); } 151 if (st.avail) { params.set('avail', 'soon'); } 152 if (st.q) { params.set('q', searchInput.value.trim()); } 153 if (st.sort && st.sort !== root.dataset.sort) { params.set('sort', st.sort); } 154 var qs = params.toString(); 155 window.history.replaceState({}, '', qs ? location.pathname + '?' + qs : location.pathname); 156 } 157 158 /** 159 * ÐÑÑÑÐ°Ñ Ð²ÑдаÑа без обÑÑÑÐ½ÐµÐ½Ð¸Ñ ÑиÑаеÑÑÑ ÐºÐ°Ðº оÑибка ÑайÑа: Ñеловек не видиÑ, ÑÑо Ñами же 160 * вÑбÑаннÑе ÑилÑÑÑ Ð¸ поиÑк взаимоиÑклÑÑаÑÑ Ð´ÑÑг дÑÑга. ÐазÑваем акÑивнÑе кÑиÑеÑии Ñвно. 161 */ 162 function explainEmpty(st) { 163 var hint = document.getElementById('ox8bk-empty-hint'); 164 if (!hint) { return; } 165 var parts = []; 166 if (st.spec.length) { 167 var names = [].slice.call(document.querySelectorAll('#ox8bk-filter-form input[name="spec"]:checked')) 168 .map(function (i) { 169 var lb = i.closest('label'); 170 return lb ? lb.textContent.trim() : ''; 171 }) 172 .filter(Boolean); 173 if (names.length) { parts.push('ÑпеÑиалÑноÑÑÑ: ' + names.join(', ')); } 174 } 175 if (st.age) { parts.push('пÑиÑм ' + (st.age === 'child' ? 'деÑÑм' : 'взÑоÑлÑм')); } 176 if (st.branch.length) { parts.push('Ñилиал вÑбÑан'); } 177 if (st.avail) { parts.push('окна ÑÐµÐ³Ð¾Ð´Ð½Ñ Ð¸Ð»Ð¸ завÑÑа'); } 178 if (st.q) { parts.push('поиÑк: «' + searchInput.value.trim() + '»'); } 179 180 if (parts.length < 2) { 181 hint.hidden = true; 182 hint.textContent = ''; 183 return; 184 } 185 hint.textContent = 'УÑÐ»Ð¾Ð²Ð¸Ñ ÑейÑÐ°Ñ â ' + parts.join('; ') + '. ÐеÑоÑÑно, они не ÑоÑеÑаÑÑÑÑ Ð´ÑÑг Ñ Ð´ÑÑгом.'; 186 hint.hidden = false;
187 } 188 189 function apply() { 190 var st = state(); 191 var limit = soonLimit(); 192 var visible = []; 193 194 cards.forEach(function (card) { 195 var ok = matches(card, st, limit); 196 card.hidden = !ok; 197 if (ok) { visible.push(card); } 198 }); 199 200 visible.sort(function (a, b) { return compare(a, b, st.sort, limit); }); 201 visible.forEach(function (card, index) { card.style.order = String(index); }); 202 203 if (counter) { counter.textContent = String(visible.length); } 204 if (emptyBox) { emptyBox.hidden = visible.length > 0; } 205 if (!visible.length) { explainEmpty(st); } 206 207 syncUrl(st); 208 scheduleFallback(st, visible.length); 209 } 210 211 function scheduleFallback(st, count) { 212 if (!fallbackBox || !fallbackGrid) { return; } 213 var need = (st.q !== '' || st.spec.length > 0 || st.branch.length > 0 || st.avail || st.age !== '') && count < THRESHOLD; 214 if (!need) { 215 fallbackBox.hidden = true; 216 fallbackGrid.innerHTML = ''; 217 return; 218 } 219 if (fbTimer) { clearTimeout(fbTimer); } 220 fbTimer = setTimeout(function () { loadFallback(st); }, 250); 221 } 222 223 function loadFallback(st) { 224 var params = new URLSearchParams(); 225 params.set('action', 'ox8ru:booking.Booking.list'); 226 params.set('limit', '1'); 227 params.set('site_id', root.dataset.site || 's1'); 228 if (st.q) { params.set('query', searchInput.value.trim()); } 229 if (st.spec.length) { params.set('speciality', st.spec.join(',')); } 230 if (st.age) { params.set('age', st.age); } 231 if (st.branch.length) { params.set('branch', st.branch.join(',')); } 232 if (st.avail) { params.set('availability', 'soon'); } 233 234 fetch('/bitrix/services/main/ajax.php?' + params.toString(), { credentials: 'same-origin' }) 235 .then(function (r) { return r.json(); }) 236 .then(function (data) { 237 var items = (data && data.data && data.data.fallback_items) || []; 238 if (!items.length) { 239 fallbackBox.hidden = true; 240 fallbackGrid.innerHTML = ''; 241 return; 242 } 243 fallbackGrid.innerHTML = items.map(renderFallbackCard).join(''); 244 fallbackBox.hidden = false; 245 }) 246 .catch(function () { 247 fallbackBox.hidden = true; 248 }); 249 } 250 251 function esc(value) { 252 return String(value == null ? '' : value) 253 .replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); 254 } 255 256 function renderFallbackCard(item) { 257 var photo = item.photo 258 ? '<img class="ox8bk-card__photo" src="' + esc(item.photo) + '" alt="' + esc(item.name) + '" width="171" height="171" loading="lazy">' 259 : '<span class="ox8bk-card__photo ox8bk-card__photo--empty"></span>'; 260 var link = item.url 261 ? '<a class="ox8bk-card__name-link" href="' + esc(item.url) + '">' + esc(item.name) + '</a>' 262 : esc(item.name); 263 return '<article class="ox8bk-card ox8bk-card--noslots">' 264 + '<div class="ox8bk-card__left"><div class="ox8bk-card__media">' + photo + '</div></div>' 265 + '<div class="ox8bk-card__right"><h3 class="ox8bk-card__name">' + link + '</h3>' 266 + '<div class="ox8bk-card__divider"></div>' 267 + '<div class="ox8bk-card__noslots">СейÑÐ°Ñ Ð½ÐµÑ ÑвободнÑÑ Ð¾ÐºÐ¾Ð½</div>' 268 + '</div></article>'; 269 } 270 271 function openFilter(open) { 272 if (!aside || !toggle) { return; } 273 aside.classList.toggle('is-open', open); 274 toggle.setAttribute('aria-expanded', open ? 'true' : 'false'); 275 } 276 277 if (searchInput) { 278 searchInput.addEventListener('input', function () { 279 if (timer) { clearTimeout(timer); } 280 timer = setTimeout(apply, 200); 281 }); 282 } 283 if (sortSelect) { sortSelect.addEventListener('change', apply); } 284 if (filterForm) { 285 filterForm.addEventListener('change', function (e) { 286 if (e.target && e.target.tagName === 'INPUT') { apply(); } 287 }); 288 filterForm.addEventListener('submit', function (e) { e.preventDefault(); }); 289 } 290 if (toggle) { toggle.addEventListener('click', function () { openFilter(!aside.classList.contains('is-open')); }); } 291 if (closeBtn) { closeBtn.addEventListener('click', function () { openFilter(false); }); }
292 if (applyBtn) { applyBtn.addEventListener('click', function () { openFilter(false); }); } 293 if (resetBtn) { 294 resetBtn.addEventListener('click', function () { 295 if (filterForm) { filterForm.reset(); } 296 if (searchInput) { searchInput.value = ''; } 297 apply(); 298 }); 299 } 300 301 document.addEventListener('keydown', function (e) { 302 if (e.key === 'Escape' && aside && aside.classList.contains('is-open')) { openFilter(false); } 303 }); 304 305 document.addEventListener('click', function (e) { 306 if (!aside || !aside.classList.contains('is-open')) { return; } 307 if (aside.contains(e.target) || (toggle && toggle.contains(e.target))) { return; } 308 openFilter(false); 309 }); 310 311 apply(); 312})(); 313 314 315(function () { 316 'use strict'; 317 318 var root = document.getElementById('ox8bk-root'); 319 if (!root) { return; } 320 321 var AJAX = '/bitrix/services/main/ajax.php'; 322 var about = document.getElementById('ox8bk-about'); 323 var book = document.getElementById('ox8bk-book'); 324 if (!about || !book) { return; } 325 326 var cache = {}; 327 var current = null; 328 var picked = { service: null, date: '', slot: null }; 329 var calMonth = null; 330 331 var MONTHS = ['ÑнваÑÑ', 'ÑевÑалÑ', 'маÑÑ', 'апÑелÑ', 'май', 'иÑнÑ', 'иÑлÑ', 'авгÑÑÑ', 'ÑенÑÑбÑÑ', 'окÑÑбÑÑ', 'ноÑбÑÑ', 'декабÑÑ']; 332 333 function esc(v) { 334 return String(v == null ? '' : v) 335 .replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); 336 } 337 338 /** 339 * СÑÑаниÑа оÑдаÑÑÑÑ Ð¸Ð· композиÑного кÑÑа, поÑÑÐ¾Ð¼Ñ sessid в ÑазмеÑке Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÑоÑÑÑ Ñим. 340 * ÐÑиоÑиÑÐµÑ â знаÑение из ÑдÑа (его composite обновлÑÐµÑ Ð¿Ñи оживлении ÑÑÑаниÑÑ), ÑазмеÑка â ÑолбÑк. 341 */ 342 function getSessid() { 343 try { 344 if (typeof BX !== 'undefined' && typeof BX.bitrix_sessid === 'function') { 345 var s = BX.bitrix_sessid(); 346 if (s) { return s; } 347 } 348 } catch (e) {} 349 return root.dataset.sessid || ''; 350 } 351 352 function isCsrfError(res) { 353 if (!res || !res.errors || !res.errors.length) { return false; } 354 for (var i = 0; i < res.errors.length; i++) { 355 var e = res.errors[i] || {}; 356 var text = String(e.message || '') + ' ' + String(e.code || ''); 357 if (text.toLowerCase().indexOf('csrf') !== -1) { return true; } 358 } 359 return false; 360 } 361 362 /** 363 * ÐÑи invalid_csrf ÑдÑо Ñамо кладÑÑ Ð°ÐºÑÑалÑнÑй Ñокен в customData.csrf â беÑÑм его без лиÑнего запÑоÑа. 364 */ 365 function csrfFromResponse(res) { 366 if (!res || !res.errors) { return ''; } 367 for (var i = 0; i < res.errors.length; i++) { 368 var cd = (res.errors[i] || {}).customData; 369 if (cd && cd.csrf) { return cd.csrf; } 370 } 371 return ''; 372 } 373 374 function refreshSessid(cb) { 375 fetch(AJAX + '?action=ox8ru%3Abooking.Booking.token&site_id=' + encodeURIComponent(root.dataset.site || 's1'), { 376 method: 'GET', 377 credentials: 'same-origin', 378 cache: 'no-store' 379 }) 380 .then(function (r) { return r.json(); }) 381 .then(function (res) { 382 cb(res && res.data && res.data.sessid ? res.data.sessid : ''); 383 }) 384 .catch(function () { cb(''); }); 385 } 386 387 function openModal(el) { 388 el.hidden = false; 389 el.setAttribute('aria-hidden', 'false'); 390 document.documentElement.style.overflow = 'hidden'; 391 } 392 393 function closeModal(el) { 394 el.hidden = true; 395 el.setAttribute('aria-hidden', 'true'); 396 if (about.hidden && book.hidden) { 397 document.documentElement.style.overflow = ''; 398 } 399 } 400 401 function load(id) { 402 if (cache[id]) { return Promise.resolve(cache[id]); } 403 var url = AJAX + '?action=ox8ru%3Abooking.Booking.doctor&id=' + encodeURIComponent(id) 404 + '&site_id=' + encodeURIComponent(root.dataset.site || 's1'); 405 return fetch(url, { credentials: 'same-origin' }) 406 .then(function (r) { return r.json(); }) 407 .then(function (res) { 408 if (!res || res.status !== 'success' || !res.data) { throw new Error('bad'); } 409 cache[id] = res.data; 410 return res.data; 411 }); 412 } 413 414 function stars(n) { 415 var full = Math.max(0, Math.min(5, parseInt(n, 10) || 0)); 416 return new Array(full + 1).join('â ') + new Array(6 - full).join('â'); 417 } 418 419 function renderAbout(data) { 420 var d = data.doctor; 421 about.querySelector('#ox8bk-about-title').textContent = d.name || ''; 422 renderAboutSlots(d); 423 var panels = about.querySelectorAll('.ox8bk-tabs__panel'); 424 425 var html = ''; 426 if (data.about) { 427 html += '<div class="ox8bk-block"><div class="ox8bk-block__title">РвÑаÑе</div>' 428 + '<div class="ox8bk-block__text">' + esc(data.about) + '</div></div>'; 429 } 430 if (d.tags && d.tags.length) { 431 html += '<div class="ox8bk-block"><div class="ox8bk-block__title">ЧÑо леÑиÑ</div><div class="ox8bk-card__tags">' 432 + d.tags.map(function (t) { return '<span class="ox8bk-chip">' + esc(t) + '</span>'; }).join('') 433 + '</div></div>'; 434 } 435 if (data.education && data.education.length) { 436 html += '<div class="ox8bk-block"><div class="ox8bk-block__title">ÐбÑазование и квалиÑикаÑиÑ</div><ul class="ox8bk-list">' 437 + data.education.map(function (e) { return '<li class="ox8bk-list__item">' + esc(e) + '</li>'; }).join('') 438 + '</ul></div>'; 439 } 440 panels[0].innerHTML = html || '<div class="ox8bk-block__text">ÐпиÑание пока не заполнено.</div>'; 441 442 panels[1].innerHTML = (data.services && data.services.length) 443 ? data.services.map(function (s) { 444 return '<div class="ox8bk-service"><div><div class="ox8bk-service__name">' + esc(s.name) + '</div>' 445 + '<div class="ox8bk-service__meta">' + (s.duration ? s.duration + ' мин' : '') + '</div></div>' 446 + '<div class="ox8bk-service__price">' + (s.price ? s.price + ' ÑÑб.' : 'â') + '</div></div>'; 447 }).join('') 448 : '<div class="ox8bk-block__text">Ð¦ÐµÐ½Ñ ÑÑоÑнÑйÑе в клинике.</div>'; 449 450 panels[2].innerHTML = (data.reviews && data.reviews.length) 451 ? data.reviews.map(function (r) { 452 return '<div class="ox8bk-review"><div class="ox8bk-review__head">' 453 + '<span class="ox8bk-review__stars">' + stars(r.rating) + '</span>' 454 + '<span class="ox8bk-review__date">' + esc(r.date) + '</span></div>' 455 + '<div class="ox8bk-review__text">' + esc(r.text) + '</div>' 456 + '<div class="ox8bk-review__author">' + esc(r.author) + '</div></div>'; 457 }).join('') 458 : '<div class="ox8bk-block__text">ÐÑзÑвов пока неÑ.</div>'; 459 } 460 461 /** 462 * ÐлижайÑие окна пÑÑмо в каÑÑоÑке вÑаÑа: дейÑÑвие должно бÑÑÑ Ñам же, где Ñеловек ÑиÑÐ°ÐµÑ 463 * пÑо вÑаÑа, а не ÑолÑко кнопкой Ð²Ð½Ð¸Ð·Ñ Ð¼Ð¾Ð´Ð°Ð»ÐºÐ¸. 464 */ 465 function renderAboutSlots(d) { 466 var box = document.getElementById('ox8bk-about-slots'); 467 var list = document.getElementById('ox8bk-about-slots-list'); 468 if (!box || !list) { return; } 469 var slots = (d && d.slots) || []; 470 if (!slots.length) { 471 box.hidden = true; 472 list.innerHTML = ''; 473 return; 474 } 475 var visible = slots.slice(0, 4); 476 var html = visible.map(function (s) { 477 return '<button type="button" class="ox8bk-slot" data-action="book" data-id="' + parseInt(d.id, 10) + '"' 478 + ' data-start="' + esc(s.dt_start) + '" data-end="' + esc(s.dt_end) + '">' 479 + fmtHuman(s.dt_start) + ', ' + String(s.dt_start).substring(11, 16) + '</button>'; 480 }).join(''); 481 html += '<button type="button" class="ox8bk-slot ox8bk-slot--more" data-action="book" data-id="' + parseInt(d.id, 10) + '">' 482 + 'ÐÑбÑаÑÑ Ð´ÑÑгое вÑемÑ</button>'; 483 list.innerHTML = html; 484 box.hidden = false;
485 } 486 487 function showTab(name) { 488 about.querySelectorAll('.ox8bk-tabs__btn').forEach(function (b) { 489 var on = b.dataset.tab === name; 490 b.classList.toggle('is-active', on); 491 b.setAttribute('aria-selected', on ? 'true' : 'false'); 492 }); 493 about.querySelectorAll('.ox8bk-tabs__panel').forEach(function (p) { 494 p.classList.toggle('is-active', p.dataset.panel === name); 495 }); 496 } 497 498 function openAbout(id, tab) { 499 var loader = about.querySelector('.ox8bk-modal__loader'); 500 openModal(about); 501 showTab(tab || 'about'); 502 loader.hidden = false; 503 load(id).then(function (data) { 504 current = data; 505 renderAbout(data); 506 loader.hidden = true; 507 about.querySelector('#ox8bk-about-book').dataset.id = id; 508 }).catch(function () { 509 loader.hidden = true; 510 about.querySelector('[data-panel="about"]').innerHTML = 511 '<div class="ox8bk-block__text">Ðе ÑдалоÑÑ Ð·Ð°Ð³ÑÑзиÑÑ Ð´Ð°Ð½Ð½Ñе вÑаÑа.</div>'; 512 }); 513 } 514 515 function fmtDate(iso) { 516 var p = String(iso).split('-'); 517 return p[2] + '.' + p[1] + '.' + p[0]; 518 } 519 520 var MONTHS_GEN = ['ÑнваÑÑ', 'ÑевÑалÑ', 'маÑÑа', 'апÑелÑ', 'маÑ', 'иÑнÑ', 'иÑлÑ', 'авгÑÑÑа', 'ÑенÑÑбÑÑ', 'окÑÑбÑÑ', 'ноÑбÑÑ', 'декабÑÑ']; 521 522 function fmtHuman(iso) { 523 var p = String(iso).substring(0, 10).split('-'); 524 if (p.length < 3) { return ''; } 525 return parseInt(p[2], 10) + ' ' + MONTHS_GEN[parseInt(p[1], 10) - 1]; 526 } 527 528 /** 529 * Сводка вÑбоÑа над ÑоÑмой: ÑÑо именно Ñеловек ÑейÑÐ°Ñ Ð±ÑониÑÑеÑ. 530 * Ðез Ð½ÐµÑ Ð²ÑбÑанное вÑÐµÐ¼Ñ Ð²Ð¸Ð´Ð½Ð¾ ÑолÑко в ÐºÐ½Ð¾Ð¿ÐºÐ°Ñ Ð½Ð¸Ð¶Ðµ по ÑоÑме â полÑзоваÑÐµÐ»Ñ ÑеÑÑÐµÑ ÐºÐ¾Ð½ÑекÑÑ. 531 */ 532 function updateSummary() { 533 var box = document.getElementById('ox8bk-summary'); 534 var text = document.getElementById('ox8bk-summary-text'); 535 if (!box || !text) { return; } 536 var parts = []; 537 if (picked.service) { parts.push(picked.service.name); } 538 if (picked.slot && picked.slot.dt_start) { 539 parts.push(fmtHuman(picked.slot.dt_start) + ', ' + String(picked.slot.dt_start).substring(11, 16)); 540 } else if (picked.date) { 541 parts.push(fmtHuman(picked.date) + ' â вÑÐµÐ¼Ñ Ð½Ðµ вÑбÑано'); 542 } 543 if (!parts.length) { 544 box.hidden = true; 545 text.textContent = ''; 546 return; 547 } 548 text.textContent = parts.join(' · '); 549 box.hidden = false; 550 } 551 552 /** 553 * ÐозÑаÑÑÐ½Ð°Ñ Ð²Ð¸Ð»ÐºÐ° пÑÐ¸Ð½Ð°Ð´Ð»ÐµÐ¶Ð¸Ñ ÑÑлÑге, а не вÑаÑÑ â показÑваем ÐµÑ ÑÑдом Ñ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸ÐµÐ¼, 554 * ÑÑÐ¾Ð±Ñ Ñеловек видел огÑаниÑение ÐРвÑбоÑа, а не поÑле оÑказа. 555 */ 556 function ageHint(s) { 557 var min = parseInt(s.age_min, 10) || 0; 558 var max = parseInt(s.age_max, 10) || 0; 559 if (!max || (min <= 0 && max >= 100)) { return ''; } 560 var t; 561 if (min <= 0) { t = 'до ' + max + ' леÑ'; } 562 else if (max >= 100) { t = 'Ð¾Ñ ' + min + ' леÑ'; } 563 else { t = min + 'â' + max + ' леÑ'; } 564 return ' <span class="ox8bk-option__age">' + t + '</span>'; 565 } 566 567 function ageAt(birthday, iso) { 568 if (!birthday) { return -1; } 569 var b = birthday.split('-'); 570 if (b.length < 3) { return -1; } 571 var on = iso ? String(iso).substring(0, 10).split('-') : null; 572 var ref = on && on.length === 3 573 ? new Date(parseInt(on[0], 10), parseInt(on[1], 10) - 1, parseInt(on[2], 10)) 574 : new Date(); 575 var age = ref.getFullYear() - parseInt(b[0], 10); 576 var m = (ref.getMonth() + 1) - parseInt(b[1], 10); 577 if (m < 0 || (m === 0 && ref.getDate() < parseInt(b[2], 10))) { age--; } 578 return age; 579 } 580 581 /** 582 * specId â ÑÑлÑга, вÑбÑÐ°Ð½Ð½Ð°Ñ ÐµÑÑ Ð´Ð¾ оÑкÑÑÑÐ¸Ñ Ð¼Ð¾Ð´Ð°Ð»ÐºÐ¸ (клик по плаÑке Ñ Ñеной на каÑÑоÑке вÑаÑа). 583 * Человек Ñже Ñказал, заÑем пÑиÑÑл â пеÑеÑпÑаÑиваÑÑ Ð² ÑоÑме Ñо же Ñамое не нÑжно. 584 */
585 function renderServices(data, specId) { 586 var list = document.getElementById('ox8bk-services-list'); 587 var label = document.getElementById('ox8bk-services-label'); 588 var services = data.services || []; 589 list.innerHTML = services.map(function (s, i) { 590 return '<button type="button" class="ox8bk-option" data-index="' + i + '">' 591 + '<span>' + esc(s.name) + ageHint(s) + '</span>' 592 + '<span class="ox8bk-option__badge">' + (s.price ? s.price + ' ÑÑб.' : '') + (s.duration ? ' · ' + s.duration + ' мин' : '') + '</span>' 593 + '</button>'; 594 }).join(''); 595 var preselect = -1; 596 if (specId) { 597 services.forEach(function (s, i) { 598 if (preselect < 0 && String(s.speciality_id) === String(specId)) { preselect = i; } 599 }); 600 } 601 if (services.length === 1) { 602 selectService(0); 603 } else if (preselect >= 0) { 604 selectService(preselect); 605 } else { 606 picked.service = null; 607 label.textContent = 'ÐÑÐ±Ð¾Ñ ÑÑлÑги'; 608 } 609 } 610 611 function selectService(index) { 612 var s = (current.services || [])[index]; 613 if (!s) { return; } 614 picked.service = s; 615 document.getElementById('ox8bk-services-label').textContent = s.name + (s.price ? ' â ' + s.price + ' ÑÑб.' : ''); 616 document.getElementById('ox8bk-f-spec').value = s.speciality_id; 617 document.getElementById('ox8bk-f-price').value = s.price || 0; 618 document.querySelectorAll('#ox8bk-services-list .ox8bk-option').forEach(function (b) { 619 b.classList.toggle('is-active', String(b.dataset.index) === String(index)); 620 }); 621 document.getElementById('ox8bk-services').classList.remove('is-open'); 622 updateSummary(); 623 } 624 625 /** 626 * ÐодÑвеÑждение Ñага: коÑоÑÐºÐ°Ñ Ð¿Ð¾Ð´ÑвеÑка кнопки, в коÑоÑÑÑ ÑолÑко ÑÑо лÑг вÑбоÑ. 627 * Ðез Ð½ÐµÑ Ð¿ÐµÑÐµÑ Ð¾Ð´ Ð¼ÐµÐ¶Ð´Ñ Ñагами незамеÑен ÑомÑ, кÑо оÑвлÑкÑÑ Ð½Ð° ÑекÑндÑ. 628 */ 629 function flashStep(el) { 630 if (!el) { return; } 631 el.classList.remove('is-just-set'); 632 void el.offsetWidth; 633 el.classList.add('is-just-set'); 634 setTimeout(function () { el.classList.remove('is-just-set'); }, 700); 635 } 636 637 function revealStep(el) { 638 if (!el || el.hidden) { return; } 639 try { el.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } catch (e) {} 640 } 641
642 function slotsByDate() { 643 var map = {}; 644 ((current && current.doctor && current.doctor.slots) || []).forEach(function (s) { 645 var d = String(s.dt_start).substring(0, 10); 646 if (!map[d]) { map[d] = []; } 647 map[d].push(s); 648 }); 649 return map; 650 } 651 652 function renderCalendar() { 653 var map = slotsByDate(); 654 var grid = document.getElementById('ox8bk-cal-grid'); 655 var title = document.getElementById('ox8bk-cal-title'); 656 var dates = Object.keys(map).sort(); 657 if (!calMonth && dates.length) { calMonth = dates[0].substring(0, 7); } 658 if (!calMonth) { calMonth = new Date().toISOString().substring(0, 7); } 659 660 var year = parseInt(calMonth.substring(0, 4), 10); 661 var month = parseInt(calMonth.substring(5, 7), 10) - 1; 662 title.textContent = MONTHS[month] + ' ' + year; 663 664 var first = new Date(year, month, 1); 665 var shift = (first.getDay() + 6) % 7; 666 var days = new Date(year, month + 1, 0).getDate(); 667 var html = ''; 668 for (var i = 0; i < shift; i++) { html += '<span></span>'; } 669 for (var d = 1; d <= days; d++) { 670 var iso = year + '-' + String(month + 1).padStart(2, '0') + '-' + String(d).padStart(2, '0'); 671 var free = !!map[iso]; 672 html += '<button type="button" class="ox8bk-day ' + (free ? 'ox8bk-day--free' : 'ox8bk-day--busy') 673 + (picked.date === iso ? ' is-active' : '') + '" data-date="' + iso + '"' 674 + (free ? '' : ' disabled') + '>' + d + '</button>'; 675 } 676 grid.innerHTML = html; 677 678 var months = dates.map(function (x) { return x.substring(0, 7); }); 679 document.getElementById('ox8bk-cal-prev').disabled = months.indexOf(calMonth) <= 0; 680 document.getElementById('ox8bk-cal-next').disabled = months.lastIndexOf(calMonth) === -1 681 || calMonth >= months[months.length - 1]; 682 683 // ÐоÑÐ¸Ð·Ð¾Ð½Ñ ÑаÑпиÑÐ°Ð½Ð¸Ñ Ð¾Ð³ÑаниÑен Ñем, ÑÑо оÑкÑÑла клиника. Ðез поÑÑÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸ÑÐ¾Ð²Ð°Ð½Ð½Ð°Ñ 684 // ÑÑÑелка «впеÑÑд» ÑиÑаеÑÑÑ ÐºÐ°Ðº поломка календаÑÑ. 685 var note = document.getElementById('ox8bk-cal-note'); 686 if (note) { 687 note.textContent = dates.length 688 ? 'ÐапиÑÑ Ð¾ÑкÑÑÑа по ' + fmtHuman(dates[dates.length - 1]) 689 : ''; 690 } 691 } 692 693 function renderTimes() {
694 var map = slotsByDate(); 695 var list = document.getElementById('ox8bk-times-list'); 696 var items = picked.date ? (map[picked.date] || []) : []; 697 list.innerHTML = items.length 698 ? items.map(function (s) { 699 var t = String(s.dt_start).substring(11, 16); 700 return '<button type="button" class="ox8bk-time' + (picked.slot && picked.slot.dt_start === s.dt_start ? ' is-active' : '') 701 + '" data-start="' + esc(s.dt_start) + '" data-end="' + esc(s.dt_end) + '">' + t + '</button>'; 702 }).join('') 703 : '<div class="ox8bk-block__text">СнаÑала вÑбеÑиÑе даÑÑ.</div>'; 704 } 705 706 707 // Ð¦ÐµÐ»Ñ Ð²ÐµÐ±-аналиÑики пÑи ÑÑпеÑной запиÑи. 708 // ÐÑновной пÑÑÑ â ÑÑаÑное ÑобÑÑие ÑÐµÐ¼Ñ (onCounterGoals): Ñак же делал ÑÑаÑÑй модÑлÑ, 709 // и ÑÑÑÑÑик беÑÑÑÑÑ Ð¸Ð· наÑÑÑоек ÑайÑа. ФолбÑк â пÑÑмой ym(), еÑли ÑÐµÐ¼Ñ Ð½ÐµÑ (коÑобка). 710 // ===== Ðеб-аналиÑика модÑÐ»Ñ ===== 711 // ÐÑÑ ÐºÐ¾Ð½ÑигÑÑаÑÐ¸Ñ Ð¿ÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ð¸Ð· наÑÑÑоек модÑÐ»Ñ Ð¾Ð´Ð½Ð¸Ð¼ JSON â здеÑÑ Ð½ÐµÑ Ð½Ð¸ имÑн Ñелей, 712 // ни ID ÑÑÑÑÑиков. ÐÑÑÑой конÑиг = аналиÑика вÑклÑÑена, вÑе вÑÐ·Ð¾Ð²Ñ ÑÑановÑÑÑÑ Ð¿ÑÑÑÑми. 713 // ÐденÑиÑикаÑоÑÑ Ð²Ð¸Ð·Ð¸Ñа Ð´Ð»Ñ CRM и Ñквозной аналиÑики. 714 // СобиÑаем в Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð¾ÑпÑавки: позже ÑÑи знаÑÐµÐ½Ð¸Ñ Ð½Ðµ воÑÑÑановиÑÑ â заÑвка навÑегда 715 // оÑÑанеÑÑÑ Â«Ð¿ÑÑмÑм Ð·Ð°Ñ Ð¾Ð´Ð¾Ð¼Â» в оÑÑÑÑÐ°Ñ . 716 function readCookie(name) { 717 try { 718 var m = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/([.$?*|{}()\[\]\\\/+^])/g, '\\$1') + '=([^;]*)')); 719 return m ? decodeURIComponent(m[1]) : ''; 720 } catch (e) { return ''; } 721 } 722 723 function collectTracking() { 724 var t = {}; 725 // ClientID ÐеÑÑики: ÑÑаÑнÑй ÑпоÑоб â getClientID, кÑка _ym_uid â ÑолбÑк 726 try { 727 for (var k in window) { 728 if (k.indexOf('yaCounter') === 0 && window[k] && typeof window[k].getClientID === 'function') { 729 t.ym_client_id = String(window[k].getClientID() || ''); 730 break; 731 } 732 } 733 } catch (e) {} 734 if (!t.ym_client_id) { t.ym_client_id = readCookie('_ym_uid'); } 735 736 // yclid ÐиÑекÑа: из адÑеÑа либо из кÑки, еÑли ÐµÑ Ð¿Ð¾ÑÑавили ÑанÑÑе 737 try { 738 var yclid = new URLSearchParams(window.location.search).get('yclid'); 739 t.yclid = yclid || readCookie('yclid') || readCookie('_ym_yclid'); 740 } catch (e) {} 741 742 // Google: в кÑке _ga Ð»ÐµÐ¶Ð¸Ñ GA1.1.<clientId>, нÑÐ¶Ð½Ñ Ð¿Ð¾Ñледние две ÑаÑÑи 743 var ga = readCookie('_ga'); 744 if (ga) { 745 var parts = ga.split('.'); 746 if (parts.length >= 4) { t.ga_client_id = parts[2] + '.' + parts[3]; } 747 } 748 749 t.roistat_visit = readCookie('roistat_visit'); 750 t.ct_session_id = readCookie('_ct_session_id') || (window.ct_session_id ? String(window.ct_session_id) : ''); 751 752 var clean = {}; 753 for (var key in t) { if (t[key]) { clean[key] = t[key]; } } 754 return clean; 755 } 756 var analytics = (function () { 757 var cfg = null; 758 function conf() { 759 if (cfg !== null) { return cfg; } 760 cfg = {}; 761 try { 762 var host = document.querySelector('[data-analytics]'); 763 if (host) { cfg = JSON.parse(host.getAttribute('data-analytics') || '{}') || {}; } 764 } catch (e) { cfg = {}; } 765 return cfg; 766 } 767 function fireGoal(name) { 768 if (!name) { return; } 769 try { 770 if (window.BX && BX.onCustomEvent) { 771 BX.onCustomEvent('onCounterGoals', [{ goal: name }]); 772 return; 773 } 774 } catch (e) {} 775 try { 776 for (var k in window) { 777 if (k.indexOf('yaCounter') === 0 && window[k] && typeof window[k].reachGoal === 'function') { 778 window[k].reachGoal(name); 779 return; 780 } 781 } 782 } catch (e) {} 783 } 784 return { 785 goal: function (key) { 786 var c = conf(); 787 if (!c.goals) { return; } 788 fireGoal(c.goals[key]); 789 }, 790 legacyGoal: function () { 791 var c = conf(); 792 fireGoal(c.legacyGoal); 793 }, 794 // Ð¢Ð¾Ð²Ð°Ñ = ÑÑлÑга, бÑенд = вÑаÑ, каÑегоÑÐ¸Ñ = напÑавление, ваÑÐ¸Ð°Ð½Ñ = ÑоÑÐ¼Ð°Ñ Ð¿ÑиÑма 795 product: function (doctor, service) { 796 var c = conf(); 797 if (!c.ecommerce || !doctor) { return null; } 798 var p = { 799 id: String((service && service.speciality_id) || ''), 800 name: (service && service.name) || '', 801 brand: doctor.name || '', 802 category: (service && (service.category || service.name)) || '', 803 quantity: 1 804 }; 805 if (doctor.is_online) { p.variant = 'Ðнлайн'; } else { p.variant = 'ÐÑно'; } 806 if (c.ecommerce.withPrice && service && service.price) { p.price = service.price; } 807 return p; 808 }, 809 ecommerce: function (action, product, extra) { 810 var c = conf(); 811 if (!c.ecommerce || !product) { return; } 812 var payload = { currencyCode: c.ecommerce.currency }; 813 payload[action] = { products: [product] }; 814 if (extra) { 815 for (var k in extra) { if (extra.hasOwnProperty(k)) { payload[action][k] = extra[k]; } } 816 } 817 try { 818 var name = c.ecommerce.dataLayer || 'dataLayer'; 819 window[name] = window[name] || []; 820 window[name].push({ ecommerce: payload }); 821 } catch (e) {} 822 } 823 }; 824 })(); 825 function reachBookingGoal() { 826 var host = document.querySelector('[data-goal]'); 827 var goal = host ? (host.getAttribute('data-goal') || '').trim() : ''; 828 if (!goal) { return; } 829 var delivered = false;
830 try { 831 if (window.BX && BX.onCustomEvent) { 832 BX.onCustomEvent('onCounterGoals', [{ goal: goal }]); 833 delivered = true; 834 } 835 } catch (e) {} 836 if (delivered) { return; } 837 try { 838 for (var k in window) { 839 if (k.indexOf('yaCounter') === 0 && window[k] && typeof window[k].reachGoal === 'function') { 840 window[k].reachGoal(goal); 841 return; 842 } 843 } 844 if (typeof window.ym === 'function' && window.Ya && Ya.Metrika2 && Ya.Metrika2.counters) { 845 var list = Ya.Metrika2.counters(); 846 for (var i = 0; i < list.length; i++) { window.ym(list[i].id, 'reachGoal', goal); } 847 } 848 } catch (e) {} 849 } 850 851 function openBook(id, start, end, specId) { 852 picked = { service: null, date: '', slot: null }; 853 calMonth = null; 854 var form = document.getElementById('ox8bk-form'); 855 form.hidden = false; 856 document.getElementById('ox8bk-success').hidden = true; 857 document.getElementById('ox8bk-form-error').hidden = true; 858 document.getElementById('ox8bk-date-label').textContent = 'ÐаÑа'; 859 document.getElementById('ox8bk-time-label').textContent = 'ÐÑемÑ'; 860 document.getElementById('ox8bk-calendar').hidden = true; 861 document.getElementById('ox8bk-times').hidden = true; 862 var commentReset = document.getElementById('ox8bk-f-comment'); 863 if (commentReset) { 864 // ÐÑиÑли из пÑайÑа/ÑÑаÑÑи Ñ ?svc= â кладÑм ÑÑлÑÐ³Ñ Ð² комменÑаÑий, ÑÑÐ¾Ð±Ñ ÐºÐ»Ð¸Ð½Ð¸ÐºÐ° видела запÑоÑ. 865 var svc = (root.dataset.service || '').trim(); 866 commentReset.value = svc ? ('ÐапиÑÑ Ð½Ð° ÑÑлÑгÑ: ' + svc) : ''; 867 } 868 updateSummary(); 869 openModal(book); 870 871 load(id).then(function (data) { 872 current = data; 873 document.getElementById('ox8bk-f-doctor').value = data.doctor.id; 874 document.getElementById('ox8bk-f-branch').value = data.doctor.branch_id || 0; 875 document.getElementById('ox8bk-book-subtitle').textContent = data.doctor.name || ''; 876 // ФоÑÐ¼Ð°Ñ Ð¿ÑиÑма: Ñеловек должен ÑвидеÑÑ ÑÑо ÐÐ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð°Ð½ÐºÐµÑÑ, а не пÑÐ¸ÐµÑ Ð°Ð² в клиникÑ. 877 var onlineNote = document.getElementById('ox8bk-online-note'); 878 if (onlineNote) { 879 var isOnline = !!data.doctor.is_online; 880 onlineNote.hidden = !isOnline; 881 if (isOnline) { 882 var svcName = (data.doctor.online_service || '').trim(); 883 document.getElementById('ox8bk-online-note-text').innerHTML = 884 '<b>ÐÑиÑм пÑÐ¾Ñ Ð¾Ð´Ð¸Ñ Ð¾Ð½Ð»Ð°Ð¹Ð½.<\/b> ÐÑиезжаÑÑ Ð² ÐºÐ»Ð¸Ð½Ð¸ÐºÑ Ð½Ðµ нÑжно â клиника ÑвÑжеÑÑÑ Ð¸ пÑиÑлÑÑ ÑÑÑÐ»ÐºÑ Ð½Ð° конÑÑлÑÑаÑиÑ.' + 885 (svcName ? (' УÑлÑга: ' + svcName.replace(/</g, '<') + '.') : ''); 886 } 887 } 888 renderServices(data, specId); 889 analytics.goal('open'); 890 try { 891 var firstSvc = picked.service || ((data.services && data.services.length) ? data.services[0] : null); 892 analytics.ecommerce('detail', analytics.product(data.doctor, firstSvc)); 893 } catch (e) {} 894 if (start) { 895 picked.date = String(start).substring(0, 10); 896 picked.slot = { dt_start: start, dt_end: end }; 897 document.getElementById('ox8bk-f-start').value = start; 898 document.getElementById('ox8bk-f-end').value = end; 899 document.getElementById('ox8bk-date-label').textContent = fmtDate(picked.date); 900 document.getElementById('ox8bk-time-label').textContent = String(start).substring(11, 16); 901 calMonth = picked.date.substring(0, 7); 902 // Ð¡Ð»Ð¾Ñ Ð²ÑбÑан ÑÑÐ°Ð·Ñ Ð² каÑÑоÑке â Ñаг воÑонки ÑÐ¾Ñ Ð¶Ðµ, ÑÑо и вÑÐ±Ð¾Ñ Ð² календаÑе 903 analytics.goal('slot'); 904 try { analytics.ecommerce('add', analytics.product(data.doctor, picked.service || (data.services && data.services[0]))); } catch (e) {} 905 }
906 renderCalendar(); 907 renderTimes(); 908 updateSummary(); 909 // ÐÑкÑÑли ÑоÑÐ¼Ñ Ð±ÐµÐ· вÑбÑанного окна («ÐÑбÑаÑÑ Ð²ÑемÑ», «еÑÑ N», кнопка в каÑÑоÑке вÑаÑа) â 910 // ÑÑÐ°Ð·Ñ Ð¿Ð¾ÐºÐ°Ð·Ñваем календаÑÑ, инаÑе Ñеловек Ð²Ð¸Ð´Ð¸Ñ Ð°Ð½ÐºÐµÑÑ Ð²Ð¼ÐµÑÑо Ñого, за Ñем пÑиÑÑл. 911 if (!start) { 912 document.getElementById('ox8bk-calendar').hidden = false; 913 renderCalendar(); 914 } 915 }); 916 } 917 918 document.addEventListener('click', function (e) { 919 var trigger = e.target.closest('[data-action]'); 920 if (trigger) { 921 var id = parseInt(trigger.dataset.id || '0', 10); 922 if (id > 0 && trigger.dataset.action === 'about') { 923 e.preventDefault(); 924 openAbout(id, trigger.dataset.tab); 925 return; 926 } 927 if (id > 0 && trigger.dataset.action === 'book') { 928 e.preventDefault(); 929 if (!about.hidden) { closeModal(about); } 930 openBook(id, trigger.dataset.start, trigger.dataset.end, trigger.dataset.spec); 931 return; 932 } 933 } 934 935 if (e.target.closest('[data-close]')) { 936 closeModal(e.target.closest('.ox8bk-modal')); 937 return; 938 } 939 940 var tabBtn = e.target.closest('.ox8bk-tabs__btn'); 941 if (tabBtn) { showTab(tabBtn.dataset.tab); return; } 942 943 var aboutBook = e.target.closest('#ox8bk-about-book'); 944 if (aboutBook) { 945 closeModal(about); 946 openBook(parseInt(aboutBook.dataset.id || '0', 10)); 947 return; 948 } 949 950 var head = e.target.closest('#ox8bk-services-head'); 951 if (head) { document.getElementById('ox8bk-services').classList.toggle('is-open'); return; } 952 953 var opt = e.target.closest('#ox8bk-services-list .ox8bk-option'); 954 if (opt) { selectService(parseInt(opt.dataset.index, 10)); return; } 955 956 if (e.target.closest('#ox8bk-pick-date')) { 957 var cal = document.getElementById('ox8bk-calendar'); 958 cal.hidden = !cal.hidden; 959 document.getElementById('ox8bk-times').hidden = true; 960 if (!cal.hidden) { renderCalendar(); } 961 return; 962 } 963 if (e.target.closest('#ox8bk-pick-time')) { 964 var tm = document.getElementById('ox8bk-times'); 965 tm.hidden = !tm.hidden; 966 document.getElementById('ox8bk-calendar').hidden = true; 967 if (!tm.hidden) { renderTimes(); } 968 return; 969 } 970 971 var prev = e.target.closest('#ox8bk-cal-prev'); 972 var next = e.target.closest('#ox8bk-cal-next'); 973 if (prev || next) { 974 var months = Object.keys(slotsByDate()).map(function (x) { return x.substring(0, 7); }); 975 var uniq = months.filter(function (v, i) { return months.indexOf(v) === i; }); 976 var idx = uniq.indexOf(calMonth); 977 if (prev && idx > 0) { calMonth = uniq[idx - 1]; } 978 if (next && idx < uniq.length - 1) { calMonth = uniq[idx + 1]; } 979 renderCalendar(); 980 return; 981 } 982 983 var day = e.target.closest('.ox8bk-day--free'); 984 if (day) { 985 picked.date = day.dataset.date; 986 picked.slot = null; 987 document.getElementById('ox8bk-date-label').textContent = fmtDate(picked.date); 988 document.getElementById('ox8bk-time-label').textContent = 'ÐÑемÑ'; 989 document.getElementById('ox8bk-f-start').value = ''; 990 document.getElementById('ox8bk-f-end').value = ''; 991 renderCalendar(); 992 document.getElementById('ox8bk-calendar').hidden = true; 993 document.getElementById('ox8bk-times').hidden = false; 994 renderTimes(); 995 updateSummary(); 996 flashStep(document.getElementById('ox8bk-pick-date')); 997 revealStep(document.getElementById('ox8bk-times')); 998 var firstTime = document.querySelector('#ox8bk-times .ox8bk-time'); 999 if (firstTime) { try { firstTime.focus({ preventScroll: true }); } catch (e) {} } 1000 return; 1001 } 1002 1003 var time = e.target.closest('.ox8bk-time'); 1004 if (time) { 1005 picked.slot = { dt_start: time.dataset.start, dt_end: time.dataset.end }; 1006 document.getElementById('ox8bk-f-start').value = time.dataset.start; 1007 document.getElementById('ox8bk-f-end').value = time.dataset.end; 1008 document.getElementById('ox8bk-time-label').textContent = String(time.dataset.start).substring(11, 16); 1009 analytics.goal('slot'); 1010 try { 1011 if (current) { analytics.ecommerce('add', analytics.product(current.doctor, picked.service)); } 1012 } catch (e) {} 1013 renderTimes(); 1014 document.getElementById('ox8bk-times').hidden = true; 1015 updateSummary(); 1016 flashStep(document.getElementById('ox8bk-pick-time')); 1017 return; 1018 } 1019 1020 var sw = e.target.closest('.ox8bk-switch'); 1021 if (sw) { 1022 document.querySelectorAll('.ox8bk-switch').forEach(function (b) { 1023 b.classList.toggle('is-active', b === sw); 1024 }); 1025 var rel = sw.dataset.relative === 'Y'; 1026 document.getElementById('ox8bk-f-relative').value = rel ? 'Y' : 'N'; 1027 document.getElementById('ox8bk-relative-consent').hidden = !rel; 1028 } 1029 }); 1030 1031 document.addEventListener('keydown', function (e) { 1032 if (e.key !== 'Escape') { return; } 1033 if (!about.hidden) { closeModal(about); } 1034 if (!book.hidden) { closeModal(book); } 1035 }); 1036 1037 var phone = document.getElementById('ox8bk-f-phone'); 1038 if (phone) { 1039 phone.addEventListener('input', function () { 1040 var digits = phone.value.replace(/\D/g, ''); 1041 if (digits.indexOf('8') === 0) { digits = '7' + digits.substring(1); } 1042 if (digits.indexOf('7') !== 0) { digits = '7' + digits; } 1043 digits = digits.substring(0, 11); 1044 var out = '+7'; 1045 if (digits.length > 1) { out += ' (' + digits.substring(1, 4); } 1046 if (digits.length >= 5) { out += ') ' + digits.substring(4, 7); } 1047 if (digits.length >= 8) { out += '-' + digits.substring(7, 9); } 1048 if (digits.length >= 10) { out += '-' + digits.substring(9, 11); } 1049 phone.value = out; 1050 }); 1051 } 1052 1053 function setError(name, message) { 1054 var box = document.querySelector('.ox8bk-field__error[data-for="' + name + '"]'); 1055 var input = document.getElementById('ox8bk-f-' + name); 1056 if (box) { 1057 box.textContent = message || ''; 1058 box.classList.toggle('is-visible', !!message); 1059 } 1060 if (input) { input.classList.toggle('is-error', !!message); } 1061 } 1062 1063 document.getElementById('ox8bk-form').addEventListener('submit', function (e) { 1064 e.preventDefault(); 1065 1066 var hp = document.getElementById('ox8bk-f-hp'); 1067 if (hp && hp.value !== '') { return; } 1068 1069 var errorBox = document.getElementById('ox8bk-form-error'); 1070 errorBox.hidden = true; 1071 setError('fio', ''); 1072 setError('phone', ''); 1073 setError('birthday', ''); 1074 1075 var fio = document.getElementById('ox8bk-f-fio').value.trim().replace(/\s+/g, ' '); 1076 var parts = fio.split(' '); 1077 var ok = true; 1078 1079 if (parts.length < 2) { 1080 setError('fio', 'УкажиÑе ÑÐ°Ð¼Ð¸Ð»Ð¸Ñ Ð¸ имÑ'); 1081 ok = false;
1082 } 1083 1084 var digits = document.getElementById('ox8bk-f-phone').value.replace(/\D/g, ''); 1085 if (digits.length !== 11) { 1086 setError('phone', 'УкажиÑе ÑелеÑон полноÑÑÑÑ'); 1087 ok = false; 1088 } 1089 1090 var birthdayEl = document.getElementById('ox8bk-f-birthday'); 1091 if (birthdayEl && !birthdayEl.value) { 1092 setError('birthday', 'УкажиÑе даÑÑ ÑождениÑ'); 1093 ok = false; 1094 } 1095 1096 var formErrors = []; 1097 1098 document.getElementById('ox8bk-pick-date').classList.remove('is-error'); 1099 1100 if (!picked.service) { 1101 formErrors.push('ÐÑбеÑиÑе ÑÑлÑгÑ'); 1102 } else if (birthdayEl && birthdayEl.value) { 1103 // ÐозÑаÑÑ ÑÑиÑаем на Ð´ÐµÐ½Ñ Ð¿ÑиÑма, а не на ÑегоднÑ: на гÑаниÑе 18 Ð»ÐµÑ ÑÑо ÑазнÑе оÑвеÑÑ. 1104 var age = ageAt(birthdayEl.value, document.getElementById('ox8bk-f-start').value); 1105 var min = parseInt(picked.service.age_min, 10) || 0; 1106 var max = parseInt(picked.service.age_max, 10) || 0; 1107 if (age >= 0 && max && (age < min || age > max)) { 1108 var what = 'Ðа пÑиÑм «' + picked.service.name + '» '; 1109 var limit; 1110 if (min <= 0) { limit = 'запиÑÑваÑÑ Ð¿Ð°ÑиенÑов до ' + max + ' леÑ'; } 1111 else if (max >= 100) { limit = 'запиÑÑваÑÑ Ð¿Ð°ÑиенÑов Ð¾Ñ ' + min + ' леÑ'; } 1112 else { limit = 'запиÑÑваÑÑ Ð¿Ð°ÑиенÑов Ð¾Ñ ' + min + ' до ' + max + ' леÑ'; } 1113 formErrors.push(what + limit + '. ÐÑбеÑиÑе дÑÑгÑÑ ÑÑлÑÐ³Ñ Ð¸Ð»Ð¸ пÑовеÑÑÑе даÑÑ ÑождениÑ.'); 1114 } 1115 } 1116 if (!document.getElementById('ox8bk-f-start').value) { 1117 formErrors.push('ÐÑбеÑиÑе даÑÑ Ð¸ вÑемÑ'); 1118 document.getElementById('ox8bk-pick-date').classList.add('is-error'); 1119 } 1120 if (!document.getElementById('ox8bk-f-consent').checked) { 1121 formErrors.push('ÐÑжно ÑоглаÑие на обÑабоÑÐºÑ Ð¿ÐµÑÑоналÑнÑÑ Ð´Ð°Ð½Ð½ÑÑ '); 1122 } 1123 1124 var relative = document.getElementById('ox8bk-f-relative').value === 'Y'; 1125 if (relative && !document.getElementById('ox8bk-f-consent2').checked) { 1126 formErrors.push('ÐодÑвеÑдиÑе пÑаво запиÑаÑÑ ÑÑого Ñеловека'); 1127 } 1128 1129 if (formErrors.length) { 1130 errorBox.textContent = formErrors[0]; 1131 errorBox.hidden = false; 1132 ok = false; 1133 } 1134 1135 if (!ok) { return; } 1136 1137 var btn = document.getElementById('ox8bk-submit'); 1138 var caption = btn.textContent; 1139 btn.disabled = true; 1140 btn.textContent = 'ÐÑпÑавкаâ¦'; 1141 1142 var body = new FormData(); 1143 body.append('site_id', root.dataset.site || 's1'); 1144 try { 1145 var tracking = collectTracking(); 1146 if (tracking && Object.keys(tracking).length) { body.append('tracking', JSON.stringify(tracking)); } 1147 } catch (e) {} 1148 body.append('fields[doctor_id]', document.getElementById('ox8bk-f-doctor').value); 1149 body.append('fields[speciality_id]', document.getElementById('ox8bk-f-spec').value); 1150 body.append('fields[branch_id]', document.getElementById('ox8bk-f-branch').value); 1151 body.append('fields[dt_start]', document.getElementById('ox8bk-f-start').value); 1152 body.append('fields[dt_end]', document.getElementById('ox8bk-f-end').value); 1153 body.append('fields[price]', document.getElementById('ox8bk-f-price').value); 1154 body.append('fields[last_name]', parts[0] || ''); 1155 body.append('fields[first_name]', parts[1] || ''); 1156 body.append('fields[second_name]', parts.slice(2).join(' ')); 1157 body.append('fields[phone]', digits); 1158 body.append('fields[birthday]', birthdayEl ? birthdayEl.value : ''); 1159 body.append('fields[for_relative]', relative ? 'Y' : 'N'); 1160 // ÐомменÑаÑий ÑÑ Ð¾Ð´Ð¸Ñ Ð² API и в веб-ÑоÑмÑ. ÐÑли поле ÑкÑÑÑо опÑией, но Ñеловек пÑиÑÑл 1161 // Ñ ÐºÐ¾Ð½ÐºÑеÑной ÑÑлÑгой (?svc=), клиника вÑÑ Ñавно должна видеÑÑ, заÑем он ÑÑл. 1162 var commentEl = document.getElementById('ox8bk-f-comment'); 1163 var commentValue = commentEl ? commentEl.value.trim() : '';
1164 if (commentValue === '') { 1165 var svcFallback = (root.dataset.service || '').trim(); 1166 if (svcFallback) { commentValue = 'ÐапиÑÑ Ð½Ð° ÑÑлÑгÑ: ' + svcFallback; } 1167 } 1168 if (commentValue !== '') { body.append('fields[comment]', commentValue); } 1169 1170 send(getSessid(), false); 1171 1172 function send(sessid, isRetry) { 1173 if (body.has('sessid')) { body.delete('sessid'); } 1174 body.append('sessid', sessid); 1175 1176 fetch(AJAX + '?action=ox8ru%3Abooking.Booking.create', { 1177 method: 'POST', 1178 body: body, 1179 credentials: 'same-origin' 1180 }) 1181 .then(function (r) { return r.json(); }) 1182 .then(function (res) { 1183 if (!isRetry && isCsrfError(res)) { 1184 var inline = csrfFromResponse(res); 1185 if (inline) { send(inline, true); return; } 1186 refreshSessid(function (fresh) { 1187 if (fresh) { send(fresh, true); } 1188 else { 1189 btn.disabled = false; 1190 btn.textContent = caption; 1191 errorBox.textContent = 'СеÑÑÐ¸Ñ ÑÑÑаÑела. ÐбновиÑе ÑÑÑаниÑÑ Ð¸ попÑобÑйÑе Ñнова.'; 1192 errorBox.hidden = false; 1193 } 1194 }); 1195 return; 1196 } 1197 btn.disabled = false; 1198 btn.textContent = caption; 1199 if (res && res.status === 'success' && res.data && res.data.success) { 1200 reachBookingGoal(); 1201 analytics.goal('success'); 1202 try { 1203 var pr = current ? analytics.product(current.doctor, picked.service) : null; 1204 var orderId = (res.data && (res.data.uuid || res.data.id)) || ''; 1205 analytics.ecommerce('purchase', pr, { 1206 actionField: { id: String(orderId), revenue: (pr && pr.price) ? pr.price : 0 } 1207 }); 1208 } catch (e) {} 1209 document.getElementById('ox8bk-form').hidden = true; 1210 var box = document.getElementById('ox8bk-success'); 1211 document.getElementById('ox8bk-success-text').textContent = 1212 'ÐÑ Ð¶Ð´Ñм Ð²Ð°Ñ ' + fmtDate(picked.date) + ' в ' 1213 + String(document.getElementById('ox8bk-f-start').value).substring(11, 16) + '.'; 1214 box.hidden = false; 1215 return; 1216 } 1217 var msg = 'Ðе ÑдалоÑÑ Ð·Ð°Ð¿Ð¸ÑаÑÑÑÑ. ÐопÑобÑйÑе еÑÑ Ñаз.'; 1218 var errCode = ''; 1219 if (res && res.errors && res.errors.length) { 1220 msg = res.errors[0].message || msg; 1221 errCode = String(res.errors[0].code || ''); 1222 } 1223 analytics.goal('error'); 1224 if (errCode === 'slot_taken' && current && current.doctor) { 1225 var retryDoctor = current.doctor.id; 1226 var retrySpecEl = document.getElementById('ox8bk-f-spec'); 1227 var retrySpec = retrySpecEl ? retrySpecEl.value : 0; 1228 delete cache[retryDoctor]; 1229 openBook(retryDoctor, '', '', retrySpec); 1230 } 1231 errorBox.textContent = msg; 1232 errorBox.hidden = false; 1233 }) 1234 .catch(function () { 1235 btn.disabled = false; 1236 btn.textContent = caption; 1237 errorBox.textContent = 'ÐÑибка ÑеÑи. ÐопÑобÑйÑе еÑÑ Ñаз.'; 1238 errorBox.hidden = false; 1239 }); 1240 } 1241 }); 1242})();
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.