PHPIndex

This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).

.gitignore
wget 'https://lists2.roe3.org/wander/.gitignore'
View Content
node_modules/
LICENCE.md
wget 'https://lists2.roe3.org/wander/LICENCE.md'
View Content
The MIT License (MIT)
=====================

Copyright (c) 2026 Susam Pal

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
README.md
wget 'https://lists2.roe3.org/wander/README.md'
View Content
Wander the Small Web
====================

Wander is a small, decentralised, self-hosted web console that lets
your visitors explore random pages from a community of personal
websites.

[![Wander][IMG]][CON]

Each Wander console loads personal websites and pages recommended by
the Wander community.  Further, each Wander console can link to other
Wander consoles, forming a lightweight, decentralised network for
browsing the small web of personal websites.

Visit https://susam.net/wander/ to see an example of Wander console.

[IMG]: https://susam.github.io/blob/img/wander/wander-0.1.0.png
[CON]: https://susam.net/wander/


How It Works
------------

A Wander console does two things:

1. **Wander pages**: It loads a random page from a list of websites
   and pages recommended by the community.

2. **Wander consoles**: It can send the visitor to another Wander
   console on a different website, where they can continue wandering
   the Web using the new Wander console.

A Wander console is just a directory on your web server with two files:

- [index.html](index.html) - This is the HTML tool that implements the Wander console.
- [wander.js](wander.js) - This is where you define the list of
  websites and pages you recommend and the other Wander consoles you
  want your console to link to.

The most interesting aspect of Wander console is that everything
happens on the client-side, on the user's web browser.  As a website
owner, you do not need to set up any server-side components.  The only
thing you need to do is place the above two files somewhere on your
webserver, preferrably, at the directory `/wander/`.


Wandering the Web
-----------------

A visitor begins at a Wander console. This might be your own console
or someone else's. For example, you could start at
https://susam.net/wander/ right now.

Once on a Wander console, the visitor can click the **Wander** button
at the top left to visit websites and pages recommended by the
community.

The first time you wander, the recommendation comes from the current
console. The second time you wander, it comes from another console
linked by the first. The third time you wander, it comes from a
console linked by the second, and so on.  In each hop, a console is
chosen at random from those linked by the previous one.  A website
recommendation is then chosen at random from that console.  In this
way, each hop takes you deeper and deeper into the Wander network.


Setup
-----

1. Download this bundle:
   https://codeberg.org/susam/wander/archive/main.zip

2. Extract the following files:

   - `index.html`
   - `wander.js`

3. Place them on your website, for example:

   ```
   wander
   ├── index.html
   └── wander.js
   ```

4. Open `wander.js` and edit the following JavaScript object:

   ```javascript
   window.wander = {
     pages: [
     ],
     consoles: [
     ],
   }
   ```

   The value for the `pages` property is a list of websites and pages
   you recommend to the community.

   The vlaue for the `consoles` property is a list of consoles your
   console should link to.

   Please see https://susam.net/wander/wander.js for an example.

5. Once your console is live, share it with others in the following
   community thread:

   https://codeberg.org/susam/wander/issues/1

   Hopefully, someone will link to your console and then visitors to
   their console may receive recommendations from your `wander.js`.


Licence
-------

This is free and open source software.  You can use, copy, modify,
merge, publish, distribute, sublicence and/or sell copies of it, under
the terms of the MIT Licence.  See [LICENCE.md][L] for details.

This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied.  See [LICENCE.md][L] for details.

[L]: LICENCE.md
index.html
wget 'https://lists2.roe3.org/wander/index.html'
View Content
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Wander</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body {
        margin: 0;
        padding: 1em;
        line-height: 1.5;
      }
      button, input, noscript, dialog {
        font-family: courier, monospace;
        font-size: medium;
        font-weight: bold;
      }
      button, input {
        box-sizing: border-box;
        cursor: pointer;
        margin-bottom: 1em;
        margin-right: 0.25em;
        min-height: 2.5em;
        width: 6em;
      }
      header button:last-of-type {
        margin-right: 0;
      }
      input {
        padding-left: 0.5em;
        margin-bottom: 1em;
        width: 18.5em;
      }
      dialog {
        border: thick solid;
        max-width: 40em;
        max-height: 80vh;
        overflow: auto;
      }
      dialog h1 {
        font-size: 1.5em;
        margin: 0;
        text-align: center;
      }
      dialog button {
        display: block;
        margin: 2em auto 1em auto;
      }
      #urlw, #bReload, #bGo, #bCopy {
        display: none;
      }
      #urln {
        margin-right: 0;
      }
      noscript {
        font-size: x-large;
        border: medium double;
        display: block;
        padding: 1em;
        margin-bottom: 1em;
        text-align: center;
      }
      iframe {
        background: #fff;
        border: thick solid;
        box-sizing: border-box;
        display: none;
        height: calc(100vh - 12.5em);
        width: 100%;
      }
      @media (min-width: 70em) {
        iframe {
          height: calc(100vh - 5.5em);
        }
        #urln {
          display: none;
        }
        #bReload, #bGo, #bCopy, #urlw {
          display: inline-block;
        }
        #urlw {
          width: 25em;
        }
      }
      body {
        background: #696;
      }
      iframe {
        border-color: #363;
      }
      button, input, dialog, dialog button {
        color: #030;
        background: #bdb;
        border-color: #363;
      }
      button:hover, dialog button:hover {
        background: #9d9;
      }
      button:active, dialog button:active {
        background: #8c8;
      }
      noscript {
        border-color: #600;
        color: #600;
      }
      @media (prefers-color-scheme: dark) {
        body {
          background: #121;
        }
        button, input {
          color: #0c0;
          background: #010;
        }
        button:hover {
          background: #020;
        }
        button:active {
          background: #030;
        }
        noscript {
          border-color: #f30;
          color: #f30;
        }
      }
    </style>
    <script src="wander.js"></script>
    <script>
      const LOGGING = true

      function init () {
        initConsole()
        document.getElementById('bWander').addEventListener('click', wanderPage)
        document.getElementById('bConsole').addEventListener('click', wanderConsole)
        document.getElementById('bReload').addEventListener('click', reloadFrame)
        document.getElementById('bGo').addEventListener('click', goLink)
        document.getElementById('bCopy').addEventListener('click', copyLink)
        document.getElementById('bAbout').addEventListener('click', showAbout)
        document.getElementById('urln').addEventListener('keydown', keyHandler)
        document.getElementById('urlw').addEventListener('keydown', keyHandler)
        wanderPage()
      }

      function initConsole () {
        const ul = document.getElementById('wanderers')
        for (const url of window.wander.consoles) {
          const li = document.createElement('li')
          const a = document.createElement('a')
          li.append(a)
          ul.append(li)
          a.href = url
          a.textContent = url
        }
        document.getElementById('iframe').addEventListener('load', wanderDone)
      }

      function wanderPage () {
        const iframe = document.getElementById('iframe')
        const url = pick(filter(window.wander.pages))
        document.getElementById('urln').value = url
        document.getElementById('urlw').value = url
        iframe.src = url
        log('Wandering to', url)
      }

      function wanderDone () {
        const iframe = document.getElementById('iframe')
        log('Wandering complete')
        if (iframe.style.display !== 'block') {
          iframe.style.display = 'block'
          log('Displayed iframe')
        }
        loadConsole()
      }

      function wanderConsole () {
        document.getElementById('dConsole').showModal()
      }

      function loadConsole () {
        const backup = window.wanderer
        let url = pick(filter(window.wander.consoles))
        if (!url.endsWith('/')) {
          url += '/'
        }
        url += 'wander.js'
        const script = document.createElement('script')
        log('Loading console', url)
        script.addEventListener('load', function () {
          log('Loaded console', url)
        })
        script.addEventListener('error', function (e) {
          log('Failed to load console', url, e)
          window.wanderer = backup
          script.remove()
        })
        script.src = url
        document.head.append(script)
      }

      function reloadFrame () {
        const iframe = document.getElementById('iframe')
        const src = iframe.src
        iframe.src = src
      }

      function keyHandler (e) {
        if (e.key === 'Enter') {
          goLink()
        }
      }

      function goLink () {
        let value = visibleInput().value
        if (!value.toLowerCase().startsWith('https:') &&
            !value.toLowerCase().startsWith('http:')) {
          value = matchingProtocol() + '//' + value
        }
        document.getElementById('urln').value = value
        document.getElementById('urlw').value = value
        document.getElementById('iframe').src = value
      }

      function copyLink () {
        const input = visibleInput()
        input.select()
        document.execCommand('copy')
        window.setTimeout(function () { input.blur() }, 125)
      }

      function showAbout () {
        document.getElementById('dAbout').showModal()
      }

      function pick (items) {
        return items[Math.floor(Math.random() * items.length)]
      }

      function filter (items) {
        return items.filter(function (item) {
          return item.startsWith(matchingProtocol())
        })
      }

      function visibleInput () {
        const urln = document.getElementById('urln')
        const urlw = document.getElementById('urlw')
        return urln.offsetParent ? urln : urlw
      }

      function matchingProtocol () {
        let protocol = window.location.protocol
        if (protocol !== 'http:' && protocol !== 'https:') {
          protocol = 'https:'
        }
        return protocol
      }

      function log () {
        if (LOGGING) {
          const args = Array.prototype.slice.call(arguments)
          console.log('[wander] ' + args.join(' '))
        }
      }

      window.addEventListener('load', init)
    </script>
  </head>
  <body>
    <noscript>JavaScript is required to use this application.</noscript>
    <header>
      <button id="bWander">Wander</button><!--
      --><button id="bConsole">Console</button><!--
      --><button id="bReload">Reload</button><!--
      --><input id="urlw" autocomplete="off"><!--
      --><button id="bGo">Go</button><!--
      --><button id="bCopy">Copy</button><!--
      --><button id="bAbout">About</button><!--
      --><input id="urln">
    </header>
    <iframe id="iframe"></iframe>
    <dialog id="dConsole">
      <h1>Wander to Another Console</h1>
      <p>
        By wandering to another console, you will leave the current
        console on this website and reach a Wander console on a
        different website.  You can then continue wandering the Web
        using the console on that website.
      </p>
      <ul id="wanderers"></ul>
      <form method="dialog">
        <button>Close</button>
      </form>
    </dialog>
    <dialog id="dAbout">
      <h1>About Wander</h1>
      <p>
        Hello!  You are currently on a Wander console!  A Wander
        console lets you browse random websites and pages from the
        Wander community.  The Wander community consists of
        individuals who develop and maintain their own personal
        websites.
      </p>
      <p>
        To set up your own Wander console, download
        <a href="https://codeberg.org/susam/wander/archive/main.zip">this ZIP file</a>,
        extract index.html and wander.js, and place them
        in the /wander/ directory of your website. Then edit
        wander.js by following the directions at
        <a href="https://codeberg.org/susam/wander#readme">codeberg.org/susam/wander</a>.
      </p>
      <p>
        That's it! Once your /wander/ directory is ready on your web
        server, you can share a link to your Wander console in
        <a href="https://codeberg.org/susam/wander/issues/1">this
        community thread</a>.  Hopefully, someone will add your
        console to theirs and you will become part of the Wander
        network.
      </p>
      <form method="dialog">
        <button>Close</button>
      </form>
    </dialog>
  </body>
</html>
wander.js
wget 'https://lists2.roe3.org/wander/wander.js'
View Content
window.wander = {
  // Websites and pages you recommend to other wanderers.
  pages: [
    'https://midnight.pub/',
    'https://susam.net/',
    'https://susam.net/fizz-buzz-with-cosines.html'
  ],
  // Other Wander consoles that visitors can reach from your console.
  consoles: [
    'https://susam.net/wander/',
  ],
}