summaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
Diffstat (limited to 'public')
-rw-r--r--public/package.json32
-rw-r--r--public/src/api.tsx39
-rw-r--r--public/src/components.tsx257
-rw-r--r--public/src/header.tsx10
-rw-r--r--public/src/home.tsx11
-rw-r--r--public/src/index-page.tsx10
-rw-r--r--public/src/index.html33
-rw-r--r--public/src/jsx.tsx338
-rw-r--r--public/src/tsconfig.json121
-rw-r--r--public/src/user.tsx6
-rw-r--r--public/src/util.tsx60
-rw-r--r--public/static/fonts/LICENSE.TXT428
-rw-r--r--public/static/fonts/Web437_DOS-V_re_ANK16.woffbin0 -> 11200 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_ANK19.woffbin0 -> 11212 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_ANK24.woffbin0 -> 12792 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_ANK30.woffbin0 -> 13028 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_JPN12.woffbin0 -> 9516 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_JPN16.woffbin0 -> 11432 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_JPN19.woffbin0 -> 11528 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_JPN24.woffbin0 -> 13016 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_JPN30.woffbin0 -> 13248 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_PRC16.woffbin0 -> 11372 bytes
-rw-r--r--public/static/fonts/Web437_DOS-V_re_PRC19.woffbin0 -> 11412 bytes
-rw-r--r--public/static/fonts/Web437_IBM_PGC.woffbin0 -> 9760 bytes
-rw-r--r--public/static/highlight.css7
-rw-r--r--public/static/icon.icobin0 -> 7594 bytes
-rw-r--r--public/static/main.css788
-rw-r--r--public/static/networkheaven.jpgbin0 -> 75906 bytes
-rw-r--r--public/static/networkheaven.pngbin0 -> 900513 bytes
-rw-r--r--public/static/nh.jpgbin0 -> 10349 bytes
-rw-r--r--public/static/nh.pngbin0 -> 49370 bytes
-rw-r--r--public/tsconfig.json117
-rw-r--r--public/types/custom.d.ts8
-rw-r--r--public/webpack-dev.config.cjs46
-rw-r--r--public/webpack-prod.config.cjs49
35 files changed, 2360 insertions, 0 deletions
diff --git a/public/package.json b/public/package.json
new file mode 100644
index 0000000..4418f02
--- /dev/null
+++ b/public/package.json
@@ -0,0 +1,32 @@
+{
+ "type": "module",
+ "name": "jqueryjsx",
+ "version": "1.0.0",
+ "main": "index.js",
+ "scripts": {
+ "build": "webpack --config webpack-prod.config.cjs",
+ "start": "webpack serve --open --config webpack-dev.config.cjs"
+ },
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "dependencies": {
+ "@types/jquery": "^3.5.32",
+ "@types/react": "^18.3.12",
+ "copy-webpack-plugin": "^12.0.2",
+ "css-loader": "^7.1.2",
+ "highlight.js": "^11.11.1",
+ "html-webpack-plugin": "^5.6.3",
+ "jquery": "^4.0.0-beta.2",
+ "jsx-runtime": "^1.2.0",
+ "nakedjsx": "^0.17.2",
+ "style-loader": "^4.0.0",
+ "typescript": "^5.6.3"
+ },
+ "devDependencies": {
+ "ts-loader": "^9.5.1",
+ "webpack": "^5.95.0",
+ "webpack-cli": "^5.1.4",
+ "webpack-dev-server": "^5.1.0"
+ }
+}
diff --git a/public/src/api.tsx b/public/src/api.tsx
new file mode 100644
index 0000000..fa941ec
--- /dev/null
+++ b/public/src/api.tsx
@@ -0,0 +1,39 @@
+export const url = "https://forum.networkheaven.net/api/";
+
+export interface ReqParams {
+ method: string,
+ body?: string,
+}
+
+export async function post( endpoint: string, body: Object ) {
+ return await req( endpoint, {
+ method: "POST",
+ body: JSON.stringify( body ),
+ } );
+}
+
+export async function req( endpoint: string, params: ReqParams ) {
+ const res = await fetch( `${url}/${endpoint}`, {
+ method: params.method,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: params.body,
+ } );
+
+ if( !res.ok ) {
+ let json = null;
+ try {
+ json = await res.json();
+ } catch( e: any ) {
+ throw new Error( "error contacting server" );
+ }
+
+ throw new Error( json.msg );
+ }
+
+ const json = await res.json();
+ if( json.status != 'ok' )
+ throw new Error( json.msg );
+ return json;
+}
diff --git a/public/src/components.tsx b/public/src/components.tsx
new file mode 100644
index 0000000..689179a
--- /dev/null
+++ b/public/src/components.tsx
@@ -0,0 +1,257 @@
+import $ from "jquery";
+import * as JSX from "./jsx";
+import { Header } from "./header";
+
+const popup_stack = [];
+window.onclick = ( e: Event ) => {
+ if( !e.target ) return;
+
+ if( popup_stack.length > 0 ) {
+ const last = popup_stack[popup_stack.length - 1];
+ if( last.el[0] == e.target || last.el.find( e.target ).length != 0 ) return;
+ e.preventDefault();
+ e.stopPropagation();
+ if( last.fn )
+ last.fn();
+
+ popup_stack.pop();
+ last.el.remove();
+ }
+}
+
+/**
+ * appends an element to the DOM and saves it in the popup stack to be removed.
+**/
+export function addPopup( element: any ) {
+ document.body.appendChild( element[0] );
+ setTimeout( () => popup_stack.push( { el: element, fn: null } ) );
+}
+
+/**
+ * closes the topmost popup
+**/
+export function closePopup() {
+ if( popup_stack.length > 0 ) {
+ const last = popup_stack[popup_stack.length - 1];
+ if( last.fn )
+ last.fn();
+
+ popup_stack.pop();
+ setTimeout( () => last.el.remove() );
+ }
+}
+
+/**
+ * sets a callback that will get executed once the current popup is closed
+ **/
+export function onPopupClosed( fn: Function ) {
+ setTimeout( () => {
+ if( popup_stack.length > 0 ) {
+ popup_stack[popup_stack.length - 1].fn = fn;
+ }
+ } );
+}
+
+/*
+ * accepts "folded" prop
+**/
+export function RolldownListItem( props: any ) {
+ const ret = <div class="rolldown">
+ <div class="rolldown-collapsed-container">
+ <div class="rolldown-icon-container">&gt;</div>
+ <div class="rolldown-collapsed">{props.folded}</div>
+ </div>
+ <div class="rolldown-expanded-container">
+ <div class="rolldown-expanded">{props.children}</div>
+ </div>
+ </div>;
+
+ ret.onclick = () => {
+ $( ret ).toggleClass( "active" );
+ $( ret ).find( ".rolldown-expanded-container" ).toggleClass( 'active' );
+ };
+
+ return ret;
+}
+
+export function Spinner( props: any ) {
+ let spinner_steps = [
+ '/',
+ '-',
+ '\\',
+ '|',
+ '/',
+ '-',
+ '\\',
+ '|'
+ ];
+
+ const id = props.id || '';
+
+ let el = $( <div class="spinner" id={ id } style={ props.style || '' } /> );
+ let i = 0;
+ let loop = () => {
+ el.text( spinner_steps[i++] );
+ if( i > spinner_steps.length )
+ i = 0;
+
+ if( el[0].isConnected )
+ setTimeout( loop, 100 );
+ };
+
+ setTimeout( loop, 100 );
+ return el[0];
+}
+
+/*
+ * accepts title prop
+**/
+export function RolldownList( props: any ) {
+ return <GroupBox title={props.title} style={props.style} innerStyle="margin: 0px; width: 100%">
+ <div class="rolldownlist">
+ { props.children }
+ </div>
+ </GroupBox>
+}
+
+/*
+ * accepts title prop and optional innerStyle
+**/
+export function GroupBox( props: any ) {
+ return <div class="groupbox" id={props.id || ''} style={props.style || ''}>
+ <span class="grouptitle">
+ {props.title}
+ </span>
+ <span class="groupbody" style={props.innerStyle || ''}>
+ {props.children}
+ </span>
+ </div>
+}
+
+export function Page( props: any ) {
+ return <>
+ <Header />
+ <div id="page-main">
+ <div id="page-main-content">
+ {props.children}
+ </div>
+ </div>
+
+ <BackgroundToggle />
+ </>
+}
+
+export function DropdownItem( props: any ) {
+ return <div class="dropdown-inner" style={ props.style || "" } onclick={ props.onclick || null }>
+ {props.children}
+ </div>
+}
+
+/**
+ * supports innerStyle for styling the actual dropdown picker
+ **/
+export function Dropdown( props: any ) {
+ const children = props.children;
+ let title = props.title;
+ let inline = props.inline;
+ let onchange = props.onchange;
+ let classes = props.class || '';
+ let style = props.style || "";
+ let id = props.id || '';
+ let innerStyle = props.innerStyle || "";
+
+ const showItems = ( e: Event ) => {
+ e.preventDefault();
+ const target = $( e.target as HTMLElement );
+
+ const newDropdown = $( <div class="dropdown-wrapper" style={ innerStyle }>
+ { children.map( ( child: HTMLElement ) => {
+ if( !child.onclick ) child.onclick = onchange; return child;
+ } ) }
+ </div> );
+ target.parent().append( newDropdown[0] );
+ setTimeout( () => popup_stack.push( { el: newDropdown, fn: null } ) );
+ }
+
+ if( inline ) {
+ return <button class={ 'dropdown' + ' ' + classes } style={ style } onclick={ showItems } id={ id }>
+ { title }
+ </button>
+ }
+ else {
+ return <>
+ <label>{ title }</label>
+ <button class={ 'dropdown' + ' ' + classes } style={ style } onclick={ showItems } id={ id }>
+ </button>
+ </>
+ }
+}
+
+export function OkPopup( props: any ) {
+ const children = props.children;
+ const classes = props.class || '';
+ const style = props.style || '';
+ const id = props.id || '';
+
+ const onclick = ( e: Event ) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if( props.onclick )
+ props.onclick();
+
+ closePopup();
+ }
+
+ return <div class={ "popup-msg " + classes } id={ id } style={ style }>
+ { children }
+ <div style="display: flex; justify-content: center">
+ <button onclick={ onclick }>Ok</button>
+ </div>
+ </div>
+}
+
+export function OkCancelPopup( props: any ) {
+ const children = props.children;
+ const classes = props.class || '';
+ const style = props.style || '';
+ const id = props.id || '';
+
+ const onclick = ( e: Event ) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if( props.onclick )
+ props.onclick();
+
+ closePopup();
+ }
+
+ const oncancel = ( e: Event ) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ closePopup();
+ }
+
+ return <div class={ "popup-msg " + classes } id={ id } style={ style }>
+ { children }
+ <div style="display: flex; justify-content: center">
+ <button onclick={ onclick } style="margin-right: 10px">Ok</button>
+ <button onclick={ oncancel }>Cancel</button>
+ </div>
+ </div>
+}
+
+export function BackgroundToggle() {
+ const toggleBackground = () => {
+ const main = $( "#page-main" );
+ if( main.css( "display" ) == "none" ) {
+ main.css( "display", "" );
+ } else {
+ main.css( "display", "none" );
+ }
+ }
+
+ return <div id="background-toggle" onclick={ toggleBackground }>
+ show background
+ </div>
+}
diff --git a/public/src/header.tsx b/public/src/header.tsx
new file mode 100644
index 0000000..731241d
--- /dev/null
+++ b/public/src/header.tsx
@@ -0,0 +1,10 @@
+import $ from 'jquery';
+import * as JSX from './jsx';
+
+export function Header( props: any ) {
+ return <div class="border-wrapper" style="z-index: 1">
+ <div class="header">
+ forum.networkheaven.net
+ </div>
+ </div>
+}
diff --git a/public/src/home.tsx b/public/src/home.tsx
new file mode 100644
index 0000000..eb7a674
--- /dev/null
+++ b/public/src/home.tsx
@@ -0,0 +1,11 @@
+import $ from "jquery";
+import * as JSX from "./jsx";
+import { Page } from "./components";
+
+export default function Home() {
+ return <Page>
+ <div class="page-title">
+ <h3 style="font-family: JPN24; width: max-content" class="gradient">NETWORKHEAVEN</h3>
+ </div>
+ </Page>;
+}
diff --git a/public/src/index-page.tsx b/public/src/index-page.tsx
new file mode 100644
index 0000000..dabaf05
--- /dev/null
+++ b/public/src/index-page.tsx
@@ -0,0 +1,10 @@
+import * as JSX from "./jsx";
+import Home from "./home";
+
+JSX.setDefaultTitle( "forum.networkheaven.net" );
+JSX.addRoute( "/", () => <Home /> );
+
+document.head.appendChild( <link rel="shortcut icon" href="/static/icon.ico" /> );
+
+const url = new URL( window.location.href );
+JSX.navigateParams( url.pathname, url.searchParams.entries() );
diff --git a/public/src/index.html b/public/src/index.html
new file mode 100644
index 0000000..215dd8f
--- /dev/null
+++ b/public/src/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="description" content="networkheaven forum">
+ <meta name="viewport" content="user-scalable=yes">
+ <title>forum.networkheaven.net</title>
+ <link rel="stylesheet" href="/static/main.css">
+ <link href="/static/highlight.css" rel="preload" as="style" onload="this.rel='stylesheet'">
+ <link rel="icon" href="data:image/png;base64,iVBORw0KGgo=">
+ <noscript>
+ <link rel="shortcut icon" href="/static/icon.ico" type="image">
+ </noscript>
+ </head>
+ <body>
+ <div id="moneyjsx-root">
+ <div id="homepage" style="padding-top: 0px">
+ <noscript>
+
+ <!--- for browsers with noscript !--->
+ <div id="ascii-art">
+
+ </div>
+ <h3 style="margin-top: 5px;">forum.networkheaven.net</h3>
+ <div>
+ you need javascript to use this website
+ </div>
+ </noscript>
+ </div>
+ </div>
+ </body>
+</html>
+
diff --git a/public/src/jsx.tsx b/public/src/jsx.tsx
new file mode 100644
index 0000000..db46f15
--- /dev/null
+++ b/public/src/jsx.tsx
@@ -0,0 +1,338 @@
+import $ from 'jquery';
+const assetAttributeNames = new Set( ['data', 'srcset', 'src', 'href'] );
+
+let curRoute = null;
+export interface Route {
+ path: string,
+ component: Function,
+ wildcard: boolean
+};
+
+const routes: Route[] = [];
+let err404page = "/";
+let rootId = "moneyjsx-root";
+let defaultTitle = "";
+let onprenavigate: Function = () => {};
+let onpostnavigate: Function = () => {};
+
+function routeForPath( route: string ) : Function | null {
+ if( !routes[route] ) {
+ for( let key of Object.keys( routes ) ) {
+ const r = routes[key];
+
+ if( r.wildcard ) {
+ if( route.slice( 0, r.path.length ) == r.path ) {
+ return r.component;
+ }
+ }
+ }
+
+ return null;
+ } else return routes[route].component;
+}
+
+/**
+ * sets the id of the element to be replaced by the navigator
+ **/
+export function setRootId( rootId: string ) {
+ rootId = rootId;
+}
+
+export function setDefaultTitle( title: string ) {
+ defaultTitle = title;
+}
+
+/**
+ * adds a route component to the routes list
+ * the component function must return either a jquery or a DOM element
+ **/
+export function addRoute( name: string, component: Function ) {
+ let path = name;
+ let wildcard = false;
+ if( path[path.length - 1] === "*" ) {
+ path = path.substring( 0, path.length - 1 );
+ wildcard = true;
+ }
+
+ routes[path] = { path, component, wildcard };
+}
+
+/**
+ * sets the route for a 404 page
+ **/
+export function set404Route( name: string ) {
+ err404page = name;
+}
+
+/**
+ * sets the callback that will get called when a route changes
+ **/
+export function onPreNavigate( callback: Function ) {
+ onprenavigate = callback;
+}
+
+/**
+ * sets the callback that will get called when a route changes
+ **/
+export function onPostNavigate( callback: Function ) {
+ onpostnavigate = callback;
+}
+
+
+/**
+ * replaces the root element with the route component
+ **/
+export function navigate( route: string ) {
+ let url = new URL( window.location.href );
+ let cb = routeForPath( route );
+ url.pathname = route;
+ if( !cb )
+ return navigate( err404page );
+
+ if( curRoute != cb )
+ window.history.pushState( {}, null, url.href );
+
+ onprenavigate();
+ setTitle();
+ const el = $( cb() );
+ curRoute = cb;
+
+ $( `#${rootId}` ).children().remove();
+ $( `#${rootId}` ).append( el );
+ onpostnavigate();
+}
+
+/**
+ * navigate with params. see: navigate
+ **/
+export function navigateParams( route: string, params: any ) {
+ let url = new URL( window.location.href );
+ let uparams = new URLSearchParams( params );
+ url.pathname = route;
+ url.search = uparams.toString();
+ let cb = routeForPath( route );
+ if( !cb )
+ return navigate( err404page );
+
+ if( curRoute != cb )
+ window.history.pushState( {}, null, url.href );
+
+ onprenavigate();
+ setTitle();
+ const el = $( cb() );
+ curRoute = cb;
+
+ $( `#${rootId}` ).children().remove();
+ $( `#${rootId}` ).append( el );
+ onpostnavigate();
+}
+
+/**
+ * convenience function to pass to href elements
+ **/
+export function href( e: Event ) {
+ const el = $( e.target );
+ if( el.is( 'a' ) ) {
+ e.preventDefault();
+ navigate( el.attr( 'href' ) );
+ }
+}
+
+/**
+ * wrapper for history.pushState
+ **/
+export function pushParams( params: any ) {
+ const url = new URL( window.location.href );
+ url.search = new URLSearchParams( params ).toString();
+
+ window.history.pushState( {}, null, url.href );
+}
+
+/**
+ * navigates without adding a history entry
+ * useful for e.g. re-rendering a page after waiting for a data callback
+**/
+export function navigateParamsSilent( route: string, params: any ) {
+ let url = new URL( window.location.href );
+ let uparams = new URLSearchParams( params );
+ url.pathname = route;
+ url.search = uparams.toString();
+ let cb = routeForPath( route );
+ if( !cb )
+ return navigateSilent( err404page );
+
+ onprenavigate();
+ setTitle();
+ const el = $( cb() );
+ curRoute = cb;
+
+ $( `#${rootId}` ).children().remove();
+ $( `#${rootId}` ).append( el );
+ onpostnavigate();
+}
+
+/**
+ * see: navigateParamsSilent
+ **/
+export function navigateSilent( route: string ) {
+ let url = new URL( window.location.href );
+ url.pathname = route;
+ let cb = routeForPath( route );
+ if( !cb )
+ return navigateSilent( err404page );
+
+ onprenavigate();
+ setTitle();
+ const el = $( cb() );
+ curRoute = cb;
+
+ $( `#${rootId}` ).children().remove();
+ $( `#${rootId}` ).append( el );
+ onpostnavigate();
+}
+
+/**
+ * action when the back button is pressed
+**/
+export function onPopState() {
+ let url = new URL( window.location.href );
+ let uparams = new URLSearchParams( url.searchParams );
+ url.search = uparams.toString();
+ let cb = routeForPath( url.pathname );
+ if( !cb )
+ return navigateSilent( err404page );
+
+ if( cb == curRoute )
+ return;
+
+ onprenavigate();
+ setTitle();
+ const el = $( cb() );
+ curRoute = cb;
+
+ $( `#${rootId}` ).children().remove();
+ $( `#${rootId}` ).append( el );
+ onpostnavigate();
+}
+
+/**
+ * navigates to the parent directory from the current page
+**/
+export function goUpDirectory() {
+ const url = new URL( window.location.href );
+ if( url.pathname.endsWith( "/" ) )
+ url.pathname = url.pathname.slice( 0, -1 );
+ let idx = url.pathname.lastIndexOf( "/" );
+ if( idx === -1 )
+ return;
+ url.pathname = url.pathname.slice( 0, url.pathname.lastIndexOf( "/" ) );
+ navigate( url.pathname );
+}
+
+export function getRoutes() : Route[] {
+ return routes;
+}
+
+
+// internal stuff below
+
+const originalAppendChild = Element.prototype.appendChild;
+Element.prototype.appendChild = function( child: any ) {
+ if( Array.isArray( child ) ) {
+ for( const childArrayMember of child )
+ this.appendChild( childArrayMember );
+
+ return child;
+ }
+ else if( typeof child === 'string' ) {
+ return originalAppendChild.call( this, document.createTextNode( child ) );
+ }
+ else if( child ) {
+ return originalAppendChild.call( this, child );
+ }
+};
+
+export function createElement( tag: any, props: any, ...children: any ) {
+ props = props || {};
+
+ if( typeof tag === "function" ) {
+ props.children = children;
+ return tag( props );
+ }
+
+ if( tag === 'raw-content' ) {
+ const dummy = document.createElement( 'div' );
+ dummy.innerHTML = props.content;
+ return [...dummy.children];
+ }
+
+ const element = document.createElement( tag );
+
+ for( const [name, value] of Object.entries( props ) ) {
+ if( name.startsWith( 'on' ) ) {
+ const lowercaseName = name.toLowerCase();
+
+ if( lowercaseName in window ) {
+ element.addEventListener( lowercaseName.substring( 2 ), value );
+ continue;
+ }
+ }
+
+ if( name == 'ref' ) {
+ ( value as any ).current = element;
+ continue;
+ }
+
+ if( value === false )
+ continue;
+
+ if( value === true ) {
+ element.setAttribute( name, '' );
+ continue;
+ }
+
+ if( assetAttributeNames.has( name ) ) {
+ if( typeof value === 'string' ) {
+ element.setAttribute( name, value );
+ }
+ continue;
+ }
+
+ element.setAttribute( name, value );
+ };
+
+ for( const child of children )
+ element.appendChild( child );
+
+ return element;
+}
+
+export function createFragment( props: any ) {
+ return props.children;
+}
+
+function setTitle() {
+ document.title = defaultTitle;
+}
+
+document.addEventListener( "click", e => {
+ let a = ( e.target ) as HTMLAnchorElement;
+ if( !a )
+ return;
+ if( a.origin !== location.origin )
+ return;
+
+ e.preventDefault();
+ if( !a.onclick ) {
+ if( routeForPath( a.pathname ) )
+ return navigate( a.pathname );
+
+ const hasExt = /\.[a-zA-Z0-9]{1,8}$/.test( a.href );
+ if( hasExt )
+ location.href = a.href;
+ else
+ navigate( err404page );
+ }
+} );
+
+window.onpopstate = onPopState;
diff --git a/public/src/tsconfig.json b/public/src/tsconfig.json
new file mode 100644
index 0000000..b964594
--- /dev/null
+++ b/public/src/tsconfig.json
@@ -0,0 +1,121 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+ "moduleResolution": "node",
+ /* Language and Environment */
+ "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ "lib": [
+ "ES2017",
+ "DOM",
+ "DOM.Iterable",
+ "ScriptHost"
+ ],
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ "jsx": "react", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ "jsxFactory": "JSX.createElement",
+ "jsxFragmentFactory": "JSX.createFragment",
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "es2020", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ "baseUrl": ".",
+ "paths": {
+ "*": ["types/*"]
+ }, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ "typeRoots": [
+ "./node_modules/@types",
+ "./types"
+ ], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "noUncheckedSideEffectImports": true, /* Check side effect imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ "strictNullChecks": false, /* When type checking, take into account 'null' and 'undefined'. */
+ "strictFunctionTypes": false, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ "strictBindCallApply": false, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ "strictPropertyInitialization": false, /* Check for class properties that are declared but not set in the constructor. */
+ "strictBuiltinIteratorReturn": false, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
+ "noImplicitThis": false, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
diff --git a/public/src/user.tsx b/public/src/user.tsx
new file mode 100644
index 0000000..d909d9e
--- /dev/null
+++ b/public/src/user.tsx
@@ -0,0 +1,6 @@
+import $ from 'jquery';
+
+import * as JSX from './jsx';
+import * as api from './api';
+
+
diff --git a/public/src/util.tsx b/public/src/util.tsx
new file mode 100644
index 0000000..f083d9f
--- /dev/null
+++ b/public/src/util.tsx
@@ -0,0 +1,60 @@
+export function escapeHtml( html: string ) {
+ const entityMap = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ "'": '&#39;',
+ '/': '&#x2F;',
+ '`': '&#x60;',
+ '=': '&#x3D;'
+ };
+
+ return String( html ).replace( /[&<>"'`=\/]/g, ( s ) => {
+ return entityMap[s];
+ } );
+}
+
+export function parseJWT( token: string ) : any {
+ const parts = token.split( '.' );
+ let encoded = parts[1];
+ encoded = encoded.replace(/-/g, '+').replace(/_/g, '/');
+ const pad = encoded.length % 4;
+ if( pad === 1 )
+ throw new Error( 'what the fuck' );
+ if( pad > 1 )
+ encoded += new Array( 5 - pad ).join( '=' );
+
+ const payload = JSON.parse( atob( encoded ) );
+ return payload;
+}
+
+export function sizeHumanReadable( size: number, short: boolean = false ) {
+ if( size < 1024 )
+ return size + (short? 'B' : ' B');
+ else if( size < 1024 * 1024 )
+ return ( size / 1024 ).toFixed( short? 1: 2 ) + (short? 'K' : ' KB');
+ else if( size < 1024 * 1024 * 1024 )
+ return ( size / 1024 / 1024 ).toFixed( short? 1 : 2 ) + (short? 'M' : ' MB');
+ else
+ return ( size / 1024 / 1024 / 1024 ).toFixed( short? 1 : 2 ) + (short? 'G':' GB');
+}
+
+export function monthToNumber( month: string ) {
+ const months = [
+ '',
+ 'jan',
+ 'feb',
+ 'mar',
+ 'apr',
+ 'may',
+ 'jun',
+ 'jul',
+ 'aug',
+ 'sep',
+ 'oct',
+ 'nov',
+ 'dec'
+ ];
+ return months.indexOf( month.toLowerCase() );
+};
diff --git a/public/static/fonts/LICENSE.TXT b/public/static/fonts/LICENSE.TXT
new file mode 100644
index 0000000..fd662a7
--- /dev/null
+++ b/public/static/fonts/LICENSE.TXT
@@ -0,0 +1,428 @@
+Attribution-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+ Considerations for licensors: Our public licenses are
+ intended for use by those authorized to give the public
+ permission to use material in ways otherwise restricted by
+ copyright and certain other rights. Our licenses are
+ irrevocable. Licensors should read and understand the terms
+ and conditions of the license they choose before applying it.
+ Licensors should also secure all rights necessary before
+ applying our licenses so that the public can reuse the
+ material as expected. Licensors should clearly mark any
+ material not subject to the license. This includes other CC-
+ licensed material, or material used under an exception or
+ limitation to copyright. More considerations for licensors:
+ wiki.creativecommons.org/Considerations_for_licensors
+
+ Considerations for the public: By using one of our public
+ licenses, a licensor grants the public permission to use the
+ licensed material under specified terms and conditions. If
+ the licensor's permission is not necessary for any reason--for
+ example, because of any applicable exception or limitation to
+ copyright--then that use is not regulated by the license. Our
+ licenses grant only permissions under copyright and certain
+ other rights that a licensor has authority to grant. Use of
+ the licensed material may still be restricted for other
+ reasons, including because others have copyright or other
+ rights in the material. A licensor may make special requests,
+ such as asking that all changes be marked or described.
+ Although not required by our licenses, you are encouraged to
+ respect those requests where reasonable. More_considerations
+ for the public:
+ wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-ShareAlike 4.0 International Public
+License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-ShareAlike 4.0 International Public License ("Public
+License"). To the extent this Public License may be interpreted as a
+contract, You are granted the Licensed Rights in consideration of Your
+acceptance of these terms and conditions, and the Licensor grants You
+such rights in consideration of benefits the Licensor receives from
+making the Licensed Material available under these terms and
+conditions.
+
+
+Section 1 -- Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar
+ Rights that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated, altered,
+ arranged, transformed, or otherwise modified in a manner requiring
+ permission under the Copyright and Similar Rights held by the
+ Licensor. For purposes of this Public License, where the Licensed
+ Material is a musical work, performance, or sound recording,
+ Adapted Material is always produced where the Licensed Material is
+ synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material in
+ accordance with the terms and conditions of this Public License.
+
+ c. BY-SA Compatible License means a license listed at
+ creativecommons.org/compatiblelicenses, approved by Creative
+ Commons as essentially the equivalent of this Public License.
+
+ d. Copyright and Similar Rights means copyright and/or similar rights
+ closely related to copyright including, without limitation,
+ performance, broadcast, sound recording, and Sui Generis Database
+ Rights, without regard to how the rights are labeled or
+ categorized. For purposes of this Public License, the rights
+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
+ Rights.
+
+ e. Effective Technological Measures means those measures that, in the
+ absence of proper authority, may not be circumvented under laws
+ fulfilling obligations under Article 11 of the WIPO Copyright
+ Treaty adopted on December 20, 1996, and/or similar international
+ agreements.
+
+ f. Exceptions and Limitations means fair use, fair dealing, and/or
+ any other exception or limitation to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+
+ g. License Elements means the license attributes listed in the name
+ of a Creative Commons Public License. The License Elements of this
+ Public License are Attribution and ShareAlike.
+
+ h. Licensed Material means the artistic or literary work, database,
+ or other material to which the Licensor applied this Public
+ License.
+
+ i. Licensed Rights means the rights granted to You subject to the
+ terms and conditions of this Public License, which are limited to
+ all Copyright and Similar Rights that apply to Your use of the
+ Licensed Material and that the Licensor has authority to license.
+
+ j. Licensor means the individual(s) or entity(ies) granting rights
+ under this Public License.
+
+ k. Share means to provide material to the public by any means or
+ process that requires permission under the Licensed Rights, such
+ as reproduction, public display, public performance, distribution,
+ dissemination, communication, or importation, and to make material
+ available to the public including in ways that members of the
+ public may access the material from a place and at a time
+ individually chosen by them.
+
+ l. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament and of
+ the Council of 11 March 1996 on the legal protection of databases,
+ as amended and/or succeeded, as well as other essentially
+ equivalent rights anywhere in the world.
+
+ m. You means the individual or entity exercising the Licensed Rights
+ under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You a worldwide, royalty-free,
+ non-sublicensable, non-exclusive, irrevocable license to
+ exercise the Licensed Rights in the Licensed Material to:
+
+ a. reproduce and Share the Licensed Material, in whole or
+ in part; and
+
+ b. produce, reproduce, and Share Adapted Material.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where
+ Exceptions and Limitations apply to Your use, this Public
+ License does not apply, and You do not need to comply with
+ its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section
+ 6(a).
+
+ 4. Media and formats; technical modifications allowed. The
+ Licensor authorizes You to exercise the Licensed Rights in
+ all media and formats whether now known or hereafter created,
+ and to make technical modifications necessary to do so. The
+ Licensor waives and/or agrees not to assert any right or
+ authority to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights, including
+ technical modifications necessary to circumvent Effective
+ Technological Measures. For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)
+ (4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ a. Offer from the Licensor -- Licensed Material. Every
+ recipient of the Licensed Material automatically
+ receives an offer from the Licensor to exercise the
+ Licensed Rights under the terms and conditions of this
+ Public License.
+
+ b. Additional offer from the Licensor -- Adapted Material.
+ Every recipient of Adapted Material from You
+ automatically receives an offer from the Licensor to
+ exercise the Licensed Rights in the Adapted Material
+ under the conditions of the Adapter's License You apply.
+
+ c. No downstream restrictions. You may not offer or impose
+ any additional or different terms or conditions on, or
+ apply any Effective Technological Measures to, the
+ Licensed Material if doing so restricts exercise of the
+ Licensed Rights by any recipient of the Licensed
+ Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or
+ may be construed as permission to assert or imply that You
+ are, or that Your use of the Licensed Material is, connected
+ with, or sponsored, endorsed, or granted official status by,
+ the Licensor or others designated to receive attribution as
+ provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not
+ licensed under this Public License, nor are publicity,
+ privacy, and/or other similar personality rights; however, to
+ the extent possible, the Licensor waives and/or agrees not to
+ assert any such rights held by the Licensor to the limited
+ extent necessary to allow You to exercise the Licensed
+ Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this
+ Public License.
+
+ 3. To the extent possible, the Licensor waives any right to
+ collect royalties from You for the exercise of the Licensed
+ Rights, whether directly or through a collecting society
+ under any voluntary or waivable statutory or compulsory
+ licensing scheme. In all other cases the Licensor expressly
+ reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified
+ form), You must:
+
+ a. retain the following if it is supplied by the Licensor
+ with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed
+ Material and any others designated to receive
+ attribution, in any reasonable manner requested by
+ the Licensor (including by pseudonym if
+ designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of
+ warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the
+ extent reasonably practicable;
+
+ b. indicate if You modified the Licensed Material and
+ retain an indication of any previous modifications; and
+
+ c. indicate the Licensed Material is licensed under this
+ Public License, and include the text of, or the URI or
+ hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any
+ reasonable manner based on the medium, means, and context in
+ which You Share the Licensed Material. For example, it may be
+ reasonable to satisfy the conditions by providing a URI or
+ hyperlink to a resource that includes the required
+ information.
+
+ 3. If requested by the Licensor, You must remove any of the
+ information required by Section 3(a)(1)(A) to the extent
+ reasonably practicable.
+
+ b. ShareAlike.
+
+ In addition to the conditions in Section 3(a), if You Share
+ Adapted Material You produce, the following conditions also apply.
+
+ 1. The Adapter's License You apply must be a Creative Commons
+ license with the same License Elements, this version or
+ later, or a BY-SA Compatible License.
+
+ 2. You must include the text of, or the URI or hyperlink to, the
+ Adapter's License You apply. You may satisfy this condition
+ in any reasonable manner based on the medium, means, and
+ context in which You Share Adapted Material.
+
+ 3. You may not offer or impose any additional or different terms
+ or conditions on, or apply any Effective Technological
+ Measures to, Adapted Material that restrict exercise of the
+ rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share all or a substantial
+ portion of the contents of the database;
+
+ b. if You include all or a substantial portion of the database
+ contents in a database in which You have Sui Generis Database
+ Rights, then the database in which You have Sui Generis Database
+ Rights (but not its individual contents) is Adapted Material,
+
+ including for purposes of Section 3(b); and
+ c. You must comply with the conditions in Section 3(a) if You Share
+ all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+ c. The disclaimer of warranties and limitation of liability provided
+ above shall be interpreted in a manner that, to the extent
+ possible, most closely approximates an absolute disclaimer and
+ waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and
+ Similar Rights licensed here. However, if You fail to comply with
+ this Public License, then Your rights under this Public License
+ terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under
+ Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided
+ it is cured within 30 days of Your discovery of the
+ violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any
+ right the Licensor may have to seek remedies for Your violations
+ of this Public License.
+
+ c. For the avoidance of doubt, the Licensor may also offer the
+ Licensed Material under separate terms or conditions or stop
+ distributing the Licensed Material at any time; however, doing so
+ will not terminate this Public License.
+
+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+ License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different
+ terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the
+ Licensed Material not stated herein are separate from and
+ independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and
+ shall not be interpreted to, reduce, limit, restrict, or impose
+ conditions on any use of the Licensed Material that could lawfully
+ be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is
+ deemed unenforceable, it shall be automatically reformed to the
+ minimum extent necessary to make it enforceable. If the provision
+ cannot be reformed, it shall be severed from this Public License
+ without affecting the enforceability of the remaining terms and
+ conditions.
+
+ c. No term or condition of this Public License will be waived and no
+ failure to comply consented to unless expressly agreed to by the
+ Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted
+ as a limitation upon, or waiver of, any privileges and immunities
+ that apply to the Licensor or You, including from the legal
+ processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
+
diff --git a/public/static/fonts/Web437_DOS-V_re_ANK16.woff b/public/static/fonts/Web437_DOS-V_re_ANK16.woff
new file mode 100644
index 0000000..a61193e
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_ANK16.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_ANK19.woff b/public/static/fonts/Web437_DOS-V_re_ANK19.woff
new file mode 100644
index 0000000..6842296
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_ANK19.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_ANK24.woff b/public/static/fonts/Web437_DOS-V_re_ANK24.woff
new file mode 100644
index 0000000..a54c6ad
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_ANK24.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_ANK30.woff b/public/static/fonts/Web437_DOS-V_re_ANK30.woff
new file mode 100644
index 0000000..623d973
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_ANK30.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_JPN12.woff b/public/static/fonts/Web437_DOS-V_re_JPN12.woff
new file mode 100644
index 0000000..04ea332
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_JPN12.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_JPN16.woff b/public/static/fonts/Web437_DOS-V_re_JPN16.woff
new file mode 100644
index 0000000..6c91b6e
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_JPN16.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_JPN19.woff b/public/static/fonts/Web437_DOS-V_re_JPN19.woff
new file mode 100644
index 0000000..48f154d
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_JPN19.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_JPN24.woff b/public/static/fonts/Web437_DOS-V_re_JPN24.woff
new file mode 100644
index 0000000..a77aca7
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_JPN24.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_JPN30.woff b/public/static/fonts/Web437_DOS-V_re_JPN30.woff
new file mode 100644
index 0000000..3073f72
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_JPN30.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_PRC16.woff b/public/static/fonts/Web437_DOS-V_re_PRC16.woff
new file mode 100644
index 0000000..4ddf047
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_PRC16.woff
Binary files differ
diff --git a/public/static/fonts/Web437_DOS-V_re_PRC19.woff b/public/static/fonts/Web437_DOS-V_re_PRC19.woff
new file mode 100644
index 0000000..322c960
--- /dev/null
+++ b/public/static/fonts/Web437_DOS-V_re_PRC19.woff
Binary files differ
diff --git a/public/static/fonts/Web437_IBM_PGC.woff b/public/static/fonts/Web437_IBM_PGC.woff
new file mode 100644
index 0000000..23a07a1
--- /dev/null
+++ b/public/static/fonts/Web437_IBM_PGC.woff
Binary files differ
diff --git a/public/static/highlight.css b/public/static/highlight.css
new file mode 100644
index 0000000..faa5d4d
--- /dev/null
+++ b/public/static/highlight.css
@@ -0,0 +1,7 @@
+/*!
+ Theme: Synth Midnight Terminal Dark
+ Author: Michaël Ball (http://github.com/michael-ball/)
+ License: ~ MIT (or more permissive) [via base16-schemes-source]
+ Maintainer: @highlightjs/core-team
+ Version: 2021.09.0
+*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#c1c3c4;background:#050608}.hljs ::selection,.hljs::selection{background-color:#28292a;color:#c1c3c4}.hljs-comment{color:#474849}.hljs-tag{color:#a3a5a6}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#c1c3c4}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#b53b50}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#ea770d}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#c9d364}.hljs-strong{font-weight:700;color:#c9d364}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#06ea61}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#42fff9}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#03aeff}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#ea5ce2}.hljs-emphasis{color:#ea5ce2;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#cd6320}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700} \ No newline at end of file
diff --git a/public/static/icon.ico b/public/static/icon.ico
new file mode 100644
index 0000000..759724a
--- /dev/null
+++ b/public/static/icon.ico
Binary files differ
diff --git a/public/static/main.css b/public/static/main.css
new file mode 100644
index 0000000..1ebd88f
--- /dev/null
+++ b/public/static/main.css
@@ -0,0 +1,788 @@
+:root {
+ --back: #888;
+ --front: #E76969;
+ --green: #69E769;
+ --site-font: JPN16;
+ --gradient: linear-gradient(90deg,rgba(255, 100, 255, 1) 0%, rgba(0, 255, 255, 1) 100%);
+}
+
+@font-face {
+ font-family: Terminal;
+ src: url( /static/fonts/Web437_IBM_PGC.woff )
+}
+
+@font-face {
+ font-family: JPN12;
+ src: url( /static/fonts/Web437_DOS-V_re_JPN12.woff )
+}
+
+@font-face {
+ font-family: JPN16;
+ src: url( /static/fonts/Web437_DOS-V_re_JPN16.woff )
+}
+
+
+@font-face {
+ font-family: JPN19;
+ src: url( /static/fonts/Web437_DOS-V_re_JPN19.woff )
+}
+
+@font-face {
+ font-family: JPN24;
+ src: url( /static/fonts/Web437_DOS-V_re_JPN24.woff )
+}
+
+@font-face {
+ font-family: JPN30;
+ src: url( /static/fonts/Web437_DOS-V_re_JPN30.woff )
+}
+
+#moneyjsx-root {
+ display: flex;
+ flex-direction: column;
+ padding: 0;
+ margin: 0;
+ font-weight: normal;
+}
+
+input[type="radio"] {
+ appearance: none;
+ background-color: var( --back );
+ margin: 0;
+ font: inherit;
+ color: #888;
+ width: 1em;
+ height: 1em;
+ border: 0.15em solid currentColor;
+ border-radius: 50%;
+ display: grid;
+ place-content: center;
+}
+
+input[type="radio"]::before {
+ content: "";
+ width: 0.75em;
+ height: 0.75em;
+ border-radius: 50%;
+ transform: scale(0);
+ transition: 120ms transform ease-in-out;
+ box-shadow: inset 1em 1em var(--front);
+}
+
+input[type="radio"]:checked::before {
+ transform: scale(1);
+}
+
+.border-wrapper {
+ justify-content: center;
+ flex-direction: row;
+ align-self: center;
+ position: relative;
+ display: flex;
+ width: calc( 100% - 2px );
+ background: var(--gradient);
+ padding: 1px 1px 1px 1px;
+ margin: 0px;
+}
+
+.header {
+ width: calc(100% - 2px);
+ background: var(--back);
+ font-family: JPN24;
+ font-size: 26px;
+ height: 38px;
+ display: flex;
+ flex-direction: row;
+}
+
+code {
+ font-family: Terminal;
+ font-size: 18px;
+ white-space: pre-wrap;
+ background: #212325;
+ padding: 5px 7px !important;
+ border-top: 1px solid #aaa;
+ border-left: 1px solid #888;
+ border-right: 1px solid #666;
+ border-bottom: 1px solid #000;
+ display: inline-block;
+ margin-top: 3px;
+ margin-bottom: 3px;
+}
+
+html, body {
+ height: 100%;
+ overflow: auto;
+}
+
+input {
+ background-color: #222;
+ border: 1px solid var(--front);
+ color: #888;
+ font-family: Code;
+}
+
+button {
+ cursor: pointer;
+ border: 1px solid var(--front);
+ background-color: #222;
+ color: #fff;
+ font-size: 16px;
+ font-family: var( --site-font );
+}
+
+textarea {
+ border: 1px solid var( --front );
+ background-color: #222;
+ color: white;
+}
+
+hr {
+ border-top: 1px solid #aaa;
+ background: #ccc;
+ height: 1px;
+ width: calc( 100% - 2px );
+}
+
+a {
+ text-decoration: none;
+ background: var( --gradient );
+ background-clip: border-box;
+ text-decoration: underline;
+ -webkit-text-fill-color: transparent;
+ -webkit-background-clip: text;
+ color: #c6a6ff;
+}
+
+
+a:hover {
+ cursor: pointer;
+ color: #fff;
+ background: var( --gradient );
+ background-clip: border-box;
+ text-decoration: underline;
+ -webkit-text-fill-color: unset;
+ -webkit-background-clip: text;
+ text-decoration: underline;
+}
+
+.gradient {
+ background: var( --gradient );
+ background-clip: border-box;
+ -webkit-text-fill-color: transparent;
+ -webkit-background-clip: text;
+}
+
+.nogradient {
+ -webkit-text-fill-color: white;
+ -webkit-background-clip: text;
+ background: none;
+}
+
+a.nogradient {
+ margin-bottom: 0px;
+ text-decoration: none;
+}
+
+a.nogradient:hover {
+ background: var( --gradient );
+ background-clip: border-box;
+ -webkit-text-fill-color: transparent;
+ -webkit-background-clip: text;
+ text-decoration: underline;
+}
+
+a.nogradient::after {
+ content:'.';
+ -webkit-text-fill-color: transparent;
+ color: transparent;
+ width:calc( 100% - 7px );
+ display:inline-block;
+ z-index: 2;
+ height: 1px;
+ margin-top: -2px;
+ background: #30e8bf;
+ padding: 0;
+ background: var( --gradient );
+}
+
+a.nogradient:hover::after {
+ background: none;
+ content: '';
+}
+
+
+body {
+ background-color: var( --back );
+ background-image: url( "/static/networkheaven.jpg" );
+ font-family: var( --site-font );
+ font-smooth: never !important;
+ background-position: center;
+ background-size: cover;
+ flex-direction: column;
+ display: flex;
+ color: #fff;
+ margin: 0;
+}
+
+
+#page-main {
+ flex-direction: row;
+ justify-content: center;
+ align-items: initial;
+ text-align: center;
+ padding: 30px 0 0;
+ overflow-x: clip;
+ display: flex;
+ width: 100%;
+ z-index: 0;
+ margin: 0;
+ margin-bottom: 30px;
+}
+
+#page-main-content {
+ width: 90%;
+ background: var( --back );
+ border-left: 1px solid #ccc;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #888;
+ border-bottom: 1px solid #222;
+ position: relative;
+}
+
+
+.red { color: #e96969; }
+.black { color: #212325; }
+.green { color: #10bb10; }
+.btn_close { background: #ccc; }
+
+
+#ascii-art {
+ white-space: pre-wrap;
+ font-family: Code;
+ font-size: 12px;
+ overflow:clip;
+ padding-top: 5px;
+}
+
+#ascii-art span {
+ padding: 0;
+ margin: 0;
+}
+
+#sidebar {
+ width: 230px;
+ height: 100%;
+ margin-right: 20px;
+ text-align: left;
+ background: var( --back );
+ border-left: 1px solid #ccc;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #888;
+ border-bottom: 1px solid #222;
+}
+
+#sidebar h3 {
+ margin-top: 8px;
+ margin-bottom: 8px;
+ background: var( --gradient );
+ background-clip: border-box;
+ text-decoration: none;
+ -webkit-text-fill-color: transparent;
+ -webkit-background-clip: text;
+ width: fit-content;
+ font-family: JPN24;
+ font-size: 24px;
+ margin-left: 4px;
+}
+
+#sidebar h4 {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ width: fit-content;
+ font-family: JPN16;
+ font-size: 17px;
+ margin-left: 4px;
+}
+
+.sidebar-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding-right: 4px;
+ margin-bottom: 0px;
+}
+
+.page-title {
+ display: flex;
+ justify-content: center
+}
+
+.page-title h3 {
+ margin-top: 15px;
+ margin-bottom: 6px;
+ font-size: 25px;
+}
+
+h5 {
+ font-family: JPN16;
+ font-size: 17px;
+ font-weight: normal;
+ margin-top: 12px;
+}
+
+
+.package-entry-date,
+.package-entry-time {
+ padding: 0 10px;
+}
+
+#blog-entry {
+ display: flex;
+ justify-content: center;
+ padding-top: 8px;
+ width: 95%;
+ font-size: 17px;
+}
+
+#blog-entry h4 {
+ margin-top: 4px;
+ margin-bottom: 4px;
+ font-family: JPN19;
+ font-size: 20px;
+}
+
+#blog-entry ul {
+ padding-left: 30px;
+}
+
+@media( max-width: 1100px ) {
+ #page-main-content {
+ width: calc( 100% - 20px ) !important;
+ min-width: unset !important;
+ margin-right: 20px;
+ margin-left: 0 !important;
+ };
+
+ #sidebar {
+ margin-left: 20px;
+ }
+
+ .page-title h3 {
+ margin-top: 8px;
+ margin-bottom: 0px;
+ }
+
+ .page-title {
+ }
+
+
+ h5 {
+ margin-top: 9px;
+ }
+
+ #sidebar h3 {
+ margin-top: 8px;
+ margin-bottom: 8px;
+ background: var( --gradient );
+ background-clip: border-box;
+ text-decoration: none;
+ -webkit-text-fill-color: transparent;
+ -webkit-background-clip: text;
+ width: fit-content;
+ font-family: JPN24;
+ font-size: 24px;
+ margin-left: 4px;
+ }
+
+ .sidebar-row {
+ margin-bottom: 8px;
+ }
+
+ #sidebar h4 {
+ font-family: JPN12;
+ font-size: 13px;
+ }
+
+ #package-entries table {
+ font-family: JPN12 !important;
+ font-size: 13px !important;
+ text-align: right;
+ }
+
+ #ascii-art {
+ font-size: 9px;
+ }
+
+ #blog-entry {
+ width: 98%;
+ font-family: JPN12;
+ font-size: 13px;
+ }
+}
+
+@media( max-width: 740px ) {
+ #sidebar {
+ width: 200px;
+ }
+
+ #sidebar h4 {
+ font-size: 10px;
+ margin-bottom: 0px;
+ margin-top: 0px;
+ }
+
+ .page-title h3 {
+ font-size: 20px;
+ margin-top: 6px;
+ margin-bottom: 0px;
+ }
+
+ h5 {
+ font-family: JPN16;
+ font-size: 10px;
+ font-weight: normal;
+ margin-top: 7px;
+ }
+
+ #sidebar h3 {
+ font-size: 20px;
+ }
+
+ .sidebar-row {
+ margin-bottom: 10px;
+ }
+
+
+ #package-entries table {
+ font-family: JPN12 !important;
+ font-size: 8px !important;
+ text-align: right;
+ width: 99% !important;
+ }
+
+ #ascii-art {
+ font-size: 6px;
+ }
+
+ .package-entry-date,
+ .package-entry-time {
+ padding: 0;
+ }
+}
+
+
+@media( max-width: 500px ) {
+ #sidebar {
+ width: 150px;
+ margin-left: 5px;
+ margin-right: 5px;
+ }
+
+ #page-main-content {
+ margin-right: 5px;
+ }
+
+ #page-main {
+ padding-top: 10px;
+ }
+
+ #blog-entry ul {
+ padding-left: 20px;
+ }
+
+ #blog-entry h4 {
+ font-size: 13px;
+ }
+
+ .page-title h3 {
+ font-size: 17px;
+ margin-top: 8px;
+ margin-bottom: 0px;
+ }
+
+ #ascii-art {
+ font-size: 4px;
+ }
+
+ #sidebar h4 {
+ font-size: 8px;
+ margin-bottom: 0px;
+ margin-top: 0px;
+ }
+
+ #sidebar h3 {
+ font-size: 17px;
+ }
+
+ h5 {
+ font-family: JPN12;
+ font-size: 9px;
+ font-weight: normal;
+ margin-top: 4px;
+ }
+
+ #blog-entry {
+ font-size: 10px;
+ }
+
+ #blog-entry code {
+ font-size: 10px;
+ }
+}
+
+@media( max-width: 350px ) {
+ #ascii-art {
+ font-size: 3px;
+ }
+
+ #sidebar h4 {
+ font-size: 6px;
+ margin-bottom: 0px;
+ margin-top: 0px;
+ }
+
+ #sidebar h3 {
+ font-size: 14px;
+ }
+
+ #blog-entry {
+ font-size: 7px;
+ }
+
+ #blog-entry code {
+ font-size: 7px;
+ }
+
+ .sidebar-row {
+ margin-bottom: 13px;
+ }
+
+ #sidebar {
+ width: 90px;
+ }
+}
+
+
+.groupbox {
+ border: 1px solid var( --front );
+ background-color: var( --back );
+ justify-content: flex-start;
+ align-items: flex-start;
+ flex-direction: column;
+ height: fit-content;
+ min-height: 25px;
+ min-width: 333px; /* may need to change this for mobile */
+ max-width: 600px;
+ display: flex;
+ margin: 5px;
+ z-index: 1;
+ flex: 1;
+}
+.groupbox > .grouptitle {
+ background-color: var( --back );
+ font-family: inherit;
+ position: relative;
+ margin: 0 0 -15px; /* stupid hardcode, but its: ( title font height - 4 ) * -1 */
+ padding: 0px 5px;
+ font-size: 18px;
+ z-index: 2;
+ top: -9px;
+ left: 5px;
+}
+.groupbody {
+ width: calc( 100% - 5px );
+ height: calc( 100% - 5px );
+ text-align: left;
+ margin: 5px;
+}
+
+#popup-msgbox {
+ display: none;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ left: 50%;
+ min-width: 250px;
+ border: 1px solid var( --front );
+ background: var( --back );
+ min-height: 120px;
+ top: 45vh;
+ transform: translate( -50%, -50% );
+}
+
+
+.dropdown {
+ background-color: var( --back );
+ border-color: var( --front );
+ text-align: center;
+ color: #888;
+ height: 24px;
+ color: #fff;
+}
+
+.dropdown-wrapper {
+ border: 2px inset var( --front );
+ background-color: var( --back );
+ justify-content: space-evenly;
+ flex-direction: column;
+ align-items: center;
+ position: absolute;
+ width: 256px;
+ z-index: 10;
+ margin-left: 10px;
+}
+
+.dropdown-inner {
+ height: 30px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-evenly;
+ border-bottom: 1px solid #333;
+ cursor: pointer;
+}
+
+.rolldownlist {
+ height: fit-content;
+ position: relative;
+ width: 100%;
+ padding: 0;
+ margin: 0;
+}
+
+.rolldown {
+ transition: background-color 0.3s ease-in-out;
+ border-bottom: 1px solid #E76969;
+ justify-content: space-between;
+ flex-direction: column;
+ display: flex;
+ height: auto;
+ width: 100%;
+}
+
+.rolldown:nth-of-type( 1 ) { margin-top: -5px; }
+.rolldown:nth-last-of-type( 1 ) { border-bottom: none; }
+.rolldown:hover { cursor: pointer; }
+
+.rolldown-collapsed-container {
+ overflow: hidden;
+ padding: 10px;
+ display: flex;
+ height: auto;
+ width: 100%;
+}
+
+.rolldown-collapsed {
+ float: left;
+ padding: 0;
+}
+
+.rolldown-expanded-container {
+ text-align: center;
+ overflow: hidden;
+ transition: max-height 0.3s linear;
+ max-height: 0px;
+ margin: 0;
+ padding: 0;
+}
+
+.rolldown-expanded-container.active {
+ display: flex !important;
+ max-height: 200px;
+ height: auto;
+}
+
+.rolldown-expanded {
+ margin: 20px;
+}
+
+.rolldown-expanded a { color: white; }
+
+.rolldown-icon-container {
+ margin-right: 15px;
+ position: relative;
+ float: left;
+ height: fit-content;
+}
+
+.rolldown.active .rolldown-icon-container {
+ transform: rotate( 90deg );
+}
+
+.popup {
+ border: 1px solid var( --front );
+ background-color: var( --back );
+ box-shadow: 1px 1px 2px black;
+ justify-content: center;
+ align-items: center;
+ align-self: center;
+ flex-direction: column;
+ position: absolute;
+ max-height: 888px;
+ max-width: 600px;
+ min-width: 300px;
+ display: flex;
+ padding: 20px;
+ z-index: 5;
+ width: 85%;
+ top: 50%;
+ left: 50%;
+ transform: translate( -50%, -50% );
+}
+
+.popup-msg {
+ border: 1px solid var( --front );
+ background-color: var( --back );
+ box-shadow: 1px 1px 2px black;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 1000;
+ padding: 10px;
+ display: flex;
+ flex-direction: column;
+}
+
+.column {
+ display: flex;
+ flex-direction: column;
+}
+
+#background-toggle {
+ position: absolute;
+
+ bottom: 0px;
+ right: 0px;
+ background: #888;
+ border-left: 1px solid #ccc;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #888;
+ border-bottom: 1px solid #222;
+}
+
+#package-entries {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ justify-content: center;
+ align-items: center;
+}
+
+#package-entries table {
+ text-align: right;
+ font-size: 17px;
+}
+
+#package-entries table tr td:first-child {
+ text-align: left !important;
+}
+
+.package-entry {
+ display: flex;
+ width: 80%;
+
+ justify-content: space-between;
+}
diff --git a/public/static/networkheaven.jpg b/public/static/networkheaven.jpg
new file mode 100644
index 0000000..e6f93db
--- /dev/null
+++ b/public/static/networkheaven.jpg
Binary files differ
diff --git a/public/static/networkheaven.png b/public/static/networkheaven.png
new file mode 100644
index 0000000..97c2cf5
--- /dev/null
+++ b/public/static/networkheaven.png
Binary files differ
diff --git a/public/static/nh.jpg b/public/static/nh.jpg
new file mode 100644
index 0000000..f8d65bb
--- /dev/null
+++ b/public/static/nh.jpg
Binary files differ
diff --git a/public/static/nh.png b/public/static/nh.png
new file mode 100644
index 0000000..fa8d531
--- /dev/null
+++ b/public/static/nh.png
Binary files differ
diff --git a/public/tsconfig.json b/public/tsconfig.json
new file mode 100644
index 0000000..8f78ddc
--- /dev/null
+++ b/public/tsconfig.json
@@ -0,0 +1,117 @@
+{
+ "compilerOptions": {
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+ "moduleResolution": "bundler",
+ "target": "es2020",
+ "lib": [
+ "ES2017",
+ "DOM",
+ "DOM.Iterable",
+ "ScriptHost"
+ ],
+ // "lib": [],
+ "jsx": "react",
+ // "experimentalDecorators": true,
+ // "emitDecoratorMetadata": true,
+ "jsxFactory": "JSX.createElement",
+ "jsxFragmentFactory": "JSX.createFragment",
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "es2020", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ "baseUrl": ".",
+ "paths": {
+ "*": ["types/*"]
+ }, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ "typeRoots": [
+ "./node_modules/@types",
+ "./types"
+ ], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "noUncheckedSideEffectImports": true, /* Check side effect imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ //"checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ "strictNullChecks": false, /* When type checking, take into account 'null' and 'undefined'. */
+ "strictFunctionTypes": false, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ "strictBindCallApply": false, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ "strictPropertyInitialization": false, /* Check for class properties that are declared but not set in the constructor. */
+ "strictBuiltinIteratorReturn": false, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
+ "noImplicitThis": false, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
diff --git a/public/types/custom.d.ts b/public/types/custom.d.ts
new file mode 100644
index 0000000..b7f740e
--- /dev/null
+++ b/public/types/custom.d.ts
@@ -0,0 +1,8 @@
+declare namespace JSX {
+ type Element = any;
+ type ElementClass = any;
+ interface IntrinsicElements {
+ [elemName: string]: any;
+ }
+}
+
diff --git a/public/webpack-dev.config.cjs b/public/webpack-dev.config.cjs
new file mode 100644
index 0000000..6cffec7
--- /dev/null
+++ b/public/webpack-dev.config.cjs
@@ -0,0 +1,46 @@
+const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+
+module.exports = {
+ mode: 'development',
+ entry: './src/index-page.tsx',
+ module: {
+ rules: [
+ {
+ test: /\.tsx?$/,
+ use: {
+ loader: 'ts-loader',
+ options: {
+ compilerOptions: {
+ module: 'es2020'
+ }
+ }
+ },
+ exclude: /node_modules/,
+ }
+ ],
+ },
+ resolve: {
+ extensions: ['.tsx', '.jsx', ".js"],
+ },
+ output: {
+ filename: 'bundle.js',
+ path: path.resolve(__dirname, 'dist'),
+ publicPath: '/'
+ },
+ plugins: [
+ new HtmlWebpackPlugin({
+ template: './src/index.html',
+ }),
+ new CopyWebpackPlugin({
+ patterns: [
+ { from: './static/**' }
+ ]
+ })
+ ],
+ devServer: {
+ historyApiFallback: true,
+ port: 9000,
+ },
+};
diff --git a/public/webpack-prod.config.cjs b/public/webpack-prod.config.cjs
new file mode 100644
index 0000000..1a67053
--- /dev/null
+++ b/public/webpack-prod.config.cjs
@@ -0,0 +1,49 @@
+const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+
+module.exports = {
+ mode: 'production',
+ entry: './src/index-page.tsx',
+ module: {
+ rules: [
+ {
+ test: /\.tsx?$/,
+ use: {
+ loader: 'ts-loader',
+ options: {
+ compilerOptions: {
+ module: 'es2020'
+ }
+ }
+ },
+ exclude: /node_modules/,
+ }
+ ],
+ },
+ resolve: {
+ alias: {
+ jquery: "jquery/slim"
+ },
+ extensions: ['.tsx', '.jsx', ".js"],
+ },
+ output: {
+ filename: 'bundle.js',
+ path: path.resolve(__dirname, 'dist'),
+ publicPath: '/'
+ },
+ plugins: [
+ new HtmlWebpackPlugin({
+ template: './src/index.html',
+ }),
+ new CopyWebpackPlugin({
+ patterns: [
+ { from: './static/**' }
+ ]
+ })
+ ],
+ devServer: {
+ historyApiFallback: true,
+ port: 9000,
+ },
+};