From e9525ab15cd556ce660ed55b7de11df67d792fa1 Mon Sep 17 00:00:00 2001 From: bat Date: Sat, 25 Mar 2023 07:05:49 +0000 Subject: [PATCH 1/9] add edit icon --- components/header.js | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/components/header.js b/components/header.js index 06f9be1..306a064 100644 --- a/components/header.js +++ b/components/header.js @@ -5,11 +5,19 @@ export class Header extends HTMLElement { `, dot: ` +`, + edit: ` + + +`, + check: ` + `, } constructor() { super() + this.editing = false this.attachShadow({mode: 'open'}) this.addButton(this.icons.menu, 'nav', () => { this.menu.pages = this.getPages() @@ -17,6 +25,11 @@ export class Header extends HTMLElement { this.overlay.classList.add('open') }) this.addDivider() + this.editBtn = this.addButton(this.editIcon, 'edit', () => { + this.dispatchEvent(new CustomEvent( + 'click-edit', {bubbles: true} + )) + }) this.addButton(this.icons.dot, 'page', () => { this.pageMenuPanel.classList.add('open') this.overlay.classList.add('open') @@ -33,6 +46,7 @@ export class Header extends HTMLElement { if (onClick) { b.addEventListener('click', onClick) } + return b } addDivider() { @@ -53,7 +67,7 @@ export class Header extends HTMLElement { this.overlay.addEventListener('click', () => { this.close() }) - this.menu.addEventListener('close', () => { + this.menu.addEventListener('close-menu', () => { this.close() }) } @@ -76,7 +90,7 @@ export class Header extends HTMLElement { el.click() this.shadowRoot.removeChild(el) }) - this.pageMenu.addEventListener('close', () => { + this.pageMenu.addEventListener('close-menu', () => { this.close() }) this.pageMenuPanel = document.createElement('div') @@ -166,4 +180,19 @@ export class Header extends HTMLElement { getPages() { return Object.keys(localStorage).slice().sort() } + + set editing(value) { + this._editing = value + if (this.editBtn) { + this.editBtn.innerHTML = this.editIcon + } + } + + get editing() { + return this._editing + } + + get editIcon() { + return this.editing ? this.icons.check : this.icons.edit + } } \ No newline at end of file From d9532864c1f4a35df23f655d4e579e820daaabb7 Mon Sep 17 00:00:00 2001 From: bat Date: Sat, 25 Mar 2023 08:15:10 +0000 Subject: [PATCH 2/9] Update custom event name --- components/nav-menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nav-menu.js b/components/nav-menu.js index 53919e6..f260642 100644 --- a/components/nav-menu.js +++ b/components/nav-menu.js @@ -35,7 +35,7 @@ export class NavMenu extends HTMLElement { this.shadowRoot.addEventListener('click', e => { if (e.target.classList.contains('page')) { this.dispatchEvent(new CustomEvent( - 'close', {bubbles: true} + 'close-menu', {bubbles: true} )) } }) From 5ae07be15d7829b90bc10bc51fc816c0d3c87f2e Mon Sep 17 00:00:00 2001 From: bat Date: Sat, 25 Mar 2023 20:35:17 +0000 Subject: [PATCH 3/9] Update custom event name --- components/page-menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/page-menu.js b/components/page-menu.js index 61fd0da..a433bda 100644 --- a/components/page-menu.js +++ b/components/page-menu.js @@ -45,7 +45,7 @@ export class PageMenu extends HTMLElement { this.shadowRoot.appendChild(btn) btn.addEventListener('click', () => { this.dispatchEvent(new CustomEvent( - 'close', {bubbles: true} + 'close-menu', {bubbles: true} )) handler() }) From d597db2258a21dd906b3cd7a20ca9e80e8627bd4 Mon Sep 17 00:00:00 2001 From: bat Date: Sat, 25 Mar 2023 21:33:04 +0000 Subject: [PATCH 4/9] Toggle editing --- components/layout.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/components/layout.js b/components/layout.js index 4665841..cf125d0 100644 --- a/components/layout.js +++ b/components/layout.js @@ -3,6 +3,12 @@ export class Layout extends HTMLElement { super() this.attachShadow({mode: 'open'}) this.header = document.createElement('m-header') + this.editing = false + this.header.editing = this.editing + this.header.addEventListener('click-edit', () => { + this.editing = !this.editing + this.header.editing = this.editing + }) this.shadowRoot.appendChild(this.header) this.load() addEventListener('hashchange', () => { @@ -39,9 +45,6 @@ export class Layout extends HTMLElement { } get path() { - return new URL( - window.location.hash.slice(1) || '/', - window.location - ).pathname + return '/hello' } } \ No newline at end of file From 598ee5079b567910495917b9aeb8fb8c4b3437e4 Mon Sep 17 00:00:00 2001 From: bat Date: Sat, 25 Mar 2023 23:05:29 +0000 Subject: [PATCH 5/9] use property to store page body --- components/page.js | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/components/page.js b/components/page.js index d5d429a..779ba02 100644 --- a/components/page.js +++ b/components/page.js @@ -17,7 +17,7 @@ const frameHtml = ` - + ${' ` @@ -37,10 +37,7 @@ export class Page extends HTMLElement { const shadow = this.attachShadow({mode: 'open'}) this.textArea = document.createElement('textarea') this.textArea.addEventListener('input', e => { - localStorage.setItem( - this.path, - e.target.value - ) + this.body = e.target.value }) const div = document.createElement('div') div.classList.add('twrap') @@ -49,9 +46,7 @@ export class Page extends HTMLElement { } connectedCallback() { - this.textArea.value = localStorage.getItem( - this.path - ) ?? '' + this.textArea.value = this.body const style = document.createElement('style') style.textContent = ` :host { @@ -78,9 +73,7 @@ export class Page extends HTMLElement { } ` this.shadowRoot.append(style) - if (this.path.startsWith('/sandbox/')) { - this.initFrame() - } + this.initFrame() } initFrame() { @@ -107,4 +100,21 @@ export class Page extends HTMLElement { 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 '' + } + } } \ No newline at end of file From d5a477b534579980ffb3cc6bea983cf04a01e249 Mon Sep 17 00:00:00 2001 From: bat Date: Sun, 26 Mar 2023 01:09:12 +0000 Subject: [PATCH 6/9] support custom csp and nested sandboxes --- components/layout.js | 24 +++++++++++++----------- components/page.js | 25 ++++++++++++++----------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/components/layout.js b/components/layout.js index cf125d0..7a3baca 100644 --- a/components/layout.js +++ b/components/layout.js @@ -2,18 +2,9 @@ export class Layout extends HTMLElement { constructor() { super() this.attachShadow({mode: 'open'}) - this.header = document.createElement('m-header') + this.csp = "default-src 'self' 'unsafe-inline' 'unsafe-eval'" this.editing = false - this.header.editing = this.editing - this.header.addEventListener('click-edit', () => { - this.editing = !this.editing - this.header.editing = this.editing - }) - this.shadowRoot.appendChild(this.header) - this.load() - addEventListener('hashchange', () => { - this.load() - }) + this.header = document.createElement('m-header') } connectedCallback() { @@ -30,12 +21,23 @@ export class Layout extends HTMLElement { } ` this.shadowRoot.appendChild(style) + this.header.editing = this.editing + this.header.addEventListener('click-edit', () => { + this.editing = !this.editing + this.header.editing = this.editing + }) + this.shadowRoot.appendChild(this.header) + this.load() + addEventListener('hashchange', () => { + this.load() + }) } load() { const path = this.path const prevPage = this.page this.page = document.createElement('m-page') + this.page.csp = this.csp this.page.path = path if (prevPage !== undefined) { prevPage.remove() diff --git a/components/page.js b/components/page.js index 779ba02..24d0741 100644 --- a/components/page.js +++ b/components/page.js @@ -35,6 +35,7 @@ export class Page extends HTMLElement { constructor() { super() const shadow = this.attachShadow({mode: 'open'}) + this.csp = "default-src 'self' 'unsafe-inline' 'unsafe-eval'" this.textArea = document.createElement('textarea') this.textArea.addEventListener('input', e => { this.body = e.target.value @@ -80,16 +81,18 @@ export class Page extends HTMLElement { const wrap = document.createElement('div') this.shadowRoot.appendChild(wrap) const tmp = document.createElement('iframe') - tmp.sandbox = "allow-same-origin allow-scripts" - const url = new URL( - '/-/frame', location.href - ) - url.searchParams.set( - 'csp', - "default-src 'self' 'unsafe-inline' 'unsafe-eval'" - ) - url.searchParams.set('html', frameHtml) - tmp.src = url.href + 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 ) @@ -97,7 +100,7 @@ export class Page extends HTMLElement { this.frame = frames[frames.length - 1] this.textArea.addEventListener('blur', e => { const msg = ['srcdoc', e.target.value] -this.frame.contentWindow.postMessage(msg) +this.frame.contentWindow.postMessage(msg, '*') }) } From a5b043cd439ebace368f4c4e0f377803e48cb75c Mon Sep 17 00:00:00 2001 From: bat Date: Sun, 26 Mar 2023 05:09:21 +0000 Subject: [PATCH 7/9] toggle view and edit --- components/layout.js | 16 ++++++++++++++-- components/page.js | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/components/layout.js b/components/layout.js index 7a3baca..bd22996 100644 --- a/components/layout.js +++ b/components/layout.js @@ -3,8 +3,8 @@ export class Layout extends HTMLElement { super() this.attachShadow({mode: 'open'}) this.csp = "default-src 'self' 'unsafe-inline' 'unsafe-eval'" - this.editing = false this.header = document.createElement('m-header') + this.editing = false } connectedCallback() { @@ -24,7 +24,6 @@ export class Layout extends HTMLElement { this.header.editing = this.editing this.header.addEventListener('click-edit', () => { this.editing = !this.editing - this.header.editing = this.editing }) this.shadowRoot.appendChild(this.header) this.load() @@ -39,6 +38,7 @@ export class Layout extends HTMLElement { this.page = document.createElement('m-page') this.page.csp = this.csp this.page.path = path + this.editing = this.editing if (prevPage !== undefined) { prevPage.remove() } @@ -49,4 +49,16 @@ export class Layout extends HTMLElement { get path() { return '/hello' } + + set editing(value) { + this._editing = value + this.header.editing = this.editing + if (this.page) { + this.page.editing = this.editing + } + } + + get editing() { + return this._editing + } } \ No newline at end of file diff --git a/components/page.js b/components/page.js index 24d0741..2233ee9 100644 --- a/components/page.js +++ b/components/page.js @@ -36,6 +36,7 @@ export class Page extends HTMLElement { 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 @@ -72,9 +73,16 @@ export class Page extends HTMLElement { 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() { @@ -120,4 +128,22 @@ this.frame.contentWindow.postMessage(msg, '*') 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 + } } \ No newline at end of file From 5d3026ae6b583fd31503f657cdb280e0ced8b295 Mon Sep 17 00:00:00 2001 From: bat Date: Sun, 26 Mar 2023 06:21:18 +0000 Subject: [PATCH 8/9] Skip waiting, update service worker --- app.js | 5 +++-- sw.js | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index f0fbb32..4de2874 100644 --- a/app.js +++ b/app.js @@ -12,8 +12,9 @@ customElements.define('m-page-menu', PageMenu) class Setup { async run() { - navigator.serviceWorker - .addEventListener('controllerchange', () => { + const sw = navigator.serviceWorker + + sw.addEventListener('controllerchange', () => { if (this.registration.active) { window.location.reload(true) } diff --git a/sw.js b/sw.js index b8de43e..6c8bede 100644 --- a/sw.js +++ b/sw.js @@ -13,6 +13,7 @@ async function initCache() { } self.addEventListener("install", event => { + self.skipWaiting() event.waitUntil(initCache()) }) From b23f71a062d6f5bbbeff67c35db61170720d3938 Mon Sep 17 00:00:00 2001 From: bat Date: Sun, 26 Mar 2023 07:06:14 +0000 Subject: [PATCH 9/9] load initial page and display plain text --- components/page.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/components/page.js b/components/page.js index 2233ee9..a9bac7a 100644 --- a/components/page.js +++ b/components/page.js @@ -107,9 +107,22 @@ export class Page extends HTMLElement { const frames = wrap.getElementsByTagName('iframe') this.frame = frames[frames.length - 1] this.textArea.addEventListener('blur', e => { - const msg = ['srcdoc', e.target.value] -this.frame.contentWindow.postMessage(msg, '*') + 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) {