const Cookies = require('js-cookie'); function serializeFormData(obj, prefix = '') { const str = []; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; const newKey = prefix ? `${prefix}[${key}]` : key; if (value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof File) && !(value instanceof Blob)) { // Рекурсивно обрабатываем вложенные объекты str.push(serializeFormData(value, newKey)); } else if (Array.isArray(value)) { // Обрабатываем массивы value.forEach((item, index) => { if (typeof item === 'object' && item !== null && !(item instanceof File) && !(item instanceof Blob)) { str.push(serializeFormData(item, `${newKey}[${index}]`)); } else { str.push(encodeURIComponent(`${newKey}[${index}]`) + '=' + encodeURIComponent(item)); } }); } else { str.push(encodeURIComponent(newKey) + '=' + encodeURIComponent(value)); } } } return str.join('&'); } function sendRequest(data, url, method = "POST", dataType = "json", withCredentials = true, contentType = null) { return new Promise(async (resolve, reject) => { try { const methodUpper = method.toUpperCase(); const isGetOrHead = methodUpper === "GET" || methodUpper === "HEAD"; // Для GET/HEAD перемещаем данные в URL if (isGetOrHead && data) { const params = new URLSearchParams(data); url = `${url}${url.includes('?') ? '&' : '?'}${params}`; } // Автоматическое определение content-type для не-GET запросов let finalContentType = contentType; if (!finalContentType && !isGetOrHead) { finalContentType = "application/x-www-form-urlencoded"; if (data && Object.values(data).some(value => value instanceof File || value instanceof Blob)) { finalContentType = "multipart/form-data"; } } let body = null; let headers = {}; // Подготовка тела только для не-GET запросов if (!isGetOrHead && data) { switch (finalContentType) { case "application/json": body = JSON.stringify(data); headers["Content-Type"] = "application/json"; break; case "multipart/form-data": body = new FormData(); Object.entries(data).forEach(([key, value]) => { body.append(key, value); }); break; default: // Используем правильную сериализацию для вложенных объектов body = serializeFormData(data); headers["Content-Type"] = "application/x-www-form-urlencoded"; } } const response = await fetch(url, { method: methodUpper, headers: headers, credentials: withCredentials ? "include" : "same-origin", body: body }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } const responseData = await response[dataType](); resolve(responseData); } catch (error) { reject(error); } }); } function isMobile() { if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) { return true; } return false; } function getCityId(address) { const cityMap = new Map([ ['Волгоград', 96], ['Воронеж', 98], ['Краснодар', 3018], ['Саратов', 94] ]); for (const [cityName, cityId] of cityMap) { if (address.includes(cityName)) { return cityId; } } return null; } function getCityCRM(region) { switch (region) { case '92': return 96; // Волгоград case '93': return 98; // Воронеж case '94': return 3018; // Краснодар default: return 94; // Саратов } } function getLicensePersonLink() { if (/cabinet\.sovamed\.ru/m.test(location.hostname) || /cabinet\.wmtmed\.ru/m.test(location.hostname)) { return 'https://cabinet.sovamed.ru/docs/soglasie-cabinet.pdf'; } return 'https://cabinet.sovamed.ru/docs/soglasie-site.pdf'; } function getLicenseLink(region) { switch (region) { case '92': // Волгоград return 'https://volgograd.sovamed.ru/o-sove/dokumenty-i-licenzii/politika-konfidencialnosti/'; case '93': // Воронеж return 'https://voronezh.sovamed.ru/o-sove/dokumenty-i-licenzii/politika-konfidencialnosti/'; case '94': // Краснодар return 'https://wmtmed.ru/about/confidentiality_policy.php'; case '95': // Совенок return 'https://sovenok.sovamed.ru/o-sove/dokumenty-i-licenzii/politika-konfidencialnosti/'; case '96': // Комфорт return 'https://sovamed.ru/docs/comfort_politika.pdf'; default: // Саратов return 'https://sovamed.ru/o-sove/dokumenty-i-licenzii/politika-konfidencialnosti/'; } } function renderCapcha(wrap) { const elem = document.getElementById('smart-captcha'); if (elem) { elem.parentNode.removeChild(elem); } wrap.innerHTML = ''; var recaptcha = document.createElement('div'); recaptcha.id = "smart-captcha"; recaptcha.dataset.controller = "smartCaptcha"; wrap.append(recaptcha); var validRecaptcha = document.createElement('div'); validRecaptcha.classList = "msg-valid valid-captcha"; wrap.append(validRecaptcha); return recaptcha; } function countDown(options) { var timer, instance = this, seconds = options.seconds || 10, updateStatus = options.onUpdateStatus || function () {}, counterEnd = options.onCounterEnd || function () {}; function decrementCounter() { updateStatus(seconds); if (seconds === 0) { counterEnd(); instance.stop(); } seconds--; } this.start = function () { clearInterval(timer); timer = 0; seconds = options.seconds; timer = setInterval(decrementCounter, 1000); }; this.stop = function () { clearInterval(timer); }; } function getApiHostname() { if (/sovamed\.ru/m.test(location.hostname)) { return 'https://api.sovamed.ru'; } return 'https://api.wmtmed.ru'; } function getHostname() { if (/sovamed\.ru/m.test(location.hostname)) { return 'https://cabinet.sovamed.ru'; } return 'https://cabinet.wmtmed.ru'; } function getRegionIdByHost() { switch (location.host) { case 'volgograd.sovamed.ru': // Волгоград var regionId = 92; break; case 'voronezh.sovamed.ru': // Воронеж var regionId = 93; break; case 'sovenok.sovamed.ru': // Совенок var regionId = 95; break; case 'comfort.sovamed.ru': // Совенок var regionId = 96; break; case 'wmtmed.ru': // Краснодар var regionId = 94; break; default: // Саратов var regionId = 91; break; }; return regionId; } const SESSION_ID_PARAM = '_ct_session_id'; function readSessionIdCookie() { if (typeof document === 'undefined') { return null; } const v = Cookies.get(SESSION_ID_PARAM); if (v == null || String(v).trim() === '') { return null; } return String(v).trim(); } /** * Если в query есть sessionId — пишет session-cookie (до закрытия браузера) и убирает параметр из URL. */ function syncSessionIdFromUrl() { if (typeof window === 'undefined' || !window.location) { return; } const url = new URL(window.location.href); const raw = url.searchParams.get(SESSION_ID_PARAM); if (raw === null) { return; } const value = String(raw).trim(); if (!value) { url.searchParams.delete(SESSION_ID_PARAM); const next = url.pathname + url.search + url.hash; if (next !== window.location.pathname + window.location.search + window.location.hash) { window.location.replace(next); } return; } const secure = window.location.protocol === 'https:' ? '; Secure' : ''; document.cookie = `${SESSION_ID_PARAM}=${encodeURIComponent(value)}; path=/; SameSite=Lax${secure}`; url.searchParams.delete(SESSION_ID_PARAM); window.location.replace(url.pathname + url.search + url.hash); } function addAlert(msg, alertSystem, id, color = 'alert-danger') { var alert = document.createElement('div'); alert.id = id; alert.classList = 'alert alert-dismissible fade show'; alert.classList.add(color); alert.setAttribute('role', 'alert'); var divMsg = document.createElement('div'); divMsg.classList = 'alert-msg'; divMsg.innerHTML = msg; alert.append(divMsg); alertSystem.prepend(alert); } function getSessionId() { const fromCt = window.ct?.('calltracking_params')?.[0]?.sessionId; if (fromCt != null && String(fromCt).trim() !== '') { return String(fromCt).trim(); } const fromCookie = readSessionIdCookie(); if (fromCookie) { return fromCookie; } return null; } function addUrlParam(url, param, value) { const urlObj = new URL(url); if (!urlObj.searchParams.has(param)) { urlObj.searchParams.set(param, value); } return urlObj.toString(); } module.exports = { addUrlParam: addUrlParam, getSessionId: getSessionId, syncSessionIdFromUrl: syncSessionIdFromUrl, addAlert: addAlert, getRegionIdByHost: getRegionIdByHost, getApiHostname : getApiHostname, getHostname: getHostname, countDown: countDown, isMobile: isMobile, sendRequest: sendRequest, getLicensePersonLink: getLicensePersonLink, getLicenseLink: getLicenseLink, getCityCRM: getCityCRM, getCityId: getCityId, renderCapcha: renderCapcha };