on master help sign in
Merge branch 'master' into master
commit14c7069a7a7bd5ba9e9fdacf0c53ff8b382e7fe2 authorHamish MacDonald <h@kor.nz> dateWed 24 Jul 2019, 23:10 (7 years ago) parent04e96c4f91f45e
8 files changed+102−62
.github/ISSUE_TEMPLATE/broken-link.md+10 .github/ISSUE_TEMPLATE/inappropriate-content.md+14 .github/ISSUE_TEMPLATE/name-of-website.md−9 .github/PULL_REQUEST_TEMPLATE/add-site.md+14 README.md+13−5 links/main.css+6−2 scripts/hallway.js+41−41 scripts/sites.js+4−5
.github/ISSUE_TEMPLATE/broken-link.mdnew file+10
@@ -0,0 +1,10 @@
1+---
2+name: broken.link
3+about: Report a broken link in the webring
4+title: ''
5+labels: ''
6+assignees: neauoire, kodedninja, joshavanier, ckipp01
7+
8+---
9+
10+**Name of the site:**
.github/ISSUE_TEMPLATE/inappropriate-content.mdnew file+14
@@ -0,0 +1,14 @@
1+---
2+name: inappropriate.content
3+about: Report inappropriate content in the webring
4+title: ''
5+labels: ''
6+assignees: neauoire, kodedninja, joshavanier, ckipp01
7+
8+---
9+
10+**Name of the site:**
11+
12+
13+
14+**Please explain what is inappropriate and link if necessary:**
.github/ISSUE_TEMPLATE/name-of-website.mddeleted−9
@@ -1,9 +0,0 @@
1−---
2−name: name.of.website
3−about: Describe this issue template's purpose here.
4−
5−---
6−
7−```
8−Position of webring icon on site: ex: footer, header
9−```
.github/PULL_REQUEST_TEMPLATE/add-site.mdnew file+14
@@ -0,0 +1,14 @@
1+---
2+name: add.site
3+about: Submitting a new site to the webring
4+title: ''
5+labels: ''
6+assignees: neauoire, kodedninja, joshavanier, ckipp01
7+
8+---
9+
10+**Position of webring icon on site:**
11+
12+ex: footer, header
13+
14+**Make sure the contact key is also filled out in case we need to contact you**
README.md+13−5
@@ -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**.
44
55## Join the webring
66
@@ -9,10 +9,18 @@ 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`.
12+- Add your website information to the [sites.js](https://github.com/XXIIVV/webring/edit/master/scripts/sites.js) file. The `url` and `contact` keys are required, but you can also include `title`, `type`, `author`, `rss`, and `feed`.
1313- 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 complaining about *https*, go ahead and host the icon yourself.
16+
17+### Webring criteria
18+
19+**Single page websites, websites acting only as portals to other social platforms and websites with content involving violence, racism, sexism or speciesism, will be rejected**.
20+
21+The aim of the webring is to dipslay **hand-crafted personal** websites. Your business site is probably not the best fit for the webring, and will be rejected.
22+
23+If it's seen that your website is in violation to any of these rules your site will be removed from the webring. If you fix the issues, feel free to submit another PR to join back in.
1624
1725### Circular Linking
1826
@@ -35,7 +43,7 @@ To join, create a `.txt` file on your site, and add its URL to [your webring ent
35432015-12-12T12:00:00+01:00 Fiat lux!
3644```
3745
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.
46+This file needs to be accessible via CORS, so if you're self-hosting, please make sure to allow the `webring.xxiivv.com` origin.
3947Here's an example of how to do so with Nginx:
4048
4149```
@@ -61,4 +69,4 @@ This repository does not contain mature API capabilities, but there are a couple
6169
6270## Need Help?
6371
64−The ring is managed by [@neauoire](https://twitter.com/neauoire), but any member of the network is also welcome to join this repository as a collaborator to help manage new links and Pull Requests. Read more [here](https://wiki.xxiivv.com/webring).
72+The ring is managed by [@neauoire](https://twitter.com/neauoire), but any member of the network is also welcome to join this repository as a collaborator to help manage new links and Pull Requests. Read more about the webring [here](https://wiki.xxiivv.com/webring).
links/main.css+6−2
@@ -1,7 +1,7 @@
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;}
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;}
55body div ul { max-width: 800px; border-bottom: 2px solid white;padding-bottom: 15px;margin-bottom:15px; overflow: hidden;}
66body div p { display:block; padding:0px 5px; line-height: 20px; max-width: 600px}
77body div p a { text-decoration: underline;}
@@ -12,7 +12,11 @@ body div p.readme { max-width:500px; }
1212/* Hallway */
1313
1414body #hallway #entries { width: calc(100vw - 300px) !important; }
15−body #hallway #sidebar { position: absolute;top: 0px;right: 0px;padding: 0px 15px;width: 200px; }
15+body #hallway #sidebar { position: absolute;top: 0px;right: 0px;padding: 15px;width: 200px; }
16+
17+body #hallway #pagination { margin: 10px; text-align: center; }
18+body #hallway #pagination span { padding: 3px; margin: 2px; cursor: pointer; }
19+body #hallway #pagination span.selected { background: white; color:black; }
1620
1721body #hallway #sidebar > * { border-bottom: 0px; line-height: 16px; padding-bottom: 0px }
1822body #hallway #sidebar > * li { text-transform: lowercase; }
scripts/hallway.js+41−41
@@ -1,7 +1,7 @@
11'use strict'
22
33const re_channel = /(\s|^)\/([a-zA-Z0-9]+)/g
4−const re_user = /(\s|^)\@([a-zA-Z0-9]+)/g
4+const re_user = /((\s|^)@&lt;[a-zA-Z0-9./:\-_\+~#= ]*&gt;)/g
55const re_tag = /([^&]|^)\#([a-zA-Z0-9]+)/g
66const re_url = /((https?):\/\/(([-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b)([-a-zA-Z0-9@:%_\+.~#?&//=]*)))/g
77
@@ -10,7 +10,8 @@ function Hallway (sites) {
1010 this.sites = sites
1111 this.el = document.createElement('div')
1212 this.el.id = 'hallway'
13− this.filter = window.location.hash.replace('#', '').trim()
13+ const filterAndPage = window.location.hash.split('^')
14+ this.finder = { filter: filterAndPage[0].trim().replace('#', ''), page: filterAndPage[1] || 1 }
1415 this.cache = null
1516
1617 this.install = function (host) {
@@ -25,24 +26,30 @@ function Hallway (sites) {
2526
2627 this.refresh = function (feeds = this.cache) {
2728 const entries = this.findEntries(feeds)
28− const channels = this.findChannels(entries)
29− const users = this.findUsers(entries)
29+ const channels = this.find(entries, 'channel')
30+ const users = this.find(entries, 'author')
3031 const tags = this.findTags(entries)
32+ const relevantEntries = entries.filter(val => !this.finder.filter || (val.author === this.finder.filter || val.channel === this.finder.filter || val.tags.includes(this.finder.filter)))
3133
3234 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>
35+ <div id='entries'>
36+ <ul>
37+ ${entries.filter((val) => !this.finder.filter || (val.author === this.finder.filter || val.channel === this.finder.filter || val.tags.includes(this.finder.filter))).filter((_, id) => id < Number(this.finder.page) * 20 && id >= (Number(this.finder.page) - 1) * 20).reduce((acc, val) => acc + this.templateEntry(val) + '\n', '')}
38+ </ul>
39+ <div id='pagination'>
40+ ${[...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>`, '')}
41+ </div>
42+ </div>
3643 <div id='sidebar'>
3744 <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` }, '')}
45+ <li onclick='filter("")' class='${hallway.finder.filter === '' ? 'selected' : ''}'><a href='#'>hallway <span class='right'>${entries.length}</span></a></li>
46+ ${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`, '')}
4047 </ul>
4148 <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` }, '')}
49+ ${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`, '')}
4350 </ul>
4451 <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` }, '')}
52+ ${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`, '')}
4653 </ul>
4754 </div>
4855 <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>`
@@ -54,26 +61,15 @@ function Hallway (sites) {
5461
5562 // Entries
5663
57− this.findChannels = function (entries) {
58− const channels = {}
64+ this.find = function (entries, p) {
65+ const h = {}
5966 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
67+ if (entries[id] && entries[id][p]) { h[entries[id][p]] = h[entries[id][p]] ? h[entries[id][p]] + 1 : 1 }
6368 }
64− return channels
69+ return h
6570 }
6671
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){
72+ this.findTags = function (entries) {
7773 const tags = {}
7874 for (const id in entries) {
7975 const entry = entries[id]
@@ -91,29 +87,33 @@ function Hallway (sites) {
9187 a.push(feeds[id].content[i])
9288 }
9389 }
94− return a.sort( (a, b) => a.offset - b.offset )
90+ return a.sort((a, b) => a.offset - b.offset)
91+ }
92+
93+ this.findMention = function (found) {
94+ const mention = Object.keys(feeds).filter(user => found.indexOf(feeds[user].path) > -1)
95+ return mention.length === 1 ? ` <span class='user'>@${mention[0]}</span>` : ` <span class='user'>${found}</span>`
9596 }
9697
9798 this.templateEntry = function (entry) {
9899 entry.html = entry.body
99100 .replace(re_channel, `$1<span class='channel'>/$2</span>`)
100− .replace(re_user, `$1<span class='user'>@$2</span>`)
101+ .replace(re_user, this.findMention)
101102 .replace(re_tag, `$1<span class='tag'>#$2</span>`)
102103 .replace(re_url, `<a target="_blank" href='$1'>$1</a>`)
103−
104+
104105 const filter = window.location.hash.substr(1).replace(/\+/g, ' ').toLowerCase()
105106 const highlight = filter === entry.author.toLowerCase()
106107 const origin = feeds[entry.author].path
107108
108109 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>`
109110 }
110−
111+
111112 // Feeds
112113
113114 this.findFeeds = function () {
114115 console.log('Finding feeds..')
115− for (const id in sites) {
116− const site = sites[id]
116+ for (const site of sites) {
117117 if (site.feed && site.author) {
118118 feeds[site.author] = { path: site.feed }
119119 }
@@ -141,7 +141,7 @@ function Hallway (sites) {
141141 // Utils
142142
143143 function parseFeed (author, feed) {
144− const lines = feed.split('\n')
144+ const lines = feed.split('\n').filter((line) => line.substr(0, 1) !== '#')
145145 const entries = []
146146 for (const id in lines) {
147147 const line = lines[id].trim()
@@ -150,7 +150,7 @@ function Hallway (sites) {
150150 const date = parts[0].trim()
151151 const body = escapeHtml(parts[1].trim()).trim()
152152 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())
153+ const tags = (body.match(re_tag) || []).map(a => a.substr(a.indexOf('#') + 1).toLowerCase())
154154 const offset = new Date() - new Date(date)
155155 entries.push({ date, body, author, offset, channel, tags })
156156 }
@@ -159,9 +159,8 @@ function Hallway (sites) {
159159
160160 function timeAgo (dateParam) {
161161 const date = typeof dateParam === 'object' ? dateParam : new Date(dateParam)
162− const DAY_IN_MS = 86400000 // 24 * 60 * 60 * 1000
163162 const today = new Date()
164− const yesterday = new Date(today - DAY_IN_MS)
163+ const yesterday = new Date(today - 86400000)
165164 const seconds = Math.round((today - date) / 1000)
166165 const minutes = Math.round(seconds / 60)
167166 const isToday = today.toDateString() === date.toDateString()
@@ -183,14 +182,15 @@ function Hallway (sites) {
183182
184183 return `${Math.floor(minutes / 1440)} days ago`
185184 }
185+}
186186
187− function escapeHtml (unsafe) {
188− return unsafe.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;')
189− }
187+function escapeHtml (unsafe) {
188+ return unsafe.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;')
190189}
191190
192191function filter (name) {
193192 window.location.hash = name
194− hallway.filter = window.location.hash.replace('#', '').trim()
193+ const filterAndPage = window.location.hash.split('^')
194+ hallway.finder = { filter: filterAndPage[0].trim().replace('#', ''), page: filterAndPage[1] || 1 }
195195 hallway.refresh()
196196}
scripts/sites.js+4−5
@@ -8,8 +8,8 @@ const sites = [
88 { url: 'http://estevancarlos.com' },
99 { url: 'https://electro.pizza', title: 'electro pizza', type: 'blog', author: 'rho', rss: 'https://electro.pizza/feed.xml', feed: 'https://electro.pizza/twtxt.txt' },
1010 { 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'},
11+ { url: 'https://avanier.now.sh', title: 'Arachne', 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' },
1313 { url: 'https://liamcooke.com' },
1414 { url: 'https://electricgecko.de' },
1515 { url: 'https://wichniow.ski' },
@@ -33,12 +33,10 @@ const sites = [
3333 { url: 'https://palomakop.tv' },
3434 { url: 'https://travisshears.com' },
3535 { url: 'https://v-os.ca' },
36− { url: 'http://secretinternet.club' },
3736 { url: 'https://jmandel.xyz' },
3837 { url: 'https://systems.ws' },
3938 { url: 'https://jamesin.space' },
4039 { url: 'https://nathanwentworth.co' },
41− { url: 'https://eswat.ca' },
4240 { url: 'https://uonai.space' },
4341 { url: 'http://controls.ee' },
4442 { url: 'https://wasin.io' },
@@ -93,5 +91,6 @@ const sites = [
9391 { url: 'https://notes.stuartpb.com/', title: 'notes.stuartpb.com', type: 'wiki', author: 'stuartpb', contact: 's@stuartpb.com', feed: 'https://twtxt.stuartpb.com/xxiivv.txt' },
9492 { url: 'http://xxiii.co', title: 'xxiii', type: 'portfolio', author: 'serocell', contact: 'psignal@s900.net', rss: 'http://serocell.com/feeds/serocell.xml', feed: 'http://xxiii.co/twtxt.txt' },
9593 { url: 'https://kor.nz', title: 'kor', type: 'wiki', author: 'kormyen', contact: 'h@kor.nz', feed: 'https://kor.nz/twtxt.txt' },
96− { url: 'http://xxiii.co', title: 'xxiii', type: 'wiki', author: 'serocell', contact: 'psignal@s900.net', rss: 'http://serocell.com/feeds/serocell.xml' }
94+ { url: 'https://lublin.se', author: 'quite', contact: 'quite@hack.org', feed: 'https://lublin.se/twtxt.txt' },
95+ { url: 'https://zanneth.com', title: 'zanneth.com', author: 'zanneth', type: 'blog', contact: 'root@zanneth.com' }
9796]