elegance-js 2.1.18 → 2.1.20

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.
@@ -1,442 +1,574 @@
1
- import "../shared/bindServerElements";
2
- import { ObjectAttributeType } from "../helpers/ObjectAttributeType";
3
- console.log("Elegance.JS is loading..");
4
- let pd = {};
5
- let ld = {};
6
- var BindLevel = /* @__PURE__ */ ((BindLevel2) => {
7
- BindLevel2[BindLevel2["STRICT"] = 1] = "STRICT";
8
- BindLevel2[BindLevel2["SCOPED"] = 2] = "SCOPED";
9
- return BindLevel2;
10
- })(BindLevel || {});
11
- ;
12
- Object.assign(window, {
13
- observe: (subjects, updateCallback) => {
14
- return {
15
- subjects,
16
- updateCallback,
17
- isAttribute: true,
18
- type: ObjectAttributeType.OBSERVER
19
- };
20
- },
21
- eventListener: (subjects, eventListener) => {
22
- return {
23
- subjects,
24
- eventListener,
25
- isAttribute: true,
26
- type: ObjectAttributeType.STATE
27
- };
28
- }
29
- });
30
- const domParser = new DOMParser();
31
- const xmlSerializer = new XMLSerializer();
32
- const pageStringCache = /* @__PURE__ */ new Map();
33
- const loc = window.location;
34
- const doc = document;
35
- let cleanupProcedures = [];
36
- const sanitizePathname = (pn) => {
37
- if (!pn.endsWith("/") || pn === "/") return pn;
38
- return pn.slice(0, -1);
39
- };
40
- let currentPage = sanitizePathname(loc.pathname);
41
- function getAllPaths(pathname) {
42
- const sanitized = pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
43
- const parts = sanitized.split("/").filter(Boolean);
44
- const subpaths = [
45
- "/",
46
- ...parts.map((_, i) => "/" + parts.slice(0, i + 1).join("/"))
1
+ "use strict";
2
+ (() => {
3
+ // src/shared/serverElements.ts
4
+ var createBuildableElement = (tag) => {
5
+ return (options, ...children) => ({
6
+ tag,
7
+ options: options || {},
8
+ children
9
+ });
10
+ };
11
+ var createChildrenlessBuildableElement = (tag) => {
12
+ return (options) => ({
13
+ tag,
14
+ options: options || {},
15
+ children: null
16
+ });
17
+ };
18
+ var childrenlessElementTags = [
19
+ "area",
20
+ "base",
21
+ "br",
22
+ "col",
23
+ "embed",
24
+ "hr",
25
+ "img",
26
+ "input",
27
+ "link",
28
+ "meta",
29
+ "source",
30
+ "track",
31
+ "path",
32
+ "rect"
33
+ ];
34
+ var elementTags = [
35
+ "a",
36
+ "address",
37
+ "article",
38
+ "aside",
39
+ "audio",
40
+ "blockquote",
41
+ "body",
42
+ "button",
43
+ "canvas",
44
+ "caption",
45
+ "colgroup",
46
+ "data",
47
+ "datalist",
48
+ "dd",
49
+ "del",
50
+ "details",
51
+ "dialog",
52
+ "div",
53
+ "dl",
54
+ "dt",
55
+ "fieldset",
56
+ "figcaption",
57
+ "figure",
58
+ "footer",
59
+ "form",
60
+ "h1",
61
+ "h2",
62
+ "h3",
63
+ "h4",
64
+ "h5",
65
+ "h6",
66
+ "head",
67
+ "header",
68
+ "hgroup",
69
+ "html",
70
+ "iframe",
71
+ "ins",
72
+ "label",
73
+ "legend",
74
+ "li",
75
+ "main",
76
+ "map",
77
+ "meter",
78
+ "nav",
79
+ "noscript",
80
+ "object",
81
+ "ol",
82
+ "optgroup",
83
+ "option",
84
+ "output",
85
+ "p",
86
+ "picture",
87
+ "pre",
88
+ "progress",
89
+ "q",
90
+ "section",
91
+ "select",
92
+ "summary",
93
+ "table",
94
+ "tbody",
95
+ "td",
96
+ "template",
97
+ "textarea",
98
+ "tfoot",
99
+ "th",
100
+ "thead",
101
+ "time",
102
+ "tr",
103
+ "ul",
104
+ "video",
105
+ "span",
106
+ "script",
107
+ "abbr",
108
+ "b",
109
+ "bdi",
110
+ "bdo",
111
+ "cite",
112
+ "code",
113
+ "dfn",
114
+ "em",
115
+ "i",
116
+ "kbd",
117
+ "mark",
118
+ "rp",
119
+ "rt",
120
+ "ruby",
121
+ "s",
122
+ "samp",
123
+ "small",
124
+ "strong",
125
+ "sub",
126
+ "sup",
127
+ "u",
128
+ "wbr",
129
+ "title",
130
+ "svg"
47
131
  ];
48
- if (sanitized === "/") return ["/"];
49
- return subpaths;
50
- }
51
- const createStateManager = (subjects, bindLevel) => {
52
- const state = {
53
- subjects: subjects.map((subject) => {
54
- const s = {
55
- ...subject,
56
- observers: /* @__PURE__ */ new Map()
132
+ var elements = {};
133
+ var childrenlessElements = {};
134
+ for (const element of elementTags) {
135
+ elements[element] = createBuildableElement(element);
136
+ }
137
+ for (const element of childrenlessElementTags) {
138
+ childrenlessElements[element] = createChildrenlessBuildableElement(element);
139
+ }
140
+ var allElements = {
141
+ ...elements,
142
+ ...childrenlessElements
143
+ };
144
+
145
+ // src/shared/bindServerElements.ts
146
+ Object.assign(globalThis, elements);
147
+ Object.assign(globalThis, childrenlessElements);
148
+
149
+ // src/client/client.ts
150
+ console.log("Elegance.JS is loading..");
151
+ var pd = {};
152
+ var ld = {};
153
+ Object.assign(window, {
154
+ observe: (subjects, updateCallback) => {
155
+ return {
156
+ subjects,
157
+ updateCallback,
158
+ isAttribute: true,
159
+ type: 2 /* OBSERVER */
160
+ };
161
+ },
162
+ eventListener: (subjects, eventListener) => {
163
+ return {
164
+ subjects,
165
+ eventListener,
166
+ isAttribute: true,
167
+ type: 1 /* STATE */
57
168
  };
58
- s.signal = () => {
59
- for (const observer of s.observers.values()) {
169
+ }
170
+ });
171
+ var domParser = new DOMParser();
172
+ var xmlSerializer = new XMLSerializer();
173
+ var pageStringCache = /* @__PURE__ */ new Map();
174
+ var loc = window.location;
175
+ var doc = document;
176
+ var cleanupProcedures = [];
177
+ var sanitizePathname = (pn) => {
178
+ if (!pn.endsWith("/") || pn === "/") return pn;
179
+ return pn.slice(0, -1);
180
+ };
181
+ var currentPage = sanitizePathname(loc.pathname);
182
+ var createStateManager = (subjects, bindLevel) => {
183
+ const state = {
184
+ subjects: subjects.map((subject) => {
185
+ const s = {
186
+ ...subject,
187
+ observers: /* @__PURE__ */ new Map()
188
+ };
189
+ s.signal = () => {
190
+ for (const observer of s.observers.values()) {
191
+ try {
192
+ observer(s.value);
193
+ } catch (e) {
194
+ console.error(e);
195
+ continue;
196
+ }
197
+ }
198
+ };
199
+ return s;
200
+ }),
201
+ destroy: (s) => {
202
+ state.subjects.splice(state.subjects.indexOf(s), 1);
203
+ },
204
+ get: (id) => {
205
+ const subject = state.subjects.find((s) => s.id === id);
206
+ if (subject) return subject;
207
+ if (bindLevel === 2 /* SCOPED */) return void 0;
208
+ const parts = window.location.pathname.split("/").filter(Boolean);
209
+ const paths = [
210
+ ...parts.map((_, i) => "/" + parts.slice(0, i + 1).join("/")),
211
+ "/"
212
+ ].reverse();
213
+ for (const item of paths) {
214
+ const sanitized = sanitizePathname(item);
215
+ const data = ld[sanitized];
216
+ if (!data) continue;
217
+ const entry = data.stateManager.get(id);
218
+ if (entry) return entry;
219
+ }
220
+ return void 0;
221
+ },
222
+ /**
223
+ Bind is deprecated, but kept as a paramater to not upset legacy code.
224
+ */
225
+ getAll: (refs) => refs?.map((ref) => {
226
+ return state.get(ref.id);
227
+ }),
228
+ observe: (subject, observer, key) => {
229
+ subject.observers.delete(key);
230
+ subject.observers.set(key, observer);
231
+ },
232
+ unobserve: (subject, key) => {
233
+ subject.observers.delete(key);
234
+ }
235
+ };
236
+ return state;
237
+ };
238
+ var initPageData = (data, currentPage2, previousPage, bindLevel) => {
239
+ if (!data) {
240
+ console.error("Data for page " + currentPage2 + " is null.");
241
+ return;
242
+ }
243
+ let state = data?.stateManager;
244
+ if (!state) {
245
+ state = createStateManager(data.state || [], bindLevel);
246
+ data.stateManager = state;
247
+ }
248
+ for (const subject of state.subjects) {
249
+ if (!subject.observers) subject.observers = /* @__PURE__ */ new Map();
250
+ }
251
+ for (const ooa of data.ooa || []) {
252
+ const els = doc.querySelectorAll(`[key="${ooa.key}"]`);
253
+ let values = {};
254
+ for (const { id } of ooa.refs) {
255
+ const subject = state.get(id);
256
+ values[subject.id] = subject.value;
257
+ const updateFunction = (value) => {
258
+ values[id] = value;
60
259
  try {
61
- observer(s.value);
260
+ const newValue = ooa.update(...Object.values(values));
261
+ let attribute = ooa.attribute === "class" ? "className" : ooa.attribute;
262
+ for (const el of Array.from(els)) {
263
+ el[attribute] = newValue;
264
+ }
62
265
  } catch (e) {
63
266
  console.error(e);
64
- continue;
267
+ return;
65
268
  }
269
+ };
270
+ updateFunction(subject.value);
271
+ try {
272
+ state.observe(subject, updateFunction, ooa.key);
273
+ } catch (e) {
274
+ console.error(e);
275
+ return;
66
276
  }
67
- };
68
- return s;
69
- }),
70
- destroy: (s) => {
71
- state.subjects.splice(state.subjects.indexOf(s), 1);
72
- },
73
- get: (id) => {
74
- const subject = state.subjects.find((s) => s.id === id);
75
- if (subject) return subject;
76
- if (bindLevel === 2 /* SCOPED */) return void 0;
77
- const parts = window.location.pathname.split("/").filter(Boolean);
78
- const paths = [
79
- ...parts.map((_, i) => "/" + parts.slice(0, i + 1).join("/")),
80
- "/"
81
- ].reverse();
82
- for (const item of paths) {
83
- const sanitized = sanitizePathname(item);
84
- const data = ld[sanitized];
85
- if (!data) continue;
86
- const entry = data.stateManager.get(id);
87
- if (entry) return entry;
88
277
  }
89
- return void 0;
90
- },
91
- /**
92
- Bind is deprecated, but kept as a paramater to not upset legacy code.
93
- */
94
- getAll: (refs) => refs?.map((ref) => {
95
- return state.get(ref.id);
96
- }),
97
- observe: (subject, observer, key) => {
98
- subject.observers.delete(key);
99
- subject.observers.set(key, observer);
100
- },
101
- unobserve: (subject, key) => {
102
- subject.observers.delete(key);
103
278
  }
104
- };
105
- return state;
106
- };
107
- const initPageData = (data, currentPage2, previousPage, bindLevel) => {
108
- if (!data) {
109
- console.error("Data for page " + currentPage2 + " is null.");
110
- return;
111
- }
112
- let state = data?.stateManager;
113
- if (!state) {
114
- state = createStateManager(data.state || [], bindLevel);
115
- data.stateManager = state;
116
- }
117
- for (const subject of state.subjects) {
118
- if (!subject.observers) subject.observers = /* @__PURE__ */ new Map();
119
- }
120
- for (const ooa of data.ooa || []) {
121
- const els = doc.querySelectorAll(`[key="${ooa.key}"]`);
122
- let values = {};
123
- for (const { id } of ooa.refs) {
124
- const subject = state.get(id);
125
- values[subject.id] = subject.value;
126
- const updateFunction = (value) => {
127
- values[id] = value;
279
+ for (const soa of data.soa || []) {
280
+ const el = doc.querySelector(`[key="${soa.key}"]`);
281
+ if (!el) {
282
+ console.error("Could not find SOA element for SOA:", soa);
283
+ continue;
284
+ }
285
+ const subject = state.get(soa.id);
286
+ if (typeof subject.value === "function") {
128
287
  try {
129
- const newValue = ooa.update(...Object.values(values));
130
- let attribute = ooa.attribute === "class" ? "className" : ooa.attribute;
131
- for (const el of Array.from(els)) {
132
- el[attribute] = newValue;
133
- }
288
+ el[soa.attribute] = (event) => subject.value(state, event);
134
289
  } catch (e) {
135
290
  console.error(e);
136
291
  return;
137
292
  }
138
- };
139
- updateFunction(subject.value);
140
- try {
141
- state.observe(subject, updateFunction, ooa.key);
142
- } catch (e) {
143
- console.error(e);
144
- return;
293
+ } else {
294
+ el[soa.attribute] = subject.value;
145
295
  }
146
296
  }
147
- }
148
- for (const soa of data.soa || []) {
149
- const el = doc.querySelector(`[key="${soa.key}"]`);
150
- if (!el) {
151
- console.error("Could not find SOA element for SOA:", soa);
152
- continue;
153
- }
154
- const subject = state.get(soa.id);
155
- if (typeof subject.value === "function") {
156
- try {
157
- el[soa.attribute] = (event) => subject.value(state, event);
158
- } catch (e) {
159
- console.error(e);
160
- return;
297
+ const loadHooks = data.lh;
298
+ for (const loadHook of loadHooks || []) {
299
+ const wasInScope = previousPage ? previousPage.startsWith(currentPage2) : false;
300
+ if (wasInScope && bindLevel !== 1 /* STRICT */) {
301
+ continue;
161
302
  }
162
- } else {
163
- el[soa.attribute] = subject.value;
164
- }
165
- }
166
- const loadHooks = data.lh;
167
- for (const loadHook of loadHooks || []) {
168
- const wasInScope = previousPage ? previousPage.startsWith(currentPage2) : false;
169
- if (wasInScope && bindLevel !== 1 /* STRICT */) {
170
- continue;
171
- }
172
- const fn = loadHook.fn;
173
- try {
174
- let cleanupFunction;
175
- if (fn.constructor.name === "AsyncFunction") {
176
- const res = fn(state);
177
- res.then((cleanupFunction2) => {
178
- if (cleanupFunction2) {
303
+ const fn = loadHook.fn;
304
+ try {
305
+ let cleanupFunction;
306
+ if (fn.constructor.name === "AsyncFunction") {
307
+ const res = fn(state);
308
+ res.then((cleanupFunction2) => {
309
+ if (cleanupFunction2) {
310
+ cleanupProcedures.push({
311
+ cleanupFunction: cleanupFunction2,
312
+ page: `${currentPage2}`,
313
+ loadHook,
314
+ bindLevel
315
+ });
316
+ }
317
+ });
318
+ } else {
319
+ cleanupFunction = fn(state);
320
+ if (cleanupFunction) {
179
321
  cleanupProcedures.push({
180
- cleanupFunction: cleanupFunction2,
322
+ cleanupFunction,
181
323
  page: `${currentPage2}`,
182
324
  loadHook,
183
325
  bindLevel
184
326
  });
185
327
  }
186
- });
187
- } else {
188
- cleanupFunction = fn(state);
189
- if (cleanupFunction) {
190
- cleanupProcedures.push({
191
- cleanupFunction,
192
- page: `${currentPage2}`,
193
- loadHook,
194
- bindLevel
195
- });
196
328
  }
329
+ } catch (e) {
330
+ console.error(e);
331
+ return;
197
332
  }
198
- } catch (e) {
199
- console.error(e);
200
- return;
201
333
  }
202
- }
203
- };
204
- const loadPage = async (previousPage = null) => {
205
- const fixedUrl = new URL(loc.href);
206
- fixedUrl.pathname = sanitizePathname(fixedUrl.pathname);
207
- const pathname = fixedUrl.pathname;
208
- currentPage = pathname;
209
- pageStringCache.set(
210
- currentPage,
211
- xmlSerializer.serializeToString(doc)
212
- );
213
- history.replaceState(null, "", fixedUrl.href);
214
- {
215
- const parts = window.location.pathname.split("/").filter(Boolean);
216
- const paths = [
217
- ...parts.map((_, i) => "/" + parts.slice(0, i + 1).join("/")),
218
- "/"
219
- ];
220
- for (const path of paths) {
221
- const layoutDataScript = document.querySelector(`script[data-layout="true"][data-pathname="${path}"]`);
222
- if (!layoutDataScript) {
223
- continue;
334
+ };
335
+ var loadPage = async (previousPage = null) => {
336
+ const fixedUrl = new URL(loc.href);
337
+ fixedUrl.pathname = sanitizePathname(fixedUrl.pathname);
338
+ const pathname = fixedUrl.pathname;
339
+ currentPage = pathname;
340
+ pageStringCache.set(
341
+ currentPage,
342
+ xmlSerializer.serializeToString(doc)
343
+ );
344
+ history.replaceState(null, "", fixedUrl.href);
345
+ {
346
+ const parts = window.location.pathname.split("/").filter(Boolean);
347
+ const paths = [
348
+ ...parts.map((_, i) => "/" + parts.slice(0, i + 1).join("/")),
349
+ "/"
350
+ ];
351
+ for (const path of paths) {
352
+ const layoutDataScript = document.querySelector(`script[data-layout="true"][data-pathname="${path}"]`);
353
+ if (!layoutDataScript) {
354
+ continue;
355
+ }
356
+ const { data } = await import(layoutDataScript.src);
357
+ if (!ld[pathname]) ld[pathname] = data;
358
+ {
359
+ const dataScript = document.querySelector(`script[data-hook="true"][data-pathname="${sanitizePathname(pathname)}"]`);
360
+ if (dataScript) dataScript.remove();
361
+ }
362
+ initPageData(ld[pathname], path, previousPage, 2 /* SCOPED */);
224
363
  }
225
- const { data } = await import(layoutDataScript.src);
226
- if (!ld[pathname]) ld[pathname] = data;
364
+ }
365
+ {
366
+ const pageDataScript = document.head.querySelector(`script[data-page="true"][data-pathname="${sanitizePathname(pathname)}"]`);
367
+ const { data } = await import(pageDataScript.src);
368
+ if (!pd[pathname]) pd[pathname] = data;
227
369
  {
228
370
  const dataScript = document.querySelector(`script[data-hook="true"][data-pathname="${sanitizePathname(pathname)}"]`);
229
371
  if (dataScript) dataScript.remove();
230
372
  }
231
- initPageData(ld[pathname], path, previousPage, 2 /* SCOPED */);
232
- }
233
- }
234
- {
235
- const pageDataScript = document.head.querySelector(`script[data-page="true"][data-pathname="${sanitizePathname(pathname)}"]`);
236
- const { data } = await import(pageDataScript.src);
237
- if (!pd[pathname]) pd[pathname] = data;
238
- {
239
- const dataScript = document.querySelector(`script[data-hook="true"][data-pathname="${sanitizePathname(pathname)}"]`);
240
- if (dataScript) dataScript.remove();
241
- }
242
- initPageData(pd[pathname], currentPage, previousPage, 1 /* STRICT */);
243
- }
244
- console.info(
245
- `Loading finished, cleanupProcedures are currently:`,
246
- cleanupProcedures
247
- );
248
- };
249
- const fetchPage = async (targetURL) => {
250
- const pathname = sanitizePathname(targetURL.pathname);
251
- if (pageStringCache.has(pathname)) {
252
- return domParser.parseFromString(pageStringCache.get(pathname), "text/html");
253
- }
254
- const res = await fetch(targetURL);
255
- const newDOM = domParser.parseFromString(await res.text(), "text/html");
256
- {
257
- const dataScripts = Array.from(newDOM.querySelectorAll('script[data-module="true"]'));
258
- const currentScripts = Array.from(document.head.querySelectorAll('script[data-module="true"]'));
259
- for (const dataScript of dataScripts) {
260
- const existing = currentScripts.find((s) => s.src === dataScript.src);
261
- if (existing) {
262
- continue;
263
- }
264
- document.head.appendChild(dataScript);
265
- }
266
- }
267
- {
268
- const pageDataScript = newDOM.querySelector('script[data-page="true"]');
269
- if (!pageDataScript) {
270
- return;
373
+ initPageData(pd[pathname], currentPage, previousPage, 1 /* STRICT */);
271
374
  }
272
- if (!pd[pathname]) {
273
- await import(pageDataScript.src);
375
+ console.info(
376
+ `Loading finished, cleanupProcedures are currently:`,
377
+ cleanupProcedures
378
+ );
379
+ };
380
+ var fetchPage = async (targetURL) => {
381
+ const pathname = sanitizePathname(targetURL.pathname);
382
+ if (pageStringCache.has(pathname)) {
383
+ return domParser.parseFromString(pageStringCache.get(pathname), "text/html");
274
384
  }
275
- }
276
- {
277
- const layoutDataScripts = Array.from(newDOM.querySelectorAll('script[data-layout="true"]'));
278
- for (const script of layoutDataScripts) {
279
- const url = new URL(script.src, window.location.origin);
280
- const dir = url.pathname.substring(0, url.pathname.lastIndexOf("/")) || "/";
281
- const pathname2 = sanitizePathname(dir);
282
- if (!ld[pathname2]) {
283
- await import(script.src);
385
+ const res = await fetch(targetURL);
386
+ const newDOM = domParser.parseFromString(await res.text(), "text/html");
387
+ {
388
+ const dataScripts = Array.from(newDOM.querySelectorAll('script[data-module="true"]'));
389
+ const currentScripts = Array.from(document.head.querySelectorAll('script[data-module="true"]'));
390
+ for (const dataScript of dataScripts) {
391
+ const existing = currentScripts.find((s) => s.src === dataScript.src);
392
+ if (existing) {
393
+ continue;
394
+ }
395
+ document.head.appendChild(dataScript);
284
396
  }
285
397
  }
286
- }
287
- pageStringCache.set(pathname, xmlSerializer.serializeToString(newDOM));
288
- return newDOM;
289
- };
290
- const navigateLocally = async (target, pushState = true) => {
291
- const targetURL = new URL(target);
292
- const pathname = sanitizePathname(targetURL.pathname);
293
- console.log(
294
- `%c${currentPage} -> ${targetURL.pathname}`,
295
- "font-size: 18px; font-weight: 600; color: lightgreen;"
296
- );
297
- let newPage = await fetchPage(targetURL);
298
- if (!newPage) return;
299
- if (pathname === currentPage) return;
300
- for (const cleanupProcedure of [...cleanupProcedures]) {
301
- const isInScope = pathname.startsWith(cleanupProcedure.page);
302
- if (!isInScope || cleanupProcedure.bindLevel === 1 /* STRICT */) {
303
- try {
304
- cleanupProcedure.cleanupFunction();
305
- } catch (e) {
306
- console.error(e);
398
+ {
399
+ const pageDataScript = newDOM.querySelector('script[data-page="true"]');
400
+ if (!pageDataScript) {
307
401
  return;
308
402
  }
309
- cleanupProcedures.splice(cleanupProcedures.indexOf(cleanupProcedure), 1);
403
+ if (!pd[pathname]) {
404
+ await import(pageDataScript.src);
405
+ }
310
406
  }
311
- }
312
- let oldPageLatest = doc.body;
313
- let newPageLatest = newPage.body;
314
- {
315
- const newPageLayouts = Array.from(newPage.querySelectorAll("template[layout-id]"));
316
- const oldPageLayouts = Array.from(doc.querySelectorAll("template[layout-id]"));
317
- const size = Math.min(newPageLayouts.length, oldPageLayouts.length);
318
- for (let i = 0; i < size; i++) {
319
- const newPageLayout = newPageLayouts[i];
320
- const oldPageLayout = oldPageLayouts[i];
321
- const newLayoutId = newPageLayout.getAttribute("layout-id");
322
- const oldLayoutId = oldPageLayout.getAttribute("layout-id");
323
- if (newLayoutId !== oldLayoutId) {
324
- break;
407
+ {
408
+ const layoutDataScripts = Array.from(newDOM.querySelectorAll('script[data-layout="true"]'));
409
+ for (const script of layoutDataScripts) {
410
+ const url = new URL(script.src, window.location.origin);
411
+ const dir = url.pathname.substring(0, url.pathname.lastIndexOf("/")) || "/";
412
+ const pathname2 = sanitizePathname(dir);
413
+ if (!ld[pathname2]) {
414
+ await import(script.src);
415
+ }
325
416
  }
326
- oldPageLatest = oldPageLayout.nextElementSibling;
327
- newPageLatest = newPageLayout.nextElementSibling;
328
417
  }
329
- }
330
- oldPageLatest.replaceWith(newPageLatest);
331
- {
332
- doc.head.querySelector("title")?.replaceWith(
333
- newPage.head.querySelector("title") ?? ""
418
+ pageStringCache.set(pathname, xmlSerializer.serializeToString(newDOM));
419
+ return newDOM;
420
+ };
421
+ var navigateLocally = async (target, pushState = true) => {
422
+ const targetURL = new URL(target);
423
+ const pathname = sanitizePathname(targetURL.pathname);
424
+ console.log(
425
+ `%c${currentPage} -> ${targetURL.pathname}`,
426
+ "font-size: 18px; font-weight: 600; color: lightgreen;"
334
427
  );
335
- const update = (targetList, matchAgainst, action) => {
336
- for (const target2 of targetList) {
337
- const matching = matchAgainst.find((n) => n.isEqualNode(target2));
338
- if (matching) {
339
- continue;
428
+ let newPage = await fetchPage(targetURL);
429
+ if (!newPage) return;
430
+ if (pathname === currentPage) return;
431
+ for (const cleanupProcedure of [...cleanupProcedures]) {
432
+ const isInScope = pathname.startsWith(cleanupProcedure.page);
433
+ if (!isInScope || cleanupProcedure.bindLevel === 1 /* STRICT */) {
434
+ try {
435
+ cleanupProcedure.cleanupFunction();
436
+ } catch (e) {
437
+ console.error(e);
438
+ return;
340
439
  }
341
- action(target2);
440
+ cleanupProcedures.splice(cleanupProcedures.indexOf(cleanupProcedure), 1);
342
441
  }
343
- };
344
- const oldTags = Array.from([
345
- ...Array.from(document.head.querySelectorAll("link")),
346
- ...Array.from(document.head.querySelectorAll("meta")),
347
- ...Array.from(document.head.querySelectorAll("script")),
348
- ...Array.from(document.head.querySelectorAll("base")),
349
- ...Array.from(document.head.querySelectorAll("style"))
350
- ]);
351
- const newTags = Array.from([
352
- ...Array.from(newPage.head.querySelectorAll("link")),
353
- ...Array.from(newPage.head.querySelectorAll("meta")),
354
- ...Array.from(newPage.head.querySelectorAll("script")),
355
- ...Array.from(newPage.head.querySelectorAll("base")),
356
- ...Array.from(newPage.head.querySelectorAll("style"))
357
- ]);
358
- update(newTags, oldTags, (node) => document.head.appendChild(node));
359
- update(oldTags, newTags, (node) => node.remove());
360
- }
361
- if (pushState) history.pushState(null, "", targetURL.href);
362
- await loadPage(currentPage);
363
- currentPage = pathname;
364
- if (targetURL.hash) {
365
- doc.getElementById(targetURL.hash.slice(1))?.scrollIntoView();
366
- }
367
- };
368
- window.onpopstate = async (event) => {
369
- event.preventDefault();
370
- const target = event.target;
371
- await navigateLocally(target.location.href, false);
372
- history.replaceState(null, "", target.location.href);
373
- };
374
- const renderRecursively = (element, attributes) => {
375
- if (typeof element === "boolean") {
376
- return null;
377
- }
378
- if (typeof element === "number" || typeof element === "string") {
379
- return document.createTextNode(element.toString());
380
- }
381
- if (Array.isArray(element)) {
382
- const fragment = document.createDocumentFragment();
383
- element.forEach((item) => {
384
- const childNode = renderRecursively(item, attributes);
385
- if (childNode) fragment.appendChild(childNode);
386
- });
387
- return fragment;
388
- }
389
- const domElement = document.createElement(element.tag);
390
- if (typeof element.options !== "object" && element.options !== void 0) {
391
- const childNode = renderRecursively(element.options, attributes);
392
- if (childNode) domElement.appendChild(childNode);
393
- } else if (typeof element.options === "object") {
394
- const { tag, options, children } = element.options;
395
- if (tag !== void 0 || options !== void 0 || children !== void 0) {
442
+ }
443
+ let oldPageLatest = doc.body;
444
+ let newPageLatest = newPage.body;
445
+ {
446
+ const newPageLayouts = Array.from(newPage.querySelectorAll("template[layout-id]"));
447
+ const oldPageLayouts = Array.from(doc.querySelectorAll("template[layout-id]"));
448
+ const size = Math.min(newPageLayouts.length, oldPageLayouts.length);
449
+ for (let i = 0; i < size; i++) {
450
+ const newPageLayout = newPageLayouts[i];
451
+ const oldPageLayout = oldPageLayouts[i];
452
+ const newLayoutId = newPageLayout.getAttribute("layout-id");
453
+ const oldLayoutId = oldPageLayout.getAttribute("layout-id");
454
+ if (newLayoutId !== oldLayoutId) {
455
+ break;
456
+ }
457
+ oldPageLatest = oldPageLayout.nextElementSibling;
458
+ newPageLatest = newPageLayout.nextElementSibling;
459
+ }
460
+ }
461
+ oldPageLatest.replaceWith(newPageLatest);
462
+ {
463
+ doc.head.querySelector("title")?.replaceWith(
464
+ newPage.head.querySelector("title") ?? ""
465
+ );
466
+ const update = (targetList, matchAgainst, action) => {
467
+ for (const target2 of targetList) {
468
+ const matching = matchAgainst.find((n) => n.isEqualNode(target2));
469
+ if (matching) {
470
+ continue;
471
+ }
472
+ action(target2);
473
+ }
474
+ };
475
+ const oldTags = Array.from([
476
+ ...Array.from(document.head.querySelectorAll("link")),
477
+ ...Array.from(document.head.querySelectorAll("meta")),
478
+ ...Array.from(document.head.querySelectorAll("script")),
479
+ ...Array.from(document.head.querySelectorAll("base")),
480
+ ...Array.from(document.head.querySelectorAll("style"))
481
+ ]);
482
+ const newTags = Array.from([
483
+ ...Array.from(newPage.head.querySelectorAll("link")),
484
+ ...Array.from(newPage.head.querySelectorAll("meta")),
485
+ ...Array.from(newPage.head.querySelectorAll("script")),
486
+ ...Array.from(newPage.head.querySelectorAll("base")),
487
+ ...Array.from(newPage.head.querySelectorAll("style"))
488
+ ]);
489
+ update(newTags, oldTags, (node) => document.head.appendChild(node));
490
+ update(oldTags, newTags, (node) => node.remove());
491
+ }
492
+ if (pushState) history.pushState(null, "", targetURL.href);
493
+ await loadPage(currentPage);
494
+ currentPage = pathname;
495
+ if (targetURL.hash) {
496
+ doc.getElementById(targetURL.hash.slice(1))?.scrollIntoView();
497
+ }
498
+ };
499
+ window.onpopstate = async (event) => {
500
+ event.preventDefault();
501
+ const target = event.target;
502
+ await navigateLocally(target.location.href, false);
503
+ history.replaceState(null, "", target.location.href);
504
+ };
505
+ var renderRecursively = (element, attributes) => {
506
+ if (typeof element === "boolean") {
507
+ return null;
508
+ }
509
+ if (typeof element === "number" || typeof element === "string") {
510
+ return document.createTextNode(element.toString());
511
+ }
512
+ if (Array.isArray(element)) {
513
+ const fragment = document.createDocumentFragment();
514
+ element.forEach((item) => {
515
+ const childNode = renderRecursively(item, attributes);
516
+ if (childNode) fragment.appendChild(childNode);
517
+ });
518
+ return fragment;
519
+ }
520
+ const domElement = document.createElement(element.tag);
521
+ if (typeof element.options !== "object" && element.options !== void 0) {
396
522
  const childNode = renderRecursively(element.options, attributes);
397
523
  if (childNode) domElement.appendChild(childNode);
398
- } else {
399
- for (const [attrName, attrValue] of Object.entries(element.options)) {
400
- if (typeof attrValue === "object") {
401
- const { isAttribute } = attrValue;
402
- if (isAttribute === void 0 || isAttribute === false) {
403
- console.error("Objects are not valid option property values.");
404
- throw "";
524
+ } else if (typeof element.options === "object") {
525
+ const { tag, options, children } = element.options;
526
+ if (tag !== void 0 || options !== void 0 || children !== void 0) {
527
+ const childNode = renderRecursively(element.options, attributes);
528
+ if (childNode) domElement.appendChild(childNode);
529
+ } else {
530
+ for (const [attrName, attrValue] of Object.entries(element.options)) {
531
+ if (typeof attrValue === "object") {
532
+ const { isAttribute } = attrValue;
533
+ if (isAttribute === void 0 || isAttribute === false) {
534
+ console.error("Objects are not valid option property values.");
535
+ throw "";
536
+ }
537
+ attributes.push({
538
+ ...attrValue,
539
+ field: attrName,
540
+ element: domElement
541
+ });
542
+ continue;
405
543
  }
406
- attributes.push({
407
- ...attrValue,
408
- field: attrName,
409
- element: domElement
410
- });
411
- continue;
544
+ domElement.setAttribute(attrName.toLowerCase(), attrValue);
412
545
  }
413
- domElement.setAttribute(attrName.toLowerCase(), attrValue);
414
546
  }
415
547
  }
416
- }
417
- if (element.children !== null) {
418
- if (Array.isArray(element.children)) {
419
- element.children.forEach((child) => {
420
- const childNode = renderRecursively(child, attributes);
548
+ if (element.children !== null) {
549
+ if (Array.isArray(element.children)) {
550
+ element.children.forEach((child) => {
551
+ const childNode = renderRecursively(child, attributes);
552
+ if (childNode) domElement.appendChild(childNode);
553
+ });
554
+ } else {
555
+ const childNode = renderRecursively(element.children, attributes);
421
556
  if (childNode) domElement.appendChild(childNode);
422
- });
423
- } else {
424
- const childNode = renderRecursively(element.children, attributes);
425
- if (childNode) domElement.appendChild(childNode);
557
+ }
426
558
  }
559
+ return domElement;
560
+ };
561
+ globalThis.client = {
562
+ navigateLocally,
563
+ fetchPage,
564
+ currentPage,
565
+ sanitizePathname,
566
+ getReference: (id) => document.querySelector(`[ref="${id}"]`),
567
+ renderRecursively
568
+ };
569
+ try {
570
+ loadPage();
571
+ } catch (e) {
572
+ console.error(e);
427
573
  }
428
- return domElement;
429
- };
430
- globalThis.client = {
431
- navigateLocally,
432
- fetchPage,
433
- currentPage,
434
- sanitizePathname,
435
- getReference: (id) => document.querySelector(`[ref="${id}"]`),
436
- renderRecursively
437
- };
438
- try {
439
- loadPage();
440
- } catch (e) {
441
- console.error(e);
442
- }
574
+ })();
@@ -511,7 +511,9 @@ async function buildLayout(filePath, directory, generateDynamic = false) {
511
511
  const { pageContentHTML, metadataHTML } = result;
512
512
  function splitAround(str, sub) {
513
513
  const i = str.indexOf(sub);
514
- if (i === -1) throw new Error("substring does not exist in parent string");
514
+ if (i === -1) {
515
+ throw new Error(`Whilst layout ${directory}, the splitter could not be found. Make sure to use the "child" prop passed into the layout.`);
516
+ }
515
517
  return {
516
518
  startHTML: str.substring(0, i),
517
519
  endHTML: str.substring(i + sub.length)
@@ -519,7 +521,9 @@ async function buildLayout(filePath, directory, generateDynamic = false) {
519
521
  }
520
522
  function splitAt(str, sub) {
521
523
  const i = str.indexOf(sub) + sub.length;
522
- if (i === -1) throw new Error("substring does not exist in parent string");
524
+ if (i === -1) {
525
+ throw new Error(`Whilst layout ${directory}, the splitter could not be found. Make sure to use the "child" prop passed into the layout's metadata.`);
526
+ }
523
527
  return {
524
528
  startHTML: str.substring(0, i),
525
529
  endHTML: str.substring(i)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elegance-js",
3
- "version": "2.1.18",
3
+ "version": "2.1.20",
4
4
  "description": "Web-Framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "native": "./scripts/native-test.js"
11
11
  },
12
12
  "scripts": {
13
- "build": "esbuild --format=esm --platform=node --bundle=false --outdir=./dist --platform=node --out-extension:.js=.mjs \"./src/**/*.ts\" && tsc",
13
+ "build": "esbuild --format=esm --platform=node --bundle=false --outdir=./dist --out-extension:.js=.mjs \"./src/**/*.ts\" && esbuild --format=iife --platform=browser --bundle=true --outfile=./dist/client/client.mjs --out-extension:.js=.mjs ./src/client/client.ts && tsc ",
14
14
  "compile-docs": "node ./dist/compile_docs.mjs --environment=development",
15
15
  "compile-docs-bun": "bun run ./dist/compile_docs.mjs --environment=development",
16
16
  "prepare": "npm run build"