tickle functionality restored
This commit is contained in:
		
							
								
								
									
										195
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										195
									
								
								index.html
									
									
									
									
									
								
							@@ -3,199 +3,18 @@
 | 
			
		||||
  <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;
 | 
			
		||||
      }
 | 
			
		||||
      body {
 | 
			
		||||
        padding: 3em;
 | 
			
		||||
        max-width: 52em;
 | 
			
		||||
        margin: 0 auto;
 | 
			
		||||
      }
 | 
			
		||||
      pre {
 | 
			
		||||
        margin: 0;
 | 
			
		||||
        padding: 3em;
 | 
			
		||||
        max-width: inherit;
 | 
			
		||||
        max-height: 100vh;
 | 
			
		||||
        overflow: scroll;
 | 
			
		||||
        background: rgb(192, 192, 192);
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        left: 50%;
 | 
			
		||||
        transition: all 1s cubic-bezier(0.68, -0.55, 0.265, 1.55);
 | 
			
		||||
        transform: translate(-50%, -100%);
 | 
			
		||||
      }
 | 
			
		||||
      .is-source-enabled pre {
 | 
			
		||||
        transform: translate(-50%, 0);
 | 
			
		||||
      }
 | 
			
		||||
      button {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        border: none;
 | 
			
		||||
        margin: 0;
 | 
			
		||||
        text-decoration: none;
 | 
			
		||||
        font-family: inherit;
 | 
			
		||||
        font-size: 1rem;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        align-content: center;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
        background: var(--accent);
 | 
			
		||||
        color: var(--background);
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        padding: 0.5em 1em;
 | 
			
		||||
        border-radius: 0 0 0.2em 0;
 | 
			
		||||
        box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
 | 
			
		||||
        -webkit-appearance: none;
 | 
			
		||||
        -moz-appearance: none;
 | 
			
		||||
      }
 | 
			
		||||
      h1,
 | 
			
		||||
      h2,
 | 
			
		||||
      h3,
 | 
			
		||||
      h4 {
 | 
			
		||||
        color: var(--accent);
 | 
			
		||||
        font-family: "Lobster", serif;
 | 
			
		||||
      }
 | 
			
		||||
      a {
 | 
			
		||||
        color: var(--accent);
 | 
			
		||||
        position: relative;
 | 
			
		||||
        text-decoration: none;
 | 
			
		||||
        padding: 0.1em;
 | 
			
		||||
      }
 | 
			
		||||
      a::after {
 | 
			
		||||
        content: "";
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        background-color: var(--accent);
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        bottom: 3px;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        height: 1px;
 | 
			
		||||
        z-index: -1;
 | 
			
		||||
        transition: all 0.1s ease-in;
 | 
			
		||||
      }
 | 
			
		||||
      a:hover {
 | 
			
		||||
        color: var(--background);
 | 
			
		||||
        transition: all 0.3s ease-in-out;
 | 
			
		||||
      }
 | 
			
		||||
      a:hover::after {
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        transition: all 0.3s ease-in-out;
 | 
			
		||||
      }
 | 
			
		||||
      .menu,
 | 
			
		||||
      .burger {
 | 
			
		||||
        position: fixed;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
      }
 | 
			
		||||
      .menu {
 | 
			
		||||
        padding: 1em;
 | 
			
		||||
        transform: translateX(-100%);
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        transition: all 0.3s ease-out;
 | 
			
		||||
        gap: 1em;
 | 
			
		||||
      }
 | 
			
		||||
      .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;
 | 
			
		||||
      }
 | 
			
		||||
      .menu a:hover {
 | 
			
		||||
        background: var(--accent);
 | 
			
		||||
        color: var(--background);
 | 
			
		||||
      }
 | 
			
		||||
      .is-menu-open .menu {
 | 
			
		||||
        transform: translateX(0);
 | 
			
		||||
        transition-duration: 0.1s;
 | 
			
		||||
      }
 | 
			
		||||
      .is-menu-open .burger {
 | 
			
		||||
        color: transparent;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .is-menu-open .burger,
 | 
			
		||||
      #Loading {
 | 
			
		||||
        top: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        border-radius: 0;
 | 
			
		||||
        background: rgba(17, 17, 17, 0.2);
 | 
			
		||||
        cursor: default;
 | 
			
		||||
      }
 | 
			
		||||
      #Loading {
 | 
			
		||||
        position: fixed;
 | 
			
		||||
        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;
 | 
			
		||||
      }
 | 
			
		||||
      #Loading > * {
 | 
			
		||||
        animation: load 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
 | 
			
		||||
      }
 | 
			
		||||
      .is-loading #Loading {
 | 
			
		||||
        opacity: 1;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        transition: opacity 1s ease-in, height 0 linear;
 | 
			
		||||
      }
 | 
			
		||||
      @keyframes load {
 | 
			
		||||
        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);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
    <link href="./modules/templates/blog.css" rel="stylesheet" />
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <nav class="left-nav">
 | 
			
		||||
      <button id="Burger" class="burger">≡</button>
 | 
			
		||||
      <div id="Menu" class="menu"></div>
 | 
			
		||||
    </nav>
 | 
			
		||||
    <main id="Body"></main>
 | 
			
		||||
    <section>
 | 
			
		||||
      <button class="burger">≡</button>
 | 
			
		||||
      <nav class="menu"></nav>
 | 
			
		||||
    </section>
 | 
			
		||||
    <main></main>
 | 
			
		||||
    <pre id="Source"></pre>
 | 
			
		||||
    <div id="Loading">
 | 
			
		||||
      <p>❤</p>
 | 
			
		||||
    </div>
 | 
			
		||||
    <script type="module" src="./modules/templateBlog.mjs"></script>
 | 
			
		||||
    <script type="module" src="./modules/templates/blog.mjs"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
export * from './tickle/index.mjs'
 | 
			
		||||
export * from './utils/index.mjs'
 | 
			
		||||
export { default as blog } from './templateBlog.mjs'
 | 
			
		||||
export { default as musings } from './templateMusings.mjs'
 | 
			
		||||
export * as templates from './templates/index.mjs'
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
import { fetchText } from "./utils/fetchText.mjs";
 | 
			
		||||
import { getElementById } from "./utils/getElementById.mjs";
 | 
			
		||||
import { onDocumentKeyUp } from "./utils/onDocumentKey.mjs";
 | 
			
		||||
import { parseFileList } from "./tickle/parseFileList.mjs";
 | 
			
		||||
import {createMenuEntriesFromFileList} from "./tickle/createMenuEntriesFromFileList.mjs"
 | 
			
		||||
import { sortFileListLines } from "./tickle/sortFileListLines.mjs"
 | 
			
		||||
import { mode } from "./tickle/mode.mjs";
 | 
			
		||||
import {bootstrapRouter} from "./tickle/bootstrapRouter.mjs";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Loads the article list, parses it, creates the menu items
 | 
			
		||||
*/
 | 
			
		||||
export const bootstrap = () => {
 | 
			
		||||
  
 | 
			
		||||
  const [Menu, Body, Source, Burger] = ["Menu", "Body", "Source", "Burger"].map(
 | 
			
		||||
    getElementById
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  Burger.addEventListener("click", mode.menuOpen.toggle);
 | 
			
		||||
 | 
			
		||||
  mode.loading.on();
 | 
			
		||||
  fetchText("./files.txt").then((lines)=>{
 | 
			
		||||
    const links = parseFileList(lines)
 | 
			
		||||
    const firstHref = links[0].href;
 | 
			
		||||
    sortFileListLines(links)
 | 
			
		||||
    Menu.appendChild(createMenuEntriesFromFileList(links))
 | 
			
		||||
    bootstrapRouter(firstHref, (content, raw)=>{
 | 
			
		||||
      Body.innerHTML = "";
 | 
			
		||||
      Source.innerHTML = raw;
 | 
			
		||||
      Body.appendChild(content);
 | 
			
		||||
    })
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  onDocumentKeyUp("?", mode.sourceEnable.toggle);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default bootstrap;
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
import { fetchText } from "./utils/fetchText.mjs";
 | 
			
		||||
import { getElementById } from "./utils/getElementById.mjs";
 | 
			
		||||
import { onDocumentKeyUp } from "./utils/onDocumentKey.mjs";
 | 
			
		||||
import { parseFileList } from "./tickle/parseFileList.mjs";
 | 
			
		||||
import {createMenuEntriesFromFileList} from "./tickle/createMenuEntriesFromFileList.mjs"
 | 
			
		||||
import { sortFileListLines } from "./tickle/sortFileListLines.mjs"
 | 
			
		||||
import { mode } from "./tickle/mode.mjs";
 | 
			
		||||
import {bootstrapRouter} from "./tickle/bootstrapRouter.mjs";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Loads the article list, parses it, creates the menu items
 | 
			
		||||
*/
 | 
			
		||||
export const bootstrap = () => {
 | 
			
		||||
  
 | 
			
		||||
  const [Menu, Body, Source, Burger] = ["Menu", "Body", "Source", "Burger"].map(
 | 
			
		||||
    getElementById
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  Burger.addEventListener("click", mode.menuOpen.toggle);
 | 
			
		||||
 | 
			
		||||
  mode.loading.on();
 | 
			
		||||
  fetchText("./files.txt").then((lines)=>{
 | 
			
		||||
    const links = parseFileList(lines)
 | 
			
		||||
    const firstHref = links[0].href;
 | 
			
		||||
    sortFileListLines(links)
 | 
			
		||||
    Menu.appendChild(createMenuEntriesFromFileList(links))
 | 
			
		||||
    bootstrapRouter(firstHref, (content, raw)=>{
 | 
			
		||||
      Body.innerHTML = "";
 | 
			
		||||
      Source.innerHTML = raw;
 | 
			
		||||
      Body.appendChild(content);
 | 
			
		||||
    })
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  onDocumentKeyUp("?", mode.sourceEnable.toggle);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default bootstrap;
 | 
			
		||||
							
								
								
									
										177
									
								
								modules/templates/blog.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								modules/templates/blog.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
			
		||||
@import url('https://fonts.bunny.net/css2?family=Lobster&family=Nunito+Sans:ital,wght@0,400;0,700;1,400&display=swap');
 | 
			
		||||
 | 
			
		||||
:root {
 | 
			
		||||
  --accent: hotpink;
 | 
			
		||||
  --background: #fdfdfd;
 | 
			
		||||
  font-family: "Nunito Sans", sans-serif;
 | 
			
		||||
}
 | 
			
		||||
body,
 | 
			
		||||
html {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
body {
 | 
			
		||||
  padding: 3em;
 | 
			
		||||
  max-width: 52em;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
}
 | 
			
		||||
pre {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 3em;
 | 
			
		||||
  max-width: inherit;
 | 
			
		||||
  max-height: 100vh;
 | 
			
		||||
  overflow: scroll;
 | 
			
		||||
  background: rgb(192, 192, 192);
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transition: all 1s cubic-bezier(0.68, -0.55, 0.265, 1.55);
 | 
			
		||||
  transform: translate(-50%, -100%);
 | 
			
		||||
}
 | 
			
		||||
.is-source-enabled pre {
 | 
			
		||||
  transform: translate(-50%, 0);
 | 
			
		||||
}
 | 
			
		||||
button {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  border: none;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  font-family: inherit;
 | 
			
		||||
  font-size: 1rem;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-content: center;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
  background: var(--accent);
 | 
			
		||||
  color: var(--background);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  padding: 0.5em 1em;
 | 
			
		||||
  border-radius: 0 0 0.2em 0;
 | 
			
		||||
  box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
 | 
			
		||||
  appearance: none;
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
  -moz-appearance: none;
 | 
			
		||||
}
 | 
			
		||||
h1,
 | 
			
		||||
h2,
 | 
			
		||||
h3,
 | 
			
		||||
h4 {
 | 
			
		||||
  color: var(--accent);
 | 
			
		||||
  font-family: "Lobster", serif;
 | 
			
		||||
}
 | 
			
		||||
a {
 | 
			
		||||
  color: var(--accent);
 | 
			
		||||
  position: relative;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  padding: 0.1em;
 | 
			
		||||
}
 | 
			
		||||
a::after {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  background-color: var(--accent);
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  bottom: 3px;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 1px;
 | 
			
		||||
  z-index: -1;
 | 
			
		||||
  transition: all 0.1s ease-in;
 | 
			
		||||
}
 | 
			
		||||
a:hover {
 | 
			
		||||
  color: var(--background);
 | 
			
		||||
  transition: all 0.3s ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
a:hover::after {
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  transition: all 0.3s ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
.menu,
 | 
			
		||||
.burger {
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
}
 | 
			
		||||
.menu {
 | 
			
		||||
  padding: 1em;
 | 
			
		||||
  transform: translateX(-100%);
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  transition: all 0.3s ease-out;
 | 
			
		||||
  gap: 1em;
 | 
			
		||||
}
 | 
			
		||||
.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;
 | 
			
		||||
}
 | 
			
		||||
.menu a:hover {
 | 
			
		||||
  background: var(--accent);
 | 
			
		||||
  color: var(--background);
 | 
			
		||||
}
 | 
			
		||||
.is-menu-open .menu {
 | 
			
		||||
  transform: translateX(0);
 | 
			
		||||
  transition-duration: 0.1s;
 | 
			
		||||
}
 | 
			
		||||
.is-menu-open .burger {
 | 
			
		||||
  color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-menu-open .burger,
 | 
			
		||||
#Loading {
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  border-radius: 0;
 | 
			
		||||
  background: rgba(17, 17, 17, 0.2);
 | 
			
		||||
  cursor: default;
 | 
			
		||||
}
 | 
			
		||||
#Loading {
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
#Loading > * {
 | 
			
		||||
  animation: load 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
 | 
			
		||||
}
 | 
			
		||||
.is-loading #Loading {
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  transition: opacity 1s ease-in, height 0 linear;
 | 
			
		||||
}
 | 
			
		||||
@keyframes load {
 | 
			
		||||
  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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								modules/templates/blog.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								modules/templates/blog.mjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
import { fetchText } from "../utils/fetchText.mjs";
 | 
			
		||||
import { getElementByCSSSelector } from "../utils/getElementByCSSSelector.mjs";
 | 
			
		||||
import { onDocumentKeyUp } from "../utils/onDocumentKey.mjs";
 | 
			
		||||
import { parseFileList } from "../tickle/parseFileList.mjs";
 | 
			
		||||
import { createMenuEntriesFromFileList } from "../tickle/createMenuEntriesFromFileList.mjs";
 | 
			
		||||
import { sortFileListLines } from "../tickle/sortFileListLines.mjs";
 | 
			
		||||
import { mode } from "../tickle/mode.mjs";
 | 
			
		||||
import { bootstrapRouter } from "../tickle/bootstrapRouter.mjs";
 | 
			
		||||
 | 
			
		||||
export const bootstrap = async () => {
 | 
			
		||||
  const [Menu, Body, Source, Burger] = [
 | 
			
		||||
    "nav",
 | 
			
		||||
    "main",
 | 
			
		||||
    "#Source",
 | 
			
		||||
    ".burger",
 | 
			
		||||
  ].map(getElementByCSSSelector);
 | 
			
		||||
 | 
			
		||||
  Burger.addEventListener("click", mode.menuOpen.toggle);
 | 
			
		||||
 | 
			
		||||
  mode.loading.on();
 | 
			
		||||
  const lines = await fetchText("../files.txt");
 | 
			
		||||
  const links = parseFileList(lines);
 | 
			
		||||
  const firstHref = links[0].href;
 | 
			
		||||
  sortFileListLines(links);
 | 
			
		||||
  Menu.appendChild(createMenuEntriesFromFileList(links));
 | 
			
		||||
  
 | 
			
		||||
  bootstrapRouter(firstHref, (content, raw) => {
 | 
			
		||||
    Body.innerHTML = "";
 | 
			
		||||
    Source.innerHTML = raw;
 | 
			
		||||
    Body.appendChild(content);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  onDocumentKeyUp("?", mode.sourceEnabled.toggle);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default bootstrap;
 | 
			
		||||
 | 
			
		||||
if (!new URL(import.meta.url).searchParams.has("load")) {
 | 
			
		||||
  bootstrap();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								modules/templates/index.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								modules/templates/index.mjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
export { default as blog } from './blog.mjs'
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
import { makeTemplate } from "./utils/print.mjs";
 | 
			
		||||
import { generateDomFromString } from "./utils/generateDomFromString.mjs";
 | 
			
		||||
import { makeTemplate } from "../utils/print.mjs";
 | 
			
		||||
import { generateDomFromString } from "../utils/generateDomFromString.mjs";
 | 
			
		||||
/**
 | 
			
		||||
 * @typedef {import("./parseFileList.mjs").ParsedLine} ParsedLine
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
import { documentMode } from "./utils/documentMode.mjs";
 | 
			
		||||
import { documentMode } from "../utils/documentMode.mjs";
 | 
			
		||||
 | 
			
		||||
export const mode = {
 | 
			
		||||
  loading: documentMode("loading"),
 | 
			
		||||
  sourceEnable: documentMode("source-enabled"),
 | 
			
		||||
  sourceEnabled: documentMode("source-enabled"),
 | 
			
		||||
  menuOpen: documentMode("menu-open"),
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
 | 
			
		||||
import { today } from "./utils/today.mjs";
 | 
			
		||||
import { today } from "../utils/today.mjs";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @typedef {Exclude<ReturnType<typeof parseFileListLine>, null>} ParsedLine
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								modules/utils/createCustomElement.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								modules/utils/createCustomElement.mjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
 | 
			
		||||
const createOptions = () => ({
 | 
			
		||||
  name: "my-custom-element",
 | 
			
		||||
  css: ":host{}",
 | 
			
		||||
  html: "",
 | 
			
		||||
  ParentClass: HTMLElement,
 | 
			
		||||
  observedAttributes: /** @type {string[]}*/ ([]),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * WIP: minimal API for custom elements. Do not use!
 | 
			
		||||
 * @typedef {ReturnType<typeof createOptions>} Options
 | 
			
		||||
 * @param {Partial<Options>} [options]
 | 
			
		||||
 */
 | 
			
		||||
export const createCustomElement = (options) => {
 | 
			
		||||
  const {
 | 
			
		||||
    name,
 | 
			
		||||
    css,
 | 
			
		||||
    html,
 | 
			
		||||
    observedAttributes: attrs,
 | 
			
		||||
    ParentClass,
 | 
			
		||||
  } = { ...createOptions(), ...options };
 | 
			
		||||
 | 
			
		||||
  class CustomClass extends ParentClass {
 | 
			
		||||
    static template = document.createElement("template");
 | 
			
		||||
    static stylesheet = new CSSStyleSheet();
 | 
			
		||||
 | 
			
		||||
    constructor(){
 | 
			
		||||
      super()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Registers the custom element. If it was already registered, this is a no-op.
 | 
			
		||||
     * @param {string} tag
 | 
			
		||||
     */
 | 
			
		||||
    static define(tag = name) {
 | 
			
		||||
      if (!customElements.get(tag)) {
 | 
			
		||||
        customElements.define(tag, this);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static get observedAttributes() {
 | 
			
		||||
      return attrs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attributeChangedCallback(property, oldValue, newValue) {
 | 
			
		||||
      if (oldValue === newValue) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      this[property] = newValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @type {AbortController|null} */
 | 
			
		||||
    _abortController = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If no <template> tag is provided in the page, this uses the parent classe's
 | 
			
		||||
     * template and css to create markup
 | 
			
		||||
     * Use this in the constructor
 | 
			
		||||
     */
 | 
			
		||||
    _autoCreateShadow(){
 | 
			
		||||
      if (!this.shadowRoot) {
 | 
			
		||||
        const { stylesheet, template } =
 | 
			
		||||
          Object.getPrototypeOf(this).constructor;
 | 
			
		||||
        this.shadowRoot = this.attachShadow({ mode: "open" });
 | 
			
		||||
        this.shadowRoot.adoptedStyleSheets = [stylesheet];
 | 
			
		||||
        this.shadowRoot.replaceChildren(template.content.cloneNode(true));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getAbortSignal(){
 | 
			
		||||
      if(!this._abortController){
 | 
			
		||||
        this._abortController = new AbortController()
 | 
			
		||||
      }
 | 
			
		||||
      return this._abortController.signal
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Aborts any event that used the AbortSignal.
 | 
			
		||||
     * Use this in the `disconnectedCallback` call
 | 
			
		||||
     */
 | 
			
		||||
    _abort(){
 | 
			
		||||
      this._abortController && this._abortController.abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const x = new CustomClass();
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  CustomClass.template.innerHTML = html;
 | 
			
		||||
  CustomClass.stylesheet.replaceSync(css);
 | 
			
		||||
  Object.defineProperty(CustomClass, "name", { value: name.replace(/-/s,'') });
 | 
			
		||||
  return CustomClass;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default createCustomElement
 | 
			
		||||
@@ -4,12 +4,39 @@
 | 
			
		||||
 * Generates valid dom elements from a string
 | 
			
		||||
 * @param {string} htmlString
 | 
			
		||||
 */
 | 
			
		||||
export const generateDomFromString = (htmlString) =>{
 | 
			
		||||
    const children = new DOMParser().parseFromString(`<div>${htmlString}</div>`, "text/html")
 | 
			
		||||
      .children
 | 
			
		||||
    const fragment = document.createDocumentFragment()
 | 
			
		||||
    fragment.append(...children)
 | 
			
		||||
    return fragment
 | 
			
		||||
  ;}
 | 
			
		||||
export const generateDomFromString = (htmlString) => {
 | 
			
		||||
  
 | 
			
		||||
  htmlString = htmlString.trim();
 | 
			
		||||
  const dom = new DOMParser().parseFromString('<template>'+ htmlString +'</template>','text/html')
 | 
			
		||||
  const content = /** @type {HTMLTemplateElement} */(dom.head.firstElementChild).content
 | 
			
		||||
 | 
			
		||||
  const fragment = document.createDocumentFragment();
 | 
			
		||||
  fragment.append(content);
 | 
			
		||||
 | 
			
		||||
  return fragment;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default generateDomFromString;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {String} html representing a single element
 | 
			
		||||
 * @return {Element|null}
 | 
			
		||||
 */
 | 
			
		||||
function htmlToElement(html) {
 | 
			
		||||
  html = html.trim();
 | 
			
		||||
  var template = document.createElement("template");
 | 
			
		||||
  template.innerHTML = html;
 | 
			
		||||
  if (template.content.childNodes.length) {
 | 
			
		||||
  }
 | 
			
		||||
  return /** @type {Element} */ (template.content.firstChild);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {String} html representing any number of sibling elements
 | 
			
		||||
 * @return {NodeList}
 | 
			
		||||
 */
 | 
			
		||||
function htmlToElements(html) {
 | 
			
		||||
  var template = document.createElement("template");
 | 
			
		||||
  template.innerHTML = html;
 | 
			
		||||
  return template.content.childNodes;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								modules/utils/getElementByCSSSelector.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								modules/utils/getElementByCSSSelector.mjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
import { isLocalHost } from "./isLocalHost.mjs";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets an element by a valid selector if the element exists, otherwise throws, but only if running in localhost environments.
 | 
			
		||||
 * Use this in the initial setup to verify all elements exist
 | 
			
		||||
 * @param {string} selector
 | 
			
		||||
 * @return {HTMLElement}
 | 
			
		||||
 */
 | 
			
		||||
export const getElementByCSSSelector = (selector) => {
 | 
			
		||||
  const element = document && document.querySelector(selector);
 | 
			
		||||
  if (isLocalHost && !element) {
 | 
			
		||||
    throw new Error(`Element "#${selector}" was not found`);
 | 
			
		||||
  }
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  return element;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default getElementByCSSSelector
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
//@ts-check
 | 
			
		||||
 | 
			
		||||
import { changeTitle } from "./changeTitle.mjs";
 | 
			
		||||
//import { createCustomElement } from "./createCustomElement.mjs";
 | 
			
		||||
import { documentMode } from "./documentMode.mjs";
 | 
			
		||||
import { documentState } from "./documentState.mjs";
 | 
			
		||||
import { fetchMarkdown } from "./fetchMarkdown.mjs";
 | 
			
		||||
@@ -11,6 +12,7 @@ import {
 | 
			
		||||
  hasCurrentHashUrl,
 | 
			
		||||
  hasNoHashUrl,
 | 
			
		||||
} from "./getCurrentHashUrl.mjs";
 | 
			
		||||
import { getElementByCSSSelector } from "./getElementByCSSSelector.mjs";
 | 
			
		||||
import { getElementById } from "./getElementById.mjs";
 | 
			
		||||
import { getFirstTitleContent } from "./getFirstTitleContent.mjs";
 | 
			
		||||
import { identity, awaitedIdentity } from "./identity.mjs";
 | 
			
		||||
@@ -45,6 +47,7 @@ export {
 | 
			
		||||
  getCurrentHashUrl,
 | 
			
		||||
  hasCurrentHashUrl,
 | 
			
		||||
  hasNoHashUrl,
 | 
			
		||||
  getElementByCSSSelector,
 | 
			
		||||
  getElementById,
 | 
			
		||||
  getFirstTitleContent,
 | 
			
		||||
  html,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user