summaryrefslogtreecommitdiff
path: root/static
diff options
context:
space:
mode:
authorLinus Lee <linus@thesephist.com>2020-09-24 05:45:33 -0400
committerLinus Lee <linus@thesephist.com>2020-09-24 05:45:33 -0400
commit030094045aa127e1e2a3800624c6a3e45c4c8c90 (patch)
treebea4c989bcb3e8312f765ccf4f9a1751c0b3cd6e /static
parentc79c0e4fec62e535732fff69fc216a0f31f95566 (diff)
downloadtabloid-fake-closure-030094045aa127e1e2a3800624c6a3e45c4c8c90.tar.gz
tabloid-fake-closure-030094045aa127e1e2a3800624c6a3e45c4c8c90.zip
Begin working on web app
Diffstat (limited to 'static')
-rw-r--r--static/css/blocks.min.css2
-rw-r--r--static/css/main.css34
-rw-r--r--static/index.html15
-rw-r--r--static/js/lang.js637
-rw-r--r--static/js/main.js59
-rw-r--r--static/js/torus.min.js1
6 files changed, 748 insertions, 0 deletions
diff --git a/static/css/blocks.min.css b/static/css/blocks.min.css
new file mode 100644
index 0000000..7a5db2a
--- /dev/null
+++ b/static/css/blocks.min.css
@@ -0,0 +1,2 @@
+body{--block-text-color:#222;--block-background-color:#fff;--block-accent-color:#00ae86;--block-shadow-color:#444}.block{display:block;color:var(--block-text-color);border:3px solid var(--block-text-color);border-radius:3px;padding:4px 8px;background:var(--block-background-color);font-weight:700;cursor:pointer;box-sizing:border-box;position:relative;top:-2px;left:-2px;transition:transform .2s;margin:8px 6px 10px;z-index:1;user-select:none;-webkit-user-select:none;-moz-user-select:none}.block.wrapper,.block.wrapper.inline{display:inline-block;padding:0}.block.wrapper>*{margin:0}.block:before{content:"";background:var(--block-background-color);border:3px solid var(--block-text-color);border-radius:3px;position:absolute;top:-3px;left:-3px;height:100%;width:100%;z-index:-1}.block:focus,.block:hover{transform:translate(2px,2px)}.block:after{content:"";display:block;background:var(--block-shadow-color);border:3px solid var(--block-text-color);border-radius:3px;height:100%;width:100%;position:absolute;top:3px;left:3px;right:0;z-index:-2;transition:transform .2s}.block:focus:after,.block:hover:after{transform:translate(-2px,-3px)}.block:active{color:var(--block-text-color);transform:translate(3px,3px)}.block:active:after{transform:translate(-4px,-4px)}.block:focus{outline:none}.block.fixed{cursor:auto}.block.fixed:active,.block.fixed:active:after,.block.fixed:active:before,.block.fixed:focus,.block.fixed:focus:after,.block.fixed:focus:before,.block.fixed:hover,.block.fixed:hover:after,.block.fixed:hover:before{transform:none}.block.accent{color:var(--block-background-color)}.block.accent,.block.accent:before{background:var(--block-accent-color)}.block.inline{display:inline-block;font-size:.75em;padding:0 6px;margin:3px 2px 1px 4px}.block.inline:after{top:-1px;left:-1px}.block.inline:focus,.block.inline:hover{transform:translate(1px,1px)}.block.inline:focus:after,.block.inline:hover:after{transform:translate(-1px,-1px)}.block.inline:active{transform:translate(2px,2px)}.block.round,.block.round:after,.block.round:before{border-radius:30px}.block.round:after{left:1px}
+/*# sourceMappingURL=blocks.min.css.map */ \ No newline at end of file
diff --git a/static/css/main.css b/static/css/main.css
new file mode 100644
index 0000000..696faf6
--- /dev/null
+++ b/static/css/main.css
@@ -0,0 +1,34 @@
+html {
+ --ff-mono: 'IBM Plex Mono', monospace;
+ --ff-sans: 'IBM Plex Sans', sans-serif;
+ --ff-tabloid: 'Alfa Slab One', sans-serif;
+ font-size: 18px;
+}
+
+/* layout */
+
+html,
+body {
+ --block-accent-color: #e02345;
+ margin: 0;
+}
+
+main {
+ width: calc(100% - 1.5em);
+ max-width: 840px;
+ margin: 1em auto;
+ font-family: var(--ff-sans);
+}
+
+h1,
+h2,
+h3 {
+ font-family: var(--ff-tabloid);
+ font-weight: normal;
+ color: var(--block-accent-color);
+}
+
+input,
+textarea {
+ font-family: var(--ff-mono);
+}
diff --git a/static/index.html b/static/index.html
new file mode 100644
index 0000000..c62e1e8
--- /dev/null
+++ b/static/index.html
@@ -0,0 +1,15 @@
+<!doctype html>
+
+<meta charset="utf-8">
+<title>Tabloid: the clickbait headline programming language</title>
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<link href="https://fonts.googleapis.com/css2?family=Alfa+Slab+One&family=IBM+Plex+Mono&family=IBM+Plex+Sans:wght@400;700&display=swap" rel="stylesheet">
+<link rel="stylesheet" href="/css/blocks.min.css">
+<link rel="stylesheet" href="/css/main.css">
+
+<body>
+ <noscript>Oops, please turn on JavaScript to enjoy Tabloid :)</noscript>
+ <script src="/js/torus.min.js"></script>
+ <script src="/js/lang.js"></script>
+ <script src="/js/main.js"></script>
+</body>
diff --git a/static/js/lang.js b/static/js/lang.js
new file mode 100644
index 0000000..d5ed749
--- /dev/null
+++ b/static/js/lang.js
@@ -0,0 +1,637 @@
+/* Tabloid: the clickbait headline programming language */
+
+/* tokenizer */
+
+/**
+ * Reads in char or word chunks
+ */
+class Reader {
+ constructor(str, base = '') {
+ this.base = base;
+ this.i = 0;
+ this.str = str;
+ }
+ peek() {
+ return this.str[this.i];
+ }
+ next() {
+ return this.str[this.i++];
+ }
+ hasNext() {
+ return this.str[this.i] !== undefined;
+ }
+ backstep() {
+ this.i--;
+ }
+ readUntil(pred) {
+ let result = this.base.slice();
+ while (this.hasNext() && !pred(this.peek())) {
+ result += this.next();
+ }
+ return result;
+ }
+ dropWhitespace() {
+ this.readUntil(c => !!c.trim());
+ }
+ expect(tok) {
+ const next = this.next();
+ if (next !== tok) {
+ throw new Error(`Parsing error: expected ${tok}, got ${next}`);
+ }
+ }
+}
+
+/**
+ * Split into words for easier tokenization
+ * with keywords.
+ */
+class Wordifier {
+ constructor(str) {
+ this.reader = new Reader(prog);
+ this.tokens = [];
+ }
+ wordify() {
+ if (this.tokens.length) return this.tokens;
+
+ while (this.reader.hasNext()) {
+ const next = this.reader.next();
+ switch (next) {
+ case '(': {
+ this.tokens.push('(');
+ break;
+ }
+ case ')': {
+ this.tokens.push(')');
+ break;
+ }
+ case ',': {
+ this.tokens.push(',');
+ break;
+ }
+ case '"':
+ case "'": {
+ this.wordifyString(next);
+ break;
+ }
+ default: {
+ // read until WS
+ this.reader.backstep();
+ this.tokens.push(this.reader.readUntil(c => {
+ return !c.trim() || ['(', ')', ','].includes(c)
+ }));
+ }
+ }
+ this.reader.dropWhitespace();
+ }
+ return this.tokens.slice(1);
+ }
+ wordifyString(endChar) {
+ let acc = '';
+ acc += this.reader.readUntil(c => c == endChar);
+ while (acc.endsWith('\\') || !this.reader.hasNext()) {
+ acc += this.reader.readUntil(c => c != endChar);
+ }
+ this.reader.next(); // throw away closing char
+ this.tokens.push('"' + acc);
+ }
+}
+
+const T = {
+ LParen: Symbol('LParen'),
+ RParen: Symbol('RParen'),
+ Comma: Symbol('Comma'),
+ DiscoverHowTo: Symbol('DiscoverHowTo'),
+ With: Symbol('With'),
+ Of: Symbol('Of'),
+ WeSaid: Symbol('WeSaid'),
+ WhatIf: Symbol('WhatIf'),
+ LiesBang: Symbol('LiesBang'),
+ EndOfStory: Symbol('EndOfStory'),
+ ExpertsClaim: Symbol('ExpertsClaim'),
+ ToBe: Symbol('ToBe'),
+ YouWontWantToMiss: Symbol('YouWontWantToMiss'),
+ IsActually: Symbol('IsActually'),
+ And: Symbol('And'),
+ Or: Symbol('Or'),
+ Add: Symbol('Add'),
+ Subtract: Symbol('Subtract'),
+ Multiply: Symbol('Multiply'),
+ Divide: Symbol('Divide'),
+ Modulo: Symbol('Modulo'),
+ Beats: Symbol('Beats'), // >
+ SmallerThan: Symbol('SmallerThan'), // <
+ ShockingDevelopment: Symbol('ShockingDevelopment'),
+ PleaseLikeAndSubscribe: Symbol('PleaseLikeAndSubscribe'),
+
+ // not implemented yet
+ StayTuned: Symbol('StayTuned'),
+ Unexpectedly: Symbol('Unexpectedly'),
+ TotallyRight: Symbol('TotallyRight'),
+ CompletelyWrong: Symbol('CompletelyWrong'),
+}
+
+const BINARY_OPS = [
+ T.IsActually,
+ T.And,
+ T.Or,
+ T.Add,
+ T.Subtract,
+ T.Multiply,
+ T.Divide,
+ T.Modulo,
+ T.Beats,
+ T.SmallerThan,
+];
+
+function tokenize(prog) {
+ const reader = new Reader(new Wordifier(prog).wordify(), []);
+ const tokens = [];
+
+ while (reader.hasNext()) {
+ const next = reader.next();
+ switch (next) {
+ case 'DISCOVER': {
+ reader.expect('HOW');
+ reader.expect('TO');
+ tokens.push(T.DiscoverHowTo);
+ break;
+ }
+ case 'WITH': {
+ tokens.push(T.With);
+ break;
+ }
+ case 'OF': {
+ tokens.push(T.Of);
+ break;
+ }
+ case 'WE': {
+ reader.expect('SAID');
+ tokens.push(T.WeSaid);
+ break;
+ }
+ case 'WHAT': {
+ reader.expect('IF');
+ tokens.push(T.WhatIf);
+ break;
+ }
+ case 'LIES!': {
+ tokens.push(T.LiesBang);
+ break;
+ }
+ case 'END': {
+ reader.expect('OF');
+ reader.expect('STORY');
+ tokens.push(T.EndOfStory);
+ break;
+ }
+ case 'EXPERTS': {
+ reader.expect('CLAIM');
+ tokens.push(T.ExpertsClaim);
+ break;
+ }
+ case 'TO': {
+ reader.expect('BE');
+ tokens.push(T.ToBe);
+ break;
+ }
+ case 'YOU': {
+ reader.expect('WON\'T');
+ reader.expect('WANT');
+ reader.expect('TO');
+ reader.expect('MISS');
+ tokens.push(T.YouWontWantToMiss);
+ break;
+ }
+ case 'IS': {
+ reader.expect('ACTUALLY');
+ tokens.push(T.IsActually);
+ break;
+ }
+ case 'AND': {
+ tokens.push(T.And);
+ break;
+ }
+ case 'OR': {
+ tokens.push(T.Or);
+ break;
+ }
+ case 'ADD': {
+ tokens.push(T.Add);
+ break;
+ }
+ case 'SUBTRACT': {
+ tokens.push(T.Subtract);
+ break;
+ }
+ case 'MULTIPLY': {
+ tokens.push(T.Multiply);
+ break;
+ }
+ case 'DIVIDE': {
+ tokens.push(T.Divide);
+ break;
+ }
+ case 'MODULO': {
+ tokens.push(T.Modulo);
+ break;
+ }
+ case 'BEATS': {
+ tokens.push(T.Beats);
+ break;
+ }
+ case 'SMALLER': {
+ reader.expect('THAN');
+ tokens.push(T.SmallerThan);
+ break;
+ }
+ case 'SHOCKING': {
+ reader.expect('DEVELOPMENT');
+ tokens.push(T.ShockingDevelopment);
+ break;
+ }
+ case 'PLEASE': {
+ reader.expect('LIKE');
+ reader.expect('AND');
+ reader.expect('SUBSCRIBE');
+ tokens.push(T.PleaseLikeAndSubscribe);
+ break;
+ }
+ case 'STAY': {
+ reader.expect('TUNED');
+ tokens.push(T.StayTuned);
+ break;
+ }
+ case 'UNEXPECTEDLY': {
+ tokens.push(T.Unexpectedly);
+ break;
+ }
+ case 'TOTALLY': {
+ reader.expect('RIGHT');
+ tokens.push(T.TotallyRight);
+ break;
+ }
+ case 'COMPLETELY': {
+ reader.expect('WRONG');
+ tokens.push(T.CompletelyWrong);
+ break;
+ }
+ case '(': {
+ tokens.push(T.LParen);
+ break;
+ }
+ case ')': {
+ tokens.push(T.RParen);
+ break;
+ }
+ case ',': {
+ tokens.push(T.Comma);
+ break;
+ }
+ default: {
+ if (!isNaN(parseFloat(next))) {
+ // number literal
+ tokens.push(parseFloat(next));
+ } else {
+ // string or varname
+ tokens.push(next);
+ }
+ }
+ }
+ }
+ return tokens;
+}
+
+/* parser */
+
+const N = {
+ NumberLiteral: Symbol('NumberLiteral'),
+ StringLiteral: Symbol('StringLiteral'),
+ FnDecl: Symbol('FnDecl'),
+ FnCall: Symbol('FnCall'),
+ Ident: Symbol('Ident'),
+ Assignment: Symbol('Assignment'),
+ BinaryOp: Symbol('BinaryOp'),
+ IfExpr: Symbol('IfExpr'),
+ ExprGroup: Symbol('ExprGroup'),
+ ReturnExpr: Symbol('ReturnExpr'),
+ ProgEndExpr: Symbol('ProgEndExpr'),
+ PrintExpr: Symbol('PrintExpr'),
+}
+
+class Parser {
+ constructor(tokens) {
+ this.tokens = new Reader(tokens, []);
+ }
+ /**
+ * Atom
+ * Ident
+ * NumberLiteral
+ * StringLiteral
+ * FnCall
+ * FnDecl
+ * ExprGroup
+ *
+ * Expression:
+ * (begins with atom)
+ * BinaryOp
+ * Atom
+ * (begins with keyword)
+ * IfExpr
+ * Assignment
+ * ReturnExpr
+ * ProgEndExpr
+ * PrintExpr
+ *
+ */
+ parse() {
+ const nodes = [];
+ while (this.tokens.hasNext()) {
+ nodes.push(this.expr());
+ }
+ return nodes;
+ }
+ expectIdentString() {
+ const ident = this.tokens.next();
+ if (typeof ident === 'string' && !ident.startsWith('"')) {
+ return ident;
+ }
+ throw new Error(`Parsing error: expected identifier, got ${ident.toString()}`);
+ }
+ atom() {
+ const next = this.tokens.next();
+ if (typeof next === 'number') {
+ return {
+ type: N.NumberLiteral,
+ val: next,
+ }
+ } else if (typeof next === 'string') {
+ if (next.startsWith('"')) {
+ return {
+ type: N.StringLiteral,
+ val: next.substr(1),
+ }
+ }
+ const ident = {
+ type: N.Ident,
+ val: next,
+ }
+ if (this.tokens.peek() === T.Of) {
+ return this.fnCall(ident);
+ }
+ return ident;
+ } else if (next === T.DiscoverHowTo) {
+ // fn literal
+ const fnName = this.tokens.next();
+ if (this.tokens.peek(T.With)) {
+ this.tokens.next(); // with
+ // with args
+ const args = [this.expectIdentString()];
+ while (this.tokens.peek() === T.Comma) {
+ this.tokens.next(); // comma
+ args.push(this.expectIdentString());
+ }
+ return {
+ type: N.FnDecl,
+ name: fnName,
+ args: args,
+ body: this.expr(),
+ }
+ } else {
+ return {
+ type: N.FnDecl,
+ name: fnName,
+ args: [],
+ body: this.expr(),
+ }
+ }
+ } else if (next === T.WeSaid) {
+ // block
+ const exprs = [];
+ while (this.tokens.hasNext() && this.tokens.peek() !== T.EndOfStory) {
+ exprs.push(this.expr());
+ }
+ this.tokens.expect(T.EndOfStory);
+ return {
+ type: N.ExprGroup,
+ exprs: exprs,
+ };
+ }
+
+ throw new Error(`Parsing error: expected ident, literal, or block, got ${
+ next.toString()
+ } before ${this.tokens.peek().toString()}`);
+ }
+ expr() {
+ const next = this.tokens.next();
+ if (next === T.WhatIf) {
+ // if expr
+ const cond = this.expr();
+ const ifBody = this.expr();
+
+ let elseBody = null;
+ if (this.tokens.peek() == T.LiesBang) {
+ this.tokens.next(); // LiesBang
+ elseBody = this.expr();
+ }
+ return {
+ type: N.IfExpr,
+ cond: cond,
+ ifBody: ifBody,
+ elseBody: elseBody,
+ }
+ } else if (next === T.ExpertsClaim) {
+ // assignment
+ const name = this.expectIdentString();
+ this.tokens.expect(T.ToBe);
+ const val = this.expr();
+ return {
+ type: N.Assignment,
+ name,
+ val,
+ }
+ } else if (next === T.ShockingDevelopment) {
+ // return
+ return {
+ type: N.ReturnExpr,
+ val: this.expr(),
+ }
+ } else if (next === T.PleaseLikeAndSubscribe) {
+ // prog end
+ return {
+ type: N.ProgEndExpr,
+ }
+ } else if (next == T.YouWontWantToMiss) {
+ // print expr
+ return {
+ type: N.PrintExpr,
+ val: this.expr(),
+ }
+ }
+
+ this.tokens.backstep();
+ const atom = this.atom();
+ if (BINARY_OPS.includes(this.tokens.peek())) {
+ // infix binary ops
+ // TODO: support operator precedence
+ const left = atom;
+ const op = this.tokens.next();
+ const right = this.atom();
+ return {
+ type: N.BinaryOp,
+ op,
+ left,
+ right,
+ }
+ }
+
+ return atom;
+ }
+ fnCall(fnNode) {
+ this.tokens.expect(T.Of);
+ // TODO: support multiple arguments
+ const args = [this.expr()];
+ return {
+ type: N.FnCall,
+ fn: fnNode,
+ args: args,
+ }
+ }
+}
+
+/* executor (tree walk) */
+
+/**
+ * Abused (slightly) to easily return values upstack
+ */
+class ReturnError {
+ constructor(value) {
+ this.value = value;
+ }
+ unwrap() {
+ return this.value;
+ }
+}
+
+class Environment {
+ constructor(runtime) {
+ /**
+ * Runtime contains the following functions:
+ * - print(s)
+ */
+ this.runtime = runtime;
+ this.scopes = [{}]; // begin with global scope
+ }
+ run(nodes) {
+ let rv;
+ for (const node of nodes) {
+ rv = this.eval(node);
+ }
+ return rv;
+ }
+ eval(node) {
+ const scope = this.scopes[this.scopes.length - 1];
+
+ switch (node.type) {
+ case N.NumberLiteral: {
+ return node.val;
+ }
+ case N.StringLiteral: {
+ return node.val;
+ }
+ case N.FnDecl: {
+ scope[node.name] = node;
+ return node;
+ }
+ case N.FnCall: {
+ const fn = this.eval(node.fn);
+ const args = node.args.map(arg => this.eval(arg));
+
+ const calleeScope = {};
+ fn.args.forEach((argName, i) => {
+ calleeScope[argName] = args[i];
+ });
+
+ this.scopes.push(calleeScope);
+ let rv;
+ try {
+ this.eval(fn.body);
+ } catch (maybeReturnErr) {
+ if (maybeReturnErr instanceof ReturnError) {
+ rv = maybeReturnErr.unwrap();
+ } else {
+ // re-throw
+ throw maybeReturnErr;
+ }
+ }
+ this.scopes.pop();
+
+ return rv;
+ }
+ case N.Ident: {
+ let i = this.scopes.length - 1;
+ while (i >= 0) {
+ if (node.val in this.scopes[i]) {
+ return this.scopes[i][node.val];
+ }
+ i --;
+ }
+ throw new Error(`Runtime error: Undefined variable "${node.val}"`);
+ }
+ case N.Assignment: {
+ scope[node.name] = this.eval(node.val);
+ return scope[node.name];
+ }
+ case N.BinaryOp: {
+ const left = this.eval(node.left);
+ const right = this.eval(node.right);
+ switch (node.op) {
+ // TODO: other ops
+ case T.IsActually:
+ return left === right;
+ case T.Add:
+ return left + right;
+ case T.Subtract:
+ return left - right;
+ case T.Multiply:
+ return left * right;
+ default:
+ throw new Error(`Runtime error: Unknown binary op ${node.op.toString()}`);
+ }
+ }
+ case N.IfExpr: {
+ if (this.eval(node.cond)) {
+ return this.eval(node.ifBody);
+ }
+ if (node.elseBody != null) {
+ return this.eval(node.elseBody);
+ }
+ }
+ case N.ExprGroup: {
+ let rv = false; // TODO: make null value? make this illegal?
+ for (const expr of node.exprs) {
+ rv = this.eval(expr);
+ }
+ return rv;
+ }
+ case N.ReturnExpr: {
+ const rv = this.eval(node.val);
+ throw new ReturnError(rv);
+ }
+ case N.ProgEndExpr: {
+ // do nothing
+ break;
+ }
+ case N.PrintExpr: {
+ const val = this.eval(node.val);
+ Runtime.print(val);
+ return val;
+ }
+ default:
+ console.log(JSON.stringify(node, null, 2));
+ throw new Error(`Runtime error: Unknown AST Node of type ${
+ node.type.toString()
+ }:\n${JSON.stringify(node, null, 2)}`);
+ }
+ }
+}
+
diff --git a/static/js/main.js b/static/js/main.js
new file mode 100644
index 0000000..a3583ad
--- /dev/null
+++ b/static/js/main.js
@@ -0,0 +1,59 @@
+const prog = `
+DISCOVER HOW TO factorial WITH n
+WE SAID
+ WHAT IF n IS ACTUALLY 0
+ WE SAID
+ SHOCKING DEVELOPMENT 1
+ END OF STORY
+ LIES! WE SAID
+ SHOCKING DEVELOPMENT n MULTIPLY factorial OF n SUBTRACT 1
+ END OF STORY
+END OF STORY
+
+EXPERTS CLAIM result TO BE factorial OF 10
+YOU WON'T WANT TO MISS 'RESULT IS'
+YOU WON'T WANT TO MISS result
+
+PLEASE LIKE AND SUBSCRIBE
+`;
+
+const Runtime = {
+ print(s) {
+ console.log(s.toString());
+ }
+}
+
+// main
+try {
+ const tokens = tokenize(prog);
+ const nodes = new Parser(tokens).parse();
+ const env = new Environment(Runtime);
+ env.run(nodes);
+} catch (e) {
+ console.error(e);
+}
+
+const {
+ Component,
+} = window.Torus;
+
+class Editor extends Component {
+ init() {
+ this.val = '';
+ }
+ compose() {
+
+ }
+}
+
+class App extends Component {
+ compose() {
+ return jdom`<main>
+ <h1>Tabloid</h1>
+ <p class="subtitle">The Clickbait Headline Programming Language</p>
+ </main>`;
+ }
+}
+
+const app = new App();
+document.body.appendChild(app.node);
diff --git a/static/js/torus.min.js b/static/js/torus.min.js
new file mode 100644
index 0000000..1432d3b
--- /dev/null
+++ b/static/js/torus.min.js
@@ -0,0 +1 @@
+!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a={replaceChild:()=>{}};const d=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},u=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([2,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.data=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs)){const n=e.attrs[r],o=s.attrs[r];if("class"===r){const e=o;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const e=n||{},s=o;for(const r of Object.keys(s))s[r]!==e[r]&&(t.style[r]=s[r]);for(const r of Object.keys(e))void 0===s[r]&&(t.style[r]="")}else r in t?(t[r]!==o||void 0===n&&n!==o)&&(t[r]=o):n!==o&&t.setAttribute(r,o)}for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));d(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),d(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,a=r.length,h=c.length;if(h+a>0){const n=e._nodes||[],o=a<h?a:h;let i=0;for(;i<o;i++)r[i]!==c[i]&&(n[i]=u(n[i],r[i],c[i]));if(a<h)for(;i<h;i++){const e=u(void 0,void 0,c[i]);l.push([0,t,e]),n.push(e)}else{for(;i<a;i++)l.push([1,t,n[i]]);n.splice(h,a-h)}s._nodes=n}}return 0==--r&&function(){const t=l.length;for(let e=0;e<t;e++){const t=l[e],s=t[0];if(1===s)t[1].removeChild(t[2]);else if(2===s){const e=t[1],s=c(),r=e.parentNode;null!==r?(r.replaceChild(s,e),t[1]=s,t[3]=r):t[3]=a}}for(let e=0;e<t;e++){const t=l[e],s=t[0];0===s?t[1].appendChild(t[2]):2===s&&t[3].replaceChild(t[2],t[1])}l=[]}(),t};class h{constructor(...t){this.jdom=void 0,this.node=void 0,this.event={source:null,handler:()=>{}},this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends h{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof j))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event={source:null,handler:()=>{}}}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=u(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const f=new Set;let m;const p=new WeakMap,v=(t,e)=>t+"{"+e+"}",b=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(v(n,b(t,o).join(""))):s.push(v(n,b("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(b(e,o))}else s=s.concat(b(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(v(t,r)),s},g=t=>{const e=(t=>{if(!p.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);p.set(t,"_torus"+(r>>>0))}return p.get(t)})(t);let s=0;if(!f.has(e)){m||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),m=t.sheet})();const r=b("."+e,t);for(const t of r)m.insertRule(t,s++);f.add(e)}return e},y=t=>class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(g(this.styles(e)))),t}};class x extends h{get itemClass(){return h}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class j{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class w extends j{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class O extends j{constructor(t=[]){super(),this.reset(t)}get recordClass(){return w}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]<e[0]?-1:t[0]>e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const C=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t),e){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const S={render:u,Component:h,Styled:y,StyledComponent:y(h),List:x,ListOf:t=>class extends x{get itemClass(){return t}},Record:w,Store:O,StoreOf:t=>class extends O{get recordClass(){return t}},Router:class extends j{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...C(e)]),this.lastMatch=["",null],this._cb=()=>this.route(location.pathname),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=S),t.exports&&(t.exports=S)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class i{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const c=t=>{let e="";for(let s=0,r=t.length;s<r;s++)e+="-"===t[s]?t[++s].toUpperCase():t[s];return e},l=t=>{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new i(t),s=e.clipEnd("/");let r="",o=!1,l=!1;const a=[];let d=0;const u=t=>{r=r.trim(),(""!==r||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":l?r+=t:(u(),o=!0,d=1);break;case" ":l?r+=t:o||(u(),d=0);break;case"\\":l&&(t=e.next(),r+=t);break;case'"':l?(l=!1,u(!0),d=0):1===d&&(l=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,v=a.shift();const b=()=>{p=v,v=a.shift()};for(;void 0!==v;){if(1===v.type){const t=p.value;let e=v.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[c(r.trim())]=n.trim()}f[t]=s}else f[t]=e;b()}else p&&(f[p.value]=!0);b()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},a=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},o=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=l(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=a(t))}}else o("&"===e?(i=e+t.readUntil(";"),String.fromCodePoint(+/&#(\w+);/.exec(i)[1])):e);var i;return n(),e},d=new Map,u=/jdom_tpl_obj_\[(\d+)\]/,h=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=u.exec(t),r=t.split(s[0]),n=s[1],o=h(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},f=(t,e)=>{const s=[];for(const n of t)for(const t of h(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},m=(t,e)=>{if(t.length<14)return t;{const s=u.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+m(r[1],e)}}},p=(t,e)=>{for(let s=0,r=t.length;s<r;s++){const r=t[s];"string"==typeof r?t[s]=m(r,e):Array.isArray(r)?p(r,e):v(r,e)}},v=(t,e)=>{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=m(n,e):Array.isArray(n)?"children"===s?t.children=f(n,e):p(n,e):r(n)&&v(n,e)}},b=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=b(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},g=new Map,y={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!d.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new i(o(t.map(t=>t.replace(/\s+/g," ")),r)),c=a(n)[0],l=typeof c,u=JSON.stringify(c);d.set(s,t=>{if("string"===l)return m(c,t);if("object"===l){const e={},s=JSON.parse(u);return v(Object.assign(e,s),t),e}return null})}return d.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${o(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=o(t,e).trim();return g.has(s)||g.set(s,b(new i("{"+s+"}"))),g.get(s)}};"object"==typeof window&&Object.assign(window,y),t.exports&&(t.exports=y)}]); \ No newline at end of file