|
|
|
|
const frameHtml = `<!doctype html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<title>Frame</title>
|
|
|
|
|
<style>
|
|
|
|
|
html, body, iframe {
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
}
|
|
|
|
|
iframe {
|
|
|
|
|
border: none;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<iframe srcdoc="" sandbox="allow-scripts"></iframe>
|
|
|
|
|
${'<'}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)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
${'</'}script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>`
|
|
|
|
|
|
|
|
|
|
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 = `<pre style="white-space: pre-wrap; margin: 8px 12px; font-family: monospace;">` +
|
|
|
|
|
doc.replace("<", "<").replace(">", ">") +
|
|
|
|
|
`</pre>`
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|