|
|
| Line 1: |
Line 1: |
| import MarkdownIt from './markdown-it.js'
| |
|
| |
|
| var self = new Hyperdrive(location)
| |
| var pathname = location.pathname.endsWith('/') ? location.pathname + 'index.md' : location.pathname
| |
| var isEditing = location.search === '?edit'
| |
|
| |
| function h (tag, attrs, ...children) {
| |
| var el = document.createElement(tag)
| |
| for (let k in attrs) {
| |
| if (k === 'cls') el.className = attrs[k]
| |
| else el.setAttribute(k, attrs[k])
| |
| }
| |
| for (let child of children) el.append(child)
| |
| return el
| |
| }
| |
|
| |
| async function ensureParentDir (p) {
| |
| let parts = p.split('/').slice(0, -1)
| |
| let acc = []
| |
| for (let part of parts) {
| |
| acc.push(part)
| |
| await self.mkdir(acc.join('/')).catch(e => undefined)
| |
| }
| |
| }
| |
|
| |
| customElements.define('wiki-header', class extends HTMLElement {
| |
| constructor () {
| |
| super()
| |
| this.load()
| |
| }
| |
|
| |
| async load () {
| |
| this.info = await self.getInfo()
| |
| this.render()
| |
| }
| |
|
| |
| render () {
| |
| this.append(h('h1', {}, h('a', {href: '/'}, this.info.title)))
| |
| if (this.info.description) {
| |
| this.append(h('p', {}, this.info.description))
| |
| }
| |
|
| |
| if (this.info.writable) {
| |
| let buttons = []
| |
|
| |
| if (!isEditing) {
| |
| let newPage = h('button', {}, 'New Page')
| |
| newPage.addEventListener('click', async (e) => {
| |
| var newPathname = prompt('Enter the path of the new page')
| |
| if (!newPathname) return
| |
| if (!newPathname.endsWith('.md')) newPathname += '.md'
| |
| await ensureParentDir(newPathname)
| |
| if ((await self.stat(newPathname).catch(e => undefined)) === undefined) {
| |
| await self.writeFile(newPathname, `# ${newPathname}`)
| |
| }
| |
| location = newPathname + '?edit'
| |
| })
| |
| buttons.push(newPage)
| |
|
| |
| if (/\.(png|jpe?g|gif|mp4|mp3|ogg|webm|mov)$/.test(pathname) === false) {
| |
| let editPage = h('button', {}, 'Edit Page')
| |
| editPage.addEventListener('click', async (e) => {
| |
| location.search = '?edit'
| |
| })
| |
| buttons.push(editPage)
| |
| }
| |
| } else {
| |
| let savePage = h('button', {cls: 'primary'}, 'Save Page')
| |
| savePage.addEventListener('click', async (e) => {
| |
| let value = document.body.querySelector('textarea.editor').value
| |
| await self.writeFile(pathname, value)
| |
| location.search = ''
| |
| })
| |
| buttons.push(savePage)
| |
| }
| |
|
| |
| let deletePage = h('button', {}, 'Delete Page')
| |
| deletePage.addEventListener('click', async (e) => {
| |
| if (!confirm('Delete this page?')) return
| |
| await self.unlink(pathname)
| |
| if (isEditing) location.search = ''
| |
| else location.reload()
| |
| })
| |
| buttons.push(deletePage)
| |
|
| |
| let editProps = h('button', {}, 'Edit Drive Properties')
| |
| editProps.addEventListener('click', async (e) => {
| |
| await navigator.drivePropertiesDialog(self.url)
| |
| if (!isEditing) location.reload()
| |
| })
| |
| buttons.push(editProps)
| |
|
| |
| this.append(h('div', {cls: 'admin'}, ...buttons))
| |
| }
| |
| }
| |
| })
| |
|
| |
| customElements.define('wiki-nav', class extends HTMLElement {
| |
| constructor () {
| |
| super()
| |
| this.load()
| |
| }
| |
|
| |
| async load () {
| |
| this.files = await self.readdir('/', {recursive: true})
| |
| this.files = this.files.filter(file => file.endsWith('.md'))
| |
| this.files.sort()
| |
| this.render()
| |
| }
| |
|
| |
| render () {
| |
| for (let file of this.files) {
| |
| let href = `/${file}`
| |
| let cls = pathname === href ? 'active' : ''
| |
| this.append(h('a', {href, cls}, file.slice(0, -3)))
| |
| }
| |
| if (this.files.length === 0) {
| |
| this.append(h('div', {cls: 'empty'}, 'This Wiki has no pages'))
| |
| }
| |
| }
| |
| })
| |
|
| |
| customElements.define('wiki-page', class extends HTMLElement {
| |
| constructor () {
| |
| super()
| |
| this.render()
| |
| }
| |
|
| |
| async render () {
| |
| // check existence
| |
| let stat = await self.stat(pathname).catch(e => undefined)
| |
| if (!stat) {
| |
| // 404
| |
| let canEdit = (await self.getInfo()).writable
| |
| if (canEdit) {
| |
| let btn = h('button', {}, 'Create Page')
| |
| btn.addEventListener('click', async (e) => {
| |
| await ensureParentDir(pathname)
| |
| await self.writeFile(pathname, `# ${pathname}`)
| |
| location.search = '?edit'
| |
| })
| |
| this.append(h('div', {cls: 'empty'}, h('h2', {}, 'This Page Does Not Exist'), btn))
| |
| } else {
| |
| this.append(h('div', {cls: 'empty'}, h('h2', {}, 'This Page Does Not Exist')))
| |
| }
| |
| return
| |
| }
| |
|
| |
| // embed content
| |
| if (/\.(png|jpe?g|gif)$/i.test(pathname)) {
| |
| this.append(h('img', {src: pathname}))
| |
| } else if (/\.(mp4|webm|mov)/i.test(pathname)) {
| |
| this.append(h('video', {controls: true}, h('source', {src: pathname})))
| |
| } else if (/\.(mp3|ogg)/i.test(pathname)) {
| |
| this.append(h('audio', {controls: true}, h('source', {src: pathname})))
| |
| } else {
| |
| let content = await self.readFile(pathname)
| |
| if (isEditing) {
| |
| // render editor
| |
| let textarea = h('textarea', {cls: 'editor'}, content)
| |
| this.append(textarea)
| |
| } else {
| |
| // render content
| |
| if (/\.(md|html)$/i.test(pathname)) {
| |
| if (pathname.endsWith('.md')) {
| |
| let md = new MarkdownIt()
| |
| content = md.render(content)
| |
| }
| |
| let contentEl = h('div', {cls: 'content'})
| |
| contentEl.innerHTML = content
| |
| this.append(contentEl)
| |
| } else {
| |
| this.append(h('pre', {cls: 'content'}, content))
| |
| }
| |
| }
| |
| }
| |
| }
| |
| })
| |