/** Cloudflare embedding update. Replace the wrapper Code.gs in full. */ /** Add the library as MoneyTracker. Setup/repair or first web open prepares all sheets. */ function mtContext_() { var properties = PropertiesService.getScriptProperties(); var active = SpreadsheetApp.getActiveSpreadsheet(); if (active) properties.setProperty('MT_SSID', active.getId()); return { spreadsheetId: active ? active.getId() : properties.getProperty('MT_SSID'), properties: properties, cache: CacheService.getScriptCache(), lock: LockService.getScriptLock() }; } function api(request) { return MoneyTracker.api(request, mtContext_()); } function setup() { return MoneyTracker.setup(mtContext_()); } function onOpen(e) { MoneyTracker.onOpen(mtContext_()); } function mtOnEdit(e) { MoneyTracker.handleEdit(e, mtContext_()); } function mtOnChange(e) { MoneyTracker.handleChange(e, mtContext_()); } function mtDaily() { MoneyTracker.runDaily(mtContext_()); } function openApp() { return MoneyTracker.openApp(mtContext_()); } function refreshNow() { return MoneyTracker.refresh(mtContext_()); } function reportThisMonth() { return MoneyTracker.reportThisMonth(mtContext_()); } /* Replace doGet in the small including-project Code.gs with this function. * Add mtCloudflareBridge_ once. No MoneyTracker library version change is needed. */ function doGet(e) { var output = MoneyTracker.serve(mtContext_(), e); var params = e && e.parameter || {}; var origin = String(params.mt_host || ''); var channel = String(params.mt_channel || ''); if (!/^https:\/\/[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::[1-9][0-9]{0,4})?$/i.test(origin) || !/^[a-f0-9]{32}$/.test(channel)) return output; // Optional: a comma-separated MT_EMBED_ORIGINS Script Property limits hosts. var allowed = String(PropertiesService.getScriptProperties().getProperty('MT_EMBED_ORIGINS') || '') .split(',').map(function(value) { return value.trim(); }).filter(Boolean); if (allowed.length && allowed.indexOf(origin) < 0) return output; var config = JSON.stringify({ origin: origin, channel: channel }).replace(/'; var html = output.getContent(); var end = html.toLowerCase().lastIndexOf(''); output.setContent(end < 0 ? html + script : html.slice(0, end) + script + html.slice(end)); return output.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL); } /* Runs only in the browser after being injected by doGet. It shares dimensions * only; login tokens, PINs, spreadsheet data and RPC stay inside Google's frame. */ function mtCloudflareBridge_(config) { if (window.top === window) return; var queued = false; var latest = null; var topWindow = window.top; function send(type, extra) { var data = { type: type, version: 1, channel: config.channel }; Object.keys(extra || {}).forEach(function(key) { data[key] = extra[key]; }); try { topWindow.postMessage(data, config.origin); } catch (e) { /* Wait for the host to be ready. */ } } function measure() { if (queued) return; queued = true; setTimeout(function() { queued = false; if (!latest) return; send('mt-app-viewport', { sequence: latest.sequence, width: window.innerWidth, height: window.innerHeight }); }, 0); } window.addEventListener('message', function(event) { var data = event.data; if (event.source !== topWindow || event.origin !== config.origin || !data || data.type !== 'mt-host-viewport' || data.version !== 1 || data.channel !== config.channel || !Number.isSafeInteger(data.sequence) || data.sequence < 1 || !Number.isFinite(data.width) || !Number.isFinite(data.height) || data.width <= 0 || data.height <= 0) return; if (latest && data.sequence <= latest.sequence) return; latest = data; measure(); }); function resized() { send('mt-app-resize'); } window.addEventListener('resize', resized, { passive: true }); window.addEventListener('orientationchange', resized, { passive: true }); window.addEventListener('pageshow', function() { send('mt-app-ready'); }, { passive: true }); document.addEventListener('visibilitychange', function() { if (!document.hidden) resized(); }); send('mt-app-ready'); }