const frameHtml = ` Frame ${'<'}script type="module"> const frame = document.getElementsByTagName('iframe')[0] addEventListener('message', event => { const d = event.data if (Array.isArray(d) && d[0] === 'srcdoc') { frame.srcdoc = d[1] } else { frame.postMessage(event.data) } }) ${' ` export class Page extends HTMLElement { constructor() { super() const shadow = this.attachShadow({mode: 'open'}) this.csp = "default-src 'self' 'unsafe-inline' 'unsafe-eval'" //this.editing = false this.textArea = document.createElement('textarea') this.textArea.addEventListener('input', e => { this.body = e.target.value }) const div = document.createElement('div') div.classList.add('twrap') div.appendChild(this.textArea) shadow.appendChild(div) } connectedCallback() { this.textArea.value = this.body const style = document.createElement('style') style.textContent = ` :host { overflow-y: auto; display: flex; flex-direction: column; align-items: stretch; } div.twrap { padding: 5px 10px; display: flex; align-items: stretch; flex-direction: column; } textarea { padding: 5px 10px; font-size: 0.90em; height: 50vh; } iframe { border: none; width: 100%; height: 90vh; } :host(.editing) iframe { display: none; } :host(.viewing) textarea { display: none; } ` this.shadowRoot.append(style) this.initFrame() this.editing = this.editing } initFrame() { const wrap = document.createElement('div') this.shadowRoot.appendChild(wrap) const tmp = document.createElement('iframe') if (this.csp !== undefined) { tmp.sandbox = "allow-same-origin allow-scripts" const url = new URL( '/-/frame', location.href ) url.searchParams.set('csp', this.csp) url.searchParams.set('html', frameHtml) tmp.src = url.href } else { tmp.sandbox = "allow-scripts" tmp.srcdoc = frameHtml } wrap.insertAdjacentHTML( 'beforeend', tmp.outerHTML ) const frames = wrap.getElementsByTagName('iframe') this.frame = frames[frames.length - 1] this.textArea.addEventListener('blur', e => { this.display(e.target.value) }) this.frame.addEventListener('load', () => { this.display(this.body) }) } display(value) { let doc = value if (!(/<\w/).test(doc.substring(0, 30))) { doc = `
` +
        doc.replace("<", "<").replace(">", ">") +
      `
` } const msg = ['srcdoc', doc] this.frame.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 } }