on master help sign in
8 files changed+93−422
since splitsnapshots
unifiedsplit
README.md+8−38 hallway.html−19 index.html−1 links/main.css+13−57 scripts/hallway.js−196 scripts/opml.js−11 scripts/portal.js+6−12 scripts/sites.js+66−88
README.md+8−38
@@ -1,6 +1,6 @@
11# Webring
22
3−This [webring](https://wiki.xxiivv.com/webring) is an attempt to inspire artists & developers to create and maintain their own website and share traffic among each other. The webring's aim is to share hand-crafted websites such as **diaries, wikis & portfolios**. To add yourself to the ring, submit an [edit](https://github.com/XXIIVV/webring/edit/master/scripts/sites.js) to this repository.
3+This [webring](https://wiki.xxiivv.com/webring) is an attempt to inspire artists & developers to create and maintain their own website and share traffic among each other. The webring's aim is to share hand-crafted websites such as **diaries, wikis & portfolios**, single pagers with nothing but outgoing links to social platforms will be rejected. To add yourself to the ring, submit an [edit](https://github.com/XXIIVV/webring/edit/master/scripts/sites.js) to this repository.
44
55## Join the webring
66
@@ -9,55 +9,25 @@ This [webring](https://wiki.xxiivv.com/webring) is an attempt to inspire artists
99```
1010
1111- Add the webring icon to your website html.
12−- Add your website information to the [sites.js](https://github.com/XXIIVV/webring/edit/master/scripts/sites.js) file. The `url` key is required, but you can also include `title`, `type`, `author`, `contact`, `rss`, and `feed`.
13−- Submit a Pull Request with **the location of the webring icon** on your site.
12+- Add your website url to the [sites.js](https://github.com/XXIIVV/webring/edit/master/scripts/sites.js) file.
13+- Submit a Pull Request with the location of the webring icon on your site.
1414
15−Alternatively, if you your website has a dark background, use `icon.white.svg`. If your website is complaining about *https*, go ahead and host the icon yourself. **Single page websites, and websites acting only as portals to other social platforms, will be rejected**.
15+Alternatively, if you your website has a dark background, use `icon.white.svg`. If your website is complaning about *https*, go ahead and host the icon yourself.
1616
1717### Circular Linking
1818
1919Instead of linking to the directory, you can also link to the next link in the ring by adding parts of your site or domain in the hash of the request url:
2020
2121```
22−<a href='https://webring.xxiivv.com/#wiki.xxiivv' target='_blank' rel="noopener noreferrer"><img src='https://webring.xxiivv.com/icon.black.svg'/></a>
22+<a href='https://webring.xxiivv.com/#wiki.xxiivv' target='_blank'><img src='https://webring.xxiivv.com/icon.black.svg'/></a>
2323```
2424
25−### Joining the hallway
26−
27−[The Hallway](https://webring.xxiivv.com/hallway.html) is a decentralized forum using [twtxt](https://twtxt.readthedocs.io/en/stable/user/twtxtfile.html) feeds.
28−
29−To join, create a `.txt` file on your site, and add its URL to [your webring entry](https://github.com/XXIIVV/Webring/blob/master/scripts/sites.js) as `feed:`. The content of the file should be a dateISO string, a tab(or 3 spaces) and a message:
30−
31−```
32−2016-02-04T13:30:00+01:00 You can really go crazy here! ┐(゚∀゚)┌
33−2016-02-03T23:05:00+01:00 @<example http://example.org/twtxt.txt> welcome to twtxt!
34−2016-02-01T11:00:00+01:00 This is just another example.
35−2015-12-12T12:00:00+01:00 Fiat lux!
36−```
37−
38−This file needs to be accessible via CORS, so if you're self-hosting, please make sure to allow the `webring.xxiivv.com` origin.
39−Here's an example of how to do so with Nginx:
40−
41−```
42−location / {
43− index index.html;
44− if ($request_method = 'GET') {
45− add_header 'Access-Control-Allow-Origin' 'https://webring.xxiivv.com';
46− add_header 'Access-Control-Allow-Methods' 'GET';
47− add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
48− add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
49− }
50−}
51−```
52−
53−You don't need to allow all origins nor allow any other methods rather than GET, doing so can harm the security of your website, please read about [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) if you intend to do anything more complicated.
54−
5525## API
5626
57−This repository does not contain mature API capabilities, but there are a couple ways to request a list of sites and other information currently in the webring.
27+This repository does not contain mature API capabilities, but there is one way to request list of sites currently in the webring.
5828
59−- Request [sites.js on xxiivv](https://webring.xxiivv.com/scripts/sites.js) or [sites.js on github](https://raw.githubusercontent.com/XXIIVV/webring/master/scripts/sites.js) and parse it. An example can be seen [here](https://gist.github.com/ckipp01/2ab7ac42e2837b4359efeb76eb49bb54).
60−- If you'd like a already parsed list, [webring-checker.now.sh/sites](https://webring-checker.now.sh/sites) will return an json array of the site objects in the webring. See [webring-checker.now.sh](https://webring-checker.now.sh) for more info on what else is available.
29+- Request [sites.js on xxiivv](https://webring.xxiivv.com/scripts/sites.js) or [sites.js on github](https://raw.githubusercontent.com/XXIIVV/webring/master/scripts/sites.js)
30+- Parse it as text from `[` to `]`, split by `,` and then trim all whitespace and `"` in resulting array.
6131
6232## Need Help?
6333
hallway.htmldeleted−19
@@ -1,19 +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 src="scripts/hallway.js"></script>
9− <script src="scripts/sites.js"></script>
10− <title>Hallway</title>
11−</head>
12−<body>
13− <script>
14− const hallway = new Hallway(sites);
15− hallway.install(document.body);
16− hallway.start();
17− </script>
18−</body>
19−</html>
index.html−1
@@ -7,7 +7,6 @@
77 <link rel="stylesheet" type="text/css" href="links/main.css"/>
88 <script src="scripts/portal.js"></script>
99 <script src="scripts/sites.js"></script>
10− <script src="scripts/opml.js"></script>
1110 <title>Webring</title>
1211</head>
1312<body>
links/main.css+13−57
@@ -1,63 +1,19 @@
11* { margin:0;padding:0;border:0;outline:0;text-decoration:none;font-weight:inherit;font-style:inherit;color:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;list-style:none;border-collapse:collapse;border-spacing:0; -webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
22
33body { background:#f4f4f4; padding:0px; margin:0px; }
4−body div { display:block; background: #111;min-height: calc(100vh - 60px);width: calc(100vw - 60px);margin: 15px;color: #333;padding: 15px;font-family:'input_mono_regular', 'Monospaced','Courier New',courier;font-size:11px; position: relative; border-radius: 2px; color:white;}
5−body div ul { max-width: 800px; border-bottom: 2px solid white;padding-bottom: 15px;margin-bottom:15px; overflow: hidden;}
6−body div p { display:block; padding:0px 5px; line-height: 20px; max-width: 600px}
7−body div p a { text-decoration: underline;}
4+body div { display:block; background: #ddd;min-height: calc(100vh - 60px);width: calc(100vw - 60px);margin: 15px;color: #555;padding: 15px;font-family:'input_mono_regular', 'Monospaced','Courier New',courier;font-size:11px; position: relative; border-radius: 2px }
5+body div #icon { display: block;width: 30px;height: 30px;background-image: url(../icon.white.svg);background-size: cover;background-repeat: no-repeat;position: absolute;bottom: 10px;right:10px;background-position: center;padding:0px }
6+body div p { display:block; padding:0px 5px; line-height: 20px}
7+body div p a { color:black; text-decoration: none; text-decoration: underline;}
88body div p a:hover { text-decoration: underline; }
9−body div p b { font-weight: normal; font-family: 'input_mono_medium' }
9+body div p b { font-weight: normal; color:#000; font-family: 'input_mono_medium' }
1010body div p.readme { max-width:500px; }
11−
12−/* Hallway */
13−
14−body #hallway #entries { width: calc(100vw - 300px) !important; }
15−body #hallway #sidebar { position: absolute;top: 0px;right: 0px;padding: 0px 15px;width: 200px; }
16−
17−body #hallway #sidebar > * { border-bottom: 0px; line-height: 16px; padding-bottom: 0px }
18−body #hallway #sidebar > * li { text-transform: lowercase; }
19−body #hallway #sidebar > * li.selected { background: white; color:black; }
20−body #hallway #sidebar > * li:hover { cursor: pointer; }
21−body #hallway #sidebar > * li span.right { color:#555; float:right; }
22−body #hallway #sidebar > * li span.right:after { content: " msgs"; }
23−body #hallway #sidebar #channels li:before { content:'/'; }
24−body #hallway #sidebar #users li:before { content:'@'; }
25−
26−body #hallway .entry { line-height: 16px; display: block; position: relative; padding-left:100px; }
27−body #hallway .entry .date { color: #555;display: block;float:right; margin-left:20px; padding-bottom: 16px;}
28−body #hallway .entry .author { font-family: 'input_mono_medium'; position: absolute;left: 0px; }
29−body #hallway .entry .body { display: block;border-bottom: 1px solid #333;padding-bottom: 10px;margin-bottom: 10px; }
30−body #hallway .entry .body a { text-decoration: underline; }
31−body #hallway .entry .body .channel { display: inline-block;border-radius: 1px;font-family: 'input_mono_medium';color: #777; }
32−body #hallway .entry .body .user { display: inline-block;border-radius: 1px;font-family: 'input_mono_medium';color: #777; }
33−body #hallway .entry .body .tag { display: inline-block;border-radius: 1px;font-family: 'input_mono_medium';color: #777; }
34−body #hallway .entry.highlight .author { color:#555; }
35−body #hallway .entry:hover .author { color:#ccc; }
36−body #hallway .entry:hover .date { color:#ccc; }
37−body #hallway .entry:hover .body { color:#ccc; border-bottom-color: #555 }
38−body #hallway p { padding-left: 60px;background-image: url(../icon.white.svg);background-size: 40px;background-repeat: no-repeat;background-position: 0px 0px; }
39−body #hallway ul { max-width: 100%; }
40−body #hallway ul li:last-child { border-bottom:0px; }
41−
42−/* Portal */
43−
44−body #portal #icon { display: block;width: 30px;height: 30px;background-image: url(../icon.white.svg);background-size: cover;background-repeat: no-repeat;position: absolute;bottom: 15px;right:15px;background-position: center;padding:0px }
45−body #portal ul { columns:3; }
46−body #portal ul li { display:block; padding-left:5px; text-overflow: ellipsis; text-transform: lowercase;}
47−body #portal ul li.wiki::after { content:"<wiki>"; color:#555; }
48−body #portal ul li.blog::after { content:"<blog>"; color:#555; }
49−body #portal ul li a { line-height: 20px; padding:0px 5px; text-decoration: none;}
50−body #portal ul li a:visited { color:#ccc; text-decoration: none;}
51−body #portal ul li a:hover { text-decoration: underline; }
52−
53−@media (max-width: 730px) {
54− body #portal ul li span.counter { display: none }
55−}
56−@media (max-width: 630px) {
57− body #portal ul { columns: 2 !important; }
58− body #portal ul li span.counter { display: none }
59−}
60−@media (max-width: 530px) {
61− body #portal ul { columns: 1 !important; }
62− body #portal ul li span.counter { display: inline }
11+body div ul { display:block; max-width: 800px; columns:3; border-bottom: 2px solid white;padding-bottom: 15px;margin-bottom:15px; overflow: hidden;}
12+body div ul li { width: 22em; display:block; padding-left:5px; overflow: hidden; text-overflow: ellipsis; }
13+body div ul li a { line-height: 20px; display: inline-block; color:#000; padding:0px 5px; text-decoration: none;}
14+body div ul li a:visited { color:#999; text-decoration: none;}
15+body div ul li a:hover { background:#ccc; color:#000; text-decoration: none; border-radius: 2px }
16+
17+@media (max-width: 950px){
18+ body div ul { columns: 2 }
6319}
scripts/hallway.jsdeleted−196
@@ -1,196 +0,0 @@
1−'use strict'
2−
3−const re_channel = /(\s|^)\/([a-zA-Z0-9]+)/g
4−const re_user = /(\s|^)\@([a-zA-Z0-9]+)/g
5−const re_tag = /([^&]|^)\#([a-zA-Z0-9]+)/g
6−const re_url = /((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− this.filter = window.location.hash.replace('#', '').trim()
14− this.cache = null
15−
16− this.install = function (host) {
17− host.appendChild(this.el)
18− this.findFeeds()
19− }
20−
21− this.start = function () {
22− this.el.innerHTML = 'hello.'
23− this.fetchFeeds()
24− }
25−
26− this.refresh = function (feeds = this.cache) {
27− const entries = this.findEntries(feeds)
28− const channels = this.findChannels(entries)
29− const users = this.findUsers(entries)
30− const tags = this.findTags(entries)
31−
32− this.el.innerHTML = `
33− <ul id='entries'>
34− ${entries.filter((val, id) => { return !this.filter || (val.author === this.filter || val.channel === this.filter || val.tags.includes(this.filter)) }).filter((val, id) => { return id < 50 }).reduce((acc, val, id) => { return acc + this.templateEntry(val) + '\n' }, '')}
35− </ul>
36− <div id='sidebar'>
37− <ul id='channels'>
38− <li onclick='filter("")' class='${hallway.filter === '' ? 'selected' : ''}'><a href='#'>hallway <span class='right'>${entries.length}</span></a></li>
39− ${Object.keys(channels).reduce((acc, val, id) => { return acc + `<li onclick='filter("${val}")' class='${hallway.filter === val ? 'selected' : ''}'><a href='#${val}'>${val} <span class='right'>${channels[val]}</span></a></li>\n` }, '')}
40− </ul>
41− <ul id='users'>
42− ${Object.keys(users).reduce((acc, val, id) => { return acc + `<li onclick='filter("${val}")' class='${hallway.filter === val ? 'selected' : ''}' href='#${val}'>${val} <span class='right'>${users[val]}</span></li>\n` }, '')}
43− </ul>
44− <ul id='tags'>
45− ${Object.keys(tags).reduce((acc, val, id) => { return acc + `<li onclick='filter("${val}")' class='${hallway.filter === val ? 'selected' : ''}' href='#${val}'>#${val} <span class='right'>${tags[val]}</span></li>\n` }, '')}
46− </ul>
47− </div>
48− <p>The <b>Hallway</b> is a decentralized forum, to join the conversation, simply create yourself a <a href="https://twtxt.readthedocs.io/en/stable/user/twtxtfile.html">twtxt</a> feed and <a href="https://github.com/XXIIVV/Webring/">add it</a> to your entry in the <a href="index.html">webring</a>.</p>`
49−
50− if (feeds) {
51− this.cache = feeds
52− }
53− }
54−
55− // Entries
56−
57− this.findChannels = function (entries) {
58− const channels = {}
59− for (const id in entries) {
60− const entry = entries[id]
61− if (!entry.channel) { continue }
62− channels[entry.channel] = channels[entry.channel] ? channels[entry.channel] + 1 : 1
63− }
64− return channels
65− }
66−
67− this.findUsers = function (entries) {
68− const users = {}
69− for (const id in entries) {
70− const entry = entries[id]
71− users[entry.author] = users[entry.author] ? users[entry.author] + 1 : 1
72− }
73− return users
74− }
75−
76− this.findTags = function (entries){
77− const tags = {}
78− for (const id in entries) {
79− const entry = entries[id]
80− entry.tags.map(tag => {
81− tags[tag] = tags[tag] ? tags[tag] + 1 : 1
82− })
83− }
84− return tags
85− }
86−
87− this.findEntries = function (feeds) {
88− const a = []
89− for (const id in feeds) {
90− for (const i in feeds[id].content) {
91− a.push(feeds[id].content[i])
92− }
93− }
94− return a.sort( (a, b) => a.offset - b.offset )
95− }
96−
97− this.templateEntry = function (entry) {
98− entry.html = entry.body
99− .replace(re_channel, `$1<span class='channel'>/$2</span>`)
100− .replace(re_user, `$1<span class='user'>@$2</span>`)
101− .replace(re_tag, `$1<span class='tag'>#$2</span>`)
102− .replace(re_url, `<a target="_blank" href='$1'>$1</a>`)
103−
104− const filter = window.location.hash.substr(1).replace(/\+/g, ' ').toLowerCase()
105− const highlight = filter === entry.author.toLowerCase()
106− const origin = feeds[entry.author].path
107−
108− 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>`
109− }
110−
111− // Feeds
112−
113− this.findFeeds = function () {
114− console.log('Finding feeds..')
115− for (const id in sites) {
116− const site = sites[id]
117− if (site.feed && site.author) {
118− feeds[site.author] = { path: site.feed }
119− }
120− }
121− console.log(`Found ${Object.keys(feeds).length} feeds.`)
122− }
123−
124− this.fetchFeeds = function () {
125− console.log(`Fetching ${Object.keys(feeds).length} feeds..`)
126− for (const id in feeds) {
127− this.fetchFeed(id, feeds[id])
128− }
129− }
130−
131− this.fetchFeed = function (id, feed) {
132− console.log(`Fetching ${id}(${feed.path})..`)
133− fetch(feed.path, { cache: 'no-store' }).then(x => x.text()).then((content) => {
134− feeds[id].content = parseFeed(id, content)
135− this.refresh(feeds)
136− }).catch((err) => {
137− console.warn(`${id}`, err)
138− })
139− }
140−
141− // Utils
142−
143− function parseFeed (author, feed) {
144− const lines = feed.split('\n')
145− const entries = []
146− for (const id in lines) {
147− const line = lines[id].trim()
148− if (line === '') { continue }
149− const parts = line.replace(' ', '\t').split('\t')
150− const date = parts[0].trim()
151− const body = escapeHtml(parts[1].trim()).trim()
152− const channel = body.substr(0, 1) === '/' ? body.split(' ')[0].substr(1).toLowerCase() : body.substr(0, 1) === '@' ? 'veranda' : 'lobby'
153− const tags = (body.match(re_tag)||[]).map(a => a.substr(a.indexOf('#')+1).toLowerCase())
154− const offset = new Date() - new Date(date)
155− entries.push({ date, body, author, offset, channel, tags })
156− }
157− return entries
158− }
159−
160− function timeAgo (dateParam) {
161− const date = typeof dateParam === 'object' ? dateParam : new Date(dateParam)
162− const DAY_IN_MS = 86400000 // 24 * 60 * 60 * 1000
163− const today = new Date()
164− const yesterday = new Date(today - DAY_IN_MS)
165− const seconds = Math.round((today - date) / 1000)
166− const minutes = Math.round(seconds / 60)
167− const isToday = today.toDateString() === date.toDateString()
168− const isYesterday = yesterday.toDateString() === date.toDateString()
169−
170− if (seconds < 5) {
171− return 'just now'
172− } else if (seconds < 60) {
173− return `${seconds} seconds ago`
174− } else if (seconds < 90) {
175− return 'a minute ago'
176− } else if (minutes < 60) {
177− return `${minutes} minutes ago`
178− } else if (isToday) {
179− return `${Math.floor(minutes / 60)} hours ago`
180− } else if (isYesterday) {
181− return 'yesterday'
182− }
183−
184− return `${Math.floor(minutes / 1440)} days ago`
185− }
186−
187− function escapeHtml (unsafe) {
188− return unsafe.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;')
189− }
190−}
191−
192−function filter (name) {
193− window.location.hash = name
194− hallway.filter = window.location.hash.replace('#', '').trim()
195− hallway.refresh()
196−}
scripts/opml.jsdeleted−11
@@ -1,11 +0,0 @@
1−let outlines = sites.filter((site) => !!site.rss).map((site) => {
2− return `<outline type="rss" xmlUrl="${site.rss}" title="${site.title}"/>`
3−})
4−
5−let template = `<?xml version="1.0" encoding="UTF-8"?><opml version="1.1"><head></head><body>${outlines.join('')}</body></opml>`
6−
7−window.onload = function () {
8− let a = document.getElementById('opml')
9− a.setAttribute('href', 'data:text/plain;charset=utf-8,' + window.encodeURIComponent(template))
10− a.setAttribute('download', 'webring.opml')
11−}
scripts/portal.js+6−12
@@ -2,7 +2,6 @@
22
33function Portal (sites) {
44 this.el = document.createElement('div')
5− this.el.id = 'portal'
65 this.sites = sites
76
87 // Templates
@@ -12,21 +11,17 @@ function Portal (sites) {
1211 }
1312
1413 function _buttons () {
15− return `<p class='buttons'><a href='#random' onClick="portal.reload('random')">Random</a> | <a href='https://github.com/XXIIVV/webring'>Information</a> <a id='icon' href='#random' onClick="portal.reload('random')"></a> | <a href="hallway.html">The Hallway</a> | <a id="opml">OPML</a></p>`
14+ return `<p class='buttons'><a href='#random' onClick="portal.reload('random')">Random</a> | <a href='https://github.com/XXIIVV/webring'>Information</a> <a id='icon' href='#random' onClick="portal.reload('random')"></a></p>`
1615 }
1716
1817 function _directory (sites) {
1918 return `
20− <ul>${sites.reduce((acc, site, id) => { return `${acc}<li class='${site.type}'><span class='counter'>${id})</span><a href='${site.url}'>${_name(site)}</a></li>` }, '')}</ul>\n${_readme()}${_buttons()}`
19+ <ul>${sites.reduce((acc, val, id) => { return `${acc}<li>${id}) <a href='${val}'>${val.split('//')[1]}</a></li>` }, '')}</ul>\n${_readme()}${_buttons()}`
2120 }
2221
23− function _name (site) {
24− return site.title ? site.title : `${site.url.split('//')[1]}`
25− }
26−
27− function _redirect (site) {
28− return `<p>Redirecting to <a href="${site.url}">${site.url}</a></p><meta http-equiv="refresh" content="3; url=${site.url}">
29− <p class='buttons'><a href='#' onClick="portal.reload('')">Directory</a> | <a href='#${site.url}' onClick="portal.reload('random')">Skip</a> | <a href='#random' onClick="portal.reload('random')">Random</a> | <a href='https://github.com/XXIIVV/webring'>Information</a> <a id='icon' href='#random' onClick="portal.reload('random')"></a></p>`
22+ function _redirect (target) {
23+ return `<p>Redirecting to <b>${target}</b></p><meta http-equiv="refresh" content="3; url=${target}">
24+ <p class='buttons'><a href='#' onClick="portal.reload('')">Directory</a> | <a href='#${target}' onClick="portal.reload('random')">Skip</a> | <a href='#random' onClick="portal.reload('random')">Random</a> | <a href='https://github.com/XXIIVV/webring'>Information</a> <a id='icon' href='#random' onClick="portal.reload('random')"></a></p>`
3025 }
3126
3227 //
@@ -55,8 +50,7 @@ function Portal (sites) {
5550 }
5651
5752 for (const id in this.sites) {
58− const site = this.sites[id]
59− if (site.url.indexOf(hash) > -1) {
53+ if (this.sites[id].indexOf(hash) > -1) {
6054 return parseInt(id)
6155 }
6256 }
scripts/sites.js+66−88
@@ -1,92 +1,70 @@
1−'use strict'
1+'use strict';
22
33// Don't forget the comma! No trailing slashes
4−// protocol://url.domain.ext
4+// protocole://url.domain.ext
55
66const sites = [
7− { url: 'https://wiki.xxiivv.com', title: 'xxiivv', type: 'wiki', author: 'neauoire', contact: 'aliceffekt@gmail.com', rss: 'https://wiki.xxiivv.com/links/rss.xml', feed: 'https://wiki.xxiivv.com/twtxt.txt' },
8− { url: 'http://estevancarlos.com' },
9− { url: 'https://electro.pizza', title: 'electro pizza', type: 'blog', author: 'rho', rss: 'https://electro.pizza/feed.xml', feed: 'https://electro.pizza/twtxt.txt' },
10− { url: 'https://ianjbattaglia.co' },
11− { url: 'https://avanier.now.sh/wiki', title: 'Ath', type: 'wiki', author: 'Joshavanier', contact: 'joshavanier@protonmail.com' },
12− { url: 'http://kaemura.com', title: 'kaemura.com', type: 'blog', author: 'kaemura', contact: 'chimera1190@gmail.com', feed: 'https://kaemura.com/twttxt.txt'},
13− { url: 'https://liamcooke.com' },
14− { url: 'https://electricgecko.de' },
15− { url: 'https://wichniow.ski' },
16− { url: 'https://hraew.autophagy.io' },
17− { url: 'http://evenunto.net' },
18− { url: 'https://anxl.faith' },
19− { url: 'https://xvw.github.io', title: 'xvw.github.io', type: 'blog', author: 'xvw', contact: 'xaviervdw@gmail.com', rss: 'https://xvw.github.io/atom.xml' },
20− { url: 'https://heracl.es' },
21− { url: 'https://felipecortez.net' },
22− { url: 'http://luminghao.com' },
23− { url: 'https://turelio.github.io' },
24− { url: 'https://spaceshipsin.space', title: 'spaceshipsin.space', type: 'hybrid', author: 'gaeel', contact: 'gaeel@spaceshipsin.space', feed: 'https://spaceshipsin.space/twtxts/hallway.txt' },
25− { url: 'https://log.lectronice.com', title: 'lectrolog.', type: 'blog', author: 'lectronice', contact: 'i.love.spam@lectronice.com', feed: 'https://lectronice.com/hallway/twtxt.txt' },
26− { url: 'https://craze.co.uk' },
27− { url: 'https://shaneckel.com' },
28− { url: 'https://cblgh.org', title: 'cblgh.org', author: 'cblgh', contact: 'cabal://cblgh.org', feed: 'https://cblgh.org/twtxt.txt' },
29− { url: 'https://ellugar.co', title: 'ellugar', type: 'wiki', author: 'afk', contact: 'afk@ellugar.co', rss: 'http://feeds.ellugar.co/ellugar-logs' },
30− { url: 'http://hur.bet' },
31− { url: 'http://chigby.org' },
32− { url: 'https://longest.voyage' },
33− { url: 'https://palomakop.tv' },
34− { url: 'https://travisshears.com' },
35− { url: 'https://v-os.ca' },
36− { url: 'http://secretinternet.club' },
37− { url: 'https://jmandel.xyz' },
38− { url: 'https://systems.ws' },
39− { url: 'https://jamesin.space' },
40− { url: 'https://nathanwentworth.co' },
41− { url: 'https://eswat.ca' },
42− { url: 'https://uonai.space' },
43− { url: 'http://controls.ee' },
44− { url: 'https://wasin.io' },
45− { url: 'https://inns.studio' },
46− { url: 'http://kokorobot.ca' },
47− { url: 'https://ameyama.com', title: '雨山', type: 'blog', author: 'jrc03c', contact: 'josh@ameyama.com', rss: 'https://ameyama.com/blog/rss.xml' },
48− { url: 'https://wake.st', title: 'wake.st', author: 'wakest', contact: '@liaizon@wake.st', feed: 'https://wake.st/twtxt.txt' },
49− { url: 'https://xarene.la' },
50− { url: 'https://alex.zyzhang.me' },
51− { url: 'http://bildwissenschaft.vortok.info' },
52− { url: 'https://jakofranko.github.com' },
53− { url: 'https://aeriform.io' },
54− { url: 'http://blog.lucasdidthis.com' },
55− { url: 'http://npisanti.com', title: 'npisanti.com', author: 'npisanti', contact: 'nicola@npisanti.com', rss: 'http://npisanti.com/journal/rss.xml' },
56− { url: 'https://underscorediscovery.ca' },
57− { url: 'https://drisc.io', title: 'drisc.io', type: 'wiki', author: 'drisc', contact: 'cory@drisc.io', feed: 'https://drisc.io/hallway/twtxt.txt' },
58− { url: 'https://neufv.systems', author: 'neufv' },
59− { url: 'https://ricky.codes' },
60− { url: 'https://maxdeviant.com' },
61− { url: 'https://tynandebold.com' },
62− { url: 'http://gytis.co' },
63− { url: 'https://nomand.co' },
64− { url: 'http://memoriata.com' },
65− { url: 'https://mmm.s-ol.nu' },
66− { url: 'https://chad.is' },
67− { url: 'https://smidgeo.com/bots' },
68− { url: 'https://iko.soy' },
69− { url: 'http://atelieroilandsugar.com' },
70− { url: 'https://magoz.is' },
71− { url: 'https://szymonkaliski.com', author: 'Szymon Kaliski', type: 'hybrid', rss: 'https://szymonkaliski.com/feed.xml' },
72− { url: 'https://phse.net', title: 'phse.net', type: 'blog', author: 'setphen', rss: 'https://phse.net/post/index.xml', feed: 'https://phse.net/twtxt/merv.txt' },
73− { url: 'https://rosano.ca' },
74− { url: 'https://soyboysky.github.io' },
75− { url: 'https://gndclouds.cc' },
76− { url: 'https://xuv.be' },
77− { url: 'https://dsdshcym.github.io' },
78− { url: 'https://wiki.chronica.xyz', title: 'chronica', type: 'wiki', author: 'ckipp', contact: 'ckipp@pm.me', feed: 'https://chronica.xyz/hallway.txt' },
79− { url: 'https://boffosocko.com' },
80− { url: 'https://hex22.org', title: 'hex22', author: 'kodedninja', contact: 'karamanhunor@pm.me', feed: 'https://t.seed.hex22.org/twtxt.txt' },
81− { url: 'https://patrikarvidsson.com' },
82− { url: 'https://sophieleetmaa.com' },
83− { url: 'https://xinniw.github.io' },
84− { url: 'https://mboxed.github.io/forida' },
85− { url: 'https://letters.vexingworkshop.com' },
86− { url: 'https://tom.org.nz/about' },
87− { url: 'https://teknari.com' },
88− { url: 'https://colectivo-de-livecoders.gitlab.io/', title: 'Colectivo de Livecoders', type: 'blog', author: 'clic', contact: 'https://t.me/clic_laplata' },
89− { url: 'https://www.madewithtea.com', title: 'madewithtea.com', type: 'blog' },
90− { url: 'https://amorris.ca', title: 'amorris', author: 'amorris', type: 'blog', feed: 'https://feed.amorris.ca/hallway.txt' },
91− { url: 'http://www.miha-co.ca', title: 'miha-co', type: 'Portfolio' }
92−]
7+ "https://wiki.xxiivv.com"
8+ , "http://estevancarlos.com"
9+ , "https://electro.pizza"
10+ , "https://monochromatic.co"
11+ , "https://joshavanier.github.io"
12+ , "http://kaemura.com"
13+ , "https://liamcooke.com"
14+ , "https://electricgecko.de"
15+ , "https://wichniow.ski"
16+ , "https://hraew.autophagy.io"
17+ , "https://evenunto.net"
18+ , "https://anxl.faith"
19+ , "https://xvw.github.io"
20+ , "https://heracl.es"
21+ , "https://felipecortez.net"
22+ , "http://luminghao.com"
23+ , "https://theiceshelf.com"
24+ , "https://turelio.github.io"
25+ , "https://spaceshipsin.space"
26+ , "http://log.lectronice.com"
27+ , "https://craze.co.uk"
28+ , "https://shaneckel.com"
29+ , "https://cblgh.org"
30+ , "https://ellugar.co"
31+ , "http://hur.bet"
32+ , "http://chigby.org"
33+ , "https://longest.voyage"
34+ , "https://palomakop.tv"
35+ , "https://travisshears.com"
36+ , "https://v-os.ca"
37+ , "http://secretinternet.club"
38+ , "https://jmandel.xyz"
39+ , "https://systems.ws"
40+ , "https://jamesin.space"
41+ , "https://nathanwentworth.co"
42+ , "https://eswat.ca"
43+ , "https://uonai.space"
44+ , "http://controls.ee"
45+ , "https://wasin.io"
46+ , "https://inns.studio"
47+ , "http://kokorobot.ca"
48+ , "https://ameyama.com"
49+ , "https://wake.st"
50+ , "https://xarene.la"
51+ , "https://alex.zyzhang.me"
52+ , "http://bildwissenschaft.vortok.info"
53+ , "https://jakofranko.github.com"
54+ , "https://aeriform.io"
55+ , "http://blog.lucasdidthis.com"
56+ , "http://npisanti.com"
57+ , "https://underscorediscovery.ca"
58+ , "https://drisc.io"
59+ , "https://neufv.website"
60+ , "https://simbolo.xyz"
61+ , "https://ricky.codes"
62+ , "https://maxdeviant.com"
63+ , "https://tynandebold.com"
64+ , "http://gytis.co"
65+ , "https://nomand.co"
66+ , "http://memoriata.com"
67+ , "https://mmm.s-ol.nu"
68+ , "https://chad.is"
69+ , "https://smidgeo.com/bots"
70+];