Files
hotelsync/public/widget-loader.js
HotelSync 85ccd32574 fix: widget modal — clean embed mode, better close button, backdrop blur
- ?embed=true renders widget without page wrapper (white bg, no padding)
- widget-loader.js appends &embed=true to modal URL automatically
- Close button is now inside dialog (not overlapping iframe), light style
- Backdrop blur effect, Escape key closes modal
- FAB: subtle translateY on hover instead of scale

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 01:28:23 +03:00

139 lines
4.7 KiB
JavaScript

/* HotelSync Widget Loader — https://hotelsync.ru */
(function () {
var s = document.currentScript || (function () {
var t = document.getElementsByTagName('script');
return t[t.length - 1];
})();
var rawUrl = s.getAttribute('data-url') || '';
var mode = s.getAttribute('data-mode') || 'floating';
var btn = s.getAttribute('data-btn') || 'Забронировать';
var color = s.getAttribute('data-color') || '#4F46E5';
var cls = s.getAttribute('data-class') || 'hotelsync-book';
// Append embed param so the page renders without its own wrapper
var url = rawUrl + (rawUrl.indexOf('?') >= 0 ? '&' : '?') + 'embed=true';
function init() {
/* ── Overlay ── */
var overlay = document.createElement('div');
overlay.style.cssText = [
'display:none', 'position:fixed', 'inset:0',
'background:rgba(0,0,0,.55)',
'backdrop-filter:blur(4px)',
'-webkit-backdrop-filter:blur(4px)',
'z-index:99998',
'align-items:center', 'justify-content:center',
'padding:20px', 'box-sizing:border-box',
].join(';');
/* ── Dialog box ── */
var dialog = document.createElement('div');
dialog.style.cssText = [
'position:relative',
'width:100%', 'max-width:480px',
'max-height:calc(100vh - 40px)',
'border-radius:24px',
'overflow:hidden',
'box-shadow:0 32px 80px rgba(0,0,0,.4)',
'background:#fff',
'display:flex', 'flex-direction:column',
].join(';');
/* ── Close button (outside iframe, top-right of dialog) ── */
var closeBtn = document.createElement('button');
closeBtn.innerHTML = '&#10005;';
closeBtn.title = 'Закрыть';
closeBtn.style.cssText = [
'position:absolute', 'top:12px', 'right:12px', 'z-index:10',
'width:34px', 'height:34px', 'border-radius:50%',
'background:rgba(255,255,255,.9)',
'backdrop-filter:blur(4px)',
'color:#374151',
'border:1px solid rgba(0,0,0,.1)',
'cursor:pointer',
'font-size:14px', 'font-weight:700',
'display:flex', 'align-items:center', 'justify-content:center',
'box-shadow:0 2px 8px rgba(0,0,0,.15)',
'transition:background .15s',
].join(';');
closeBtn.onmouseenter = function () { closeBtn.style.background = '#fff'; };
closeBtn.onmouseleave = function () { closeBtn.style.background = 'rgba(255,255,255,.9)'; };
closeBtn.onclick = function () { overlay.style.display = 'none'; };
/* ── Iframe ── */
var iframe = document.createElement('iframe');
iframe.src = url;
iframe.setAttribute('allow', 'payment');
iframe.style.cssText = [
'width:100%', 'height:680px',
'border:none', 'display:block',
'flex:1',
].join(';');
dialog.appendChild(closeBtn);
dialog.appendChild(iframe);
overlay.appendChild(dialog);
overlay.addEventListener('click', function (e) {
if (e.target === overlay) overlay.style.display = 'none';
});
// Close on Escape
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') overlay.style.display = 'none';
});
document.body.appendChild(overlay);
window.hsOpenWidget = function () { overlay.style.display = 'flex'; };
/* ── Floating button ── */
if (mode === 'floating') {
var fab = document.createElement('button');
fab.textContent = btn;
fab.style.cssText = [
'position:fixed', 'bottom:24px', 'right:24px',
'background:' + color, 'color:#fff',
'border:none', 'padding:15px 28px',
'border-radius:50px',
'font-size:15px', 'font-family:inherit', 'font-weight:700',
'letter-spacing:.01em',
'cursor:pointer', 'z-index:99997',
'box-shadow:0 6px 24px rgba(0,0,0,.25)',
'transition:transform .15s,box-shadow .15s',
].join(';');
fab.onmouseenter = function () {
fab.style.transform = 'translateY(-2px) scale(1.03)';
fab.style.boxShadow = '0 10px 32px rgba(0,0,0,.32)';
};
fab.onmouseleave = function () {
fab.style.transform = 'none';
fab.style.boxShadow = '0 6px 24px rgba(0,0,0,.25)';
};
fab.onclick = function () { window.hsOpenWidget(); };
document.body.appendChild(fab);
}
/* ── Class trigger ── */
if (mode === 'trigger') {
document.addEventListener('click', function (e) {
var el = e.target;
while (el) {
if (el.classList && el.classList.contains(cls)) {
e.preventDefault();
window.hsOpenWidget();
return;
}
el = el.parentElement;
}
});
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();