@@ -1,152 +1,136 @@
11'use strict'
2−function 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'
13−
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>.`
17−
18− this.loc = ''
19− this.byName = {}
20− this.byCat = {}
21− this.byAuthor = {}
22−
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− }
31−
32− this.start = () => {
33− this._entry.innerHTML = 'Loading..'
34− }
352
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− }
3+const Wiki = sites => {
4+ const entries = {}
5+ const main = document.getElementById('main')
6+ const aside = document.getElementById('aside')
7+ const authors = new Set()
8+ const terms = new Set()
9+
10+ const storeEntry = (author, url, parent, cat, entry) => {
11+ if (!(parent in entries)) {
12+ entries[parent] = {}
13+ entries[parent][cat] = [{ entry, author, url }]
14+ } else if (!(cat in entries[parent])) {
15+ entries[parent][cat] = [{ entry, author, url }]
5016 } 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.`
17+ entries[parent][cat].push({ entry, author, url })
5218 }
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− )
19+ terms.add(cat)
20+ authors.add(author)
6421 }
6522
66− this.go = () => {
67− this.loc = decodeURIComponent( window.location.hash.substr(1) ).toUpperCase()
68− this.refresh()
23+ const storeEntries = (author, url, parent, entries) => {
24+ Array.isArray(entries)
25+ ? storeEntry(author, url, parent, 'list-definitions', entries)
26+ : Object.keys(entries).forEach(entry => storeEntry(author, url, parent, entry, entries[entry]))
6927 }
7028
71− this.at = (q = '') => {
72− return this.loc.toUpperCase() === q.toUpperCase() || (this.cat(this.loc).toUpperCase() === q.toUpperCase() && q !== '')
29+ const transform = (author, url, ndtl) => {
30+ Object.keys(ndtl).forEach(cat => storeEntries(author, url, cat, ndtl[cat]))
7331 }
7432
75− this.random = () => {
76− const keys = Object.keys(this.byCat)
77− return keys[Math.floor(Math.random() * keys.length)]
33+ const parse = (site, content) => {
34+ console.log('Wiki', 'Parsing ' + site.wiki)
35+ const ndtl = indental(content)
36+ transform(site.author, site.url, ndtl)
7837 }
7938
80− this.related = (name) => {
81− return this.byCat[this.cat(name)]
82− }
39+ const selected = (key, category) => key.toLowerCase() === category.toLowerCase()
40+ ? 'selected'
41+ : ''
8342
84− this.cat = (name) => {
85− return this.byName[name] ? this.byName[name][0].cat : ''
43+ const formatSideBarCat = (key, entries) => (currentHtml, cat) => {
44+ const catLength = Object.keys(entries[cat]).length
45+ const newHtml = `<li class='${selected(key, cat)}'}'>
46+ <a href='#${cat}' data-msgs='${catLength}'>${cat.toLowerCase()}</a>
47+ </li>`
48+ return `${currentHtml}${newHtml}`
8649 }
8750
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− }
51+ const formatEntry = (definitions, definition) => {
52+ const formatEntryList = entryList => entryList.reduce((items, item) => `${items}<li>${item}</li>`, '')
9853
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− }
54+ const newHtml = typeof definition.entry === 'string'
55+ ? `<li>${definition.entry}
56+ <a class='author' target='_blank' href='${definition.url}'> - @${definition.author}</a>
57+ </li>`
58+ : `<li><ul>${formatEntryList(definition.entry)}
59+ <a class='author' target='_blank' href='${definition.url}'> — @${definition.author}</a>
60+ </ul></li>`
10861
109− const htmlEntries = entries.reduce(formatEntry, '')
110− return `<ul class="term"><li class="name"><strong>${name.toLowerCase()}</strong></li>${htmlEntries}</ul>`
62+ return `${definitions}${newHtml}`
11163 }
11264
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− }
65+ const formatEntries = entries => (currentHtml, category) => {
66+ const definitions = entries[category].reduce(formatEntry, '')
67+ const title = category === 'list-definitions'
68+ ? ''
69+ : `<li class='name'><strong>${category.toLowerCase()}</strong></li>`
70+ return `${currentHtml}<ul class='term'>${title}${definitions}</ul>`
12471 }
12572
126− this.add = (name, value, cat, origin) => {
127− this.byName[name] = this.byName[name] || []
128− this.byName[name].push({ name, value, cat, origin })
73+ const findRelated = key => {
74+ const searchOtherCategories = entry => {
75+ return Object.keys(entries)
76+ .filter(category => Object.keys(entries[category]).some(otherCat => otherCat === entry))
77+ }
12978
130− this.byCat[cat] = this.byCat[cat] || []
131− this.byCat[cat].push({ name, value, cat, origin })
79+ const foundMatches = Object.keys(entries[key])
80+ .filter(entry => entry !== 'list-definitions')
81+ .map(searchOtherCategories)
82+ const flattened = [].concat.apply([], foundMatches)
83+ return Array.from(new Set(flattened)).filter(related => related !== key)
84+ }
13285
133− this.byAuthor[origin.author] = this.byAuthor[origin.author] || []
134− this.byAuthor[origin.author].push({ name, value, cat, origin })
86+ const formatRelated = (allRelated, relatedCategory) => {
87+ const newHtml = `<span><a href='#${relatedCategory}'>/${relatedCategory.toLowerCase()}</a> </span>`
88+ return `${allRelated}${newHtml}`
13589 }
13690
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− }
91+ const refresh = key => {
92+ if (key) {
93+ if (entries[key] === undefined) return
94+ const items = Object.keys(entries[key]).sort().reduce(formatEntries(entries[key]), '')
95+ const relatedCategories = findRelated(key)
96+ const relatedHtml = relatedCategories.length > 0
97+ ? relatedCategories.reduce(formatRelated, 'Related categories: ')
98+ : ''
99+ main.innerHTML = `<div class='related'><h1>${key}</h1>${relatedHtml}</div>${items}`
100+ } else {
101+ const html = `Click a /topic to get started, or try a random page`
102+ const stats = `The wiki contains ${terms.size} terms in ${Object.keys(entries).length} categories, from ${authors.size} authors.`
103+ main.innerHTML = `${html}<br />${stats}`
145104 }
105+
106+ const homeCat = `<li class='${selected(key, '')}'>
107+ <a href='#' data-msgs='${terms.size}'>home</a>
108+ </li>`
109+ const cats = Object.keys(entries).sort().reduce(formatSideBarCat(key, entries), homeCat)
110+ aside.innerHTML = `<ul>${cats}</ul>`
146111 }
147112
148− String.prototype.toUrl = function () { return this.toLowerCase().replace(/ /g, '+').replace(/[^0-9a-z+:\-./~]/gi, '').trim() }
113+ return {
114+ initialize: hash => {
115+ console.log('Wiki', 'Fetching...')
116+ sites.filter(site => site.wiki && site.author).forEach(site => {
117+ fetch(site.wiki, { cache: 'no-store' }).then(x => x.text()).then(content => {
118+ parse(site, content)
119+ refresh(stripHash(hash))
120+ }).catch((err) => {
121+ console.warn(`${site.wiki}`, err)
122+ })
123+ })
124+ },
125+ refresh: key => { refresh(key) }
126+ }
127+}
149128
150− window.onload = this.go
151− window.onhashchange = this.go
129+const stripHash = hash => {
130+ const decoded = decodeURIComponent(hash)
131+ return decoded.charAt(0) === '#' ? decoded.substring(1) : decoded
152132}
133+
134+window.addEventListener('hashchange', () => {
135+ wiki.refresh(stripHash(window.location.hash).toUpperCase())
136+})