+ `
+}
\ No newline at end of file
diff --git a/day6/src/components/counter.ts b/day6/src/components/counter.ts
new file mode 100644
index 0000000..71699b7
--- /dev/null
+++ b/day6/src/components/counter.ts
@@ -0,0 +1,22 @@
+import minus from '../icons/minus.svg'
+import plus from '../icons/plus.svg'
+import refresh from '../icons/refresh.svg'
+
+export const Counter = () => {
+ return `
+
+
+ `
+}
\ No newline at end of file
diff --git a/day6/src/components/routerLink.ts b/day6/src/components/routerLink.ts
new file mode 100644
index 0000000..98b0c0f
--- /dev/null
+++ b/day6/src/components/routerLink.ts
@@ -0,0 +1,5 @@
+export const RouterLink = (link: string, name: string) => {
+ return `
+ ${name}
+ `
+}
\ No newline at end of file
diff --git a/day6/src/components/textInput.ts b/day6/src/components/textInput.ts
new file mode 100644
index 0000000..b00c227
--- /dev/null
+++ b/day6/src/components/textInput.ts
@@ -0,0 +1,10 @@
+export const TextInput = () => {
+ return `
+
+
Input is: {text}
+
+
+
+
+ `
+}
\ No newline at end of file
diff --git a/day6/src/icons/minus.svg b/day6/src/icons/minus.svg
new file mode 100644
index 0000000..3d681cb
--- /dev/null
+++ b/day6/src/icons/minus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/day6/src/icons/plus.svg b/day6/src/icons/plus.svg
new file mode 100644
index 0000000..6d61262
--- /dev/null
+++ b/day6/src/icons/plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/day6/src/icons/refresh.svg b/day6/src/icons/refresh.svg
new file mode 100644
index 0000000..482bede
--- /dev/null
+++ b/day6/src/icons/refresh.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/day6/src/layouts/404.ts b/day6/src/layouts/404.ts
new file mode 100644
index 0000000..8640612
--- /dev/null
+++ b/day6/src/layouts/404.ts
@@ -0,0 +1,11 @@
+import { RouterLink } from '../components/routerLink';
+
+
+export default () => {
+ return `
+
+
Looks like you're lost
+
${RouterLink('/', 'return home')}
+
+ `
+}
\ No newline at end of file
diff --git a/day6/src/lib/ReactiveObject.ts b/day6/src/lib/ReactiveObject.ts
new file mode 100644
index 0000000..b0431ef
--- /dev/null
+++ b/day6/src/lib/ReactiveObject.ts
@@ -0,0 +1,39 @@
+export class Reactive {
+ [x: string]: any;
+ constructor(obj: any) {
+ this.contents = obj;
+ this.listeners = {};
+ this.makeReactive(obj);
+ }
+
+ makeReactive(obj: any) {
+ Object.keys(obj).forEach(prop => this.makePropReactive(obj, prop));
+ }
+
+ makePropReactive(obj: any, key: string) {
+ let value = obj[key];
+
+ // Gotta be careful with this here
+ const that = this;
+
+ Object.defineProperty(obj, key, {
+ get() {
+ return value;
+ },
+ set(newValue) {
+ value = newValue;
+ that.notify(key)
+ }
+ })
+ }
+
+ listen(prop: any, handler: any) {
+ if (!this.listeners[prop]) this.listeners[prop] = [];
+
+ this.listeners[prop].push(handler);
+ }
+
+ notify(prop: any) {
+ this.listeners[prop].forEach((listener: (arg0: any) => any) => listener(this.contents[prop]));
+ }
+}
\ No newline at end of file
diff --git a/day6/src/lib/cookieManager.ts b/day6/src/lib/cookieManager.ts
new file mode 100644
index 0000000..8c133bc
--- /dev/null
+++ b/day6/src/lib/cookieManager.ts
@@ -0,0 +1,39 @@
+export function setCookie(name: string, value: string, expires: any, path?: string, domain?: string) {
+ var cookie = name.trimEnd() + "=" + escape(value) + ";";
+
+ if (expires) {
+ // If it's a date
+ if(expires instanceof Date) {
+ // If it isn't a valid date
+ if (isNaN(expires.getTime()))
+ expires = new Date();
+ }
+ else
+ expires = new Date(new Date().getTime() + parseInt(expires) * 1000 * 60 * 60 * 24);
+
+ cookie += "expires=" + expires.toGMTString() + ";";
+ }
+
+ if (path)
+ cookie += "path=" + path + ";";
+ if (domain)
+ cookie += "domain=" + domain + ";";
+
+ document.cookie = cookie;
+ }
+
+export function getCookie(name: string) {
+ let cname = name + "=";
+ let decodedCookie = decodeURIComponent(document.cookie);
+ let ca = decodedCookie.split(';');
+ for(let i = 0; i {
+ if (e === undefined) return
+ // here we check for elements with the name of "data-token-"
+ const querySelector = "data-token-" + e.split("").map(c => c.charCodeAt(0).toString(16).padStart(2, "0")).join("")
+ const listeningElements = document.querySelectorAll(`[${querySelector}]`)
+ listeningElements.forEach((elm) => {
+ appState.listen(e, (change: any) => elm.textContent = change);
+ })
+ })
+ // here we look for elements with the d-click attribute and on click run the function in the attribute
+ let elms = documentBody.querySelectorAll('*[d-click]')
+ elms.forEach((e) => {
+ const clickFunction = e.getAttribute("d-click")
+ if (!clickFunction) return;
+ e.addEventListener('click', () => {
+ eval(clickFunction)
+ })
+ })
+
+ // here we look for elements with the d-model attribute and if there is any input in the element then we update the appState item with the name
+ // of the attribute value
+ // example: if the user types "hello" into a text field with the d-model attribute of "text" then we update the appState item with the name "text"
+ // to "hello"
+ // similar to vue.js v-model attribute
+ let modelElms = document.querySelectorAll('input[d-model], textarea[d-model]');
+ modelElms.forEach((e) => {
+ const modelName = e.getAttribute("d-model")
+ if (!modelName) return;
+ e.addEventListener('input', (event: any) => {
+ appState.contents[modelName] = event.target.value
+ })
+ })
+}
\ No newline at end of file
diff --git a/day6/src/lib/templateRenderer.ts b/day6/src/lib/templateRenderer.ts
new file mode 100644
index 0000000..e91ec82
--- /dev/null
+++ b/day6/src/lib/templateRenderer.ts
@@ -0,0 +1,48 @@
+export const compileToString = (template: string) => {
+ const ast = parse(template);
+ let fnStr = `\`\``;
+
+ ast.map(t => {
+ // checking to see if it is an interpolation
+ if (t.startsWith("{") && t.endsWith("}")) {
+ // so first we calculate the hex value of the variable, which is needed so we can use reactivity properly
+ // then after that we append a span element with the data-token- attribute to the span
+ // finally we add the appState.contents.variables to the string so we have the value of the variable in the raw html.
+ const uuid = t.split(/{|}/).filter(Boolean)[0].trim().split("").map(c => c.charCodeAt(0).toString(16).padStart(2, "0")).join("");
+ fnStr = fnStr.substring(0, fnStr.length - 1) + `\``;
+ fnStr += `+appState.contents.${t.split(/{|}/).filter(Boolean)[0].trim()}` + `+\`\``;
+ } else {
+ // append the string to the fnStr
+ fnStr += `+\`${t}\``;
+ }
+ });
+
+ return fnStr;
+}
+
+// this function will turn a string like "hi {user}" into an array that looks something like "["hi", "{user}"] so we can loop over the array elements
+// when compiling the template
+var parse = (template: string) => {
+ let result = /{(.*?)}/g.exec(template);
+ const arr = [];
+ let firstPos;
+
+ while (result) {
+ firstPos = result.index;
+ if (firstPos !== 0) {
+ arr.push(template.substring(0, firstPos));
+ template = template.slice(firstPos);
+ }
+
+ arr.push(result[0]);
+ template = template.slice(result[0].length);
+ result = /{(.*?)}/g.exec(template);
+ }
+
+ if (template) arr.push(template);
+ return arr;
+}
+
+export const render = (template: string) => {
+ return compileToString(template)
+}
\ No newline at end of file
diff --git a/day6/src/main.ts b/day6/src/main.ts
new file mode 100644
index 0000000..1788a42
--- /dev/null
+++ b/day6/src/main.ts
@@ -0,0 +1,25 @@
+import { Reactive } from './lib/ReactiveObject'
+import { loadPage, hydratePage } from './lib/router';
+import { getCookie } from './lib/cookieManager';
+import '/src/style.css';
+
+export const appState = new Reactive({
+ count: 0,
+ cookie: getCookie('username'),
+ text: '',
+ year: '',
+ cookiedata: ""
+});
+
+// equivalent to mounted() on svelte or vue
+window.addEventListener('load', async () => {
+ // loadPage after all the index is loaded
+ if (!import.meta.env.SSR) {
+ await loadPage()
+ } else {
+ await hydratePage()
+ }
+ window.onpopstate = async () => {
+ await loadPage()
+ }
+})
\ No newline at end of file
diff --git a/day6/src/pages/index.ts b/day6/src/pages/index.ts
new file mode 100644
index 0000000..1893b54
--- /dev/null
+++ b/day6/src/pages/index.ts
@@ -0,0 +1,28 @@
+import { Counter } from '../components/counter';
+import { TextInput } from '../components/textInput';
+import { MyDad } from '../components/myDad';
+import { RouterLink } from '../components/routerLink';
+import { CookieInput } from '../components/cookieInput';
+
+
+export default () => {
+ return `
+