259 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!DOCTYPE html>
 | 
						|
<html>
 | 
						|
  <head>
 | 
						|
    <title>Tickle</title>
 | 
						|
    <meta charset="UTF-8" />
 | 
						|
    <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
						|
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
 | 
						|
    <link
 | 
						|
      href="https://fonts.googleapis.com/css2?family=Lobster&family=Nunito+Sans:ital,wght@0,400;0,700;1,400&display=swap"
 | 
						|
      rel="stylesheet"
 | 
						|
    />
 | 
						|
    <style>
 | 
						|
      :root {
 | 
						|
        --accent: hotpink;
 | 
						|
        --background: #fdfdfd;
 | 
						|
        font-family: "Nunito Sans", sans-serif;
 | 
						|
      }
 | 
						|
      body,
 | 
						|
      html {
 | 
						|
        width: 100%;
 | 
						|
        height: 100%;
 | 
						|
        margin: 0;
 | 
						|
        padding: 0;
 | 
						|
      }
 | 
						|
      .trigger {
 | 
						|
        display: none;
 | 
						|
      }
 | 
						|
      .left-nav > * {
 | 
						|
        position: fixed;
 | 
						|
        top: 0;
 | 
						|
        left: 0;
 | 
						|
      }
 | 
						|
      .left-nav > .menu {
 | 
						|
        padding: 1em;
 | 
						|
        transform: translateX(-100%);
 | 
						|
        display: flex;
 | 
						|
        flex-direction: column;
 | 
						|
        top: 0;
 | 
						|
        bottom: 0;
 | 
						|
        transition: all 0.3s ease-out;
 | 
						|
        gap: 1em;
 | 
						|
      }
 | 
						|
      .left-nav > .menu a {
 | 
						|
        text-decoration: none;
 | 
						|
        background: var(--background);
 | 
						|
        color: var(--accent);
 | 
						|
        box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
 | 
						|
        padding: 0.2em 0.4em;
 | 
						|
      }
 | 
						|
      .left-nav > .menu a:hover {
 | 
						|
        background: var(--accent);
 | 
						|
        color: var(--background);
 | 
						|
      }
 | 
						|
      .left-nav > .trigger:checked ~ .menu {
 | 
						|
        transform: translateX(0);
 | 
						|
        transition-duration: 0.1s;
 | 
						|
      }
 | 
						|
      .burger {
 | 
						|
        width: 2em;
 | 
						|
        height: 2em;
 | 
						|
        display: flex;
 | 
						|
        justify-content: center;
 | 
						|
        align-content: center;
 | 
						|
        text-align: center;
 | 
						|
        vertical-align: middle;
 | 
						|
        line-height: 2em;
 | 
						|
        border-radius: 0.2em;
 | 
						|
        box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
 | 
						|
        transition: background 0.5s ease;
 | 
						|
      }
 | 
						|
      .left-nav > .trigger:checked ~ .burger {
 | 
						|
        color: transparent;
 | 
						|
      }
 | 
						|
 | 
						|
      .left-nav > .trigger:checked ~ .burger,
 | 
						|
      #Loading {
 | 
						|
        top: 0;
 | 
						|
        right: 0;
 | 
						|
        width: 100%;
 | 
						|
        height: 100%;
 | 
						|
        border-radius: 0;
 | 
						|
        background: rgba(17, 17, 17, 0.2);
 | 
						|
      }
 | 
						|
      #Loading {
 | 
						|
        position: absolute;
 | 
						|
        text-align: center;
 | 
						|
        align-items: center;
 | 
						|
        justify-content: center;
 | 
						|
        font-size: 8rem;
 | 
						|
        color: var(--accent);
 | 
						|
        opacity: 0;
 | 
						|
        transition: opacity 0.3s ease-in, height 0s linear 0.3s;
 | 
						|
        height: 0;
 | 
						|
        display: flex;
 | 
						|
        overflow: hidden;
 | 
						|
      }
 | 
						|
      .is-loading #Loading {
 | 
						|
        opacity: 1;
 | 
						|
        height: 100%;
 | 
						|
        transition: opacity 1s ease-in, height 0 linear;
 | 
						|
      }
 | 
						|
      #Loading::after {
 | 
						|
        content: "❤";
 | 
						|
        animation: beat 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
 | 
						|
      }
 | 
						|
      @keyframes beat {
 | 
						|
        0% {
 | 
						|
          transform: scale(0.95);
 | 
						|
        }
 | 
						|
        5% {
 | 
						|
          transform: scale(1.1);
 | 
						|
        }
 | 
						|
        39% {
 | 
						|
          transform: scale(0.85);
 | 
						|
        }
 | 
						|
        45% {
 | 
						|
          transform: scale(1);
 | 
						|
        }
 | 
						|
        60% {
 | 
						|
          transform: scale(0.95);
 | 
						|
        }
 | 
						|
        100% {
 | 
						|
          transform: scale(0.9);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      #Body {
 | 
						|
        padding: 3em;
 | 
						|
        max-width: 52em;
 | 
						|
        margin: 0 auto;
 | 
						|
      }
 | 
						|
      #Body h1,
 | 
						|
      #Body h2,
 | 
						|
      #Body h3,
 | 
						|
      #Body h4 {
 | 
						|
        color: var(--accent);
 | 
						|
        font-family: "Lobster", serif;
 | 
						|
      }
 | 
						|
      #Body a {
 | 
						|
        color: var(--background);
 | 
						|
        background-color: var(--accent);
 | 
						|
        text-decoration: none;
 | 
						|
        padding: 0.1em 0.4em;
 | 
						|
      }
 | 
						|
    </style>
 | 
						|
  </head>
 | 
						|
  <body>
 | 
						|
    <nav class="left-nav">
 | 
						|
      <input id="main-nav" type="checkbox" class="trigger" />
 | 
						|
      <label for="main-nav" class="burger">≡</label>
 | 
						|
      <div id="Menu" class="menu"></div>
 | 
						|
    </nav>
 | 
						|
    <header id="Menu"></header>
 | 
						|
    <main id="Body"></main>
 | 
						|
    <div id="Loading"></div>
 | 
						|
    <script type="module">
 | 
						|
      /**
 | 
						|
       * markdown parser. Remove if you don't use markdown
 | 
						|
       */
 | 
						|
      // @ts-ignore
 | 
						|
      import { micromark } from "https://esm.sh/micromark@3?bundle";
 | 
						|
 | 
						|
      /**
 | 
						|
       * useful to check for transitions while developing styles, if the loading screen disappears too fast
 | 
						|
       *
 | 
						|
       */
 | 
						|
      const wait = (val) => new Promise((ok) => setTimeout(ok, 1, val));
 | 
						|
 | 
						|
      /**
 | 
						|
       * The elements we will need
 | 
						|
       */
 | 
						|
      const [Menu, Body, Loading] = ["Menu", "Body", "Loading"].map((id) =>
 | 
						|
        document.getElementById(id)
 | 
						|
      );
 | 
						|
 | 
						|
      /**
 | 
						|
       * cache the original title to append it to the page title
 | 
						|
       */
 | 
						|
      const mainTitle = document.title;
 | 
						|
 | 
						|
      const startLoading = () => document.body.classList.add("is-loading");
 | 
						|
      const stopLoading = () => document.body.classList.remove("is-loading");
 | 
						|
 | 
						|
      const getCurrentPage = () => {
 | 
						|
        const [path, searchStr] = (
 | 
						|
          window.location.hash[1] === "/" ? window.location.hash.slice(2) : ""
 | 
						|
        ).split("?");
 | 
						|
        const params = new URLSearchParams(searchStr);
 | 
						|
        return { path, params };
 | 
						|
      };
 | 
						|
 | 
						|
      const onHashChange = (evt) => {
 | 
						|
        const { path, params } = getCurrentPage();
 | 
						|
        if (!path) {
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
        startLoading();
 | 
						|
        return fetch(`./${path}`)
 | 
						|
          .then((response) => response.text())
 | 
						|
          .then(wait)
 | 
						|
          .then((text) => {
 | 
						|
            const [, title] = text.match(/^(#\s\w+)/) ||
 | 
						|
              text.match(/(.*?)\n===+/m) || [, path];
 | 
						|
            document.title = `${title} | ${mainTitle}`;
 | 
						|
            if (params.has("source")) {
 | 
						|
              Body.innerHTML = `<pre>${text}</pre>`;
 | 
						|
            } else {
 | 
						|
              Body.innerHTML = micromark(text);
 | 
						|
            }
 | 
						|
            stopLoading();
 | 
						|
          });
 | 
						|
      };
 | 
						|
 | 
						|
      window.addEventListener("hashchange", onHashChange);
 | 
						|
 | 
						|
      /**
 | 
						|
       * Loads the article list, parses it, creates the menu items
 | 
						|
       */
 | 
						|
      const start = () => {
 | 
						|
        startLoading();
 | 
						|
        fetch("./files.txt")
 | 
						|
          .then((response) => response.text())
 | 
						|
          .then((lines) => {
 | 
						|
            Menu.innerHTML = lines
 | 
						|
              .split(`\n`)
 | 
						|
              .map((line, index) => {
 | 
						|
                const today = new Date().toISOString().split("T")[0];
 | 
						|
                const result = line.match(
 | 
						|
                  /(?<name>.+)\.(?<ext>\w{2,3})(?:\s(?<date>[\d-]+)?(?<title>.+))?/
 | 
						|
                );
 | 
						|
                if (!result) {
 | 
						|
                  console.log(`could not parse line ${index}: [${line}]`);
 | 
						|
                  return null;
 | 
						|
                }
 | 
						|
                const {
 | 
						|
                  groups: { name, ext, date = today, title = name },
 | 
						|
                } = result;
 | 
						|
                const href = `/${name}.${ext}`;
 | 
						|
                if (!getCurrentPage().path) {
 | 
						|
                  window.location.hash = `#${href}`;
 | 
						|
                }
 | 
						|
                console.log({ name, href, date, title });
 | 
						|
                return { name, href, date, title };
 | 
						|
              })
 | 
						|
              .filter(Boolean)
 | 
						|
              .sort(({ date: a }, { date: b }) => a - b)
 | 
						|
              .map(
 | 
						|
                ({ href, title }) => `<a data-link href="#${href}">${title}</a>`
 | 
						|
              )
 | 
						|
              .join(`\n`);
 | 
						|
 | 
						|
            onHashChange();
 | 
						|
          });
 | 
						|
      };
 | 
						|
 | 
						|
      start();
 | 
						|
    </script>
 | 
						|
  </body>
 | 
						|
</html>
 |