on f181ace help sign in
webring/scripts/wiki.js
152 lines 5.5 kB history raw
1'use strict'
2function Wiki (sites) {
3 const feeds = {}
4 this.sites = sites
5 this._el = document.createElement('div')
6 this._el.id = 'wiki'
7 this._sidebar = document.createElement('div')
8 this._sidebar.id = 'sidebar'
9 this._categories = document.createElement('ul')
10 this._categories.id = 'categories'
11 this._entry = document.createElement('div')
12 this._entry.id = 'entry'
14 this._footer = document.createElement('p')
15 this._footer.id = 'footer'
16 this._footer.innerHTML = `The <strong>Wiki</strong> is a decentralized encyclopedia, to join the conversation, add a <a href="https://github.com/XXIIVV/webring#joining-the-wiki">wiki:</a> field to your entry in the <a href="https://github.com/XXIIVV/Webring/">webring</a>.`
18 this.loc = ''
19 this.byName = {}
20 this.byCat = {}
21 this.byAuthor = {}
23 this.install = (host) => {
24 this._el.appendChild(this._entry)
25 this._sidebar.appendChild(this._categories)
26 this._el.appendChild(this._sidebar)
27 this._el.appendChild(this._footer)
28 host.appendChild(this._el)
29 this.fetch()
30 }
32 this.start = () => {
33 this._entry.innerHTML = 'Loading..'
34 }
36 this.refresh = () => {
37 // Main
38 if (this.loc) {
39 if (this.byCat[this.loc]) {
40 const formatHtml = (entries, entry) => `${entries} ${this.templateTerm(entry.name, this.byName[entry.name])}<br />`
41 const html = this.byCat[this.loc].reduce(formatHtml, '')
42 this._entry.innerHTML = html
43 } else if (this.byName[this.loc]) {
44 const html = `${this.templateTerm(this.loc, this.byName[this.loc])}<br />${this.templateRelated(this.loc)}`
45 this._entry.innerHTML = html
46 } else {
47 const html = `Unknown: ${this.loc}. Return <a href='#'>home</a>, or try a <a href='${this.random()}'>random page</a>.`
48 this._entry.innerHTML = html
49 }
50 } else {
51 this._entry.innerHTML = `Click a /topic to get started, or try a <a href='#${this.random()}'>random page</a>.<br />The wiki contains ${Object.keys(this.byName).length} terms, in ${Object.keys(this.byCat).length} categories, by ${Object.keys(this.byAuthor).length} authors.`
52 }
53 // Sidebar
54 this._categories.innerHTML = Object.keys(this.byCat).reduce((acc, id) =>
55 { return this.byCat[id].length > 5
56 ? `${acc}<li ${wiki.at(id) ? 'class="selected"' : ''}>
57 <a href='#${id}' data-msgs='${this.byCat[id].length}'>${id}</a>
58 </li>`
59 : acc
60 } , `<li ${wiki.at() ? 'class="selected"' : ''}>
61 <a href='#' data-msgs='${Object.keys(this.byName).length}'>home</a>
62 </li>`
63 )
64 }
66 this.go = () => {
67 this.loc = decodeURIComponent( window.location.hash.substr(1) ).toUpperCase()
68 this.refresh()
69 }
71 this.at = (q = '') => {
72 return this.loc.toUpperCase() === q.toUpperCase() || (this.cat(this.loc).toUpperCase() === q.toUpperCase() && q !== '')
73 }
75 this.random = () => {
76 const keys = Object.keys(this.byCat)
77 return keys[Math.floor(Math.random() * keys.length)]
78 }
80 this.related = (name) => {
81 return this.byCat[this.cat(name)]
82 }
84 this.cat = (name) => {
85 return this.byName[name] ? this.byName[name][0].cat : ''
86 }
88 this.templateRelated = (name) => {
89 const relatedWords = this.related(name)
90 const html = `<ul class='col3'>${relatedWords.reduce( (acc, item, id) =>
91 { return `${acc}<li ${wiki.at(item.name) ? 'class="selected"' : ''}>
92 <a href='#${item.name.toLowerCase()}'>${item.name.toLowerCase()}</a>
93 </li>`
94 }, '') }
95 </ul>`
96 return html
97 }
99 this.templateTerm = (name, entries) => {
100 const formatEntry = (acc, entry) => {
101 if (typeof entry.value === 'string') {
102 return `${acc}<li>${entry.value}<a class='author' target='_blank' href='${entry.origin.url}'> — @${entry.origin.author}</a></li>`
103 } else {
104 const listItems = entry.value.reduce((items, item) => `${items}<li>${item}</li>`, '')
105 return `${acc}<li><ul>${listItems}<a class='author' target='_blank' href='${entry.origin.url}'> — @${entry.origin.author}</a></ul></li>`
106 }
107 }
109 const htmlEntries = entries.reduce(formatEntry, '')
110 return `<ul class="term"><li class="name"><strong>${name.toLowerCase()}</strong></li>${htmlEntries}</ul>`
111 }
113 this.fetch = () => {
114 console.log('Wiki', 'Fetching..')
115 for (const site of sites) {
116 if (!site.wiki || !site.author) { continue }
117 fetch(site.wiki, { cache: 'no-store' }).then(x => x.text()).then((content) => {
118 this.parse(site, content)
119 this.refresh(feeds)
120 }).catch((err) => {
121 console.warn(`${site.wiki}`, err)
122 })
123 }
124 }
126 this.add = (name, value, cat, origin) => {
127 this.byName[name] = this.byName[name] || []
128 this.byName[name].push({ name, value, cat, origin })
130 this.byCat[cat] = this.byCat[cat] || []
131 this.byCat[cat].push({ name, value, cat, origin })
133 this.byAuthor[origin.author] = this.byAuthor[origin.author] || []
134 this.byAuthor[origin.author].push({ name, value, cat, origin })
135 }
137 this.parse = (site, content) => {
138 console.log('Wiki', 'Parsing ' + site.wiki)
139 const cats = indental(content)
140 for (const cat in cats) {
141 const terms = cats[cat]
142 for (const name in terms) {
143 this.add(name, terms[name], cat, site)
144 }
145 }
146 }
148 String.prototype.toUrl = function () { return this.toLowerCase().replace(/ /g, '+').replace(/[^0-9a-z+:\-./~]/gi, '').trim() }
150 window.onload = this.go
151 window.onhashchange = this.go