├── .babelrc
├── .browserslistrc
├── .gitignore
├── README.md
├── images
└── memory
│ ├── arcade.png
│ ├── billiard.png
│ ├── cards.png
│ ├── chess.png
│ ├── game-controller.png
│ ├── game-controller2.png
│ ├── gaming.png
│ ├── ghost.png
│ ├── question.png
│ ├── sword.png
│ └── tetris.png
├── package-lock.json
├── package.json
├── src.html
├── arrays.html
├── calculator.html
├── dialog.html
├── fetch.html
├── fizzbuzz.html
├── memory-game.html
├── objects.html
└── todo.html
├── src
├── exec.js
├── fetch.js
├── for2.js
├── index.js
├── lib
│ ├── children.js
│ ├── events.js
│ ├── range.js
│ ├── setAttribute.js
│ └── setVisibility.js
├── scopes.js
├── stylesheet.js
├── subroutine.js
└── values.js
├── umd
├── arrays.html
├── calculator.html
├── dialog.html
├── fetch.html
├── fizzbuzz.html
├── html-lang.js
├── memory-game.html
├── objects.html
└── todo.html
├── webpack.config.js
└── what-the-hell-are-you-doing.jpg
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [["@babel/preset-env", { "useBuiltIns": false }]],
3 | "plugins": [
4 | [
5 | "@babel/plugin-transform-runtime",
6 | {
7 | "regenerator": false
8 | }
9 | ],
10 | "babel-plugin-transform-async-to-promises"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HTML is a Programming Language!
2 |
3 | HTML is now a Turing complete programming language with the `html-lang` library.
4 |
5 | `html-lang` is experimental and currently in development. So that means don't go slipping this into your production site! Proceed at your own risk!
6 |
7 | `html-lang` features no build step, no bundling, no webpack configs. Open up your HTML, include the script and start writing HTML.
8 |
9 | At only 5.7kB (gzip) in size, `html-lang` is a tiny but powerful framework.
10 |
11 | ## Why?
12 |
13 | Most frameworks are designed to be template engines. These typically mix their template language and JavaScript. Meaning you write and tie together both.
14 |
15 | While `html-lang` is similar to quite a bit of existing tech out there, the focus of this library is to bring that programming feel to HTML.
16 |
17 | This allows you to stay inside the framework.
18 |
19 | ## Install
20 |
21 | Include the script tag into the `
` section of your HTML.
22 |
23 | ```html
24 |
25 | ```
26 |
27 | ## Variables
28 |
29 | Variables are globally scoped.
30 |
31 | ```html
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | ```
47 |
48 | ## Computed Values
49 |
50 | The computed value syntax has a `:?` at the end of the Variable name.
51 |
52 | ```html
53 |
54 |
55 |
56 |
57 |
58 | ```
59 |
60 | ## Output
61 |
62 | Display a Variable
63 |
64 | ```html
65 |
66 |
67 |
68 |
69 |
70 | ```
71 |
72 | ## If / Conditional
73 |
74 | ```html
75 | X is GREATER than 10!
76 | ```
77 |
78 | An `else` can follow an `if` element.
79 |
80 | ```html
81 | X is GREATER than 10!
82 | X is NOT GREATER than 10!
83 | ```
84 |
85 | ## Loops
86 |
87 | A `for-of` loop will loop through all the items in the collection, setting the item to the variable specified.
88 |
89 | ```html
90 |
91 |
92 |
93 |
94 |
97 |
98 |
99 |
100 |
101 | ```
102 |
103 | A `for-in` loop will loop through all the items in the collection, setting the index to the variable specified.
104 |
105 | ```html
106 |
107 |
108 |
109 |
110 |
113 |
114 |
115 |
116 |
117 | ```
118 |
119 | `for-of` and `for-in` can be combined together if they both point to the same collection.
120 |
121 | ```html
122 |
123 |
124 |
125 |
126 |
129 |
130 |
131 |
132 |
133 | ```
134 |
135 | ## Subroutines
136 |
137 | A Subroutine can be created to run common tasks. Subroutines take no arguments and return no values, but do have access to the variables.
138 |
139 | ## Fetching Data
140 |
141 | ```html
142 |
143 |
144 |
145 |
146 |
151 |
152 |
153 | Loading...
154 |
155 |
156 | Error:
157 |
158 |
159 |
160 | name:
161 | gender:
162 | height: cm
163 |
164 | ```
165 |
166 | ## Examples
167 |
168 | ### FizzBuzz
169 |
170 | ```html
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | Fizz Buzz
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 | ```
187 |
188 | ### TODO List
189 |
190 | ```html
191 |
192 |
193 |
194 | add
195 |
196 |
197 |
198 |
199 |
200 |
201 | X
202 |
203 |
204 |
205 |
206 | ```
207 |
208 | ### Dialog
209 |
210 | ```html
211 |
212 |
213 |
214 |
215 |
216 |
217 | Hello Dialog!
218 |
219 |
220 | ```
221 |
222 | ### More Examples
223 |
224 | - [Memory Game](https://codepen.io/joelnet/pen/BadymQz)
225 | - [Calculator](https://codepen.io/joelnet/pen/porzEPv)
226 |
227 | ## Alternatives
228 |
229 | - [Alpine.js](https://alpinejs.dev/) — Alpine is a rugged, minimal tool for composing behavior directly in your markup. Think of it like jQuery for the modern web. Plop in a script tag and get going.
230 |
--------------------------------------------------------------------------------
/images/memory/arcade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/arcade.png
--------------------------------------------------------------------------------
/images/memory/billiard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/billiard.png
--------------------------------------------------------------------------------
/images/memory/cards.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/cards.png
--------------------------------------------------------------------------------
/images/memory/chess.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/chess.png
--------------------------------------------------------------------------------
/images/memory/game-controller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/game-controller.png
--------------------------------------------------------------------------------
/images/memory/game-controller2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/game-controller2.png
--------------------------------------------------------------------------------
/images/memory/gaming.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/gaming.png
--------------------------------------------------------------------------------
/images/memory/ghost.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/ghost.png
--------------------------------------------------------------------------------
/images/memory/question.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/question.png
--------------------------------------------------------------------------------
/images/memory/sword.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/sword.png
--------------------------------------------------------------------------------
/images/memory/tetris.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/images/memory/tetris.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@joelnet/html-lang",
3 | "version": "0.1.1",
4 | "description": "HTML is a Programming Language!",
5 | "keywords": [
6 | "html",
7 | "template"
8 | ],
9 | "author": "Joel Thoms",
10 | "license": "MIT",
11 | "scripts": {
12 | "clean": "rm -rf umd",
13 | "prebuild": "npm run clean",
14 | "build": "webpack --mode production",
15 | "dev": "webpack serve --mode development"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.15.5",
19 | "@babel/plugin-transform-runtime": "^7.15.0",
20 | "@babel/preset-env": "^7.15.6",
21 | "babel-loader": "^8.2.2",
22 | "babel-plugin-transform-async-to-promises": "^0.8.15",
23 | "glob": "^7.2.0",
24 | "html-webpack-plugin": "^5.3.2",
25 | "serve": "^12.0.1",
26 | "webpack": "^5.56.0",
27 | "webpack-cli": "^4.8.0",
28 | "webpack-dev-server": "^4.3.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src.html/arrays.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | list[ ] =
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src.html/calculator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
30 | Calculator
31 |
32 |
33 |
34 |
Calculator
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | 1
83 | 2
84 | 3
85 | +
86 |
87 |
88 | 4
89 | 5
90 | 6
91 | -
92 |
93 |
94 | 7
95 | 8
96 | 9
97 | *
98 |
99 |
100 |
101 | C
102 |
103 | 0
104 | =
105 | /
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/src.html/dialog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
27 |
28 |
29 |
30 |
Dialog
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Hello Dialog!
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src.html/fetch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
Fetch
12 |
13 |
18 |
Loading...
19 |
Error:
20 |
21 | name:
22 | gender:
23 | height: cm
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src.html/fizzbuzz.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 | Fizz Buzz
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src.html/memory-game.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
34 | Memory Game
35 |
36 |
37 |
38 |
Memory Game
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | Congratulations! You won in moves!
97 | Play Again
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
m[0] === row && m[1] === col)"
107 | isclicked:?="(clicked[0] === row && clicked[1] === col)"
108 | showcard:?="(match[0] === row && match[1] === col) || prevmatch || isclicked"
109 | >
110 |
111 |
112 |
113 |
114 |
115 |
116 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/src.html/objects.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src.html/todo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | TODO
8 |
15 |
16 |
17 |
18 |
19 |
20 | add
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | X
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/exec.js:
--------------------------------------------------------------------------------
1 | import {
2 | appendChildren,
3 | cloneChildren,
4 | removeAllChildren,
5 | } from "./lib/children";
6 | import { computeValue } from "./values";
7 |
8 | const isCmdAttribute = (attr) => attr.name === "cmd";
9 |
10 | // runs
11 | const runExecSub = (element, name) => {
12 | const sub = document.querySelector(`SUB[name=${name}]`);
13 | if (!sub) {
14 | throw new Error(`SUB "${name}" was expected to exist.`);
15 | }
16 |
17 | const children = cloneChildren(sub.children);
18 |
19 | removeAllChildren(element);
20 | appendChildren(element, children);
21 |
22 | return true;
23 | };
24 |
25 | export const runExec = (element) => {
26 | const attribute = [...element.attributes].find(isCmdAttribute);
27 |
28 | if (attribute.value.match(/^\$.*/g)) {
29 | return runExecSub(element, attribute.value.substring(1));
30 | }
31 |
32 | return computeValue(element, attribute.value);
33 | };
34 |
--------------------------------------------------------------------------------
/src/fetch.js:
--------------------------------------------------------------------------------
1 | const isJson = (response) => {
2 | const contentType = response.headers.get("content-type");
3 | return contentType && contentType.includes("application/json");
4 | };
5 |
6 | export const fetchJson = async (...args) => {
7 | const response = await fetch(...args);
8 | if (!response.ok) {
9 | throw new Error(
10 | `HTTP Error Response: ${response.status} ${response.statusText}`
11 | );
12 | }
13 |
14 | if (isJson(response)) {
15 | return {
16 | response,
17 | json: await response.json(),
18 | };
19 | }
20 |
21 | return { response, json: null };
22 | };
23 |
--------------------------------------------------------------------------------
/src/for2.js:
--------------------------------------------------------------------------------
1 | import {
2 | appendChildren,
3 | cloneChildren,
4 | removeAllChildren,
5 | } from "./lib/children";
6 | import { saveScope } from "./scopes";
7 | import { computeValue } from "./values";
8 |
9 | const isForOfAttribute = (attribute) => attribute.name.endsWith(":for:of");
10 | const isForInAttribute = (attribute) => attribute.name.endsWith(":for:in");
11 | const isForAttribute = (attribute) =>
12 | isForOfAttribute(attribute) || isForInAttribute(attribute);
13 |
14 | const getForAttributes = (element) =>
15 | [...element.attributes].filter(isForAttribute);
16 |
17 | export const isFor = (element) => getForAttributes(element).length > 0;
18 |
19 | const getValName = ({ name }) => name.substring(0, name.length - 7);
20 |
21 | const childrenCache = new WeakMap();
22 |
23 | const cacheAllChildren = (element) => {
24 | if (!childrenCache.has(element)) {
25 | childrenCache.set(element, [...element.children]);
26 | }
27 | };
28 |
29 | const saveScopes = (elements, scope) => {
30 | for (let element of elements) {
31 | saveScope(element, scope);
32 | }
33 | };
34 |
35 | export const runFor2 = (element) => {
36 | const attributes = getForAttributes(element);
37 | const forOfAttribute = attributes.find(isForOfAttribute);
38 | const forInAttribute = attributes.find(isForInAttribute);
39 | const ofName = forOfAttribute ? getValName(forOfAttribute) : null;
40 | const inName = forInAttribute ? getValName(forInAttribute) : null;
41 |
42 | cacheAllChildren(element);
43 | removeAllChildren(element);
44 |
45 | const items = computeValue(element, forOfAttribute.value);
46 | const children = childrenCache.get(element);
47 |
48 | let index = 0;
49 | for (let item of items) {
50 | const clones = cloneChildren(children);
51 | const scope = {
52 | ...(ofName && { [ofName]: item }),
53 | ...(inName && { [inName]: index }),
54 | };
55 |
56 | saveScopes(clones, scope);
57 | appendChildren(element, clones);
58 |
59 | index = index + 1;
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { runExec } from "./exec";
2 | import { fetchJson } from "./fetch";
3 | import { addEventListener, removeAllEventListeners } from "./lib/events";
4 | import { createScope, saveScope } from "./scopes";
5 | import { injectStyleSheet } from "./stylesheet";
6 | import { runSub } from "./subroutine";
7 | import {
8 | attributeValue,
9 | computeValue,
10 | globals,
11 | globalsSet,
12 | watchers,
13 | } from "./values";
14 | import { setVisibility } from "./lib/setVisibility";
15 | import { isFor, runFor2 } from "./for2";
16 | import { setAttribute } from "./lib/setAttribute";
17 |
18 | const childrenCache = new WeakMap();
19 |
20 | const getSiblings = (element) =>
21 | [...element.parentNode.children].filter((el) => el !== element);
22 |
23 | const saveVal = async (element) => {
24 | for (let attribute of element.attributes) {
25 | let name = attribute.name;
26 | let value = attribute.value;
27 |
28 | if (
29 | name.endsWith(":number") ||
30 | name.endsWith(":bool") ||
31 | name.endsWith(":object")
32 | ) {
33 | ({ name, value } = attributeValue(element, attribute));
34 | }
35 |
36 | if (name.endsWith(":fetch")) {
37 | name = name.substring(0, name.length - 6);
38 | value = { loading: true, error: null, response: null, json: null };
39 |
40 | const url = computeValue(element, attribute.value);
41 | fetchJson(url)
42 | .then(({ response, json }) => {
43 | const value = { loading: false, error: null, response, json };
44 | globalsSet(element, name, value);
45 | })
46 | .catch((error) => {
47 | const value = { loading: false, error, response: null, json: null };
48 | globalsSet(element, name, value);
49 | })
50 | .then(() => {
51 | const siblings = getSiblings(element);
52 | processChildren(siblings);
53 | });
54 | }
55 |
56 | if (name.endsWith(":?")) {
57 | name = name.substring(0, name.length - 2);
58 | value = computeValue(element, value);
59 | }
60 |
61 | globalsSet(element, name, value, true);
62 | }
63 | };
64 |
65 | const runIfElement = (element) => {
66 | if (!element.hasAttribute("test")) return false;
67 | const truthy = computeValue(element, element.getAttribute("test"));
68 |
69 | // ELSE?
70 | const next = element.nextElementSibling;
71 | if (next?.tagName === "ELSE") {
72 | next.removeAttribute("test");
73 | setAttribute(next, "test", truthy ? "false" : "true");
74 | }
75 |
76 | return !!truthy;
77 | };
78 |
79 | const runWhileElement = async (element) => {
80 | let scope = createScope(element);
81 | saveScope(element, scope);
82 | let truthy = runIfElement(element);
83 |
84 | // cache children
85 | if (!childrenCache.has(element)) {
86 | childrenCache.set(element, [...element.children]);
87 | }
88 |
89 | // remove elements real children
90 | while (element.hasChildNodes()) {
91 | element.removeChild(element.lastChild);
92 | }
93 |
94 | setVisibility(element, truthy);
95 |
96 | const children = childrenCache.get(element);
97 | while (truthy) {
98 | const clones = children.map((child) => child.cloneNode(true));
99 | for (let child of clones) {
100 | scope = { ...scope };
101 | if (child.tagName !== "VAL") {
102 | // VAL has no scope
103 | saveScope(child, scope);
104 | } else {
105 | saveScope(element, scope);
106 | }
107 | element.appendChild(child);
108 | }
109 | await processChildren(clones);
110 | truthy = runIfElement(element);
111 | }
112 |
113 | return false;
114 | };
115 |
116 | const parseAttributes = (element) => {
117 | removeAllEventListeners(element);
118 |
119 | for (let attribute of element.attributes) {
120 | if (attribute.name === "#text") {
121 | element.textContent = computeValue(element, attribute.value);
122 | } else if (attribute.name.startsWith("#")) {
123 | const value = computeValue(element, attribute.value);
124 | setAttribute(element, attribute.name.substring(1), value);
125 | }
126 | // TODO: bind:value is new. deprecate onchange:set.
127 | if (attribute.name === "onchange:set" || attribute.name === "bind:value") {
128 | globalsSet(element, attribute.value, element.value);
129 |
130 | const handler = ({ target }) => {
131 | globalsSet(element, attribute.value, target.value);
132 | };
133 |
134 | addEventListener(element, "keyup", handler);
135 | }
136 | if (attribute.name === "watch") {
137 | const keys = attribute.value.split(",").map((key) => key.trim());
138 | for (let key of keys) {
139 | if (!watchers.has(key)) {
140 | watchers.set(key, new Set());
141 | }
142 | watchers.get(key).add(element);
143 | }
144 | }
145 | if (attribute.name.startsWith("on:")) {
146 | const match = attribute.name.match(/on:(?[^:]*)(:(?.*))?/);
147 | const eventName = match ? match.groups.event : null;
148 | const name = match ? match.groups.name : null;
149 |
150 | const handler = () => {
151 | if (attribute.value.match(/^\$.*/g)) {
152 | return runSub(attribute.value.substring(1));
153 | }
154 |
155 | const value = computeValue(element, attribute.value);
156 | if (name) {
157 | globalsSet(element, name, value);
158 | }
159 | };
160 |
161 | addEventListener(element, eventName, handler);
162 | }
163 | }
164 | };
165 |
166 | const parseElement = async (element) => {
167 | if (element.tagName === "VAL") {
168 | return await saveVal(element);
169 | }
170 | if (element.tagName === "IF" || element.tagName === "ELSE") {
171 | const truthy = runIfElement(element);
172 | setVisibility(element, truthy);
173 | return truthy;
174 | }
175 | if (element.tagName === "WHILE") {
176 | return await runWhileElement(element);
177 | }
178 | if (element.tagName === "SUB") {
179 | return false;
180 | }
181 | if (element.tagName === "EXEC") {
182 | return await runExec(element);
183 | }
184 | if (isFor(element)) {
185 | return await runFor2(element);
186 | }
187 |
188 | parseAttributes(element);
189 | };
190 |
191 | export const processChildren = async (children) => {
192 | for (let element of children) {
193 | const truthy = await parseElement(element);
194 | if (element.hasChildNodes() && truthy !== false) {
195 | await processChildren(element.children);
196 | }
197 | }
198 | };
199 |
200 | const domContentLoaded = async () => {
201 | try {
202 | injectStyleSheet();
203 | await processChildren(document.body.children);
204 | } catch (err) {
205 | console.error(err);
206 | }
207 | window.globals = globals;
208 | };
209 |
210 | window.addEventListener("DOMContentLoaded", domContentLoaded, true);
211 |
--------------------------------------------------------------------------------
/src/lib/children.js:
--------------------------------------------------------------------------------
1 | export const cloneChildren = (children) =>
2 | [...children].map((child) => child.cloneNode(true));
3 |
4 | export const appendChildren = (element, children) => {
5 | for (let child of children) {
6 | element.appendChild(child);
7 | }
8 | };
9 |
10 | export const removeAllChildren = (element) => {
11 | while (element.hasChildNodes()) {
12 | element.removeChild(element.lastChild);
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/src/lib/events.js:
--------------------------------------------------------------------------------
1 | const eventListeners = new WeakMap();
2 |
3 | export const addEventListener = (element, type, handler) => {
4 | element.addEventListener(type, handler);
5 | if (!eventListeners.has(element)) {
6 | eventListeners.set(element, []);
7 | }
8 | eventListeners.set(element, [
9 | ...eventListeners.get(element),
10 | [type, handler],
11 | ]);
12 | };
13 |
14 | export const removeAllEventListeners = (element) => {
15 | if (!eventListeners.has(element)) return;
16 | for (let [type, handler] of eventListeners.get(element)) {
17 | element.removeEventListener(type, handler);
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/src/lib/range.js:
--------------------------------------------------------------------------------
1 | const rangeInc = (start, end) => {
2 | const value = [];
3 |
4 | for (let i = start; i <= end; i++) {
5 | value.push(i);
6 | }
7 |
8 | return value;
9 | };
10 |
11 | const rangeDec = (start, end) => {
12 | const value = [];
13 |
14 | for (let i = start; i >= end; i--) {
15 | value.push(i);
16 | }
17 |
18 | return value;
19 | };
20 |
21 | export const range = (start, end) =>
22 | end >= start ? rangeInc(start, end) : rangeDec(start, end);
23 |
--------------------------------------------------------------------------------
/src/lib/setAttribute.js:
--------------------------------------------------------------------------------
1 | export const setAttribute = (element, name, value) => {
2 | if (value === true) {
3 | element.setAttribute(name, "");
4 | } else if (value === false) {
5 | element.removeAttribute(name);
6 | } else {
7 | element.setAttribute(name, value);
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/src/lib/setVisibility.js:
--------------------------------------------------------------------------------
1 | export const setVisibility = (element, visible) => {
2 | element.style.display = visible ? "inline-block" : "";
3 | };
4 |
--------------------------------------------------------------------------------
/src/scopes.js:
--------------------------------------------------------------------------------
1 | import { attributeValue, scopes } from "./values";
2 |
3 | const isScopeAttribute = (attribute) => attribute.name.startsWith("scope:");
4 |
5 | export const createScope = (element) => {
6 | if (!element.hasAttributes()) {
7 | return null;
8 | }
9 |
10 | const attributes = [...element.attributes].filter(isScopeAttribute);
11 |
12 | const scope = {};
13 |
14 | for (let attribute of attributes) {
15 | const { name, value } = attributeValue(element, attribute);
16 | scope[name] = value;
17 | }
18 |
19 | return scope;
20 | };
21 |
22 | export const getScope = (element) => {
23 | const scope = {};
24 |
25 | let limit = 100;
26 |
27 | while (element != null && limit-- > 0) {
28 | const elementScope = scopes.get(element);
29 |
30 | if (elementScope) {
31 | for (let key of Object.keys(elementScope)) {
32 | if (key in scope === false) {
33 | scope[key] = elementScope[key];
34 | }
35 | }
36 | }
37 |
38 | element = element.parentNode;
39 | }
40 |
41 | return scope;
42 | };
43 |
44 | export const saveScope = (element, scope) => {
45 | scopes.set(element, scope);
46 | };
47 |
48 | export const setValue = (element, key, value) => {
49 | let limit = 100;
50 |
51 | while (element != null && limit-- > 0) {
52 | const elementScope = scopes.get(element);
53 |
54 | if (elementScope && key in elementScope) {
55 | elementScope[key] = value;
56 | return;
57 | }
58 |
59 | element = element.parentNode;
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/src/stylesheet.js:
--------------------------------------------------------------------------------
1 | const styleSheet = `
2 | val,
3 | if,
4 | else,
5 | while,
6 | sub {
7 | display: none;
8 | }
9 | `;
10 |
11 | export const injectStyleSheet = () => {
12 | const style = document.createElement("style");
13 | style.setAttribute("type", "text/css");
14 | style.textContent = styleSheet;
15 |
16 | document.head.appendChild(style);
17 | };
18 |
--------------------------------------------------------------------------------
/src/subroutine.js:
--------------------------------------------------------------------------------
1 | import { processChildren } from "./index";
2 |
3 | export const runSub = async (name, element) => {
4 | const sub = document.querySelector(`SUB[name=${name}]`);
5 | if (!sub) {
6 | throw new Error(`SUB "${name}" was expected to exist.`);
7 | }
8 |
9 | return await processChildren(sub.children);
10 | };
11 |
--------------------------------------------------------------------------------
/src/values.js:
--------------------------------------------------------------------------------
1 | import { processChildren } from "./index";
2 | import { getScope, setValue } from "./scopes";
3 | import { range } from "./lib/range";
4 |
5 | export const watchers = new Map();
6 | export const globals = new Map();
7 | export const scopes = new WeakMap();
8 | const processQueue = [];
9 | let isProcessing = false;
10 |
11 | const startQueue = () => {
12 | if (isProcessing || processQueue.length === 0) return;
13 | isProcessing = true;
14 |
15 | const action = processQueue.shift(1);
16 | action()
17 | .catch((err) => console.error(err))
18 | .finally(() => {
19 | isProcessing = false;
20 | startQueue();
21 | });
22 | };
23 |
24 | export const globalsSet = (element, key, value, runWatchers = true) => {
25 | const scope = getScope(element);
26 | if (key in scope) {
27 | setValue(element, key, value);
28 | } else {
29 | globals.set(key, value);
30 | }
31 |
32 | if (runWatchers && watchers.has(key)) {
33 | const elements = watchers.get(key);
34 | processQueue.push(() => processChildren(elements.values()));
35 | startQueue();
36 | }
37 | };
38 |
39 | export const computeValue = (element, value) => {
40 | const keyValues = new Map([...globals]);
41 |
42 | // include scope values
43 | const scope = getScope(element);
44 | Object.entries(scope).forEach(([key, value]) => {
45 | keyValues.set(key, value);
46 | });
47 |
48 | const keys = keyValues.keys();
49 | const values = keyValues.values();
50 |
51 | const func = new Function(...keys, "range", `return ${value}`);
52 | const result = func(...values, range);
53 | return result;
54 | };
55 |
56 | export const attributeValue = (element, { name, value }) => {
57 | if (name.startsWith("scope:")) {
58 | name = name.substring(6);
59 | }
60 |
61 | if (name.endsWith(":number")) {
62 | return {
63 | name: name.substring(0, name.length - 7),
64 | value: Number(value),
65 | };
66 | }
67 |
68 | if (name.endsWith(":bool")) {
69 | return {
70 | name: name.substring(0, name.length - 5),
71 | value: computeValue(element, value),
72 | };
73 | }
74 |
75 | if (name.endsWith(":object")) {
76 | return {
77 | name: name.substring(0, name.length - 7),
78 | value: computeValue(element, value),
79 | };
80 | }
81 |
82 | return {
83 | name,
84 | value,
85 | };
86 | };
87 |
--------------------------------------------------------------------------------
/umd/arrays.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | list[ ] =
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/umd/calculator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
30 | Calculator
31 |
32 |
33 |
34 |
Calculator
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | 1
83 | 2
84 | 3
85 | +
86 |
87 |
88 | 4
89 | 5
90 | 6
91 | -
92 |
93 |
94 | 7
95 | 8
96 | 9
97 | *
98 |
99 |
100 |
101 | C
102 |
103 | 0
104 | =
105 | /
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/umd/dialog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
27 |
28 |
29 |
30 |
Dialog
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Hello Dialog!
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/umd/fetch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
Fetch
12 |
13 |
18 |
Loading...
19 |
Error:
20 |
21 | name:
22 | gender:
23 | height: cm
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/umd/fizzbuzz.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 | Fizz Buzz
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/umd/html-lang.js:
--------------------------------------------------------------------------------
1 | !function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var e=n();for(var r in e)("object"==typeof exports?exports:t)[r]=e[r]}}(self,(function(){return function(){"use strict";var t={d:function(n,e){for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},o:function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},r:function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},n={};function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}function r(t,n){return r=Object.setPrototypeOf||function(t,n){return t.__proto__=n,t},r(t,n)}function o(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(n&&n.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),n&&r(t,n)}function i(t,n,e){return n in t?Object.defineProperty(t,n,{value:e,enumerable:!0,configurable:!0,writable:!0}):t[n]=e,t}function u(t,n){(null==n||n>t.length)&&(n=t.length);for(var e=0,r=new Array(n);et.length)&&(n=t.length);for(var e=0,r=new Array(n);e=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,u=!0,a=!1;return{s:function(){e=e.call(t)},n:function(){var t=e.next();return u=t.done,t},e:function(t){a=!0,i=t},f:function(){try{u||null==e.return||e.return()}finally{if(a)throw i}}}}(n);try{for(r.s();!(e=r.n()).done;){var o=e.value;t.appendChild(o)}}catch(t){r.e(t)}finally{r.f()}},v=function(t){for(;t.hasChildNodes();)t.removeChild(t.lastChild)};function h(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function y(t,n,e){return y=h()?Reflect.construct:function(t,n,e){var o=[null];o.push.apply(o,n);var i=new(Function.bind.apply(t,o));return e&&r(i,e.prototype),i},y.apply(null,arguments)}function p(t,n){return function(t){if(Array.isArray(t))return t}(t)||function(t,n){var e=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=e){var r,o,i=[],u=!0,a=!1;try{for(e=e.call(t);!(u=(r=e.next()).done)&&(i.push(r.value),!n||i.length!==n);u=!0);}catch(t){a=!0,o=t}finally{try{u||null==e.return||e.return()}finally{if(a)throw o}}return i}}(t,n)||a(t,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function d(t,n){(null==n||n>t.length)&&(n=t.length);for(var e=0,r=new Array(n);e0;){var r=A.get(t);if(r)for(var o=0,i=Object.keys(r);o0;){var o=A.get(t);if(o&&n in o)return void(o[n]=e);t=t.parentNode}},j=function(t,n){return n>=t?function(t,n){for(var e=[],r=t;r<=n;r++)e.push(r);return e}(t,n):function(t,n){for(var e=[],r=t;r>=n;r--)e.push(r);return e}(t,n)},S=new Map,O=new Map,A=new WeakMap,E=[],P=!1,x=function t(){P||0===E.length||(P=!0,E.shift(1)().catch((function(t){return console.error(t)})).finally((function(){P=!1,t()})))},I=function(t,n,e){var r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],o=m(t);if(n in o?w(t,n,e):O.set(n,e),r&&S.has(n)){var i=S.get(n);E.push((function(){return jt(i.values())})),x()}},C=function(t,n){var e=new Map(c(O)),r=m(t);Object.entries(r).forEach((function(t){var n=p(t,2),r=n[0],o=n[1];e.set(r,o)}));var o=e.keys(),i=e.values();return y(Function,c(o).concat(["range","return ".concat(n)])).apply(void 0,c(i).concat([j]))},W=function(t,n){var e=n.name,r=n.value;return e.startsWith("scope:")&&(e=e.substring(6)),e.endsWith(":number")?{name:e.substring(0,e.length-7),value:Number(r)}:e.endsWith(":bool")?{name:e.substring(0,e.length-5),value:C(t,r)}:e.endsWith(":object")?{name:e.substring(0,e.length-7),value:C(t,r)}:{name:e,value:r}},M=function(t){return"cmd"===t.name};function N(t,n,e){return e?n?n(t):t:(t&&t.then||(t=Promise.resolve(t)),n?t.then(n):t)}var k=function(t){var n=t.headers.get("content-type");return n&&n.includes("application/json")};function T(t,n){var e=t();return e&&e.then?e.then(n):n(e)}var U,$=(U=function(){return N(fetch.apply(void 0,arguments),(function(t){var n=!1;if(!t.ok)throw new Error("HTTP Error Response: ".concat(t.status," ").concat(t.statusText));return T((function(){if(k(t))return n=!0,N(t.json(),(function(n){return{response:t,json:n}}))}),(function(e){return n?e:{response:t,json:null}}))}))},function(){for(var t=[],n=0;nt.length)&&(n=t.length);for(var e=0,r=new Array(n);e=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,u=!0,a=!1;return{s:function(){e=e.call(t)},n:function(){var t=e.next();return u=t.done,t},e:function(t){a=!0,i=t},f:function(){try{u||null==e.return||e.return()}finally{if(a)throw i}}}}function V(t,n){(null==n||n>t.length)&&(n=t.length);for(var e=0,r=new Array(n);e]+)>/g,(function(t,n){return"$"+i[n]})))}if("function"==typeof o){var a=this;return t[Symbol.replace].call(this,r,(function(){var t=arguments;return"object"!==e(t[t.length-1])&&(t=[].slice.call(t)).push(u(t,a)),o.apply(this,t)}))}return t[Symbol.replace].call(this,r,o)},ot.apply(this,arguments)}function it(t,n){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(t,n).enumerable}))),e.push.apply(e,r)}return e}function ut(t){for(var n=1;n=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,u=!0,a=!1;return{s:function(){e=e.call(t)},n:function(){var t=e.next();return u=t.done,t},e:function(t){a=!0,i=t},f:function(){try{u||null==e.return||e.return()}finally{if(a)throw i}}}}function vt(t,n){(null==n||n>t.length)&&(n=t.length);for(var e=0,r=new Array(n);e=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,u=!0,a=!1;return{s:function(){e=e.call(t)},n:function(){var t=e.next();return u=t.done,t},e:function(t){a=!0,i=t},f:function(){try{u||null==e.return||e.return()}finally{if(a)throw i}}}}(c(t.attributes).filter(b));try{for(r.s();!(n=r.n()).done;){var o=n.value,i=W(t,o),u=i.name,a=i.value;e[u]=a}}catch(t){r.e(t)}finally{r.f()}return e}(t);g(t,n);var e=bt(t);for(yt.has(t)||yt.set(t,c(t.children));t.hasChildNodes();)t.removeChild(t.lastChild);_(t,e);var r=yt.get(t);return lt(function(t,n,e){for(var r;;){var o=t();if(ft(o)&&(o=o.v),!o)return u;if(o.then){r=0;break}var i,u=e();if(u&&u.then){if(!ft(u)){r=1;break}u=u.s}}var a=new ct,c=at.bind(null,a,2);return(0===r?o.then(l):1===r?u.then(f):i.then(s)).then(void 0,c),a;function f(n){u=n;do{if(!(o=t())||ft(o)&&!o.v)return void at(a,1,u);if(o.then)return void o.then(l).then(void 0,c);ft(u=e())&&(u=u.v)}while(!u||!u.then);u.then(f).then(void 0,c)}function l(t){t?(u=e())&&u.then?u.then(f).then(void 0,c):f(u):at(a,1,u)}function s(){(o=t())?o.then?o.then(l).then(void 0,c):l(o):at(a,1,u)}}((function(){return!!e}),0,(function(){var o,i=r.map((function(t){return t.cloneNode(!0)})),u=st(i);try{for(u.s();!(o=u.n()).done;){var a=o.value;n=ut({},n),"VAL"!==a.tagName?g(a,n):g(t,n),t.appendChild(a)}}catch(t){u.e(t)}finally{u.f()}return ht(jt(i),(function(){e=bt(t)}))})),(function(){return!1}))})),gt=function(t){!function(t){if(L.has(t)){var n,e=function(t,n){var e="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!e){if(Array.isArray(t)||(e=function(t,n){if(t){if("string"==typeof t)return D(t,n);var e=Object.prototype.toString.call(t).slice(8,-1);return"Object"===e&&t.constructor&&(e=t.constructor.name),"Map"===e||"Set"===e?Array.from(t):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?D(t,n):void 0}}(t))||n&&t&&"number"==typeof t.length){e&&(t=e);var r=0,o=function(){};return{s:o,n:function(){return r>=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,u=!0,a=!1;return{s:function(){e=e.call(t)},n:function(){var t=e.next();return u=t.done,t},e:function(t){a=!0,i=t},f:function(){try{u||null==e.return||e.return()}finally{if(a)throw i}}}}(L.get(t));try{for(e.s();!(n=e.n()).done;){var r=p(n.value,2),o=r[0],i=r[1];t.removeEventListener(o,i)}}catch(t){e.e(t)}finally{e.f()}}}(t);var n,e=st(t.attributes);try{var r=function(){var e=n.value;if("#text"===e.name)t.textContent=C(t,e.value);else if(e.name.startsWith("#")){var r=C(t,e.value);Z(t,e.name.substring(1),r)}if("onchange:set"!==e.name&&"bind:value"!==e.name||(I(t,e.value,t.value),R(t,"keyup",(function(n){var r=n.target;I(t,e.value,r.value)}))),"watch"===e.name){var o,i=e.value.split(",").map((function(t){return t.trim()})),u=st(i);try{for(u.s();!(o=u.n()).done;){var a=o.value;S.has(a)||S.set(a,new Set),S.get(a).add(t)}}catch(t){u.e(t)}finally{u.f()}}if(e.name.startsWith("on:")){var c=e.name.match(ot(/on:((?:(?!:)[\s\S])*)(:(.*))?/,{event:1,name:3})),f=c?c.groups.event:null,l=c?c.groups.name:null;R(t,f,(function(){if(e.value.match(/^\$.*/g))return B(e.value.substring(1));var n=C(t,e.value);l&&I(t,l,n)}))}};for(e.s();!(n=e.n()).done;)r()}catch(t){e.e(t)}finally{e.f()}},wt=pt((function(t){var n=!1;return tt((function(){if("VAL"===t.tagName)return n=!0,ht(dt(t))}),(function(e){var r=!1;if(n)return e;if("IF"===t.tagName||"ELSE"===t.tagName){var o=bt(t);return _(t,o),o}return tt((function(){if("WHILE"===t.tagName)return r=!0,ht(mt(t))}),(function(n){var e=!1;return r?n:"SUB"!==t.tagName&&tt((function(){if("EXEC"===t.tagName)return e=!0,ht(function(t){var n=c(t.attributes).find(M);return n.value.match(/^\$.*/g)?function(t,n){var e=document.querySelector("SUB[name=".concat(n,"]"));if(!e)throw new Error('SUB "'.concat(n,'" was expected to exist.'));var r=l(e.children);return v(t),s(t,r),!0}(t,n.value.substring(1)):C(t,n.value)}(t))}),(function(n){var r=!1;return e?n:tt((function(){if(function(t){return J(t).length>0}(t))return r=!0,ht(function(t){var n=J(t),e=n.find(X),r=n.find(z),o=e?K(e):null,u=r?K(r):null;!function(t){Q.has(t)||Q.set(t,c(t.children))}(t),v(t);var a,f=C(t,e.value),h=Q.get(t),y=0,p=H(f);try{for(p.s();!(a=p.n()).done;){var d=a.value,b=l(h),m=q(q({},o&&i({},o,d)),u&&i({},u,y));Y(b,m),s(t,b),y+=1}}catch(t){p.e(t)}finally{p.f()}}(t))}),(function(n){if(r)return n;gt(t)}))}))}))}))})),jt=pt((function(t){return function(t){if(t&&t.then)return t.then(nt)}(function(t,n,e){if("function"==typeof t[rt]){var r,o,i,u=t[rt]();function c(t){try{for(;!((r=u.next()).done||e&&e());)if((t=n(r.value))&&t.then){if(!ft(t))return void t.then(c,i||(i=at.bind(null,o=new ct,2)));t=t.v}o?at(o,1,t):o=t}catch(t){at(o||(o=new ct),2,t)}}if(c(),u.return){var a=function(t){try{r.done||u.return()}catch(t){}return t};if(o&&o.then)return o.then(a,(function(t){throw a(t)}));a()}return o}if(!("length"in t))throw new TypeError("Object is not iterable");for(var c=[],f=0;f
2 |
3 |
4 |
5 |
6 |
7 |
8 |
34 | Memory Game
35 |
36 |
37 |
38 |
Memory Game
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | Congratulations! You won in moves!
97 | Play Again
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
m[0] === row && m[1] === col)"
107 | isclicked:?="(clicked[0] === row && clicked[1] === col)"
108 | showcard:?="(match[0] === row && match[1] === col) || prevmatch || isclicked"
109 | >
110 |
111 |
112 |
113 |
114 |
115 |
116 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/umd/objects.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/umd/todo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | TODO
8 |
15 |
16 |
17 |
18 |
19 |
20 | add
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | X
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const fs = require("fs");
3 | const HtmlWebpackPlugin = require("html-webpack-plugin");
4 |
5 | module.exports = {
6 | entry: {
7 | ["html-lang"]: "./src/index.js",
8 | },
9 | output: {
10 | libraryTarget: "umd",
11 | path: path.resolve(__dirname, "umd"),
12 | },
13 | devServer: {
14 | static: {
15 | directory: path.join(__dirname, "src.html"),
16 | },
17 | compress: false,
18 | port: 8080,
19 | },
20 | module: {
21 | rules: [
22 | {
23 | test: /\.m?js$/,
24 | exclude: /node_modules/,
25 | use: {
26 | loader: "babel-loader",
27 | },
28 | },
29 | ],
30 | },
31 | plugins: fs.readdirSync("src.html").map(
32 | (filename) =>
33 | new HtmlWebpackPlugin({
34 | filename,
35 | template: `src.html/${filename}`,
36 | minify: false,
37 | })
38 | ),
39 | };
40 |
--------------------------------------------------------------------------------
/what-the-hell-are-you-doing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joelnet/html-lang/ad478b7f562cac32f5e951ff00dab52832437535/what-the-hell-are-you-doing.jpg
--------------------------------------------------------------------------------