const frameHtml = ` Frame ${'<'}script type="module"> let frame = undefined addEventListener('message', event => { let isNew = false const d = event.data if (Array.isArray(d) && d[0] === 'srcdoc') { isNew = frame === undefined if (isNew) { frame = document.createElement('iframe') frame.sandbox = "allow-scripts allow-top-navigation" } frame.srcdoc = d[1] } else if (frame !== undefined) { frame.postMessage(event.data) } if (isNew) { document.body.appendChild(frame) } }) ${' ` export class FileGroupPage extends HTMLElement { constructor() { super() this.attachShadow({mode: 'open'}) this.csp = "default-src 'self' 'unsafe-inline' 'unsafe-eval'" } connectedCallback() { const style = document.createElement('style') style.textContent = ` :host { display: grid; grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "main"; flex-direction: column; align-items: stretch; } iframe { border: none; margin: 0; padding: 0; grid-area: main; width: 100%; } :host(.editing) iframe.view { display: none; } :host(.viewing) iframe.edit { display: none; } ` this.shadowRoot.append(style) this.initEditFrame() this.initViewFrame() this.editing = this.editing } initEditFrame() { const frame = document.createElement('iframe') if (this.csp !== undefined) { frame.sandbox = "allow-same-origin allow-scripts allow-top-navigation" const url = new URL( '/-/frame', location.href ) url.searchParams.set('csp', this.csp) url.searchParams.set('html', frameHtml) frame.src = url.href } else { frame.sandbox = "allow-scripts allow-top-navigation" frame.srcdoc = frameHtml } frame.addEventListener('load', () => { this.displayEdit() }) this.editFrame = frame this.shadowRoot.append(frame) } initViewFrame() { const frame = document.createElement('iframe') if (this.csp !== undefined) { frame.sandbox = "allow-same-origin allow-scripts allow-top-navigation" const url = new URL( '/-/frame', location.href ) url.searchParams.set('csp', this.csp) url.searchParams.set('html', frameHtml) frame.src = url.href } else { frame.sandbox = "allow-scripts allow-top-navigation" frame.srcdoc = frameHtml } frame.addEventListener('load', () => { this.displayView() }) this.viewFrame = frame this.shadowRoot.append(frame) } displayView() { let doc = 'view here' const msg = ['srcdoc', doc] this.viewFrame.contentWindow.postMessage( msg, '*' ) } displayEdit() { let doc = 'edit here' const msg = ['srcdoc', doc] this.viewFrame.contentWindow.postMessage( msg, '*' ) } set body(value) { try { localStorage.setItem(this.path, value) } catch (err) { console.error(err) } } get body() { try { return localStorage.getItem(this.path) } catch (err) { console.error(err) return '' } } set editing(value) { this._editing = value if (this.shadowRoot.host) { const classes = this.shadowRoot.host.classList if (this.editing) { classes.add('editing') classes.remove('viewing') } else { classes.add('viewing') classes.remove('editing') } } } get editing() { return this._editing } }