on 4e65a7d help sign in
webring/scripts/wiki.js
176 lines 6.5 kB history raw
1'use strict'
3const Wiki = sites => {
4 const entries = {}
5 const main = document.getElementById('main')
6 const aside = document.getElementById('aside')
7 const progress = document.getElementById('progress')
8 const progressCounter = Progress(sites.map(site => site.wiki))
9 const authors = new Set()
10 const terms = new Set()
12 const encodeUrl = text => text.replace(/ /g, '+').trim().toLowerCase()
14 const cleanEntry = entry => {
15 const temp = document.createElement('div')
16 temp.textContent = entry
17 return temp.innerHTML
18 }
20 const storeEntry = (author, url, category, term, unCleanEntry) => {
21 const entry = Array.isArray(unCleanEntry)
22 ? unCleanEntry.map(x => cleanEntry(x))
23 : cleanEntry(unCleanEntry)
25 if (!(category in entries)) {
26 entries[category] = {}
27 entries[category][term] = [{ entry, authors: [{ author, url }] }]
28 } else if (!(term in entries[category])) {
29 entries[category][term] = [{ entry, authors: [{ author, url }] }]
30 } else {
31 const possibleIndex = entries[category][term].findIndex(existing => existing.entry === entry)
32 possibleIndex > -1
33 ? entries[category][term][possibleIndex].authors.push({ author, url })
34 : entries[category][term].push({ entry, authors: [{ author, url }] })
35 }
36 terms.add(term)
37 authors.add(author)
38 }
40 const storeEntries = (author, url, category, entries) => {
41 Array.isArray(entries)
42 ? storeEntry(author, url, category, `${category}-list-definition`, entries)
43 : Object.keys(entries).forEach(entry => storeEntry(author, url, category, entry, entries[entry]))
44 }
46 const transform = (author, url, ndtl) => {
47 Object.keys(ndtl).forEach(category => storeEntries(author, url, category, ndtl[category]))
48 }
50 const parse = (site, content) => {
51 console.log('Wiki', 'Parsing ' + site.wiki)
52 const ndtl = indental(content)
53 transform(site.author, site.url, ndtl)
54 }
56 const selected = (key, category) => key.toLowerCase() === category.toLowerCase()
57 ? 'selected'
58 : ''
60 const randomTerm = () => {
61 const keys = Object.keys(entries)
62 return keys[Math.floor(Math.random() * keys.length)]
63 }
65 const formatSideBarCat = (key, entries) => (currentHtml, cat) => {
66 const catLength = Object.keys(entries[cat]).length
67 const newHtml = `<li class='${selected(key, cat)}'}'>
68 <a href='#${encodeUrl(cat)}' data-msgs='${catLength}'>${cat.toLowerCase()}</a>
69 </li>`
70 return `${currentHtml}${newHtml}`
71 }
73 const formatLinks = text => text.replace(
74 /\b((https?|dat):\/\/[^"\s]+)/g,
75 '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>'
76 )
78 const formatAuthor = (authors, author) => {
79 return `${authors}<a class='author' target='_blank' href='${author.url}'> @${author.author}</a>`
80 }
82 const formatEntry = (definitions, definition) => {
83 const formatEntryList = entryList => entryList.reduce((items, item) => `${items}<li>${formatLinks(item)}</li>`, '')
85 const newHtml = typeof definition.entry === 'string'
86 ? `<li>${formatLinks(definition.entry)}
87 - ${definition.authors.reduce(formatAuthor, '')}
88 </li>`
89 : `<li><ul>${formatEntryList(definition.entry)}
90 - ${definition.authors.reduce(formatAuthor, '')}
91 </ul></li>`
93 return `${definitions}${newHtml}`
94 }
96 const formatEntries = entries => (currentHtml, category) => {
97 const definitions = entries[category].reduce(formatEntry, '')
98 const title = category.endsWith('list-definition')
99 ? ''
100 : `<li class='name'><strong>${category.toLowerCase()}</strong></li>`
101 return `${currentHtml}<ul class='term'>${title}${definitions}</ul>`
102 }
104 const findRelated = key => {
105 const searchOtherCategories = entry => {
106 return Object.keys(entries)
107 .filter(category => Object.keys(entries[category]).some(otherCat => otherCat === entry))
108 }
110 const foundMatches = Object.keys(entries[key])
111 .filter(entry => !entry.endsWith('list-definition'))
112 .map(searchOtherCategories)
113 const flattened = [].concat.apply([], foundMatches)
114 return Array.from(new Set(flattened)).filter(related => related !== key)
115 }
117 const formatRelated = (allRelated, relatedCategory) => {
118 const newHtml = `<span><a href='#${encodeUrl(relatedCategory)}'>/${relatedCategory.toLowerCase()}</a> </span>`
119 return `${allRelated}${newHtml}`
120 }
122 const refresh = key => {
123 if (key) {
124 if (entries[key] === undefined) return
125 const items = Object.keys(entries[key]).sort().reduce(formatEntries(entries[key]), '')
126 const relatedCategories = findRelated(key)
127 const relatedHtml = relatedCategories.length > 0
128 ? relatedCategories.reduce(formatRelated, 'Related categories: ')
129 : ''
130 main.innerHTML = `<div class='related'><h1>${key}</h1>${relatedHtml}</div>${items}`
131 } else {
132 const html = `Click a /topic to get started, or try a <a href='#${randomTerm()}'>random page</a>`
133 const stats = `The wiki contains ${terms.size} terms in ${Object.keys(entries).length} categories, from ${authors.size} authors.`
134 main.innerHTML = `<h1>WIKI</h1>${html}<br />${stats}`
135 }
137 const homeCat = `<li class='${selected(key, '')}'>
138 <a href='#' data-msgs='${terms.size}'>home</a>
139 </li>`
140 const cats = Object.keys(entries).sort().reduce(formatSideBarCat(key, entries), homeCat)
141 aside.innerHTML = `<ul>${cats}</ul>`
142 }
144 const refreshProgress = () => {
145 progress.innerHTML = `${progressCounter}`
146 }
148 return {
149 initialize: hash => {
150 console.log('Wiki', 'Fetching...')
151 refreshProgress()
152 sites.filter(site => site.wiki && site.author).forEach(site => {
153 fetch(site.wiki, { cache: 'no-store' }).then(x => x.text()).then(content => {
154 parse(site, content)
155 refresh(stripHash(hash))
156 progressCounter.success(site.wiki).then(refreshProgress)
157 }).catch((err) => {
158 console.warn(`${site.wiki}`, err)
159 progressCounter.failure(site.wiki).then(refreshProgress)
160 })
161 })
162 },
163 refresh: key => { refresh(key) }
164 }
167const decodeUrl = text => text.replace(/\+/g, ' ').trim().toUpperCase()
169const stripHash = hash => {
170 const decoded = decodeURIComponent(hash)
171 return decoded.charAt(0) === '#' ? decodeUrl(decoded.substring(1)) : decoded
174window.addEventListener('hashchange', () => {
175 wiki.refresh(stripHash(window.location.hash))
176})