-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
134 lines (124 loc) · 5.34 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link href="favicon.ico" rel="icon"/>
<style>
.visible { visibility: visible; opacity: 1; transition: opacity .5s linear; }
.hidden { visibility: hidden; opacity: 0; transition: visibility 0s .5s, opacity .5s linear; }
</style>
</head>
<body class="hidden">
<div id="app" ref="app" v-html="html"></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script type="text/javascript">
// This is used for deep linking of Single Page Apps when hosted with GitHub Pages
(function(l) {
if (l.search[1] === '/' ) {
let decoded = l.search.slice(1).split('&').map(s => s.replace(/~and~/g, '&')).join('?')
window.history.replaceState(null, null, l.pathname.slice(0, -1) + decoded + l.hash)
}
}(window.location))
</script>
<script type="module">
new Vue({
el: '#app',
data: () => ({
acct: '',
repo: '',
ref: '',
path: '',
html: null
}),
computed: {
isGhp() { return location.host.indexOf('github.io') > 0 },
pathElems() { return location.pathname.split('/').filter(elem => elem) },
prefix() { return [this.acct, this.repo].filter(elem => elem).join('/') },
base() { return this.pathElems.length > 0 ? `/${this.pathElems.join('/')}/` : '/' },
baseQarg() {
let basePathElems = this.base.split('/').filter(elem => elem).slice(this.isGhp ? 1 : 0)
return basePathElems.length > 0 ? `/${basePathElems.join('/')}/` : '/'
}
},
async mounted() {
let config = await this.loadConfig()
this.acct = config.acct || (this.isGhp ? location.host.split('.')[0] : 'jstor-labs')
this.repo = config.repo || (this.isGhp ? this.pathElems[0] : 'juncture-webapp')
this.ref = config.ref
this.path = this.pathElems.slice(this.isGhp ? 1 : 0).join('/')
document.head.querySelector('base').href = this.base
document.head.querySelector('link[rel="icon"]').href = `${this.base}favicon.ico`
console.log(`acct=${this.acct} repo=${this.repo} ref=${this.ref} path=${this.path} prefix=${this.prefix} base=${this.base} isGhp=${this.isGhp}`)
this.loadPage()
},
methods: {
async loadConfig() {
let resp = await fetch(this.isGhp ? `${this.pathElems[0]}/config.yaml` : 'config.yaml')
if (resp.ok) {
let lines = (await resp.text()).split('\n').filter(ln => ln.trim())
if (lines.length > 0 && lines[0] !== '<!DOCTYPE html>')
return Object.fromEntries(lines.map(rec => rec.split(':').map(part => part.trim())))
}
return {}
},
loadPage() {
let url = `https://api.juncture-digital.org/html/${this.prefix}/${this.path}${this.path === '' ? '' : '/'}?base=${this.baseQarg}&prefix=${this.prefix}&inline=true`
if (this.ref) url += `&ref=${this.ref}`
if (this.isGhp) url += '&ghp=true'
fetch(url).then(resp => resp.text())
.then(html => {
let [head, body] = new DOMParser().parseFromString(html, 'text/html').children[0].children
let titleEl = head.querySelector('title')
if (titleEl) document.querySelector('title').innerText = titleEl.innerText
this.html = (body.querySelector(':scope > #app') || body).innerHTML
// Loads stylesheets and scripts used by loaded page
this.loadDependencies(head.querySelectorAll('link[rel="stylesheet"]'), 0, () => this.loadDependencies(head.querySelectorAll('style'), 0))
this.loadDependencies(body.querySelectorAll('script'), 0)
})
},
loadDependencies(dependencies, i, callback) {
if (dependencies.length > 0) {
this.load(dependencies.item(i), () => {
if (i < dependencies.length-1) this.loadDependencies(dependencies, i+1, callback)
else if (callback) callback()
})
}
},
load(srcEl, callback) {
let e
if (srcEl.localName === 'link') {
e = document.createElement('link')
e.href = srcEl.href
e.rel = srcEl.rel
e.type = 'text/css'
e.addEventListener('load', callback)
document.getElementsByTagName('head')[0].appendChild(e)
} else if (srcEl.localName === 'style') {
e = document.createElement('style')
e.textContent = srcEl.textContent
e.addEventListener('load', callback)
document.getElementsByTagName('head')[0].appendChild(e)
} else if (srcEl.localName === 'script') {
e = document.createElement('script')
if (srcEl.src) {
e.src = srcEl.src
if (srcEl.type) e.type = srcEl.type
e.addEventListener('load', callback)
} else {
if (srcEl.type) e.type = srcEl.type
e.text = srcEl.text
}
document.getElementsByTagName('body')[0].appendChild(e)
if (!e.src) callback()
}
}
}
})
Vue.config.productionTip = false
</script>
</body>
</html>