|
|
@ -17,7 +17,7 @@ const frameHtml = `<!doctype html> |
|
|
</head> |
|
|
</head> |
|
|
<body> |
|
|
<body> |
|
|
<iframe srcdoc="" sandbox="allow-scripts"></iframe> |
|
|
<iframe srcdoc="" sandbox="allow-scripts"></iframe> |
|
|
<script type="module"> |
|
|
${'<'}script type="module"> |
|
|
const frame = document.getElementsByTagName('iframe')[0] |
|
|
const frame = document.getElementsByTagName('iframe')[0] |
|
|
addEventListener('message', event => { |
|
|
addEventListener('message', event => { |
|
|
const d = event.data |
|
|
const d = event.data |
|
|
@ -27,7 +27,7 @@ addEventListener('message', event => { |
|
|
frame.postMessage(event.data) |
|
|
frame.postMessage(event.data) |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
</script> |
|
|
${'</'}script> |
|
|
</body> |
|
|
</body> |
|
|
</html>` |
|
|
</html>` |
|
|
|
|
|
|
|
|
@ -35,12 +35,11 @@ export class Page extends HTMLElement { |
|
|
constructor() { |
|
|
constructor() { |
|
|
super() |
|
|
super() |
|
|
const shadow = this.attachShadow({mode: 'open'}) |
|
|
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 = document.createElement('textarea') |
|
|
this.textArea.addEventListener('input', e => { |
|
|
this.textArea.addEventListener('input', e => { |
|
|
localStorage.setItem( |
|
|
this.body = e.target.value |
|
|
this.path, |
|
|
|
|
|
e.target.value |
|
|
|
|
|
) |
|
|
|
|
|
}) |
|
|
}) |
|
|
const div = document.createElement('div') |
|
|
const div = document.createElement('div') |
|
|
div.classList.add('twrap') |
|
|
div.classList.add('twrap') |
|
|
@ -49,9 +48,7 @@ export class Page extends HTMLElement { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
connectedCallback() { |
|
|
connectedCallback() { |
|
|
this.textArea.value = localStorage.getItem( |
|
|
this.textArea.value = this.body |
|
|
this.path |
|
|
|
|
|
) ?? '' |
|
|
|
|
|
const style = document.createElement('style') |
|
|
const style = document.createElement('style') |
|
|
style.textContent = ` |
|
|
style.textContent = ` |
|
|
:host { |
|
|
:host { |
|
|
@ -76,35 +73,90 @@ export class Page extends HTMLElement { |
|
|
width: 100%; |
|
|
width: 100%; |
|
|
height: 90vh; |
|
|
height: 90vh; |
|
|
} |
|
|
} |
|
|
|
|
|
:host(.editing) iframe { |
|
|
|
|
|
display: none; |
|
|
|
|
|
} |
|
|
|
|
|
:host(.viewing) textarea { |
|
|
|
|
|
display: none; |
|
|
|
|
|
} |
|
|
` |
|
|
` |
|
|
this.shadowRoot.append(style) |
|
|
this.shadowRoot.append(style) |
|
|
if (this.path.startsWith('/sandbox/')) { |
|
|
this.initFrame() |
|
|
this.initFrame() |
|
|
this.editing = this.editing |
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
initFrame() { |
|
|
initFrame() { |
|
|
const wrap = document.createElement('div') |
|
|
const wrap = document.createElement('div') |
|
|
this.shadowRoot.appendChild(wrap) |
|
|
this.shadowRoot.appendChild(wrap) |
|
|
const tmp = document.createElement('iframe') |
|
|
const tmp = document.createElement('iframe') |
|
|
tmp.sandbox = "allow-same-origin allow-scripts" |
|
|
if (this.csp !== undefined) { |
|
|
const url = new URL( |
|
|
tmp.sandbox = "allow-same-origin allow-scripts" |
|
|
'/-/frame', location.href |
|
|
const url = new URL( |
|
|
) |
|
|
'/-/frame', location.href |
|
|
url.searchParams.set( |
|
|
) |
|
|
'csp', |
|
|
url.searchParams.set('csp', this.csp) |
|
|
"default-src 'self' 'unsafe-inline' 'unsafe-eval'" |
|
|
url.searchParams.set('html', frameHtml) |
|
|
) |
|
|
tmp.src = url.href |
|
|
url.searchParams.set('html', frameHtml) |
|
|
} else { |
|
|
tmp.src = url.href |
|
|
tmp.sandbox = "allow-scripts" |
|
|
|
|
|
tmp.srcdoc = frameHtml |
|
|
|
|
|
} |
|
|
wrap.insertAdjacentHTML( |
|
|
wrap.insertAdjacentHTML( |
|
|
'beforeend', tmp.outerHTML |
|
|
'beforeend', tmp.outerHTML |
|
|
) |
|
|
) |
|
|
const frames = wrap.getElementsByTagName('iframe') |
|
|
const frames = wrap.getElementsByTagName('iframe') |
|
|
this.frame = frames[frames.length - 1] |
|
|
this.frame = frames[frames.length - 1] |
|
|
this.textArea.addEventListener('blur', e => { |
|
|
this.textArea.addEventListener('blur', e => { |
|
|
const msg = ['srcdoc', e.target.value] |
|
|
this.display(e.target.value) |
|
|
this.frame.contentWindow.postMessage(msg) |
|
|
}) |
|
|
|
|
|
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 |
|
|
|
|
|
} |
|
|
} |
|
|
} |