3function Portal (sites) {
4 this.el = document.createElement('div')
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>'
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), [])
19 const siteTypes = siteTypesArray.reduce((output, siteType) =>
20 `${output}<a data-type="${siteType}" href="#" onclick="filterSites(event)"><${siteType}></a>`,
21 '<a class="currentType" data-type="all" href="#" onclick="filterSites(event)"><all></a>')
23 const siteLangs = siteLangsArray.reduce((output, siteLang) =>
24 `${output}<a data-lang="${siteLang}" href="#" onclick="filterSites(event)"><${siteLang}></a>`,
25 '<a class="currentLang" data-lang="all" href="#" onclick="filterSites(event)"><all></a>')
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>`
32 function _name (site) {
33 return site.title || `${site.url.split('//')[1].replace(/\/+$/,'')}`
36 function _type (site) {
37 return site.type ? `data-type="${site.type} "` : ''
40 function _lang (site) {
41 return `data-lang="${site.langs.join(' ').trim()}"`
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>`
51 this.install = function (host) {
52 host.appendChild(this.el)
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)
62 this.el.innerHTML = _directory(this.sites)
66 this.reload = function () {
67 setTimeout(() => { window.location.reload() }, 500)
70 this.navigate = function (target) {
71 setTimeout(() => { window.location.href = target }, 3000)
74 this.sitesMatchingLangs = this.sites
75 .filter(site => site.langs && site.langs.some(lang => navigator.languages.includes(lang)))
77 this.nextSiteIndex = (ceiling, index) => index === ceiling ? 0 : index + 1
78 this.randomSite = this.sitesMatchingLangs[Math.floor(Math.random() * this.sitesMatchingLangs.length)]
80 this.getSite = function (hash) {
81 if (hash === 'random') {
82 return this.randomSite
84 const index = this.sitesMatchingLangs.findIndex(site => site.url.includes(hash))
86 ? this.sitesMatchingLangs[this.nextSiteIndex(this.sitesMatchingLangs.length, index)]
92function filterSites (e) {
94 const navLink = e.target
95 const currentFilter = navLink.dataset.lang ? 'Lang' : 'Type'
97 document.querySelector(`nav a.current${currentFilter}`).classList.remove(`current${currentFilter}`)
98 navLink.classList.add(`current${currentFilter}`)
100 const typeFilter = document.querySelector('nav a.currentType').dataset.type
101 const langFilter = document.querySelector('nav a.currentLang').dataset.lang
103 const sites = document.querySelectorAll('main li')
104 sites.forEach(el => { el.style.display = 'block' })
106 sites.forEach(el => {
107 if (typeFilter !== 'all') {
108 if (el.dataset.type) {
109 if (el.dataset.type.trim() !== typeFilter) el.style.display = 'none'
111 el.style.display = 'none'
115 if (langFilter !== 'all') {
116 if (!el.dataset.lang.includes(langFilter)) el.style.display = 'none'
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list