«WWW.КУЛЬТУРА.РФ – твой гид по культуре. Узнайте больше об истории страны, искусстве и планируйте культурные выходные на портале «Культура.РФ».
...
Решаем вместе
Сложности с получением «Пушкинской карты» или приобретением билетов? Знаете, как улучшить работу учреждений культуры? Напишите — решим!
    (function () { 'use strict'; const scriptEl = document.currentScript || document.scripts[document.scripts.length - 1]; const scriptSrcUrl = new URL(scriptEl.src); const scriptUrl = (scriptSrcUrl.origin + scriptSrcUrl.pathname) .split('iframe/')[0] .replace(/\/$/, ''); // Отсекаем урл слева от iframe [корректный урл для получения ресурсов] const src = scriptEl.src; const url = new URL(src); const scriptPath = url.pathname; const scriptDir = scriptPath.substring(0, scriptPath.lastIndexOf('/')); const ASSETS_CONFIG_HOST = 'https://gu-st.ru'; const ASSETS_CDN_HOST = ASSETS_CONFIG_HOST.startsWith('#{') ? scriptUrl : ASSETS_CONFIG_HOST; function validatePosition(position) { const validPositions = [ 'bottom-right', 'bottom-left', 'top-right', 'top-left', 'middle-left', 'middle-right' ]; return validPositions.includes(position) ? position : 'bottom-right'; } function getScriptParams() { try { const params = new URLSearchParams(url.search); let cssFileName; const jsFileName = scriptPath.substring(scriptPath.lastIndexOf('/') + 1); if (jsFileName === 'boot.js') { cssFileName = 'widget-styles.css'; } else { cssFileName = jsFileName.replace('.js', '.css'); } const defaultCssUrl = `${ASSETS_CDN_HOST}${scriptDir}/${cssFileName}`; const defaultRobotImageUrl = `${ASSETS_CDN_HOST}${scriptDir}/iframe-widget.png`; const defaultMiniRobotImageUrl = `${ASSETS_CDN_HOST}${scriptDir}/iframe-mini-widget.png`; return { position: validatePosition(params.get('position') || 'bottom-right'), platform: params.get('platform') || '', region: params.get('region') || '', cssUrl: params.get('cssUrl') || defaultCssUrl, robotImageUrl: params.get('robotImageUrl') || defaultRobotImageUrl, miniRobotImageUrl: params.get('miniRobotImageUrl') || defaultMiniRobotImageUrl, saveState: params.get('saveState') !== 'false', initialState: params.get('initialState') === 'collapsed' ? 'collapsed' : 'expanded' }; } catch (error) { console.error('Ошибка парсинга параметров скрипта:', error); return { position: 'bottom-right', platform: '', cssUrl: 'widget-styles.css', robotImageUrl: 'iframe-widget.png', miniRobotImageUrl: 'iframe-mini-widget.png', saveState: true, initialState: 'expanded' }; } } const scriptParams = getScriptParams(); const BOOT_CONFIG_HOST = 'https://max.gosuslugi.ru/robot-max'; const BOOT_CDN_HOST = BOOT_CONFIG_HOST.startsWith('#{') ? scriptUrl : BOOT_CONFIG_HOST; const VERSION_CONFIG_HOST = 'https://max.gosuslugi.ru'; const VERSION_CDN_HOST = VERSION_CONFIG_HOST.startsWith('#{') ? scriptUrl : VERSION_CONFIG_HOST; const CONFIG = { BASE_URL: `${BOOT_CDN_HOST}/iframe`, VERSION_ENDPOINT: `${VERSION_CDN_HOST}/iframe/version/`, FALLBACK_VERSION: '1.0.0', TOGGLE_ICON_URL: scriptParams.miniRobotImageUrl, ROBOT_GREETING_IMG: scriptParams.robotImageUrl, ALLOWED_ORIGINS: [`${BOOT_CDN_HOST}`], CSS_LINK_ID: 'rm-widget-css-link', IFRAME_ID: 'rm-widget-iframe', TOGGLE_BTN_ID: 'rm-widget-toggle', GREETING_WIDGET_ID: 'rm-greeting-widget', POSITION: scriptParams.position, PLATFORM: scriptParams.platform, REGION: scriptParams.region, CSS_URL: scriptParams.cssUrl, SAVE_STATE: scriptParams.saveState, INITIAL_STATE: scriptParams.initialState, MOBILE_BREAKPOINT: 768, BUTTON_MARGINS: { mobile: 16, mobileBottom: 24, desktop: 30 }, BUTTON_HEIGHT: 90, DRAG_THRESHOLD: 5, SWIPE_THRESHOLD: 50, MAX_ANGLE_HORIZONTAL: 30 }; async function initWidget() { function initKeyboardDetection() { let lastHeight = window.innerHeight; let lastWidth = window.innerWidth; let keyboardVisible = false; let resizeTimeout; const KEYBOARD_THRESHOLD = 150; function detectKeyboard() { const currentHeight = window.innerHeight; const currentWidth = window.innerWidth; const heightDiff = lastHeight - currentHeight; const widthChanged = Math.abs(lastWidth - currentWidth) > 10; if (!widthChanged && heightDiff > KEYBOARD_THRESHOLD && !keyboardVisible) { keyboardVisible = true; sendKeyboardEvent(true, currentHeight, heightDiff); } else if (!widthChanged && heightDiff < -KEYBOARD_THRESHOLD && keyboardVisible) { keyboardVisible = false; sendKeyboardEvent(false, currentHeight, Math.abs(heightDiff)); } lastHeight = currentHeight; lastWidth = currentWidth; } function sendKeyboardEvent(isVisible, viewportHeight, keyboardHeight) { if (iframe && iframe.contentWindow) { iframe.contentWindow.postMessage({ type: 'keyboard-visibility-change', data: { isVisible: isVisible, viewportHeight: viewportHeight, keyboardHeight: keyboardHeight, timestamp: Date.now() } }, CONFIG.BASE_URL); } } window.addEventListener('resize', () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(detectKeyboard, 100); }); if ('visualViewport' in window) { window.visualViewport.addEventListener('resize', () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { const heightDiff = window.innerHeight - window.visualViewport.height; if (heightDiff > 50 && !keyboardVisible) { keyboardVisible = true; sendKeyboardEvent(true, window.visualViewport.height, heightDiff); } else if (heightDiff < 50 && keyboardVisible) { keyboardVisible = false; sendKeyboardEvent(false, window.visualViewport.height, 0); } }, 100); }); } } function getPositionStyles(position) { const margin = `${CONFIG.BUTTON_MARGINS.desktop}px`; const widgetPosition = { bottom: margin, }; const widgetMobilePosition = { bottom: `${CONFIG.BUTTON_MARGINS.mobileBottom}px`, transform: 'none' }; const positions = { 'bottom-right': { widget: widgetPosition, widgetMobile: widgetMobilePosition, widgetHidden: { transform: 'translateX(50%) translateY(20px)' }, widgetHiddenMobile: { transform: 'translateY(20px)' }, button: { bottom: margin, right: margin }, buttonMobile: { right: `${CONFIG.BUTTON_MARGINS.mobile}px` }, isLeft: false }, 'bottom-left': { widget: widgetPosition, widgetMobile: widgetMobilePosition, widgetHidden: { transform: 'translateX(50%) translateY(20px)' }, widgetHiddenMobile: { transform: 'translateY(20px)' }, button: { bottom: margin, left: margin }, buttonMobile: { left: `${CONFIG.BUTTON_MARGINS.mobile}px` }, isLeft: true }, 'top-right': { widget: widgetPosition, widgetMobile: widgetMobilePosition, widgetHidden: { transform: 'translateX(50%) translateY(20px)' }, widgetHiddenMobile: { transform: 'translateY(20px)' }, button: { top: margin, right: margin }, buttonMobile: { right: `${CONFIG.BUTTON_MARGINS.mobile}px` }, isLeft: false }, 'top-left': { widget: widgetPosition, widgetMobile: widgetMobilePosition, widgetHidden: { transform: 'translateX(50%) translateY(20px)' }, widgetHiddenMobile: { transform: 'translateY(20px)' }, button: { top: margin, left: margin }, buttonMobile: { left: `${CONFIG.BUTTON_MARGINS.mobile}px` }, isLeft: true }, 'middle-left': { widget: widgetPosition, widgetMobile: widgetMobilePosition, widgetHidden: { transform: 'translateX(50%) translateY(20px)' }, widgetHiddenMobile: { transform: 'translateY(20px)' }, button: { top: '50%', left: margin, transform: 'translateY(-50%)' }, buttonMobile: { left: `${CONFIG.BUTTON_MARGINS.mobile}px`, top: '50%', transform: 'translateY(-50%)' }, isLeft: true }, 'middle-right': { widget: widgetPosition, widgetMobile: widgetMobilePosition, widgetHidden: { transform: 'translateX(50%) translateY(20px)' }, widgetHiddenMobile: { transform: 'translateY(20px)' }, button: { top: '50%', right: margin, transform: 'translateY(-50%)' }, buttonMobile: { right: `${CONFIG.BUTTON_MARGINS.mobile}px`, top: '50%', transform: 'translateY(-50%)' }, isLeft: false } }; return positions[position] || positions['bottom-right']; } function injectCSS() { if (!document.getElementById(CONFIG.CSS_LINK_ID)) { const link = document.createElement('link'); link.id = CONFIG.CSS_LINK_ID; link.rel = 'stylesheet'; link.href = CONFIG.CSS_URL; link.onerror = () => { console.error('Ошибка загрузки CSS файла виджета:', CONFIG.CSS_URL); }; document.head.appendChild(link); } } function injectFontFaces() { if (document.getElementById('rm-widget-fonts')) return; const fontStyles = document.createElement('style'); fontStyles.id = 'rm-widget-fonts'; fontStyles.textContent = ` @font-face { font-family: 'Lato'; src: url('${ASSETS_CDN_HOST}${scriptDir}/fonts/Lato-Regular-v2.woff2') format('woff2'), url('${ASSETS_CDN_HOST}${scriptDir}/fonts/Lato-Regular-v2.woff') format('woff'); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: 'Lato'; src: url('${ASSETS_CDN_HOST}${scriptDir}/fonts/Lato-Bold-v2.woff2') format('woff2'), url('${ASSETS_CDN_HOST}${scriptDir}/fonts/Lato-Bold-v2.woff') format('woff'); font-weight: 700; font-style: normal; font-display: swap; } `; document.head.appendChild(fontStyles); } injectFontFaces(); injectCSS(); function createIframe() { const iframe = document.createElement('iframe'); iframe.id = CONFIG.IFRAME_ID; iframe.setAttribute('loading', 'lazy'); return iframe; } function createImg(src, alt) { const img = document.createElement('img'); img.src = src; img.alt = alt; img.addEventListener('error', () => (img.style.display = 'none')); return img; } function createBtn(iconImg) { const btn = document.createElement('button'); btn.id = CONFIG.TOGGLE_BTN_ID; btn.type = 'button'; btn.setAttribute('aria-label', 'Открыть виджет'); if (iconImg) btn.appendChild(iconImg); return btn; } function createGreetingWidget() { const container = document.createElement('div'); container.id = CONFIG.GREETING_WIDGET_ID; container.className = 'greeting-widget'; container.innerHTML = `
    Робот Макс
    \u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u0420\u043e\u0431\u043e\u0442 \u041c\u0430\u043a\u0441
    \u0421\u043f\u0440\u043e\u0441\u0438\u0442\u0435 \u043c\u0435\u043d\u044f \u043e\u0431 \u0443\u0441\u043b\u0443\u0433\u0435 \u0438\u043b\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0435\u00a0\u2014
    \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u043f\u043e\u043c\u043e\u0447\u044c
    `; return container; } function appendToBody(...elements) { elements.forEach(el => document.body.appendChild(el)); } const iframe = createIframe(); const btn = createBtn(createImg(CONFIG.TOGGLE_ICON_URL, 'Иконка виджета')); const greetingWidget = createGreetingWidget(); appendToBody(iframe, btn, greetingWidget); initKeyboardDetection(); function isMobile() { return window.innerWidth <= CONFIG.MOBILE_BREAKPOINT; } function resetButtonPosition() { ['top', 'bottom', 'left', 'right', 'transform'].forEach(prop => { btn.style[prop] = ''; greetingWidget.style[prop] = ''; }); } function applyIconFlip(isLeft) { const img = btn.querySelector('img'); if (img) { img.style.transform = isLeft ? 'scaleX(-1)' : ''; } } function applyPositioning(position = CONFIG.POSITION, targetBtn = btn, targetWidget = greetingWidget) { const positionStyles = getPositionStyles(position); resetButtonPosition(targetBtn, targetWidget); targetWidget.setAttribute('data-position', position); Object.entries(positionStyles.widget).forEach(([prop, value]) => { targetWidget.style[prop] = value; }); Object.entries(positionStyles.button).forEach(([prop, value]) => { targetBtn.style[prop] = value; }); targetWidget.dataset.hiddenTransform = positionStyles.widgetHidden.transform; applyIconFlip(positionStyles.isLeft); if (isMobile()) { Object.entries(positionStyles.widgetMobile).forEach(([prop, value]) => { targetWidget.style[prop] = value; }); Object.entries(positionStyles.buttonMobile).forEach(([prop, value]) => { targetBtn.style[prop] = value; }); targetWidget.dataset.hiddenTransformMobile = positionStyles.widgetHiddenMobile.transform; } } function resetButtonPositionFor(targetBtn, targetWidget) { ['top', 'bottom', 'left', 'right', 'transform'].forEach(prop => { targetBtn.style[prop] = ''; targetWidget.style[prop] = ''; }); } applyPositioning(); function saveButtonPosition(y) { if (CONFIG.SAVE_STATE) { localStorage.setItem('rm-widget-button-y', y.toString()); } } function loadButtonPosition() { if (!CONFIG.SAVE_STATE) return null; const savedY = localStorage.getItem('rm-widget-button-y'); return savedY ? parseFloat(savedY) : null; } function saveButtonSide(isLeft) { if (CONFIG.SAVE_STATE) { localStorage.setItem('rm-widget-button-side', isLeft ? 'left' : 'right'); } } function loadButtonSide() { if (!CONFIG.SAVE_STATE) return null; return localStorage.getItem('rm-widget-button-side'); } function setButtonY(y) { const viewportHeight = window.innerHeight; const buttonHeight = CONFIG.BUTTON_HEIGHT; const minY = CONFIG.BUTTON_MARGINS.mobileBottom; const maxY = viewportHeight - buttonHeight - CONFIG.BUTTON_MARGINS.mobileBottom; const clampedY = Math.max(minY, Math.min(maxY, y)); if (isMobile()) { btn.style.top = `${clampedY}px`; btn.style.bottom = 'auto'; } } function setButtonHorizontalPosition(isLeft) { if (isMobile()) { const margin = `${CONFIG.BUTTON_MARGINS.mobile}px`; if (isLeft) { btn.style.left = margin; btn.style.right = 'auto'; } else { btn.style.right = margin; btn.style.left = 'auto'; } applyIconFlip(isLeft); } } function initButtonPosition() { if (isMobile()) { const savedY = loadButtonPosition(); if (savedY !== null) { setButtonY(savedY); } else { const defaultY = CONFIG.POSITION.startsWith('top-') || CONFIG.POSITION.startsWith('middle-') ? CONFIG.BUTTON_MARGINS.mobileBottom : window.innerHeight - CONFIG.BUTTON_HEIGHT - CONFIG.BUTTON_MARGINS.mobileBottom; setButtonY(defaultY); } const savedSide = loadButtonSide(); const isLeft = savedSide ? savedSide === 'left' : CONFIG.POSITION.includes('left'); setButtonHorizontalPosition(isLeft); } } function getButtonCenter(useAskButton = false) { if (useAskButton) { const askBtn = greetingWidget.querySelector('.rm-widget-btn.main'); if (askBtn) { const rect = askBtn.getBoundingClientRect(); return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; } } const rect = btn.getBoundingClientRect(); if (btn.classList.contains('show') && rect.width > 0 && rect.height > 0) { return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; } const isMobileView = isMobile(); const buttonSize = isMobileView ? 80 : 76; const halfSize = buttonSize / 2; let x, y; if (isMobileView) { if (btn.style.left && btn.style.left !== 'auto') { x = parseFloat(btn.style.left) + halfSize; } else if (btn.style.right && btn.style.right !== 'auto') { x = window.innerWidth - parseFloat(btn.style.right) - halfSize; } else { if (CONFIG.POSITION.includes('left')) { x = CONFIG.BUTTON_MARGINS.mobile + halfSize; } else { x = window.innerWidth - CONFIG.BUTTON_MARGINS.mobile - halfSize; } } if (btn.style.top && btn.style.top !== 'auto') { y = parseFloat(btn.style.top) + halfSize; } else if (btn.style.bottom && btn.style.bottom !== 'auto') { y = window.innerHeight - parseFloat(btn.style.bottom) - halfSize; } else { if (CONFIG.POSITION.includes('top')) { y = CONFIG.BUTTON_MARGINS.mobile + halfSize; } else if (CONFIG.POSITION.includes('middle')) { y = window.innerHeight / 2; } else { y = window.innerHeight - CONFIG.BUTTON_MARGINS.mobileBottom - halfSize; } } } else { const position = CONFIG.POSITION; if (position.includes('left')) { x = CONFIG.BUTTON_MARGINS.desktop + halfSize; } else { x = window.innerWidth - CONFIG.BUTTON_MARGINS.desktop - halfSize; } if (position.includes('top')) { y = CONFIG.BUTTON_MARGINS.desktop + halfSize; } else if (position.includes('middle')) { y = window.innerHeight / 2; } else { y = window.innerHeight - CONFIG.BUTTON_MARGINS.desktop - halfSize; } } return { x, y }; } function setTransformOrigin(element, buttonCenter) { const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; const originX = (buttonCenter.x / viewportWidth) * 100; const originY = (buttonCenter.y / viewportHeight) * 100; element.style.setProperty('--origin-x', `${originX}%`); element.style.setProperty('--origin-y', `${originY}%`); element.style.transformOrigin = `${originX}% ${originY}%`; } function initToggleButton(targetBtn) { const dragThreshold = CONFIG.DRAG_THRESHOLD; const swipeThreshold = CONFIG.SWIPE_THRESHOLD; const maxAngleForHorizontalSwipe = CONFIG.MAX_ANGLE_HORIZONTAL; let isDragging = false; let startX = 0; let startY = 0; let startButtonY = 0; let hasMoved = false; let isHorizontalSwipe = false; let isVerticalDrag = false; function handleStart(e) { if (!isMobile()) return; isDragging = true; hasMoved = false; isHorizontalSwipe = false; isVerticalDrag = false; targetBtn.classList.add('dragging'); const clientX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX; const clientY = e.type === 'touchstart' ? e.touches[0].clientY : e.clientY; startX = clientX; startY = clientY; const currentTop = parseInt(targetBtn.style.top) || (CONFIG.POSITION.startsWith('top-') || CONFIG.POSITION.startsWith('middle-') ? CONFIG.BUTTON_MARGINS.mobileBottom : window.innerHeight - CONFIG.BUTTON_HEIGHT - CONFIG.BUTTON_MARGINS.mobileBottom); startButtonY = currentTop; } function handleMove(e) { if (!isDragging || !isMobile()) return; const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX; const clientY = e.type === 'touchmove' ? e.touches[0].clientY : e.clientY; const deltaX = clientX - startX; const deltaY = clientY - startY; const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > dragThreshold && !hasMoved) { hasMoved = true; const angle = Math.abs((Math.atan2(deltaY, deltaX) * 180) / Math.PI); const isHorizontal = angle <= maxAngleForHorizontalSwipe || angle >= 180 - maxAngleForHorizontalSwipe; if (isHorizontal && Math.abs(deltaX) > Math.abs(deltaY)) { isHorizontalSwipe = true; isVerticalDrag = false; } else { isVerticalDrag = true; isHorizontalSwipe = false; } } if (hasMoved && isVerticalDrag) { const newY = startButtonY + deltaY; setButtonY(newY); e.preventDefault(); } else if (hasMoved && isHorizontalSwipe) { e.preventDefault(); } if (hasMoved) { e.preventDefault(); } } function handleEnd(e) { if (!isDragging || !isMobile()) return; isDragging = false; targetBtn.classList.remove('dragging'); if (hasMoved) { const clientX = e.type === 'touchend' ? e.changedTouches[0].clientX : e.clientX; const deltaX = clientX - startX; if (isHorizontalSwipe && Math.abs(deltaX) >= swipeThreshold) { targetBtn.classList.add('swipe-animating'); const isLeft = deltaX < 0; setButtonHorizontalPosition(isLeft); saveButtonSide(isLeft); setTimeout(() => { targetBtn.classList.remove('swipe-animating'); }, 200); e.preventDefault(); e.stopPropagation(); } else if (isVerticalDrag) { const currentY = parseInt(targetBtn.style.top) || startButtonY; saveButtonPosition(currentY); e.preventDefault(); e.stopPropagation(); } } } targetBtn.addEventListener('mousedown', handleStart); targetBtn.addEventListener('touchstart', handleStart, { passive: false }); document.addEventListener('mousemove', handleMove); document.addEventListener('touchmove', handleMove, { passive: false }); document.addEventListener('mouseup', handleEnd); document.addEventListener('touchend', handleEnd); targetBtn.addEventListener('click', e => { if (hasMoved) { e.preventDefault(); e.stopPropagation(); return; } openIframe(); }); } initToggleButton(btn); let resizeTimer; window.addEventListener('resize', () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(() => { applyPositioning(CONFIG.POSITION, btn, greetingWidget); if (isMobile()) { initButtonPosition(); } else { resetButtonPosition(); applyPositioning(CONFIG.POSITION, btn, greetingWidget); } }, 100); }); function buildIframeUrl(baseUrl) { try { const url = new URL(baseUrl); const fullUrl = window.location.href; url.searchParams.set('link', fullUrl); if (CONFIG.PLATFORM && CONFIG.PLATFORM.trim() !== '') { url.searchParams.set('platform', CONFIG.PLATFORM.trim()); } if (CONFIG.REGION && CONFIG.REGION.trim() !== '') { url.searchParams.set('region', CONFIG.REGION.trim()); } return url.toString(); } catch (error) { console.error('Ошибка построения URL для iframe:', error); return baseUrl; } } async function fetchVersionedUrl() { try { const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), 5000); const res = await fetch(CONFIG.VERSION_ENDPOINT, { signal: ctrl.signal, method: 'GET', headers: { Accept: 'application/json', 'Cache-Control': 'no-cache' } }); clearTimeout(timer); if (!res.ok) { throw new Error(`HTTP ${res.status}: ${res.statusText}`); } const json = await res.json(); if (!json || typeof json.v !== 'string') { throw new Error('Некорректный формат ответа'); } const version = json.v.replace(/[^0-9.]/g, '') || CONFIG.FALLBACK_VERSION; const baseUrl = `${CONFIG.BASE_URL}/${version}/`; return buildIframeUrl(baseUrl); } catch (error) { if (error.name === 'AbortError') { console.error('Запрос версии прерван по тайм-ауту'); } else { console.error('Ошибка загрузки версии:', error.message); } const fallbackUrl = `${CONFIG.BASE_URL}/${CONFIG.FALLBACK_VERSION}/`; return buildIframeUrl(fallbackUrl); } } fetchVersionedUrl().then(url => (iframe.src = url)); function showToggleButton() { btn.classList.add('show'); if (isMobile()) { initButtonPosition(); } } function hideToggleButton() { btn.classList.remove('show'); } function hideGreetingWidget() { if (CONFIG.SAVE_STATE) { localStorage.setItem('rm-widget-greeting-hidden', 'true'); } btn.style.opacity = '0'; btn.style.pointerEvents = 'none'; btn.classList.add('show'); if (isMobile()) { initButtonPosition(); } requestAnimationFrame(() => { const buttonCenter = getButtonCenter(); setTransformOrigin(greetingWidget, buttonCenter); greetingWidget.classList.add('hiding'); setTimeout(() => { greetingWidget.classList.add('hidden'); greetingWidget.classList.remove('hiding'); btn.style.opacity = ''; btn.style.pointerEvents = ''; }, 400); }); } function openIframe() { const useAskButton = !greetingWidget.classList.contains('hidden'); const wasHidden = !btn.classList.contains('show') && greetingWidget.classList.contains('hidden'); if (wasHidden) { btn.style.opacity = '0'; btn.style.pointerEvents = 'none'; btn.classList.add('show'); if (isMobile()) { initButtonPosition(); } } requestAnimationFrame(() => { const buttonCenter = getButtonCenter(useAskButton); setTransformOrigin(iframe, buttonCenter); if (wasHidden) { btn.classList.remove('show'); btn.style.opacity = ''; btn.style.pointerEvents = ''; } iframe.classList.add('opening'); iframe.classList.add('open'); greetingWidget.classList.add('hidden'); greetingWidget.classList.remove('appearing'); hideToggleButton(); document.body.style.overflow = 'hidden'; setTimeout(() => { iframe.classList.remove('opening'); }, 480); }); iframe.classList.add('open'); greetingWidget.classList.add('hidden'); hideToggleButton(); document.body.style.overflow = 'hidden'; iframe.contentWindow.postMessage('openIframe', '*'); } function closeIframe() { const currentY = isMobile() ? parseInt(btn.style.top) : null; btn.style.opacity = '0'; btn.style.pointerEvents = 'none'; btn.classList.add('show'); if (isMobile()) { initButtonPosition(); } requestAnimationFrame(() => { const buttonCenter = getButtonCenter(); setTransformOrigin(iframe, buttonCenter); iframe.classList.add('closing'); setTimeout(() => { iframe.classList.remove('open'); iframe.classList.remove('closing'); btn.style.opacity = ''; btn.style.pointerEvents = ''; if (isMobile() && currentY !== null) { btn.style.top = `${currentY}px`; } document.body.style.overflow = ''; }, 480); }); } function initGreetingWidget(widgetEl) { const askBtn = widgetEl.querySelector('.rm-widget-btn.main'); const notNowBtn = widgetEl.querySelector('.rm-widget-btn:not(.main)'); const closeBtn = widgetEl.querySelector('.rm-widget-close'); if (askBtn) askBtn.addEventListener('click', openIframe); if (notNowBtn) notNowBtn.addEventListener('click', hideGreetingWidget); if (closeBtn) closeBtn.addEventListener('click', hideGreetingWidget); } initGreetingWidget(greetingWidget); if (CONFIG.SAVE_STATE && localStorage.getItem('rm-widget-greeting-hidden') === 'true') { greetingWidget.classList.add('hidden'); showToggleButton(); } else { if (CONFIG.INITIAL_STATE === 'collapsed') { greetingWidget.classList.add('hidden'); showToggleButton(); } } window.addEventListener('message', e => { if (e.data === 'closeIframe') { closeIframe(); } }); document.addEventListener('keydown', e => { if (e.key === 'Escape') { if (iframe.classList.contains('open')) { closeIframe(); } else if (!greetingWidget.classList.contains('hidden')) { hideGreetingWidget(); } } }); } if (document.readyState === 'complete') { initWidget().catch(); } else { window.addEventListener('load', () => initWidget().catch(console.error)); } })();