{{-- The loader a clinic pastes into their own website. Served as application/javascript from a stable URL, so the snippet keeps working across deploys — an asset-hashed filename would break every site that had already embedded it. Two jobs and no more: put the iframe in the page, and keep it as tall as its contents so the host never shows a scrollbar inside a scrollbar. It reads nothing from the host page and writes nothing to it beyond the frame. --}} (function () { 'use strict'; var SRC = @json($src); var ORIGIN = @json($origin); var MOUNT = 'clinic-booking-widget'; function mount() { var host = document.getElementById(MOUNT); if (!host || host.getAttribute('data-clinic-booking-ready')) { return; } host.setAttribute('data-clinic-booking-ready', '1'); var frame = document.createElement('iframe'); frame.src = SRC; frame.title = @json(__('appointments::messages.online.embed_title')); frame.loading = 'lazy'; frame.style.width = '100%'; frame.style.maxWidth = '100%'; frame.style.border = '0'; /* A first height that shows something useful before the frame reports its own. Zero would collapse the layout for the length of a round trip and make the host page jump when it arrives. */ frame.style.height = '720px'; frame.setAttribute('allowtransparency', 'true'); host.appendChild(frame); window.addEventListener('message', function (event) { /* Only this clinic's frame may resize it. Without the origin check any other frame or opener on the host page could drive the height, which is a small thing to be able to do to somebody else's site but not one we should hand out. */ if (event.origin !== ORIGIN || !event.data || event.data.channel !== 'clinic-booking') { return; } if (event.data.type === 'height' && typeof event.data.value === 'number') { /* Bounded. A bug at either end should not be able to produce a megapixel-tall iframe on a stranger's page. */ var height = Math.min(Math.max(event.data.value, 320), 4000); frame.style.height = height + 'px'; } }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', mount); } else { mount(); } })();