testing moving the functionality into small modules #5
							
								
								
									
										195
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										195
									
								
								index.html
									
									
									
									
									
								
							@@ -3,199 +3,18 @@
 | 
				
			|||||||
  <head>
 | 
					  <head>
 | 
				
			||||||
    <title>Tickle</title>
 | 
					    <title>Tickle</title>
 | 
				
			||||||
    <meta charset="UTF-8" />
 | 
					    <meta charset="UTF-8" />
 | 
				
			||||||
    <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
					    <link href="./modules/templates/blog.css" rel="stylesheet" />
 | 
				
			||||||
    <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>
 | 
					 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body>
 | 
					  <body>
 | 
				
			||||||
    <nav class="left-nav">
 | 
					    <section>
 | 
				
			||||||
      <button id="Burger" class="burger">≡</button>
 | 
					      <button class="burger">≡</button>
 | 
				
			||||||
      <div id="Menu" class="menu"></div>
 | 
					      <nav class="menu"></nav>
 | 
				
			||||||
    </nav>
 | 
					    </section>
 | 
				
			||||||
    <main id="Body"></main>
 | 
					    <main></main>
 | 
				
			||||||
    <pre id="Source"></pre>
 | 
					    <pre id="Source"></pre>
 | 
				
			||||||
    <div id="Loading">
 | 
					    <div id="Loading">
 | 
				
			||||||
      <p>❤</p>
 | 
					      <p>❤</p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <script type="module" src="./modules/templateBlog.mjs"></script>
 | 
					    <script type="module" src="./modules/templates/blog.mjs"></script>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
//@ts-check
 | 
					//@ts-check
 | 
				
			||||||
export * from './tickle/index.mjs'
 | 
					export * from './tickle/index.mjs'
 | 
				
			||||||
export * from './utils/index.mjs'
 | 
					export * from './utils/index.mjs'
 | 
				
			||||||
export { default as blog } from './templateBlog.mjs'
 | 
					export * as templates from './templates/index.mjs'
 | 
				
			||||||
export { default as musings } from './templateMusings.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
 | 
					//@ts-check
 | 
				
			||||||
import { makeTemplate } from "./utils/print.mjs";
 | 
					import { makeTemplate } from "../utils/print.mjs";
 | 
				
			||||||
import { generateDomFromString } from "./utils/generateDomFromString.mjs";
 | 
					import { generateDomFromString } from "../utils/generateDomFromString.mjs";
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @typedef {import("./parseFileList.mjs").ParsedLine} ParsedLine
 | 
					 * @typedef {import("./parseFileList.mjs").ParsedLine} ParsedLine
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
//@ts-check
 | 
					//@ts-check
 | 
				
			||||||
import { documentMode } from "./utils/documentMode.mjs";
 | 
					import { documentMode } from "../utils/documentMode.mjs";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const mode = {
 | 
					export const mode = {
 | 
				
			||||||
  loading: documentMode("loading"),
 | 
					  loading: documentMode("loading"),
 | 
				
			||||||
  sourceEnable: documentMode("source-enabled"),
 | 
					  sourceEnabled: documentMode("source-enabled"),
 | 
				
			||||||
  menuOpen: documentMode("menu-open"),
 | 
					  menuOpen: documentMode("menu-open"),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
//@ts-check
 | 
					//@ts-check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { today } from "./utils/today.mjs";
 | 
					import { today } from "../utils/today.mjs";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @typedef {Exclude<ReturnType<typeof parseFileListLine>, null>} ParsedLine
 | 
					 * @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
 | 
					 * Generates valid dom elements from a string
 | 
				
			||||||
 * @param {string} htmlString
 | 
					 * @param {string} htmlString
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const generateDomFromString = (htmlString) =>{
 | 
					export const generateDomFromString = (htmlString) => {
 | 
				
			||||||
    const children = new DOMParser().parseFromString(`<div>${htmlString}</div>`, "text/html")
 | 
					  
 | 
				
			||||||
      .children
 | 
					  htmlString = htmlString.trim();
 | 
				
			||||||
    const fragment = document.createDocumentFragment()
 | 
					  const dom = new DOMParser().parseFromString('<template>'+ htmlString +'</template>','text/html')
 | 
				
			||||||
    fragment.append(...children)
 | 
					  const content = /** @type {HTMLTemplateElement} */(dom.head.firstElementChild).content
 | 
				
			||||||
    return fragment
 | 
					
 | 
				
			||||||
  ;}
 | 
					  const fragment = document.createDocumentFragment();
 | 
				
			||||||
 | 
					  fragment.append(content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fragment;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default generateDomFromString;
 | 
					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
 | 
					//@ts-check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { changeTitle } from "./changeTitle.mjs";
 | 
					import { changeTitle } from "./changeTitle.mjs";
 | 
				
			||||||
 | 
					//import { createCustomElement } from "./createCustomElement.mjs";
 | 
				
			||||||
import { documentMode } from "./documentMode.mjs";
 | 
					import { documentMode } from "./documentMode.mjs";
 | 
				
			||||||
import { documentState } from "./documentState.mjs";
 | 
					import { documentState } from "./documentState.mjs";
 | 
				
			||||||
import { fetchMarkdown } from "./fetchMarkdown.mjs";
 | 
					import { fetchMarkdown } from "./fetchMarkdown.mjs";
 | 
				
			||||||
@@ -11,6 +12,7 @@ import {
 | 
				
			|||||||
  hasCurrentHashUrl,
 | 
					  hasCurrentHashUrl,
 | 
				
			||||||
  hasNoHashUrl,
 | 
					  hasNoHashUrl,
 | 
				
			||||||
} from "./getCurrentHashUrl.mjs";
 | 
					} from "./getCurrentHashUrl.mjs";
 | 
				
			||||||
 | 
					import { getElementByCSSSelector } from "./getElementByCSSSelector.mjs";
 | 
				
			||||||
import { getElementById } from "./getElementById.mjs";
 | 
					import { getElementById } from "./getElementById.mjs";
 | 
				
			||||||
import { getFirstTitleContent } from "./getFirstTitleContent.mjs";
 | 
					import { getFirstTitleContent } from "./getFirstTitleContent.mjs";
 | 
				
			||||||
import { identity, awaitedIdentity } from "./identity.mjs";
 | 
					import { identity, awaitedIdentity } from "./identity.mjs";
 | 
				
			||||||
@@ -45,6 +47,7 @@ export {
 | 
				
			|||||||
  getCurrentHashUrl,
 | 
					  getCurrentHashUrl,
 | 
				
			||||||
  hasCurrentHashUrl,
 | 
					  hasCurrentHashUrl,
 | 
				
			||||||
  hasNoHashUrl,
 | 
					  hasNoHashUrl,
 | 
				
			||||||
 | 
					  getElementByCSSSelector,
 | 
				
			||||||
  getElementById,
 | 
					  getElementById,
 | 
				
			||||||
  getFirstTitleContent,
 | 
					  getFirstTitleContent,
 | 
				
			||||||
  html,
 | 
					  html,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user