on master help sign in
Closed Hallway/Wiki
commit50afc43c236e5c81ed042df34aed09caa8a31ba0 authorneauoire <aliceffekt@gmail.com> dateTue 19 May 2020, 19:43 (6 years ago) parenta4a4d64
11 files changed−1640
hallway.html−20 scripts/hallway.js−232 scripts/indental.js−57 scripts/opml.js−13 scripts/portal.js−121 scripts/progress.js−41 scripts/sites.js−924 scripts/wiki.js−176 tools/extract_twtxt.js−22 tools/twtxt.json−1 wiki.html−33
hallway.htmldeleted−20
@@ -1,20 +0,0 @@
1−<!DOCTYPE html>
2−<html lang="en">
3−<head>
4− <meta charset="utf-8">
5− <meta name="viewport" content="width=device-width, initial-scale=1.0">
6− <link rel="stylesheet" type="text/css" href="links/fonts.css"/>
7− <link rel="stylesheet" type="text/css" href="links/main.css"/>
8− <script type="text/javascript">const module = { exports:{} }</script>
9− <script src="scripts/hallway.js"></script>
10− <script src="scripts/sites.js"></script>
11− <title>Hallway</title>
12−</head>
13−<body>
14− <script>
15− const hallway = new Hallway(sites);
16− hallway.install(document.body);
17− hallway.start();
18− </script>
19−</body>
20−</html>
scripts/hallway.jsdeleted−232
@@ -1,232 +0,0 @@
1−'use strict'
2−
3−const reChannel = /(\s|^)\/([a-zA-Z0-9]+)/g
4−const reUser = /((\s|^)@&lt;[a-zA-Z0-9./:\-_+~#= ]*&gt;)/g
5−const reTag = /(^|\s)(#[a-z\d-]+)/ig
6−const reUrl = /((https?):\/\/(([-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b)([-a-zA-Z0-9@:%_+.~#?&//=]*)))/g
7−
8−function Hallway (sites) {
9− const feeds = {}
10− this.sites = sites
11− this._el = document.createElement('div')
12− this._el.id = 'hallway'
13− const filterAndPage = window.location.hash.split('^')
14− this.finder = { filter: stripHash(filterAndPage[0]), page: filterAndPage[1] || 1 }
15− this.cache = null
16−
17− this._main = document.createElement('main')
18− this._main.id = 'entries'
19− this._footer = document.createElement('footer')
20− this._footer.id = 'footer'
21− this.readme = '<p>The <strong>Hallway</strong> is a decentralized forum, to join the conversation, add a <a href="https://github.com/XXIIVV/webring#joining-the-hallway">feed:</a> field to your entry in the <a href="https://github.com/XXIIVV/Webring/">webring</a>.</p>'
22− this.nav = '<p class="buttons"><a href="https://github.com/XXIIVV/webring">Information</a> | <a href="index.html">The Portal</a> | <a href="wiki.html">The Wiki</a> | <a id="opml">OPML</a></p>'
23− this._footer.innerHTML = `${this.readme}${this.nav}`
24−
25− this.install = function (host) {
26− this._el.appendChild(this._main)
27− this._el.appendChild(this._footer)
28− host.appendChild(this._el)
29− this.findFeeds()
30− }
31−
32− this.start = function () {
33− this._main.innerHTML = 'Loading..'
34− this.fetchFeeds()
35− }
36−
37− this.refresh = function (feeds = this.cache) {
38− const entries = this.findEntries(feeds)
39− const localEntries = entries.filter(entry => this.filterExternals(entry))
40− const channels = this.find(localEntries, 'channel')
41− const users = this.find(localEntries, 'author')
42− const tags = this.findTags(localEntries)
43− const relevantEntries = localEntries
44− .filter(entry => !this.finder.filter || (entry.author === this.finder.filter || entry.channel === this.finder.filter || entry.tags.includes(this.finder.filter) || entry.body.includes(this.finder.filter)))
45−
46− const mainHtml = `
47− <div><ul>
48− ${relevantEntries.filter((_, id) => id < Number(this.finder.page) * 20 && id >= (Number(this.finder.page) - 1) * 20).reduce((acc, val) => acc + this.templateEntry(val) + '\n', '')}
49− </ul>
50− <div id='pagination'>
51− ${[...Array(Math.ceil(relevantEntries.length / 20)).keys()].reduce((acc, num) => `${acc}<span class='${Number(hallway.finder.page) === num + 1 ? 'selected' : ''}' onclick='filter("${this.finder.filter}^${num + 1}")'>${num + 1}</span>`, '')}
52− </div>
53− <a id='showbar' onclick="toggleVisibility('aside');"></a></div>`
54−
55− const aside = `
56− <aside id='aside'>
57− <a id='hidebar' onclick="toggleVisibility('aside');"></a>
58− <ul id='channels'>
59− <li onclick='filter("")' class='${hallway.finder.filter === '' ? 'selected' : ''}'><a href='#'>hallway <span class='right'>${localEntries.length}</span></a></li>
60− ${Object.keys(channels).slice(0, 15).reduce((acc, val) => acc + `<li onclick='filter("${val}")' class='${hallway.finder.filter === val ? 'selected' : ''}'><a href='#${val}'>${val} <span class='right'>${channels[val]}</span></a></li>\n`, '')}
61− </ul>
62− <ul id='users'>
63− ${Object.keys(users).slice(0, 15).reduce((acc, val) => acc + `<li onclick='filter("${val}")' class='${hallway.finder.filter === val ? 'selected' : ''}' href='#${val}'>${val} <span class='right'>${users[val]}</span></li>\n`, '')}
64− </ul>
65− <ul id='tags'>
66− ${Object.keys(tags).slice(0, 15).reduce((acc, val) => acc + `<li onclick='filter("${val}")' class='${hallway.finder.filter === val ? 'selected' : ''}' href='#${val}'>#${val} <span class='right'>${tags[val]}</span></li>\n`, '')}
67− </ul>
68− </aside>`
69−
70− this._main.innerHTML = `${mainHtml}${aside}`
71−
72− if (feeds) {
73− this.cache = feeds
74− }
75− }
76−
77− // Entries
78−
79− this.filterExternals = function (entry) {
80− const matches = entry.body.match(reUser)
81− const locals = Object.keys(feeds)
82− return matches ? matches.some(match => locals.some(local => match.indexOf(feeds[local].path) !== -1)) : true
83− }
84−
85− this.find = function (entries, key) {
86− const h = {}
87− for (const entry of entries) {
88− if (entry && entry[key]) {
89− h[entry[key]] = h[entry[key]] ? h[entry[key]] + 1 : 1
90− }
91− }
92− return h
93− }
94−
95− this.findTags = function (entries) {
96− const tags = {}
97− for (const entry of entries) {
98− entry.tags.map(tag => {
99− tags[tag] = tags[tag] ? tags[tag] + 1 : 1
100− })
101− }
102− return tags
103− }
104−
105− this.findEntries = function (feeds) {
106− const a = []
107− for (const id in feeds) {
108− for (const i in feeds[id].content) {
109− a.push(feeds[id].content[i])
110− }
111− }
112− return a.sort((a, b) => a.offset - b.offset)
113− }
114−
115− this.findMention = function (found) {
116− const mention = Object.keys(feeds).filter(user => found.indexOf(feeds[user].path) > -1)
117− return ` <span class='user local'>${mention[0]}</span>`
118− }
119−
120− this.templateEntry = function (entry) {
121− entry.html = entry.body
122− .replace(reChannel, '$1<span class="channel">/$2</span>')
123− .replace(reUser, this.findMention)
124− .replace(reTag, '$1<span class="tag">$2</span>')
125− .replace(reUrl, '<a target="_blank" href="$1">$1</a>')
126−
127− const filter = window.location.hash.substr(1).replace(/\+/g, ' ').toLowerCase()
128− const highlight = filter === entry.author.toLowerCase()
129− const origin = feeds[entry.author].path
130−
131− return `<li class='entry ${highlight ? 'highlight' : ''}'><span class='date'>${timeAgo(Date.parse(entry.date))}</span> <a class='author' href='${origin}' target='_blank'>${entry.author}</a> <span class='body'>${entry.html}</span></li>`
132− }
133−
134− // Feeds
135−
136− this.findFeeds = function () {
137− console.log('Finding feeds..')
138− for (const site of sites) {
139− if (site.feed && site.author) {
140− feeds[site.author] = { path: site.feed }
141− }
142− }
143− console.log(`Found ${Object.keys(feeds).length} feeds.`)
144− }
145−
146− this.fetchFeeds = function () {
147− console.log(`Fetching ${Object.keys(feeds).length} feeds..`)
148− for (const id in feeds) {
149− this.fetchFeed(id, feeds[id])
150− }
151− }
152−
153− this.fetchFeed = function (id, feed) {
154− console.log(`Fetching ${id}(${feed.path})..`)
155− fetch(feed.path, { cache: 'no-store' }).then(x => x.text()).then((content) => {
156− feeds[id].content = parseFeed(id, content)
157− this.refresh(feeds)
158− }).catch((err) => {
159− console.warn(`${id}`, err)
160− })
161− }
162−
163− // Utils
164−
165− function parseFeed (author, feed) {
166− const lines = feed.split('\n').filter((line) => line.substr(0, 1) !== '#')
167− const entries = []
168− for (const id in lines) {
169− const line = lines[id].trim()
170− if (line === '') { continue }
171− const parts = line.replace(' ', '\t').split('\t')
172− const date = parts[0].trim()
173− const body = escapeHtml(parts[1].trim()).trim()
174− const channel = body.substr(0, 1) === '/' ? cleanTags(body.split(' ')[0].substr(1).toLowerCase()) : body.substr(0, 1) === '@' ? 'veranda' : 'lobby'
175− const tags = (body.match(reTag) || []).map(a => cleanTags(a.substr(a.indexOf('#') + 1).toLowerCase()))
176− const offset = new Date() - new Date(date)
177− entries.push({ date, body, author, offset, channel, tags })
178− }
179− return entries
180− }
181−
182− function timeAgo (dateParam) {
183− const date = typeof dateParam === 'object' ? dateParam : new Date(dateParam)
184− const today = new Date()
185− const yesterday = new Date(today - 86400000)
186− const seconds = Math.round((today - date) / 1000)
187− const minutes = Math.round(seconds / 60)
188− const isToday = today.toDateString() === date.toDateString()
189− const isYesterday = yesterday.toDateString() === date.toDateString()
190−
191− if (seconds < 5) {
192− return 'just now'
193− } else if (seconds < 60) {
194− return `${seconds} seconds ago`
195− } else if (seconds < 90) {
196− return 'a minute ago'
197− } else if (minutes < 60) {
198− return `${minutes} minutes ago`
199− } else if (isToday) {
200− return `${Math.floor(minutes / 60)} hours ago`
201− } else if (isYesterday) {
202− return 'yesterday'
203− }
204−
205− return `${Math.floor(minutes / 1440)} days ago`
206− }
207−}
208−
209−function toggleVisibility (id) {
210− const e = document.getElementById(id)
211− if (e.style.display === 'block') { e.style.display = 'none' } else { e.style.display = 'block' }
212−}
213−
214−function escapeHtml (unsafe) {
215− return unsafe.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;')
216−}
217−
218−function cleanTags (unsafe) {
219− return unsafe.replace(/([^a-z0-9]+)/gi, '-')
220−}
221−
222−function stripHash (hash) {
223− const decoded = decodeURIComponent(hash)
224− return decoded.charAt(0) === '#' ? decoded.substring(1) : decoded
225−}
226−
227−function filter (name) {
228− window.location.hash = name
229− const filterAndPage = window.location.hash.split('^')
230− hallway.finder = { filter: stripHash(filterAndPage[0]), page: filterAndPage[1] || 1 }
231− hallway.refresh()
232−}
scripts/indental.jsdeleted−57
@@ -1,57 +0,0 @@
1−'use strict'
2−
3−function indental (data, Type) {
4− function formatLine (line) {
5− const a = []
6− const h = {}
7− for (const child of line.children) {
8− if (child.key) {
9− if (h[child.key]) { console.warn(`Redefined key: ${child.key}.`) }
10− h[child.key] = child.value
11− } else if (child.children.length === 0 && child.content) {
12− a[a.length] = child.content
13− } else {
14− h[child.content.toUpperCase()] = formatLine(child)
15− }
16− }
17− return a.length > 0 ? a : h
18− }
19−
20− function makeLine (line) {
21− return line.indexOf(' : ') > -1 ? {
22− indent: line.search(/\S|$/),
23− content: line.trim(),
24− key: line.split(' : ')[0].trim().toUpperCase(),
25− value: line.split(' : ')[1].trim()
26− } : {
27− indent: line.search(/\S|$/),
28− content: line.trim(),
29− children: []
30− }
31− }
32−
33− function skipLine (line) {
34− return line.trim() !== '' && line.trim().substr(0, 1) !== ';' && line.trim().slice(-1) !== '`'
35− }
36−
37− const lines = data.split('\n').filter(skipLine).map(makeLine)
38−
39− // Assoc
40− const stack = {}
41− for (const line of lines) {
42− const target = stack[line.indent - 2]
43− if (target) { target.children.push(line) }
44− stack[line.indent] = line
45− }
46−
47− // Format
48− const h = {}
49− for (const id in lines) {
50− const line = lines[id]
51− if (line.indent > 0) { continue }
52− const key = line.content.toUpperCase()
53− if (h[key]) { console.warn(`Redefined key: ${key}, line ${id}.`) }
54− h[key] = Type ? new Type(key, formatLine(line)) : formatLine(line)
55− }
56− return h
57−}
scripts/opml.jsdeleted−13
@@ -1,13 +0,0 @@
1−const outlines = sites.filter(site => site.rss).map(site => {
2− return `<outline type="rss" xmlUrl="${site.rss}" title="${site.title}"/>`
3−})
4−
5−const template = `<?xml version="1.0" encoding="UTF-8"?><opml version="1.1"><head></head><body>${outlines.join('')}</body></opml>`
6−
7−window.addEventListener('DOMContentLoaded', () => {
8− const a = document.getElementById('opml')
9− if (a) {
10− a.setAttribute('href', 'data:text/plain;charset=utf-8,' + window.encodeURIComponent(template))
11− a.setAttribute('download', 'webring.opml')
12− }
13−})
scripts/portal.jsdeleted−121
@@ -1,121 +0,0 @@
1−'use strict'
2−
3−function Portal (sites) {
4− this.el = document.createElement('div')
5− this.el.id = 'portal'
6− this.sites = sites
7−
8− // Templates
9−
10− const _readme = '<p class="readme">This webring is an attempt to inspire artists & developers to build their own website and share traffic among each other. The ring welcomes personalized websites such as <b>diaries, wikis & portfolios</b>.</p><p>To add yourself to the ring, submit a <a href="https://github.com/XXIIVV/webring/edit/master/scripts/sites.js" target="_blank">Pull Request</a>.<br />If you found a broken link, please <a href="https://github.com/XXIIVV/webring/issues/new" target="_blank">report it</a>.</p>'
11− const _buttons = '<p class="buttons"><a href="#random" onClick="portal.reload()">Random</a> | <a href="https://github.com/XXIIVV/webring">Information</a> <a id="icon" href="#random" onClick="portal.reload()"></a> | <a href="hallway.html">The Hallway</a> | <a href="wiki.html">The Wiki</a> | <a id="opml">OPML</a></p>'
12−
13− function _directory (sites) {
14− const siteTypesArray = [...new Set(sites.map(site => site.type).filter(Boolean))]
15− const siteLangsArray = [...new Set(sites
16− .reduce((flattened, site) => flattened.concat(site.langs), [])
17− .sort())]
18−
19− const siteTypes = siteTypesArray.reduce((output, siteType) =>
20− `${output}<a data-type="${siteType}" href="#" onclick="filterSites(event)">&lt;${siteType}&gt;</a>`,
21− '<a class="currentType" data-type="all" href="#" onclick="filterSites(event)">&lt;all&gt;</a>')
22−
23− const siteLangs = siteLangsArray.reduce((output, siteLang) =>
24− `${output}<a data-lang="${siteLang}" href="#" onclick="filterSites(event)">&lt;${siteLang}&gt;</a>`,
25− '<a class="currentLang" data-lang="all" href="#" onclick="filterSites(event)">&lt;all&gt;</a>')
26−
27− const listItems = sites.reduce((acc, site, id) =>
28− `${acc}<li ${_type(site)} ${_lang(site)} id='${id}'><a href='${site.url}'>${_name(site)}</a></li>`, '')
29− return `<nav>${siteTypes}</nav><nav>${siteLangs}</nav><main><ul>${listItems}</ul></main><footer>${_readme}${_buttons}</footer>`
30− }
31−
32− function _name (site) {
33− return site.title || `${site.url.split('//')[1].replace(/\/+$/, '')}`
34− }
35−
36− function _type (site) {
37− return site.type ? `data-type="${site.type} "` : ''
38− }
39−
40− function _lang (site) {
41− return `data-lang="${site.langs.join(' ').trim()}"`
42− }
43−
44− function _redirectView (site) {
45− return `<main><p>Redirecting to <a href="${site.url}">${site.url}</a></p></main>
46− <footer><p class='buttons'><a href='#' onClick="portal.reload('')">Directory</a> | <a href='#${site.url}' onClick="portal.reload()">Skip</a> | <a href='#random' onClick="portal.reload()">Random</a> | <a href='https://github.com/XXIIVV/webring'>Information</a> <a id='icon' href='#random' onClick="portal.reload()"></a></p>`
47− }
48−
49− //
50−
51− this.install = function (host) {
52− host.appendChild(this.el)
53− }
54−
55− this.start = function () {
56− const hash = window.location.hash
57− if (hash && hash.length > 4) {
58− const site = this.getSite(hash.replace('#', '').trim())
59− this.el.innerHTML = _redirectView(site)
60− setTimeout(() => { window.location = site.url }, 3000)
61− } else {
62− this.el.innerHTML = _directory(this.sites)
63− }
64− }
65−
66− this.reload = function () {
67− setTimeout(() => { window.location.reload() }, 500)
68− }
69−
70− this.navigate = function (target) {
71− setTimeout(() => { window.location.href = target }, 3000)
72− }
73−
74− this.navLangs = [...new Set(navigator.languages.map(lang => lang.split('-').shift()))]
75−
76− this.sitesMatchingLangs = this.sites
77− .filter(site => site.langs && site.langs.some(lang => this.navLangs.includes(lang)))
78−
79− this.nextSiteIndex = (ceiling, index) => index === ceiling ? 0 : index + 1
80− this.randomSite = this.sitesMatchingLangs[Math.floor(Math.random() * this.sitesMatchingLangs.length)]
81−
82− this.getSite = function (hash) {
83− if (hash === 'random') {
84− return this.randomSite
85− } else {
86− const index = this.sitesMatchingLangs.findIndex(site => site.url.includes(hash))
87− return (index > -1)
88− ? this.sitesMatchingLangs[this.nextSiteIndex(this.sitesMatchingLangs.length, index)]
89− : this.randomSite
90− }
91− }
92−}
93−
94−function filterSites (e) {
95− e.preventDefault()
96− const navLink = e.target
97− const currentFilter = navLink.dataset.lang ? 'Lang' : 'Type'
98−
99− document.querySelector(`nav a.current${currentFilter}`).classList.remove(`current${currentFilter}`)
100− navLink.classList.add(`current${currentFilter}`)
101−
102− const typeFilter = document.querySelector('nav a.currentType').dataset.type
103− const langFilter = document.querySelector('nav a.currentLang').dataset.lang
104−
105− const sites = document.querySelectorAll('main li')
106− sites.forEach(el => { el.style.display = 'block' })
107−
108− sites.forEach(el => {
109− if (typeFilter !== 'all') {
110− if (el.dataset.type) {
111− if (el.dataset.type.trim() !== typeFilter) el.style.display = 'none'
112− } else {
113− el.style.display = 'none'
114− }
115− }
116−
117− if (langFilter !== 'all') {
118− if (!el.dataset.lang.includes(langFilter)) el.style.display = 'none'
119− }
120− })
121−}
scripts/progress.jsdeleted−41
@@ -1,41 +0,0 @@
1−'use strict'
2−
3−const Progress = keys => {
4− const stack = new Set(keys.filter(key => key))
5− const loaded = new Set()
6− const failed = new Set()
7−
8− this.ratio = () => {
9− return loaded.size / stack.size
10− }
11−
12− this.percentage = () => {
13− return (100 * this.ratio()).toFixed(2)
14− }
15−
16− const render = () => `
17− <div class='${loaded.size !== stack.size ? 'busy' : 'done'}'>
18− <p>
19− ${'|'.repeat(this.ratio() * 10)}
20− ${loaded.size !== stack.size ? this.percentage() + '% ' : ''}
21− ${loaded.size === stack.size ? 'Complete' : 'Loading'}
22− ${failed.size !== 0 ? `(${failed.size} failed)` : ''}
23− </p>
24− </div>`
25−
26− const didLoad = (key, success) => new Promise((resolve, reject) => {
27− if (stack.has(key)) {
28− loaded.add(key)
29− if (!success) failed.add(key)
30− return resolve()
31− } else {
32− return reject()
33− }
34− })
35−
36− return {
37− success: key => didLoad(key, true),
38− failure: key => didLoad(key, false),
39− toString: render
40− }
41−}
scripts/sites.jsdeleted−924
@@ -1,924 +0,0 @@
1−'use strict'
2−
3−const sites = [
4− {
5− author: 'neauoire',
6− contact: 'aliceffekt@gmail.com',
7− feed: 'https://wiki.xxiivv.com/twtxt.txt',
8− langs: ['en'],
9− rss: 'https://wiki.xxiivv.com/links/rss.xml',
10− title: 'xxiivv',
11− type: 'wiki',
12− url: 'https://wiki.xxiivv.com',
13− wiki: 'https://wiki.xxiivv.com/src/database/glossary.ndtl'
14− },
15−
16− {
17− langs: ['en'],
18− url: 'http://estevancarlos.com'
19− },
20−
21− {
22− author: 'rho',
23− feed: 'https://electro.pizza/twtxt.txt',
24− langs: ['en'],
25− title: 'electro pizza',
26− type: 'blog',
27− rss: 'https://electro.pizza/feed.xml',
28− url: 'https://electro.pizza'
29− },
30−
31− {
32− author: 'joshavanier',
33− contact: 'joshavanier@protonmail.com',
34− feed: 'https://avanier.now.sh/tw.txt',
35− langs: ['en'],
36− title: 'Arachne',
37− type: 'wiki',
38− url: 'https://avanier.now.sh'
39− },
40−
41− {
42− author: 'kaemura',
43− contact: 'chimera1190@gmail.com',
44− feed: 'https://kaemura.com/twttxt.txt',
45− langs: ['en'],
46− title: 'kaemura.com',
47− type: 'blog',
48− url: 'http://kaemura.com'
49− },
50−
51− {
52− author: 'slisne',
53− contact: '@slisne@merveilles.town',
54− feed: 'https://liamcooke.com/merveilles/tw.txt',
55− langs: ['en'],
56− title: 'liamcooke.com',
57− rss: 'https://liamcooke.com/feed.xml',
58− url: 'https://liamcooke.com',
59− wiki: 'https://liamcooke.com/merveilles/wiki.ndtl'
60− },
61−
62− {
63− langs: ['en'],
64− url: 'https://hraew.autophagy.io'
65− },
66−
67− {
68− langs: ['en'],
69− url: 'http://evenunto.net'
70− },
71−
72− {
73− langs: ['en'],
74− url: 'https://anxl.faith'
75− },
76−
77− {
78− author: 'xvw',
79− contact: 'xaviervdw@gmail.com',
80− langs: ['fr'],
81− feed: 'https://xvw.github.io/twtxt/hallway.txt',
82− rss: 'https://xvw.github.io/atom.xml',
83− title: 'planet',
84− type: 'hybrid',
85− url: 'https://xvw.github.io'
86− },
87−
88− {
89− langs: ['en', 'el'],
90− url: 'https://heracl.es'
91− },
92−
93− {
94− langs: ['en'],
95− url: 'https://turelio.github.io'
96− },
97−
98− {
99− author: 'lectronice',
100− contact: 'i.love.spam@lectronice.com',
101− feed: 'https://lectronice.com/hallway/twtxt.txt',
102− langs: ['en'],
103− title: 'shards',
104− type: 'blog',
105− url: 'https://shards.lectronice.com'
106− },
107−
108− {
109− langs: ['en'],
110− url: 'https://craze.co.uk'
111− },
112−
113− {
114− langs: ['en'],
115− url: 'https://shaneckel.com'
116− },
117−
118− {
119− author: 'cblgh',
120− contact: 'cabal://cblgh.org',
121− feed: 'https://cblgh.org/twtxt.txt',
122− langs: ['en'],
123− title: 'cblgh.org',
124− url: 'https://cblgh.org'
125− },
126−
127− {
128− author: 'afk',
129− contact: 'afk@ellugar.co',
130− langs: ['en'],
131− rss: 'http://feeds.ellugar.co/ellugar-logs',
132− title: 'ellugar',
133− type: 'wiki',
134− url: 'https://ellugar.co'
135− },
136−
137− {
138− author: 'Cameron Higby-Naquin',
139− contact: 'me@chigby.org',
140− langs: ['en'],
141− title: 'chigby.org',
142− type: 'portfolio',
143− url: 'http://chigby.org'
144− },
145−
146− {
147− langs: ['en'],
148− url: 'https://longest.voyage'
149− },
150−
151− {
152− author: 'Paloma Kop',
153− contact: 'doubleobelisk@gmail.com',
154− langs: ['en'],
155− title: 'palomakop.tv',
156− type: 'portfolio',
157− url: 'https://palomakop.tv'
158− },
159−
160− {
161− langs: ['en'],
162− url: 'https://v-os.ca'
163− },
164−
165− {
166− langs: ['en'],
167− url: 'https://jmandel.xyz'
168− },
169−
170− {
171− author: 'jda0',
172− contact: 'hi@2d4.dev',
173− feed: 'https://2d4.dev/tw.txt',
174− langs: ['en'],
175− title: '2d4.dev',
176− url: 'https://2d4.dev',
177− wiki: 'https://2d4.dev/wiki.ndtl'
178− },
179−
180− {
181− langs: ['en'],
182− url: 'https://nathanwentworth.co'
183− },
184−
185− {
186− langs: ['en'],
187− url: 'https://uonai.space'
188− },
189−
190− {
191− langs: ['en'],
192− url: 'http://controls.ee'
193− },
194−
195− {
196− langs: ['en'],
197− url: 'https://wasin.io'
198− },
199−
200− {
201− langs: ['en'],
202− url: 'https://inns.studio'
203− },
204−
205− {
206− langs: ['en'],
207− url: 'http://kokorobot.ca'
208− },
209−
210− {
211− author: 'jrc03c',
212− contact: 'josh@ameyama.com',
213− langs: ['en'],
214− title: 'ameyama',
215− type: 'blog',
216− rss: 'https://ameyama.com/blog/rss.xml',
217− url: 'https://ameyama.com',
218− wiki: 'https://ameyama.com/data/wiki.ndtl'
219− },
220−
221− {
222− author: 'wakest',
223− contact: '@liaizon@wake.st',
224− feed: 'https://wake.st/twtxt.txt',
225− langs: ['en'],
226− title: 'wake.st',
227− url: 'https://wake.st'
228− },
229−
230− {
231− langs: ['en'],
232− url: 'https://xarene.la'
233− },
234−
235− {
236− langs: ['en'],
237− url: 'https://alex.zyzhang.me'
238− },
239−
240− {
241− langs: ['en'],
242− url: 'http://bildwissenschaft.vortok.info'
243− },
244−
245− {
246− langs: ['en'],
247− url: 'https://jakofranko.github.com'
248− },
249−
250− {
251− langs: ['en'],
252− url: 'https://aeriform.io'
253− },
254−
255− {
256− langs: ['en', 'de'],
257− url: 'http://blog.lucasdidthis.com'
258− },
259−
260− {
261− author: 'npisanti',
262− contact: 'nicola@npisanti.com',
263− langs: ['en'],
264− rss: 'http://npisanti.com/journal/rss.xml',
265− title: 'npisanti.com',
266− url: 'http://npisanti.com'
267− },
268−
269− {
270− langs: ['en'],
271− url: 'https://underscorediscovery.ca'
272− },
273−
274− {
275− author: 'drisc',
276− contact: 'cory@drisc.io',
277− feed: 'https://drisc.io/hallway/twtxt.txt',
278− langs: ['en'],
279− title: 'drisc.io',
280− type: 'wiki',
281− url: 'https://drisc.io'
282− },
283−
284− {
285− langs: ['en'],
286− url: 'https://ricky.codes'
287− },
288−
289− {
290− author: 'Marshall Bowers',
291− contact: 'elliott.codes@gmail.com',
292− langs: ['en'],
293− title: 'maxdeviant.com',
294− type: 'hybrid',
295− url: 'https://maxdeviant.com'
296− },
297−
298− {
299− langs: ['en'],
300− url: 'https://tynandebold.com'
301− },
302−
303− {
304− langs: ['en'],
305− url: 'http://gytis.co'
306− },
307−
308− {
309− langs: ['en'],
310− url: 'https://nomand.co'
311− },
312−
313− {
314− langs: ['en'],
315− url: 'http://memoriata.com'
316− },
317−
318− {
319− langs: ['en'],
320− url: 'https://mmm.s-ol.nu'
321− },
322−
323− {
324− contact: 'ubuwaits@gmail.com',
325− langs: ['en'],
326− rss: 'https://chad.is/rss.xml',
327− url: 'https://chad.is'
328− },
329−
330− {
331− langs: ['en'],
332− url: 'https://smidgeo.com/bots'
333− },
334−
335− {
336− langs: ['en', 'ru'],
337− url: 'https://iko.soy'
338− },
339−
340− {
341− langs: ['en'],
342− url: 'http://atelieroilandsugar.com'
343− },
344−
345− {
346− langs: ['en'],
347− url: 'https://magoz.is'
348− },
349−
350− {
351− author: 'Szymon Kaliski',
352− langs: ['en'],
353− rss: 'https://szymonkaliski.com/feed.xml',
354− type: 'hybrid',
355− url: 'https://szymonkaliski.com'
356− },
357−
358− {
359− author: 'setphen',
360− feed: 'https://phse.net/twtxt/merv.txt',
361− langs: ['en'],
362− rss: 'https://phse.net/post/index.xml',
363− title: 'phse.net',
364− type: 'blog',
365− url: 'https://phse.net'
366− },
367−
368− {
369− author: 'rosano',
370− langs: ['en'],
371− url: 'https://rosano.ca',
372− wiki: 'https://rosano.ca/wiki.ndtl'
373− },
374−
375− {
376− langs: ['en'],
377− url: 'https://soyboysky.github.io'
378− },
379−
380− {
381− langs: ['en'],
382− url: 'https://gndclouds.cc'
383− },
384−
385− {
386− langs: ['en', 'fr'],
387− url: 'https://xuv.be'
388− },
389−
390− {
391− langs: ['en', 'zh'],
392− url: 'https://dsdshcym.github.io'
393− },
394−
395− {
396− author: 'ckipp',
397− contact: 'ckipp@pm.me',
398− langs: ['en'],
399− title: 'chris-kipp.io',
400− type: 'hybrid',
401− url: 'https://chris-kipp.io'
402− },
403−
404− {
405− langs: ['en'],
406− url: 'https://boffosocko.com'
407− },
408−
409− {
410− author: 'kodedninja',
411− contact: 'karamanhunor@pm.me',
412− feed: 'https://t.seed.hex22.org/twtxt.txt',
413− langs: ['en'],
414− title: 'hex22',
415− url: 'https://hex22.org'
416− },
417−
418− {
419− langs: ['en'],
420− url: 'https://patrikarvidsson.com'
421− },
422−
423− {
424− langs: ['en'],
425− url: 'https://sophieleetmaa.com'
426− },
427−
428− {
429− langs: ['en'],
430− url: 'https://xinniw.github.io'
431− },
432−
433− {
434− author: 'm',
435− contact: 'mboxxed@gmail.com',
436− feed: 'https://mboxed.github.io/sodatsu/tw.txt',
437− langs: ['en'],
438− title: 'sodatsu',
439− type: 'wiki',
440− url: 'https://mboxed.github.io/sodatsu'
441− },
442−
443− {
444− langs: ['en'],
445− url: 'https://letters.vexingworkshop.com'
446− },
447−
448− {
449− author: 'tomupom',
450− contact: 'tom@tomhackshaw.com',
451− langs: ['en'],
452− title: 'Tom Hackshaw',
453− type: 'portfolio',
454− url: 'https://tom.org.nz',
455− feed: 'https://a.tom.org.nz/twtxt.txt',
456− wiki: 'https://a.tom.org.nz/glossary.ndtl'
457− },
458−
459− {
460− title: 'Teknari',
461− langs: ['en'],
462− url: 'https://teknari.com',
463− author: 'teknari',
464− contact: 'teknari@teknari.com',
465− rss: 'https://teknari.com/feed.xml'
466− },
467−
468− {
469− author: 'clic',
470− contact: 'https://t.me/clic_laplata',
471− langs: ['es'],
472− title: 'Colectivo de Livecoders',
473− type: 'blog',
474− url: 'https://colectivo-de-livecoders.gitlab.io'
475− },
476−
477− {
478− langs: ['es'],
479− title: 'madewithtea.com',
480− type: 'blog',
481− url: 'https://www.madewithtea.com'
482− },
483−
484− {
485− author: 'amorris',
486− feed: 'https://feed.amorris.ca/hallway.txt',
487− langs: ['en'],
488− title: 'amorris',
489− type: 'blog',
490− url: 'https://amorris.ca',
491− wiki: 'https://wiki.amorris.ca/glossary.ndtl'
492− },
493−
494− {
495− langs: ['en'],
496− title: 'miha-co',
497− type: 'portfolio',
498− url: 'http://www.miha-co.ca'
499− },
500−
501− {
502− author: 'buzzert',
503− langs: ['en'],
504− title: 'buzzert.net',
505− type: 'blog',
506− url: 'https://buzzert.net'
507− },
508−
509− {
510− author: 'stuartpb',
511− contact: 's@stuartpb.com',
512− feed: 'https://twtxt.stuartpb.com/xxiivv.txt',
513− langs: ['en'],
514− title: 'notes.stuartpb.com',
515− type: 'wiki',
516− url: 'https://notes.stuartpb.com/'
517− },
518−
519− {
520− author: 'serocell',
521− contact: 'psignal@s900.net',
522− feed: 'https://xxiii.co/twtxt.txt',
523− langs: ['en'],
524− rss: 'https://serocell.com/feeds/serocell.xml',
525− title: 'xxiii',
526− type: 'portfolio',
527− url: 'https://xxiii.co'
528− },
529−
530− {
531− author: 'kormyen',
532− contact: 'h@kor.nz',
533− feed: 'https://kor.nz/twtxt.txt',
534− langs: ['en'],
535− title: 'kor',
536− type: 'wiki',
537− url: 'https://kor.nz',
538− wiki: 'https://kor.nz/db/glossary.ndtl'
539− },
540−
541− {
542− author: 'quite',
543− contact: 'quite@hack.org',
544− feed: 'https://lublin.se/twtxt.txt',
545− langs: ['en'],
546− url: 'https://lublin.se'
547− },
548−
549− {
550− author: 'zanneth',
551− contact: 'root@zanneth.com',
552− langs: ['en'],
553− title: 'zanneth.com',
554− type: 'blog',
555− url: 'https://zanneth.com'
556− },
557−
558− {
559− author: 'eli_oat',
560− contact: 'hi@eli.li',
561− feed: 'https://txt.eli.li/twtxt/twtxt.txt',
562− langs: ['en'],
563− rss: 'https://eli.li/feed.rss',
564− title: 'eli.li',
565− type: 'blog',
566− url: 'https://eli.li'
567− },
568−
569− {
570− author: 'gueorgui',
571− contact: 'hello@gueorgui.net',
572− langs: ['en'],
573− title: 'Gueorgui Tcherednitchenko',
574− type: 'portfolio',
575− url: 'https://gueorgui.net'
576− },
577−
578− {
579− author: 'Tate Carson',
580− contact: 'tatecarson@gmail.com',
581− langs: ['en'],
582− title: 'Tate Carson',
583− type: 'portfolio',
584− url: 'https://www.tatecarson.com'
585− },
586−
587− {
588− author: 'azlen',
589− contact: 'webring@azlen.me',
590− feed: 'https://azlen.me/twtxt.txt',
591− langs: ['en'],
592− title: 'azlen.me',
593− type: 'wiki',
594− url: 'https://azlen.me',
595− wiki: 'https://azlen.me/glossary.ndtl'
596− },
597−
598− {
599− author: 'vega',
600− contact: 'vegac@protonmail.com',
601− feed: 'https://opinionatedguide.github.io/twtxt.txt',
602− langs: ['en'],
603− title: 'OpGuides',
604− url: 'https://opinionatedguide.github.io/'
605− },
606−
607− {
608− author: 'cmaughan',
609− contact: 'mornymorny@gmail.com',
610− feed: 'https://chrismaughan.com/twtxt.txt',
611− langs: ['en'],
612− title: 'CMaughan',
613− url: 'https://chrismaughan.com/'
614− },
615−
616− {
617− author: 'RaelZero',
618− contact: 'gaz.gong@gmail.com',
619− title: 'oddworlds soliloquy',
620− langs: ['en', 'es'],
621− type: 'blog',
622− url: 'https://oddworlds.org/'
623− },
624−
625− {
626− author: 'Fundor 333',
627− contact: 'blog@fundor333.com',
628− feed: 'https://fundor333.com/twtxt.txt',
629− langs: ['en'],
630− title: 'Fundor333',
631− type: 'blog',
632− url: 'https://fundor333.com/'
633− },
634−
635− {
636− contact: 'm@cass.si',
637− langs: ['en'],
638− url: 'https://cass.si'
639− },
640−
641− {
642− author: 'dcb',
643− contact: 'dotcomboom@somnolescent.net',
644− feed: 'https://dotcomboom.somnolescent.net/twtxt.txt',
645− langs: ['en'],
646− title: 'somnolescent.net',
647− url: 'https://dotcomboom.somnolescent.net/'
648− },
649−
650− {
651− author: 'Nihiltarier',
652− contact: 'arachi@airmail.cc',
653− langs: ['en'],
654− title: 'cadmican',
655− url: 'https://cadmican.neocities.org/'
656− },
657−
658− {
659− author: 'Jonathan Skjøtt',
660− contact: 'jonathan@jskjott.com',
661− langs: ['en'],
662− title: 'jskjott',
663− url: 'https://jskjott.com'
664− },
665−
666− {
667− author: 'sixeyes',
668− contact: 'elmusho@gmail.com',
669− langs: ['en'],
670− title: 'sixey.es',
671− type: 'portfolio',
672− url: 'https://sixey.es/'
673− },
674−
675− {
676− author: '0xdstn',
677− contact: '0xdstn@protonmail.com',
678− langs: ['en'],
679− title: '0xdstn',
680− type: 'wiki',
681− url: 'https://tilde.town/~dustin/'
682− },
683−
684− {
685− author: 'James',
686− contact: 'henderson.j@protonmail.com',
687− langs: ['en'],
688− rss: 'https://jameschip.io/index.xml',
689− title: 'James Chip',
690− url: 'https://jameschip.io/'
691− },
692−
693− {
694− author: 'Patrick Monaghan',
695− contact: '0x5f.manpat@gmail.com',
696− langs: ['en'],
697− type: 'portfolio',
698− url: 'https://patrick-is.cool'
699− },
700−
701− {
702− author: 'icyphox',
703− contact: 'x@icyphox.sh',
704− langs: ['en'],
705− rss: 'https://icyphox.sh/blog/feed.xml',
706− type: 'hybrid',
707− url: 'https://icyphox.sh'
708− },
709−
710− {
711− author: 'roy niang',
712− contact: 'roy@royniang.com',
713− feed: 'https://telecom.royniang.com/updates.txt',
714− langs: ['en'],
715− title: 'roy niang',
716− type: 'portfolio',
717− url: 'https://royniang.com',
718− wiki: 'https://telecom.royniang.com/usefulnotes.ndtl'
719− },
720−
721− {
722− author: 'raul altosaar',
723− contact: 'raul.altosaar@gmail.com',
724− langs: ['en'],
725− title: 'raul altosaar',
726− type: 'portfolio',
727− url: 'https://www.raul.earth/'
728− },
729−
730− {
731− author: 'Cristi Constantin',
732− langs: ['en'],
733− rss: 'https://crlf.site/feed.xml',
734− title: 'Cr;Lf;',
735− type: 'hybrid',
736− url: 'https://crlf.site'
737− },
738−
739− {
740− contact: 'johannesg@johannesg.com',
741− langs: ['en'],
742− title: 'Jóhannes Gunnar Þorsteinsson',
743− type: 'hybrid',
744− url: 'https://www.johannesg.com'
745− },
746−
747− {
748− author: 'Michael Rupert',
749− contact: 'michaelrupert@fastmail.com',
750− langs: ['en'],
751− title: 'Provoke Analog',
752− type: 'hybrid',
753− url: 'https://provokeanalog.com'
754− },
755−
756− {
757− author: 'eti',
758− contact: 'eti@eti.tf',
759− langs: ['en'],
760− url: 'https://eti.tf'
761− },
762−
763− {
764− author: 'rezmason',
765− contact: 'jeremysachs@rezmason.net',
766− langs: ['en'],
767− title: 'rezmason.net',
768− type: 'hybrid',
769− url: 'https://rezmason.net'
770− },
771−
772− {
773− contact: 'email@estfyr.net',
774− langs: ['en', 'cz'],
775− title: 'estfyr.net',
776− type: 'hybrid',
777− url: 'https://estfyr.net'
778− },
779−
780− {
781− author: 'Payson Wallach',
782− contact: 'payson@paysonwallach.com',
783− langs: ['en'],
784− url: 'https://paysonwallach.com'
785− },
786−
787− {
788− author: 'icco',
789− contact: 'nat@natwelch.com',
790− langs: ['en'],
791− type: 'portfolio',
792− url: 'https://natwelch.com'
793− },
794−
795− {
796− author: 'whtrbt',
797− contact: 'pi@benjamin.gray.land',
798− langs: ['en'],
799− title: 'Park Imminent',
800− type: 'hybrid',
801− url: 'https://parkimminent.com'
802− },
803−
804− {
805− author: 'aklsh',
806− contact: 'akileshkannan@gmail.com',
807− langs: ['en'],
808− url: 'https://aklsh.github.io',
809− type: 'hybrid'
810− },
811−
812− {
813− author: 'hxii',
814− contact: 'paul@glushak.net',
815− langs: ['en'],
816− type: 'hybrid',
817− url: 'https://paulglushak.com'
818− },
819−
820− {
821− author: 'syx',
822− contact: 'hello@simone.computer',
823− langs: ['en', 'ita'],
824− title: 'Simone\'s Computer',
825− type: 'portfolio',
826− url: 'https://simone.computer'
827− },
828−
829− {
830− author: 'xj9',
831− contact: 'xj9@sunshinegardens.org',
832− feed: 'https://xj9.io/.well-known/webring/tw.txt',
833− langs: ['en'],
834− title: 'dreamspace',
835− type: 'blog',
836− rss: 'https://xj9.io/rss.xml',
837− url: 'https://xj9.io',
838− wiki: 'https://xj9.io/.well-known/webring/wiki.ndtl'
839− },
840−
841− {
842− contact: 'joe.jenett@jenett.org',
843− langs: ['en'],
844− rss: 'https://simply.personal.jenett.org/feed',
845− title: 'jenett. simply. personal.',
846− type: 'blog',
847− url: 'https://simply.personal.jenett.org'
848− },
849−
850− {
851− author: 'Wally',
852− contact: 'qpfiffer+xxiivvwebring@gmail.com',
853− langs: ['en'],
854− rss: 'http://q.pfiffer.org/feed.xml',
855− type: 'blog',
856− url: 'http://q.pfiffer.org/'
857− },
858−
859− {
860− author: 'ashpex',
861− contact: 'ashpex@dismail.de',
862− langs: ['en'],
863− title: 'ashpex',
864− type: 'blog',
865− url: 'https://ashpex.neocities.org'
866− },
867−
868− {
869− author: 'zvava',
870− contact: 'notminin@pm.me',
871− langs: ['en'],
872− title: 'zvava',
873− type: 'blog',
874− url: 'https://zvava.org/'
875− },
876−
877− {
878− author: 'Amorphous',
879− contact: 'dl.amorphous@gmail.com',
880− langs: ['en'],
881− url: 'https://amorphic.space'
882− },
883−
884− {
885− author: 'Edwin Wenink',
886− contact: 'edwinwenink@hotmail.com',
887− langs: ['en', 'nl'],
888− rss: 'https://www.edwinwenink.xyz/index.xml',
889− title: 'Archive Fever',
890− type: 'blog',
891− url: 'https://www.edwinwenink.xyz/'
892− },
893−
894− {
895− author: 'Anne-Laure Le Cunff',
896− contact: 'alecunff@gmail.com',
897− langs: ['en', 'fr'],
898− rss: 'https://www.mentalnodes.com/sitemap.xml',
899− title: 'Mental Nodes',
900− type: 'hybrid',
901− url: 'https://www.mentalnodes.com'
902− },
903−
904− {
905− author: 'librenauta',
906− contact: 'librenauta@riseup.net',
907− langs: ['es'],
908− title: 'copiona',
909− type: 'blog, hybrid',
910− url: 'https://copiona.com'
911− }
912−]
913−
914−/*
915−Make sure you've read the README!
916−Don't forget the comma on the previous entry!
917−No trailing slashes or commas!
918−Alphabetize your keys!
919−protocol://url.domain.ext
920−*/
921−
922−if (module && module.exports) {
923− module.exports = sites
924−}
scripts/wiki.jsdeleted−176
@@ -1,176 +0,0 @@
1−'use strict'
2−
3−const 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()
11−
12− const encodeUrl = text => text.replace(/ /g, '+').trim().toLowerCase()
13−
14− const cleanEntry = entry => {
15− const temp = document.createElement('div')
16− temp.textContent = entry
17− return temp.innerHTML
18− }
19−
20− const storeEntry = (author, url, category, term, unCleanEntry) => {
21− const entry = Array.isArray(unCleanEntry)
22− ? unCleanEntry.map(x => cleanEntry(x))
23− : cleanEntry(unCleanEntry)
24−
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− }
39−
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− }
45−
46− const transform = (author, url, ndtl) => {
47− Object.keys(ndtl).forEach(category => storeEntries(author, url, category, ndtl[category]))
48− }
49−
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− }
55−
56− const selected = (key, category) => key.toLowerCase() === category.toLowerCase()
57− ? 'selected'
58− : ''
59−
60− const randomTerm = () => {
61− const keys = Object.keys(entries)
62− return keys[Math.floor(Math.random() * keys.length)]
63− }
64−
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− }
72−
73− const formatLinks = text => text.replace(
74− /\b((https?|dat):\/\/[^"\s]+)/g,
75− '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>'
76− )
77−
78− const formatAuthor = (authors, author) => {
79− return `${authors}<a class='author' target='_blank' href='${author.url}'> @${author.author}</a>`
80− }
81−
82− const formatEntry = (definitions, definition) => {
83− const formatEntryList = entryList => entryList.reduce((items, item) => `${items}<li>${formatLinks(item)}</li>`, '')
84−
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>`
92−
93− return `${definitions}${newHtml}`
94− }
95−
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− }
103−
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− }
109−
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− }
116−
117− const formatRelated = (allRelated, relatedCategory) => {
118− const newHtml = `<span><a href='#${encodeUrl(relatedCategory)}'>/${relatedCategory.toLowerCase()}</a> </span>`
119− return `${allRelated}${newHtml}`
120− }
121−
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− }
136−
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− }
143−
144− const refreshProgress = () => {
145− progress.innerHTML = `${progressCounter}`
146− }
147−
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− }
165−}
166−
167−const decodeUrl = text => text.replace(/\+/g, ' ').trim().toUpperCase()
168−
169−const stripHash = hash => {
170− const decoded = decodeURIComponent(hash)
171− return decoded.charAt(0) === '#' ? decodeUrl(decoded.substring(1)) : decoded
172−}
173−
174−window.addEventListener('hashchange', () => {
175− wiki.refresh(stripHash(window.location.hash))
176−})
tools/extract_twtxt.jsdeleted−22
@@ -1,22 +0,0 @@
1−'use strict'
2−
3−// extract twtxt feeds from sites.js
4−
5−const fs = require('fs')
6−const sites = require('../scripts/sites.js')
7−const record = { following: {} }
8−
9−for (const site of sites) {
10− if (!site.author || !site.feed) { continue }
11− record.following[site.author] = site.feed
12−}
13−
14−const json = JSON.stringify(record)
15−
16−fs.writeFile('twtxt.json', json, 'utf8', (err) => {
17− if (err) {
18− console.log('An error occured while writing JSON Object to File.')
19− return console.log(err)
20− }
21− console.log('Done.')
22−})
tools/twtxt.jsondeleted−1
@@ -1 +0,0 @@
1−{"following":{"neauoire":"https://wiki.xxiivv.com/twtxt.txt","rho":"https://electro.pizza/twtxt.txt","joshavanier":"https://avanier.now.sh/tw.txt","kaemura":"https://kaemura.com/twttxt.txt","slisne":"https://liamcooke.com/merveilles/tw.txt","xvw":"https://xvw.github.io/twtxt/hallway.txt","lectronice":"https://lectronice.com/hallway/twtxt.txt","cblgh":"https://cblgh.org/twtxt.txt","jda0":"https://2d4.dev/tw.txt","wakest":"https://wake.st/twtxt.txt","drisc":"https://drisc.io/hallway/twtxt.txt","setphen":"https://phse.net/twtxt/merv.txt","kodedninja":"https://t.seed.hex22.org/twtxt.txt","m":"https://mboxed.github.io/sodatsu/tw.txt","tomupom":"https://a.tom.org.nz/twtxt.txt","amorris":"https://feed.amorris.ca/hallway.txt","stuartpb":"https://twtxt.stuartpb.com/xxiivv.txt","serocell":"https://xxiii.co/twtxt.txt","kormyen":"https://kor.nz/twtxt.txt","quite":"https://lublin.se/twtxt.txt","eli_oat":"https://txt.eli.li/twtxt/twtxt.txt","azlen":"https://azlen.me/twtxt.txt","vega":"https://opinionatedguide.github.io/twtxt.txt","cmaughan":"https://chrismaughan.com/twtxt.txt","Fundor 333":"https://fundor333.com/twtxt.txt","dcb":"https://dotcomboom.somnolescent.net/twtxt.txt","roy niang":"https://telecom.royniang.com/updates.txt","xj9":"https://xj9.io/.well-known/webring/tw.txt"}}
wiki.htmldeleted−33
@@ -1,33 +0,0 @@
1−<!DOCTYPE html>
2−<html lang="en">
3−<head>
4− <meta charset="utf-8">
5− <meta name="viewport" content="width=device-width, initial-scale=1.0">
6− <link rel="stylesheet" type="text/css" href="links/fonts.css"/>
7− <link rel="stylesheet" type="text/css" href="links/main.css"/>
8− <script type="text/javascript">const module = { exports:{} }</script>
9− <script src="scripts/progress.js"></script>
10− <script src="scripts/wiki.js"></script>
11− <script src="scripts/sites.js"></script>
12− <script src="scripts/indental.js"></script>
13− <script src="scripts/opml.js"></script>
14− <title>Wiki</title>
15−</head>
16−<body>
17− <div id="wiki">
18− <main>
19− <div id="main"></div>
20− <aside id="aside"></aside>
21− </main>
22− <footer id="footer">
23− <p>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>.</p>
24− <p class='buttons'><a href='https://github.com/XXIIVV/webring'>Information</a> | <a href="hallway.html">The Hallway</a> | <a href="index.html">The Portal</a> | <a id="opml">OPML</a></p>
25− <div id="progress"></div>
26− </footer>
27− </div>
28− <script>
29− const wiki = Wiki(sites)
30− wiki.initialize(window.location.hash)
31− </script>
32−</body>
33−</html>