-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.js
136 lines (113 loc) · 3.38 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { getUrlInformation } from "./getUrlInformation";
import { Tokenizer, Parser, Transformer } from "./parseMarkup";
class Ash {
routes = {};
events = {};
constructor(routes, events) {
this.render = this.render.bind(this);
this.emit = this.emit.bind(this);
this.routes = routes;
this.events = {
...events,
go: (rawPath) => {
const path = JSON.parse(rawPath)
const removedSlash = path.startsWith("/") ? path.substring(1) : path;
window.location.hash = `#${removedSlash}`;
},
};
this.render();
window.onhashchange = () => {
this.render();
};
}
async render(id) {
const url = window.location.href;
const urlInformation = getUrlInformation(Object.keys(this.routes), url);
const route = urlInformation.matchedDefinition ?? urlInformation.path;
const markup = await this.routes[route]({ urlInformation });
const tree = convert(markup, this.emit);
if (id) {
const newElement = findInTree(tree, id);
if (newElement) {
const node = this.createNode(newElement);
if (node) {
document.getElementById(id).replaceWith(node);
}
}
return;
}
document.getElementById("ashjs").innerHTML = "";
tree.forEach((element) => {
const node = this.createNode(element);
if (node) {
document.getElementById("ashjs").appendChild(node);
}
});
}
emit(event, data) {
console.debug("emit", event, data);
this.events[event](data, this.render, this.emit);
}
createNode(element) {
if (typeof element !== "object" || element === null) {
return null;
}
// Get the tag name and its content or children
const [tagName, contentOrChildren] = Object.entries(element)[0];
// Create the DOM element for the tag
const node = document.createElement(tagName);
if (typeof contentOrChildren === "string") {
node.textContent = contentOrChildren;
} else if (Array.isArray(contentOrChildren)) {
contentOrChildren.forEach((childElement) => {
if (typeof childElement === "string") {
node.textContent += childElement;
return;
}
const childNode = this.createNode(childElement);
if (childNode) {
node.appendChild(childNode);
}
});
}
// Handle additional props (attributes or event listeners)
for (const [key, value] of Object.entries(element)) {
if (key !== tagName) {
if (key.startsWith("on") && typeof value === "function") {
node.addEventListener(key.substring(2).toLowerCase(), value);
} else {
node.setAttribute(key, value.toString());
}
}
}
return node;
}
}
/**
* ash.js helper functions
*/
function findInTree(tree, id) {
for (const element of tree) {
if (element.id === id) {
return element;
}
if (element.div && Array.isArray(element.div)) {
const foundElement = findInTree(element.div, id);
if (foundElement) {
return foundElement;
}
}
}
return null;
}
function convert(markup, emit) {
const tokenizer = new Tokenizer(markup);
const tokens = tokenizer.tokenize();
const parser = new Parser(tokens);
const ast = parser.parse();
const transformer = new Transformer(ast, emit);
const result = transformer.transform();
return result;
}
window.convert = convert;
window.Ash = Ash;