@ktjs/core 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,68 +2,22 @@ const $throw = (message) => {
2
2
  throw new Error('kt.js: ' + message);
3
3
  };
4
4
 
5
- /**
6
- * & Remove `bind` because it is shockingly slower than wrapper
7
- * & `window.document` is safe because it is not configurable and its setter is undefined
8
- */
9
- const $appendChild = HTMLElement.prototype.appendChild;
10
- const originAppend = HTMLElement.prototype.append;
11
- const $append = // for ie 9/10/11
12
- typeof originAppend === 'function'
13
- ? function (...args) {
14
- return originAppend.apply(this, args);
15
- }
16
- : function (...nodes) {
17
- if (nodes.length < 50) {
18
- for (let i = 0; i < nodes.length; i++) {
19
- const node = nodes[i];
20
- if (typeof node === 'string') {
21
- $appendChild.call(this, document.createTextNode(node));
22
- }
23
- else {
24
- $appendChild.call(this, node);
25
- }
26
- }
27
- }
28
- else {
29
- const fragment = document.createDocumentFragment();
30
- for (let i = 0; i < nodes.length; i++) {
31
- const node = nodes[i];
32
- if (typeof node === 'string') {
33
- $appendChild.call(fragment, document.createTextNode(node));
34
- }
35
- else {
36
- $appendChild.call(fragment, node);
37
- }
38
- }
39
- $appendChild.call(this, fragment);
40
- }
41
- };
42
-
43
- const $isArray = Array.isArray;
44
- const $keys = Object.keys;
45
- const emptyPromiseHandler = () => ({});
46
- if (typeof Promise === 'undefined') {
47
- window.Promise = { resolve: emptyPromiseHandler, reject: emptyPromiseHandler };
48
- }
49
- const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then === 'function';
50
-
51
- function booleanHandler(element, key, value) {
5
+ const booleanHandler = (element, key, value) => {
52
6
  if (key in element) {
53
7
  element[key] = !!value;
54
8
  }
55
9
  else {
56
10
  element.setAttribute(key, value);
57
11
  }
58
- }
59
- function valueHandler(element, key, value) {
12
+ };
13
+ const valueHandler = (element, key, value) => {
60
14
  if (key in element) {
61
15
  element[key] = value;
62
16
  }
63
17
  else {
64
18
  element.setAttribute(key, value);
65
19
  }
66
- }
20
+ };
67
21
  // Attribute handlers map for optimized lookup
68
22
  const handlers = {
69
23
  checked: booleanHandler,
@@ -86,14 +40,25 @@ const handlers = {
86
40
  muted: booleanHandler,
87
41
  defer: booleanHandler,
88
42
  async: booleanHandler,
89
- hidden: function (element, _key, value) {
90
- element.hidden = !!value;
91
- },
43
+ hidden: (element, _key, value) => (element.hidden = !!value),
92
44
  };
93
- const defaultHandler = function (element, key, value) {
94
- return element.setAttribute(key, value);
45
+ const ktEventHandlers = {
46
+ 'on:ktchange': (element, handler) => element.addEventListener('change', () => handler(element.value)),
47
+ 'ontrim:ktchange': (element, handler) => element.addEventListener('change', () => handler(element.value.trim())),
48
+ 'on:ktchangenumber': (element, handler) => element.addEventListener('change', () => handler(Number(element.value))),
49
+ 'on:ktinput': (element, handler) => element.addEventListener('input', () => handler(element.value)),
50
+ 'ontrim:ktinput': (element, handler) => element.addEventListener('input', () => handler(element.value.trim())),
51
+ 'on:ktinputnumber': (element, handler) => element.addEventListener('input', () => handler(Number(element.value))),
95
52
  };
53
+
54
+ const defaultHandler = (element, key, value) => element.setAttribute(key, value);
96
55
  function attrIsObject(element, attr) {
56
+ // & deal k-if first
57
+ if ('k-if' in attr) {
58
+ if (!attr['k-if']) {
59
+ return false;
60
+ }
61
+ }
97
62
  const classValue = attr.class;
98
63
  const style = attr.style;
99
64
  if (classValue !== undefined) {
@@ -111,12 +76,17 @@ function attrIsObject(element, attr) {
111
76
  }
112
77
  delete attr.style;
113
78
  }
114
- const keys = $keys(attr);
115
- for (let i = keys.length - 1; i >= 0; i--) {
116
- const key = keys[i];
79
+ for (const key in attr) {
117
80
  const o = attr[key];
118
81
  // force register on:xxx as an event handler
119
82
  // !if o is not valid, the throwing job will be done by `on`, not kt.js
83
+ // # special handling for kt.js specific events
84
+ const ktEvent = ktEventHandlers[key];
85
+ if (ktEvent) {
86
+ ktEvent(element, o);
87
+ continue;
88
+ }
89
+ // # normal event handler
120
90
  if (key.startsWith('on:')) {
121
91
  element.addEventListener(key.slice(3), o); // chop off the `@`
122
92
  continue;
@@ -134,19 +104,66 @@ function attrIsObject(element, attr) {
134
104
  if (style !== undefined) {
135
105
  attr.style = style;
136
106
  }
107
+ return true;
137
108
  }
138
109
  function applyAttr(element, attr) {
139
110
  if (typeof attr === 'string') {
140
111
  element.className = attr;
112
+ return true;
141
113
  }
142
114
  else if (typeof attr === 'object' && attr !== null) {
143
- attrIsObject(element, attr);
115
+ return attrIsObject(element, attr);
144
116
  }
145
117
  else {
146
- $throw('attr must be an object/string.');
118
+ throw new Error('kt.js: attr must be an object/string.');
147
119
  }
148
120
  }
149
121
 
122
+ /**
123
+ * & Remove `bind` because it is shockingly slower than wrapper
124
+ * & `window.document` is safe because it is not configurable and its setter is undefined
125
+ */
126
+ const $appendChild = HTMLElement.prototype.appendChild;
127
+ const originAppend = HTMLElement.prototype.append;
128
+ const $append = // for ie 9/10/11
129
+ typeof originAppend === 'function'
130
+ ? function (...args) {
131
+ return originAppend.apply(this, args);
132
+ }
133
+ : function (...nodes) {
134
+ if (nodes.length < 50) {
135
+ for (let i = 0; i < nodes.length; i++) {
136
+ const node = nodes[i];
137
+ if (typeof node === 'string') {
138
+ $appendChild.call(this, document.createTextNode(node));
139
+ }
140
+ else {
141
+ $appendChild.call(this, node);
142
+ }
143
+ }
144
+ }
145
+ else {
146
+ const fragment = document.createDocumentFragment();
147
+ for (let i = 0; i < nodes.length; i++) {
148
+ const node = nodes[i];
149
+ if (typeof node === 'string') {
150
+ $appendChild.call(fragment, document.createTextNode(node));
151
+ }
152
+ else {
153
+ $appendChild.call(fragment, node);
154
+ }
155
+ }
156
+ $appendChild.call(this, fragment);
157
+ }
158
+ };
159
+
160
+ const $isArray = Array.isArray;
161
+ const emptyPromiseHandler = () => ({});
162
+ if (typeof Promise === 'undefined') {
163
+ window.Promise = { resolve: emptyPromiseHandler, reject: emptyPromiseHandler };
164
+ }
165
+ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then === 'function';
166
+
150
167
  function apdSingle(element, c) {
151
168
  // & JSX should ignore false, undefined, and null
152
169
  if (c === false || c === undefined || c === null) {
@@ -203,7 +220,7 @@ function applyContent(element, content) {
203
220
  * ## About
204
221
  * @package @ktjs/core
205
222
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
206
- * @version 0.13.0 (Last Update: 2026.01.15 16:05:51.084)
223
+ * @version 0.14.0 (Last Update: 2026.01.16 20:08:50.505)
207
224
  * @license MIT
208
225
  * @link https://github.com/baendlorel/kt.js
209
226
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -217,7 +234,10 @@ const h = ((tag, attr = '', content = '') => {
217
234
  // * start creating the element
218
235
  const element = document.createElement(tag);
219
236
  // * Handle content
220
- applyAttr(element, attr);
237
+ const kif = applyAttr(element, attr);
238
+ if (!kif) {
239
+ return document.createComment('k-if');
240
+ }
221
241
  applyContent(element, content);
222
242
  return element;
223
243
  });
@@ -16,6 +16,33 @@ interface KTRef<T> {
16
16
  */
17
17
  declare function ref<T = HTMLElement>(value?: T): KTRef<T>;
18
18
 
19
+ type KTHTMLElement = HTMLElement & {
20
+ /**
21
+ * Automically generate a redraw function if it is not provided
22
+ * @param props
23
+ */
24
+ redraw: (props?: KTAttribute, children?: KTRawContent) => void;
25
+ };
26
+
27
+ declare global {
28
+ namespace JSX {
29
+ type Element = KTHTMLElement;
30
+
31
+ interface IntrinsicElements {
32
+ [tag: string]: KTAttribute & { children?: KTRawContent };
33
+ }
34
+
35
+ // interface IntrinsicAttributes {
36
+ // key?: string | number;
37
+ // }
38
+ type IntrinsicAttributes = KTAttribute;
39
+
40
+ interface ElementChildrenAttribute {
41
+ children: {};
42
+ }
43
+ }
44
+ }
45
+
19
46
  type KTAvailableContent =
20
47
  | KTRef<any>
21
48
  | HTMLElement
@@ -36,6 +63,7 @@ interface KTBaseAttribute {
36
63
  [k: string]: any;
37
64
 
38
65
  ref?: KTRef<HTMLElement>;
66
+ 'k-if'?: any;
39
67
 
40
68
  id?: string;
41
69
  class?: string;
@@ -92,34 +120,17 @@ type KTPrefixedEventHandlers = {
92
120
  [EventName in keyof HTMLElementEventMap as `on:${EventName}`]?: (ev: HTMLElementEventMap[EventName]) => void;
93
121
  };
94
122
 
95
- type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
123
+ type KTSpecialEventHandlers = {
124
+ 'on:ktchange'?: (value: string) => void;
125
+ 'ontrim:ktchange'?: (value: string) => void;
126
+ 'on:ktchangenumber'?: (value: number) => void;
96
127
 
97
- type KTHTMLElement = HTMLElement & {
98
- /**
99
- * Automically generate a redraw function if it is not provided
100
- * @param props
101
- */
102
- redraw: (props?: KTAttribute, children?: KTRawContent) => void;
128
+ 'on:ktinput'?: (value: string) => void;
129
+ 'ontrim:ktinput'?: (value: string) => void;
130
+ 'on:ktinputnumber'?: (value: number) => void;
103
131
  };
104
132
 
105
- declare global {
106
- namespace JSX {
107
- type Element = KTHTMLElement;
108
-
109
- interface IntrinsicElements {
110
- [tag: string]: KTAttribute & { children?: KTRawContent };
111
- }
112
-
113
- // interface IntrinsicAttributes {
114
- // key?: string | number;
115
- // }
116
- type IntrinsicAttributes = KTAttribute;
117
-
118
- interface ElementChildrenAttribute {
119
- children: {};
120
- }
121
- }
122
- }
133
+ type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers & KTSpecialEventHandlers;
123
134
 
124
135
  type HTML<T extends HTMLTag & otherstring> = T extends HTMLTag ? HTMLElementTagNameMap[T] : HTMLElement;
125
136
  type H = (<T extends HTMLTag>(tag: T, attr?: KTRawAttr, content?: KTRawContent) => HTML<T>) & {
@@ -136,7 +147,7 @@ type H = (<T extends HTMLTag>(tag: T, attr?: KTRawAttr, content?: KTRawContent)
136
147
  * ## About
137
148
  * @package @ktjs/core
138
149
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
139
- * @version 0.13.0 (Last Update: 2026.01.15 16:05:51.084)
150
+ * @version 0.14.0 (Last Update: 2026.01.16 20:08:50.505)
140
151
  * @license MIT
141
152
  * @link https://github.com/baendlorel/kt.js
142
153
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -2,68 +2,22 @@ const $throw = (message) => {
2
2
  throw new Error('kt.js: ' + message);
3
3
  };
4
4
 
5
- /**
6
- * & Remove `bind` because it is shockingly slower than wrapper
7
- * & `window.document` is safe because it is not configurable and its setter is undefined
8
- */
9
- const $appendChild = HTMLElement.prototype.appendChild;
10
- const originAppend = HTMLElement.prototype.append;
11
- const $append = // for ie 9/10/11
12
- typeof originAppend === 'function'
13
- ? function (...args) {
14
- return originAppend.apply(this, args);
15
- }
16
- : function (...nodes) {
17
- if (nodes.length < 50) {
18
- for (let i = 0; i < nodes.length; i++) {
19
- const node = nodes[i];
20
- if (typeof node === 'string') {
21
- $appendChild.call(this, document.createTextNode(node));
22
- }
23
- else {
24
- $appendChild.call(this, node);
25
- }
26
- }
27
- }
28
- else {
29
- const fragment = document.createDocumentFragment();
30
- for (let i = 0; i < nodes.length; i++) {
31
- const node = nodes[i];
32
- if (typeof node === 'string') {
33
- $appendChild.call(fragment, document.createTextNode(node));
34
- }
35
- else {
36
- $appendChild.call(fragment, node);
37
- }
38
- }
39
- $appendChild.call(this, fragment);
40
- }
41
- };
42
-
43
- const $isArray = Array.isArray;
44
- const $keys = Object.keys;
45
- const emptyPromiseHandler = () => ({});
46
- if (typeof Promise === 'undefined') {
47
- window.Promise = { resolve: emptyPromiseHandler, reject: emptyPromiseHandler };
48
- }
49
- const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then === 'function';
50
-
51
- function booleanHandler(element, key, value) {
5
+ const booleanHandler = (element, key, value) => {
52
6
  if (key in element) {
53
7
  element[key] = !!value;
54
8
  }
55
9
  else {
56
10
  element.setAttribute(key, value);
57
11
  }
58
- }
59
- function valueHandler(element, key, value) {
12
+ };
13
+ const valueHandler = (element, key, value) => {
60
14
  if (key in element) {
61
15
  element[key] = value;
62
16
  }
63
17
  else {
64
18
  element.setAttribute(key, value);
65
19
  }
66
- }
20
+ };
67
21
  // Attribute handlers map for optimized lookup
68
22
  const handlers = {
69
23
  checked: booleanHandler,
@@ -86,14 +40,25 @@ const handlers = {
86
40
  muted: booleanHandler,
87
41
  defer: booleanHandler,
88
42
  async: booleanHandler,
89
- hidden: function (element, _key, value) {
90
- element.hidden = !!value;
91
- },
43
+ hidden: (element, _key, value) => (element.hidden = !!value),
92
44
  };
93
- const defaultHandler = function (element, key, value) {
94
- return element.setAttribute(key, value);
45
+ const ktEventHandlers = {
46
+ 'on:ktchange': (element, handler) => element.addEventListener('change', () => handler(element.value)),
47
+ 'ontrim:ktchange': (element, handler) => element.addEventListener('change', () => handler(element.value.trim())),
48
+ 'on:ktchangenumber': (element, handler) => element.addEventListener('change', () => handler(Number(element.value))),
49
+ 'on:ktinput': (element, handler) => element.addEventListener('input', () => handler(element.value)),
50
+ 'ontrim:ktinput': (element, handler) => element.addEventListener('input', () => handler(element.value.trim())),
51
+ 'on:ktinputnumber': (element, handler) => element.addEventListener('input', () => handler(Number(element.value))),
95
52
  };
53
+
54
+ const defaultHandler = (element, key, value) => element.setAttribute(key, value);
96
55
  function attrIsObject(element, attr) {
56
+ // & deal k-if first
57
+ if ('k-if' in attr) {
58
+ if (!attr['k-if']) {
59
+ return false;
60
+ }
61
+ }
97
62
  const classValue = attr.class;
98
63
  const style = attr.style;
99
64
  if (classValue !== undefined) {
@@ -111,12 +76,17 @@ function attrIsObject(element, attr) {
111
76
  }
112
77
  delete attr.style;
113
78
  }
114
- const keys = $keys(attr);
115
- for (let i = keys.length - 1; i >= 0; i--) {
116
- const key = keys[i];
79
+ for (const key in attr) {
117
80
  const o = attr[key];
118
81
  // force register on:xxx as an event handler
119
82
  // !if o is not valid, the throwing job will be done by `on`, not kt.js
83
+ // # special handling for kt.js specific events
84
+ const ktEvent = ktEventHandlers[key];
85
+ if (ktEvent) {
86
+ ktEvent(element, o);
87
+ continue;
88
+ }
89
+ // # normal event handler
120
90
  if (key.startsWith('on:')) {
121
91
  element.addEventListener(key.slice(3), o); // chop off the `@`
122
92
  continue;
@@ -134,19 +104,66 @@ function attrIsObject(element, attr) {
134
104
  if (style !== undefined) {
135
105
  attr.style = style;
136
106
  }
107
+ return true;
137
108
  }
138
109
  function applyAttr(element, attr) {
139
110
  if (typeof attr === 'string') {
140
111
  element.className = attr;
112
+ return true;
141
113
  }
142
114
  else if (typeof attr === 'object' && attr !== null) {
143
- attrIsObject(element, attr);
115
+ return attrIsObject(element, attr);
144
116
  }
145
117
  else {
146
- $throw('attr must be an object/string.');
118
+ throw new Error('kt.js: attr must be an object/string.');
147
119
  }
148
120
  }
149
121
 
122
+ /**
123
+ * & Remove `bind` because it is shockingly slower than wrapper
124
+ * & `window.document` is safe because it is not configurable and its setter is undefined
125
+ */
126
+ const $appendChild = HTMLElement.prototype.appendChild;
127
+ const originAppend = HTMLElement.prototype.append;
128
+ const $append = // for ie 9/10/11
129
+ typeof originAppend === 'function'
130
+ ? function (...args) {
131
+ return originAppend.apply(this, args);
132
+ }
133
+ : function (...nodes) {
134
+ if (nodes.length < 50) {
135
+ for (let i = 0; i < nodes.length; i++) {
136
+ const node = nodes[i];
137
+ if (typeof node === 'string') {
138
+ $appendChild.call(this, document.createTextNode(node));
139
+ }
140
+ else {
141
+ $appendChild.call(this, node);
142
+ }
143
+ }
144
+ }
145
+ else {
146
+ const fragment = document.createDocumentFragment();
147
+ for (let i = 0; i < nodes.length; i++) {
148
+ const node = nodes[i];
149
+ if (typeof node === 'string') {
150
+ $appendChild.call(fragment, document.createTextNode(node));
151
+ }
152
+ else {
153
+ $appendChild.call(fragment, node);
154
+ }
155
+ }
156
+ $appendChild.call(this, fragment);
157
+ }
158
+ };
159
+
160
+ const $isArray = Array.isArray;
161
+ const emptyPromiseHandler = () => ({});
162
+ if (typeof Promise === 'undefined') {
163
+ window.Promise = { resolve: emptyPromiseHandler, reject: emptyPromiseHandler };
164
+ }
165
+ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then === 'function';
166
+
150
167
  function apdSingle(element, c) {
151
168
  // & JSX should ignore false, undefined, and null
152
169
  if (c === false || c === undefined || c === null) {
@@ -203,7 +220,7 @@ function applyContent(element, content) {
203
220
  * ## About
204
221
  * @package @ktjs/core
205
222
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
206
- * @version 0.13.0 (Last Update: 2026.01.15 16:05:51.084)
223
+ * @version 0.14.0 (Last Update: 2026.01.16 20:08:50.505)
207
224
  * @license MIT
208
225
  * @link https://github.com/baendlorel/kt.js
209
226
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -217,7 +234,10 @@ const h = ((tag, attr = '', content = '') => {
217
234
  // * start creating the element
218
235
  const element = document.createElement(tag);
219
236
  // * Handle content
220
- applyAttr(element, attr);
237
+ const kif = applyAttr(element, attr);
238
+ if (!kif) {
239
+ return document.createComment('k-if');
240
+ }
221
241
  applyContent(element, content);
222
242
  return element;
223
243
  });
@@ -10,6 +10,33 @@ interface KTRef<T> {
10
10
  isKT: true;
11
11
  }
12
12
 
13
+ type KTHTMLElement = HTMLElement & {
14
+ /**
15
+ * Automically generate a redraw function if it is not provided
16
+ * @param props
17
+ */
18
+ redraw: (props?: KTAttribute, children?: KTRawContent) => void;
19
+ };
20
+
21
+ declare global {
22
+ namespace JSX {
23
+ type Element = KTHTMLElement;
24
+
25
+ interface IntrinsicElements {
26
+ [tag: string]: KTAttribute & { children?: KTRawContent };
27
+ }
28
+
29
+ // interface IntrinsicAttributes {
30
+ // key?: string | number;
31
+ // }
32
+ type IntrinsicAttributes = KTAttribute;
33
+
34
+ interface ElementChildrenAttribute {
35
+ children: {};
36
+ }
37
+ }
38
+ }
39
+
13
40
  type KTAvailableContent =
14
41
  | KTRef<any>
15
42
  | HTMLElement
@@ -30,6 +57,7 @@ interface KTBaseAttribute {
30
57
  [k: string]: any;
31
58
 
32
59
  ref?: KTRef<HTMLElement>;
60
+ 'k-if'?: any;
33
61
 
34
62
  id?: string;
35
63
  class?: string;
@@ -86,34 +114,17 @@ type KTPrefixedEventHandlers = {
86
114
  [EventName in keyof HTMLElementEventMap as `on:${EventName}`]?: (ev: HTMLElementEventMap[EventName]) => void;
87
115
  };
88
116
 
89
- type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
117
+ type KTSpecialEventHandlers = {
118
+ 'on:ktchange'?: (value: string) => void;
119
+ 'ontrim:ktchange'?: (value: string) => void;
120
+ 'on:ktchangenumber'?: (value: number) => void;
90
121
 
91
- type KTHTMLElement = HTMLElement & {
92
- /**
93
- * Automically generate a redraw function if it is not provided
94
- * @param props
95
- */
96
- redraw: (props?: KTAttribute, children?: KTRawContent) => void;
122
+ 'on:ktinput'?: (value: string) => void;
123
+ 'ontrim:ktinput'?: (value: string) => void;
124
+ 'on:ktinputnumber'?: (value: number) => void;
97
125
  };
98
126
 
99
- declare global {
100
- namespace JSX {
101
- type Element = KTHTMLElement;
102
-
103
- interface IntrinsicElements {
104
- [tag: string]: KTAttribute & { children?: KTRawContent };
105
- }
106
-
107
- // interface IntrinsicAttributes {
108
- // key?: string | number;
109
- // }
110
- type IntrinsicAttributes = KTAttribute;
111
-
112
- interface ElementChildrenAttribute {
113
- children: {};
114
- }
115
- }
116
- }
127
+ type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers & KTSpecialEventHandlers;
117
128
 
118
129
  type HTML<T extends HTMLTag & otherstring> = T extends HTMLTag ? HTMLElementTagNameMap[T] : HTMLElement;
119
130
  type H = (<T extends HTMLTag>(tag: T, attr?: KTRawAttr, content?: KTRawContent) => HTML<T>) & {
@@ -130,7 +141,7 @@ type H = (<T extends HTMLTag>(tag: T, attr?: KTRawAttr, content?: KTRawContent)
130
141
  * ## About
131
142
  * @package @ktjs/core
132
143
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
133
- * @version 0.13.0 (Last Update: 2026.01.15 16:05:51.084)
144
+ * @version 0.14.0 (Last Update: 2026.01.16 20:08:50.505)
134
145
  * @license MIT
135
146
  * @link https://github.com/baendlorel/kt.js
136
147
  * @link https://baendlorel.github.io/ Welcome to my site!