cradova 1.0.1 → 1.0.2
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/README.md +53 -19
- package/dist/index.d.ts +26 -0
- package/dist/index.js +468 -0
- package/dist/rolled-cradova.js +2366 -0
- package/dist/scripts/JsonDB.d.ts +287 -0
- package/dist/scripts/JsonDB.js +633 -0
- package/dist/scripts/Router.d.ts +5 -0
- package/dist/scripts/Router.js +170 -0
- package/dist/scripts/Screen.d.ts +51 -0
- package/dist/scripts/Screen.js +107 -0
- package/dist/scripts/ajax.d.ts +28 -0
- package/dist/scripts/ajax.js +63 -0
- package/dist/scripts/fns.d.ts +84 -0
- package/dist/scripts/fns.js +307 -0
- package/dist/scripts/init.d.ts +1 -0
- package/dist/scripts/init.js +25 -0
- package/dist/scripts/loadCss.d.ts +1 -0
- package/dist/scripts/loadCss.js +194 -0
- package/dist/scripts/memory.d.ts +12 -0
- package/dist/scripts/memory.js +46 -0
- package/dist/scripts/store.d.ts +12 -0
- package/dist/scripts/store.js +64 -0
- package/dist/scripts/swipe.d.ts +1 -0
- package/dist/scripts/swipe.js +114 -0
- package/dist/scripts/track.d.ts +12 -0
- package/dist/scripts/track.js +150 -0
- package/dist/scripts/widget.d.ts +8 -0
- package/dist/scripts/widget.js +21 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.js +1 -0
- package/index.ts +336 -147
- package/package.json +18 -11
- package/scripts/JsonDB.ts +134 -130
- package/scripts/Router.ts +94 -24
- package/scripts/Screen.ts +99 -39
- package/scripts/{littleAxios.ts → ajax.ts} +44 -6
- package/scripts/fns.ts +341 -0
- package/scripts/init.ts +11 -4
- package/scripts/loadCss.ts +194 -0
- package/scripts/memory.ts +44 -0
- package/scripts/store.ts +32 -21
- package/scripts/style.css +189 -0
- package/scripts/swipe.ts +4 -4
- package/scripts/track.ts +171 -0
- package/scripts/widget.ts +17 -15
- package/tsconfig.json +18 -98
- package/types.ts +15 -1
- package/{online-only-after-initial-cache.ts → workers/online-only-after-initial-cache.ts} +14 -11
- package/{service-worker.ts → workers/service-worker.ts} +17 -10
- package/docs/README.md +0 -0
- package/manifest.json +0 -38
- package/scripts/Metrics.ts +0 -66
- package/scripts/animate.ts +0 -55
- package/scripts/createState.ts +0 -27
- package/scripts/css.ts +0 -47
- package/scripts/dispatcher.ts +0 -158
- package/scripts/fetcher.ts +0 -31
- package/scripts/file-system.ts +0 -173
- package/scripts/fullscreen.ts +0 -21
- package/scripts/localStorage.ts +0 -19
- package/scripts/media.ts +0 -51
- package/scripts/promptbeforeleave.ts +0 -10
- package/scripts/reuse.ts +0 -79
- package/scripts/speaker.ts +0 -25
- package/scripts/uuid.ts +0 -10
- package/types.d.ts +0 -0
|
@@ -0,0 +1,2366 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.cradova = factory());
|
|
5
|
+
})(this, (function () { 'use strict';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Document fragment
|
|
9
|
+
* @param childrens
|
|
10
|
+
* @returns
|
|
11
|
+
*/
|
|
12
|
+
const fragment = function (...childrens) {
|
|
13
|
+
const par = document.createDocumentFragment();
|
|
14
|
+
// building it's children tree.
|
|
15
|
+
for (let i = 0; i < childrens.length; i++) {
|
|
16
|
+
const ch = childrens[i];
|
|
17
|
+
if (typeof ch === "function") {
|
|
18
|
+
par.append(ch());
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
if (ch instanceof HTMLElement) {
|
|
22
|
+
par.append(ch);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return () => par;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function swipe(item) {
|
|
30
|
+
let caller;
|
|
31
|
+
let startX = 0, startY = 0;
|
|
32
|
+
if (typeof item === "object") {
|
|
33
|
+
caller = item;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw new Error("no call given for the swipe handler");
|
|
37
|
+
}
|
|
38
|
+
function handleTouchStart(e) {
|
|
39
|
+
startX = e.changedTouches[0].screenX;
|
|
40
|
+
startY = e.changedTouches[0].screenY;
|
|
41
|
+
}
|
|
42
|
+
function handleTouchEnd(e) {
|
|
43
|
+
const diffX = e.changedTouches[0].screenX - startX;
|
|
44
|
+
const diffY = e.changedTouches[0].screenY - startY;
|
|
45
|
+
const ratioX = Math.abs(diffX / diffY);
|
|
46
|
+
const ratioY = Math.abs(diffY / diffX);
|
|
47
|
+
const absDiff = Math.abs(ratioX > ratioY ? diffX : diffY);
|
|
48
|
+
if (absDiff < 10) {
|
|
49
|
+
if (caller.touch) {
|
|
50
|
+
callback.touch(caller.touch);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (ratioX > ratioY) {
|
|
54
|
+
// left and right
|
|
55
|
+
if (diffX >= 0) {
|
|
56
|
+
if (caller.right) {
|
|
57
|
+
callback.right(caller.right);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
if (caller.left) {
|
|
62
|
+
callback.left(caller.left);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// up and down
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
if (diffY >= 0) {
|
|
69
|
+
if (caller.down) {
|
|
70
|
+
callback.down(caller.down);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
if (caller.up) {
|
|
75
|
+
callback.up(caller.up);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const escapeTSError = document.body;
|
|
81
|
+
escapeTSError.addEventListener("touchstart", handleTouchStart);
|
|
82
|
+
escapeTSError.addEventListener("touchend", handleTouchEnd);
|
|
83
|
+
const callback = {
|
|
84
|
+
touch(callback) {
|
|
85
|
+
return callback();
|
|
86
|
+
},
|
|
87
|
+
right(callback) {
|
|
88
|
+
return callback();
|
|
89
|
+
},
|
|
90
|
+
left(callback) {
|
|
91
|
+
return callback();
|
|
92
|
+
},
|
|
93
|
+
down(callback) {
|
|
94
|
+
return callback();
|
|
95
|
+
},
|
|
96
|
+
up(callback) {
|
|
97
|
+
return callback();
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/*
|
|
102
|
+
*** HOW TO USE ***
|
|
103
|
+
|
|
104
|
+
function touch(){
|
|
105
|
+
console.log("touching")
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
function up(){
|
|
111
|
+
console.log("swipe up")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
function down(){
|
|
116
|
+
console.log("swipe down")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
function right(){
|
|
121
|
+
console.log("swipe right")
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
function left(){
|
|
126
|
+
console.log("swipe left")
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
let obj = {down: down,
|
|
132
|
+
touch: touch,
|
|
133
|
+
up: up,
|
|
134
|
+
right: right,
|
|
135
|
+
left: left
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
swipe(obj)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
class store {
|
|
145
|
+
constructor(initial, useHistory) {
|
|
146
|
+
this.#index = 0;
|
|
147
|
+
this.#history = [];
|
|
148
|
+
this.#value = null;
|
|
149
|
+
this.#useHistory = false;
|
|
150
|
+
this.#value = initial;
|
|
151
|
+
if (useHistory) {
|
|
152
|
+
this.#useHistory = useHistory;
|
|
153
|
+
this.#history.push(initial);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
#index;
|
|
157
|
+
#history;
|
|
158
|
+
#value;
|
|
159
|
+
#useHistory;
|
|
160
|
+
#callabck;
|
|
161
|
+
get() {
|
|
162
|
+
return this.#value;
|
|
163
|
+
}
|
|
164
|
+
set(value) {
|
|
165
|
+
this.#value = value;
|
|
166
|
+
if (!this.#useHistory)
|
|
167
|
+
return;
|
|
168
|
+
this.#index += 1;
|
|
169
|
+
this.#history.push(value);
|
|
170
|
+
if (this.#callabck) {
|
|
171
|
+
this.#callabck(this.#value);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
setKey(name, value) {
|
|
175
|
+
if (typeof this.#value === "object") {
|
|
176
|
+
this.#value[name] = value;
|
|
177
|
+
if (!this.#useHistory)
|
|
178
|
+
return;
|
|
179
|
+
this.#history.push(this.#value);
|
|
180
|
+
this.#index += 1;
|
|
181
|
+
}
|
|
182
|
+
if (this.#callabck) {
|
|
183
|
+
this.#callabck(this.#value);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
forward() {
|
|
187
|
+
if (this.#history.length > this.#index + 1) {
|
|
188
|
+
if (!this.#useHistory)
|
|
189
|
+
return;
|
|
190
|
+
this.#value = this.#history[this.#index + 1];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
backward() {
|
|
194
|
+
if (this.#history.length > 0 && this.#index > 0) {
|
|
195
|
+
if (!this.#useHistory)
|
|
196
|
+
return;
|
|
197
|
+
this.#value = this.#history[this.#index - 1];
|
|
198
|
+
this.#index -= 1;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
listen(callabck) {
|
|
202
|
+
this.#callabck = callabck;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const Store = function (initial, useHistory = false) {
|
|
206
|
+
return new store(initial, useHistory);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Facilitates navigation within the application and initializes
|
|
211
|
+
* page views based on the matched routes.
|
|
212
|
+
*/
|
|
213
|
+
const Router = {};
|
|
214
|
+
Router["routes"] = {};
|
|
215
|
+
Router["params"] = {};
|
|
216
|
+
/**
|
|
217
|
+
*
|
|
218
|
+
* @param url
|
|
219
|
+
* @returns
|
|
220
|
+
*/
|
|
221
|
+
const checker = (url) => {
|
|
222
|
+
// first strict check
|
|
223
|
+
if (Router.routes[url]) {
|
|
224
|
+
return [Router.routes[url], null];
|
|
225
|
+
}
|
|
226
|
+
// place holder check
|
|
227
|
+
for (const path in Router.routes) {
|
|
228
|
+
if (!path.includes("$")) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
//
|
|
232
|
+
const urlFixtures = url.split("/");
|
|
233
|
+
const pathFixtures = path.split("/");
|
|
234
|
+
// length check
|
|
235
|
+
if (pathFixtures.length === urlFixtures.length) {
|
|
236
|
+
urlFixtures.shift();
|
|
237
|
+
pathFixtures.shift();
|
|
238
|
+
let isIt = true;
|
|
239
|
+
let routesParams = {};
|
|
240
|
+
for (let i = 0; i < pathFixtures.length; i++) {
|
|
241
|
+
// loosely item checking by N indexes & includes
|
|
242
|
+
// FIXME: may be problematic but works faster than any other solutions
|
|
243
|
+
// NO regex :)
|
|
244
|
+
// this is faster and better
|
|
245
|
+
if (!pathFixtures[i].includes("$") && path.includes(urlFixtures[i] + "/") && (pathFixtures.indexOf(urlFixtures[i]) === pathFixtures.lastIndexOf(urlFixtures[i]))) {
|
|
246
|
+
if (!isIt)
|
|
247
|
+
isIt = true;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
if (pathFixtures[i].includes("$")) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
isIt = false;
|
|
254
|
+
}
|
|
255
|
+
if (!(pathFixtures.indexOf(urlFixtures[i]) === pathFixtures.lastIndexOf(urlFixtures[i]))) {
|
|
256
|
+
throw new Error("cradova router doesn't allow paths with multiple names");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (isIt) {
|
|
260
|
+
for (let i = 0; i < pathFixtures.length; i++) {
|
|
261
|
+
if (pathFixtures[i].includes("$")) {
|
|
262
|
+
routesParams[pathFixtures[i].split("$")[1]] = urlFixtures[i];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return [Router.routes[path], routesParams];
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return [];
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Registers a route.
|
|
273
|
+
*
|
|
274
|
+
* @param {string} path Route path.
|
|
275
|
+
* @param {Function} controller the cradova document tree for the route.
|
|
276
|
+
*/
|
|
277
|
+
Router.route = function (path = "/", controller) {
|
|
278
|
+
Router.routes[path] = {
|
|
279
|
+
controller: controller,
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
Router.navigate = function (href, data = {}) {
|
|
283
|
+
if (typeof href !== "string") {
|
|
284
|
+
throw new TypeError("cradova err href must be a defined path but got " + href + " instaed");
|
|
285
|
+
}
|
|
286
|
+
let route = null, params, link = null;
|
|
287
|
+
if (href.includes(".")) {
|
|
288
|
+
try {
|
|
289
|
+
if (new URL(href).pathname === window.location.pathname) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
route = Router.routes[new URL(href).pathname];
|
|
293
|
+
link = new URL(href).pathname;
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
throw new Error("cradova: invlaid route " + href);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
if (href === window.location.pathname) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
[route, params] = checker(href);
|
|
304
|
+
if (route) {
|
|
305
|
+
Router.params.params = params || null;
|
|
306
|
+
Router.params.data = data || null;
|
|
307
|
+
link = href;
|
|
308
|
+
window.history.pushState({}, "", link);
|
|
309
|
+
document.body.click();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return;
|
|
313
|
+
};
|
|
314
|
+
Router.goTo = (path) => {
|
|
315
|
+
window.location.pathname = path;
|
|
316
|
+
};
|
|
317
|
+
Router.router = function (e) {
|
|
318
|
+
if (e.target.tagName === "INPUT") {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
//
|
|
322
|
+
let Alink;
|
|
323
|
+
if (e.target.tagName === "A") {
|
|
324
|
+
Alink = e.target;
|
|
325
|
+
if (Alink && Alink.href.includes("#")) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (e.target.parentElement && e.target.parentElement.tagName === "A") {
|
|
330
|
+
Alink = e.target.parentElement;
|
|
331
|
+
}
|
|
332
|
+
if (Alink && Alink.href.includes("#")) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (Alink && Alink.href.includes("javascript")) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
e.preventDefault();
|
|
339
|
+
if (Alink) {
|
|
340
|
+
if (Alink.href === "" ||
|
|
341
|
+
new URL(Alink.href).pathname === window.location.pathname) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const [route, params] = checker(new URL(Alink.href).pathname);
|
|
345
|
+
if (route) {
|
|
346
|
+
Router.params.event = e;
|
|
347
|
+
Router.params.params = params || null;
|
|
348
|
+
Router.params.data = Router.params.data || null;
|
|
349
|
+
route.controller(Router.params);
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
throw new Error("cradova err route doesn't exist " + Alink.href);
|
|
353
|
+
}
|
|
354
|
+
window.history.pushState({}, "", new URL(Alink.href).pathname);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const url = window.location.pathname;
|
|
358
|
+
const [route, params] = checker(url);
|
|
359
|
+
if (route) {
|
|
360
|
+
Router.params.event = e;
|
|
361
|
+
Router.params.params = params || null;
|
|
362
|
+
Router.params.data = Router.params.data || null;
|
|
363
|
+
route.controller(Router.params);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
/**
|
|
367
|
+
* Responds to click events anywhere in the document and when
|
|
368
|
+
* the click happens on a link that is supposed to be handled
|
|
369
|
+
* by the router, loads and displays the target page.
|
|
370
|
+
*
|
|
371
|
+
* @param {Event} e Click event.
|
|
372
|
+
*/
|
|
373
|
+
document.addEventListener("click", Router.router);
|
|
374
|
+
window.addEventListener("load", Router.router);
|
|
375
|
+
window.addEventListener("popstate", (e) => {
|
|
376
|
+
e.preventDefault();
|
|
377
|
+
Router.router(e);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
*
|
|
382
|
+
* @param name
|
|
383
|
+
* @param template
|
|
384
|
+
* @param transitions
|
|
385
|
+
*/
|
|
386
|
+
class Screen {
|
|
387
|
+
constructor(cradova_screen_initials) {
|
|
388
|
+
this.secondaryChildren = [];
|
|
389
|
+
/**
|
|
390
|
+
* this tells cradova to persist state on the screen or not
|
|
391
|
+
* persiting is better
|
|
392
|
+
*/
|
|
393
|
+
this.persist = true;
|
|
394
|
+
const { template, name, callBack, transition, persist } = cradova_screen_initials;
|
|
395
|
+
if (typeof template !== "function") {
|
|
396
|
+
throw new Error("Cradova err: only functions that returns a cradova element is valid as screen");
|
|
397
|
+
}
|
|
398
|
+
this.html = template;
|
|
399
|
+
this.name = name;
|
|
400
|
+
this.template = document.createElement("div");
|
|
401
|
+
this.template.style.width = "100%";
|
|
402
|
+
this.template.style.display = "flex";
|
|
403
|
+
this.template.style.flexDirection = "column";
|
|
404
|
+
this.template.id = "cradova-screen-set";
|
|
405
|
+
this.callBack = callBack;
|
|
406
|
+
this.transition = transition;
|
|
407
|
+
if (!persist) {
|
|
408
|
+
this.persist = false;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async package(data) {
|
|
412
|
+
this.template.innerHTML = '';
|
|
413
|
+
if (typeof this.html === "function") {
|
|
414
|
+
let fuc = await this.html(data);
|
|
415
|
+
if (typeof fuc === "function") {
|
|
416
|
+
fuc = fuc();
|
|
417
|
+
if (!(fuc instanceof HTMLElement)) {
|
|
418
|
+
throw new Error("Cradova err only parent with descendants is valid");
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
this.template.append(fuc);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
this.template.append(...this.secondaryChildren);
|
|
426
|
+
}
|
|
427
|
+
onActivate(cb) {
|
|
428
|
+
this.callBack = cb;
|
|
429
|
+
}
|
|
430
|
+
addChild(...addOns) {
|
|
431
|
+
for (let i = 0; i < addOns.length; i++) {
|
|
432
|
+
if (addOns[i] && addOns[i] instanceof HTMLElement) {
|
|
433
|
+
this.secondaryChildren.push(addOns[i]);
|
|
434
|
+
}
|
|
435
|
+
if (addOns[i] && typeof addOns[i] === "function") {
|
|
436
|
+
this.secondaryChildren.push(addOns[i]());
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
detach() {
|
|
441
|
+
// crearing the dom
|
|
442
|
+
const screens = document.querySelectorAll("#cradova-screen-set");
|
|
443
|
+
for (let i = 0; i < screens.length; i++) {
|
|
444
|
+
const screen = screens[i];
|
|
445
|
+
if (this.transition) {
|
|
446
|
+
screen.classList.remove("CRADOVA-UI-" + this.transition);
|
|
447
|
+
}
|
|
448
|
+
screen.parentElement?.removeChild(screen);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
async Activate(data) {
|
|
452
|
+
let packed = false;
|
|
453
|
+
if (document.title === this.name) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (!this.template.firstChild) {
|
|
457
|
+
packed = true;
|
|
458
|
+
await this.package(data);
|
|
459
|
+
}
|
|
460
|
+
if (!this.persist && !packed) {
|
|
461
|
+
await this.package(data);
|
|
462
|
+
}
|
|
463
|
+
document.title = this.name;
|
|
464
|
+
this.detach();
|
|
465
|
+
document.querySelector("#app-wrapper").append(this.template);
|
|
466
|
+
if (this.transition) {
|
|
467
|
+
this.template?.classList.add("CRADOVA-UI-" + this.transition);
|
|
468
|
+
// console.log(this.template.className);
|
|
469
|
+
}
|
|
470
|
+
if (this.callBack) {
|
|
471
|
+
this.callBack(this.template.firstChild, data);
|
|
472
|
+
}
|
|
473
|
+
window.scrollTo(0, 0);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// SCREEN ANIMATION CLASSES
|
|
477
|
+
Screen.SCALE_IN = "SCALE-IN";
|
|
478
|
+
Screen.SCALE_OUT = "SCALE-OUT";
|
|
479
|
+
Screen.CIRCLE_IN = "CIRCLE-IN";
|
|
480
|
+
Screen.CIRCLE_OUT = "CIRCLE-OUT";
|
|
481
|
+
Screen.FADE_OUT = "FADE-OUT";
|
|
482
|
+
Screen.FADE_IN = "FADE-IN";
|
|
483
|
+
Screen.SLIDE_UP = "SLIDE-UP";
|
|
484
|
+
Screen.SLIDE_DOWN = "SLIDE-DOWN";
|
|
485
|
+
Screen.SLIDE_LEFT = "SLIDE-LEFT";
|
|
486
|
+
Screen.SLIDE_RIGHT = "SLIDE-RIGHT";
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* JSON DB DataBase MIT Licence © 2022
|
|
490
|
+
* ************************************
|
|
491
|
+
* Created by Friday Candour @uiedbooker
|
|
492
|
+
* email > fridaymaxtour@gmail.com
|
|
493
|
+
* github > www.github.com/FridayCandour
|
|
494
|
+
* telegram > @uiedbooker
|
|
495
|
+
* JSONDB @version 1.0.0
|
|
496
|
+
* */
|
|
497
|
+
const JSONDBversion = "1.0.0";
|
|
498
|
+
let fs, isNode = false, _dirname;
|
|
499
|
+
(async function () {
|
|
500
|
+
if (!globalThis.localStorage) {
|
|
501
|
+
isNode = true;
|
|
502
|
+
}
|
|
503
|
+
})();
|
|
504
|
+
const schema = class {
|
|
505
|
+
constructor(schema_configuration_object, validators) {
|
|
506
|
+
// validations
|
|
507
|
+
if (!schema_configuration_object.columns) {
|
|
508
|
+
throw new Error("JSONDB: can't create an empty table should have some columns");
|
|
509
|
+
}
|
|
510
|
+
validators.validateColumns(schema_configuration_object.columns);
|
|
511
|
+
const isEmptyObject = function (obj) {
|
|
512
|
+
// for checking for empty objects
|
|
513
|
+
for (const _name in obj) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
return true;
|
|
517
|
+
};
|
|
518
|
+
if (schema_configuration_object.relations &&
|
|
519
|
+
!isEmptyObject(schema_configuration_object.relations)) {
|
|
520
|
+
validators.validateRelations(schema_configuration_object.relations);
|
|
521
|
+
}
|
|
522
|
+
// assignment
|
|
523
|
+
this.base_name = "";
|
|
524
|
+
this.name = schema_configuration_object.name;
|
|
525
|
+
this.last_index = -1;
|
|
526
|
+
this.columns = schema_configuration_object.columns;
|
|
527
|
+
this.relations = schema_configuration_object.relations || null;
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
class JSONDBTableWrapper {
|
|
531
|
+
constructor(self, keys) {
|
|
532
|
+
this.put = async (name, value) => {
|
|
533
|
+
if (isNode) {
|
|
534
|
+
await fs.writeFile(name + ".json", JSON.stringify(value), "utf-8");
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
localStorage.setItem(name, JSON.stringify(value));
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
this.get = async (name) => {
|
|
541
|
+
if (!isNode) {
|
|
542
|
+
return JSON.parse(localStorage.getItem(name));
|
|
543
|
+
}
|
|
544
|
+
const data = await fs.readFile(_dirname + "/" + name + ".json", {
|
|
545
|
+
encoding: "utf-8",
|
|
546
|
+
});
|
|
547
|
+
if (data) {
|
|
548
|
+
return JSON.parse(data);
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
throw new Error("JSONDB: error failed to retrieve entities from database ");
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
this.validator = (incoming, tables) => {
|
|
555
|
+
// works for type, nulllable and unique validations.
|
|
556
|
+
const outgoing = {};
|
|
557
|
+
for (const prop in this.self.columns) {
|
|
558
|
+
if (this.self.columns[prop].nullable !== true &&
|
|
559
|
+
!Object.hasOwnProperty.call(incoming, prop)) {
|
|
560
|
+
throw new Error("JSONDB: error failed to validate incoming data because " +
|
|
561
|
+
prop +
|
|
562
|
+
" is required for " +
|
|
563
|
+
this.self.name +
|
|
564
|
+
" Schema");
|
|
565
|
+
}
|
|
566
|
+
if (!this.self.columns[prop].nullable &&
|
|
567
|
+
typeof incoming[prop] !== this.self.columns[prop].type) {
|
|
568
|
+
throw new Error("JSONDB: error failed to validate incoming data because " +
|
|
569
|
+
prop +
|
|
570
|
+
"'s value " +
|
|
571
|
+
incoming[prop] +
|
|
572
|
+
" has got a wrong data type of " +
|
|
573
|
+
typeof incoming[prop] +
|
|
574
|
+
" for " +
|
|
575
|
+
this.self.name +
|
|
576
|
+
" should be " +
|
|
577
|
+
this.self.columns[prop].type +
|
|
578
|
+
" type instead");
|
|
579
|
+
}
|
|
580
|
+
if (this.self.columns[prop].unique === true) {
|
|
581
|
+
for (let i = 0; i < tables.length; i++) {
|
|
582
|
+
const element = tables[i];
|
|
583
|
+
if (element[prop] === incoming[prop]) {
|
|
584
|
+
throw new Error("JSONDB: error failed to validate incoming data because " +
|
|
585
|
+
prop +
|
|
586
|
+
" is unique for " +
|
|
587
|
+
this.self.name +
|
|
588
|
+
" Schema can't have more than one instance");
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
// cleaning time
|
|
593
|
+
outgoing[prop] = incoming[prop];
|
|
594
|
+
}
|
|
595
|
+
return outgoing;
|
|
596
|
+
};
|
|
597
|
+
this.self = self;
|
|
598
|
+
this.keys = keys;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Save with relations
|
|
602
|
+
* ---------------------
|
|
603
|
+
* @type .saveWithRelations(target table, schema, schema | schema[]) => Promise(object)
|
|
604
|
+
* @example
|
|
605
|
+
* // single relation
|
|
606
|
+
await PollTable.saveWithRelations(MessageTable, Poll, message);
|
|
607
|
+
// arrays of relations
|
|
608
|
+
await PollTable.saveWithRelations(MessageTable, Poll, allMessages);
|
|
609
|
+
*/
|
|
610
|
+
async saveWithRelations(table, incoming, relations) {
|
|
611
|
+
if (!relations || !table) {
|
|
612
|
+
throw new TypeError("JsonDB: error saving with relations table name or relation cannot be null " +
|
|
613
|
+
JSON.stringify(relations) +
|
|
614
|
+
" " +
|
|
615
|
+
JSON.stringify(table));
|
|
616
|
+
}
|
|
617
|
+
if (!table.self || !table.self.name) {
|
|
618
|
+
throw new TypeError("JsonDB: error saving with relations table is invalid " +
|
|
619
|
+
JSON.stringify(table));
|
|
620
|
+
}
|
|
621
|
+
const db = await this.get(this.self.base_name);
|
|
622
|
+
db.last_access_time = Date();
|
|
623
|
+
if (incoming && typeof incoming.index !== "number") {
|
|
624
|
+
throw new Error("JsonDB: save before saving with relations");
|
|
625
|
+
}
|
|
626
|
+
db.tables[this.self.name][incoming.index] = incoming;
|
|
627
|
+
if (relations && Array.isArray(relations)) {
|
|
628
|
+
for (let i = 0; i < relations.length; i++) {
|
|
629
|
+
if (db.Entities[this.self.name].relations[table.self.name]) {
|
|
630
|
+
if (db.Entities[this.self.name].relations[table.self.name].type ===
|
|
631
|
+
"many") {
|
|
632
|
+
db.tables[this.self.name][incoming.index].relations[table.self.name] = !db.tables[this.self.name][incoming.index].relations[table.self.name]
|
|
633
|
+
? [relations[i]]
|
|
634
|
+
: [
|
|
635
|
+
...db.tables[this.self.name][incoming.index].relations[table.self.name],
|
|
636
|
+
relations[i],
|
|
637
|
+
];
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
db.tables[this.self.name][incoming.index].relations[table.self.name] = relations[i];
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
if (relations) {
|
|
647
|
+
if (db.Entities[this.self.name].relations[table.self.name]) {
|
|
648
|
+
if (db.Entities[this.self.name].relations[table.self.name].type ===
|
|
649
|
+
"many") {
|
|
650
|
+
db.tables[this.self.name][incoming.index].relations[table.self.name] = !db.tables[this.self.name][incoming.index].relations[table.self.name]
|
|
651
|
+
? [relations]
|
|
652
|
+
: [
|
|
653
|
+
...db.tables[this.self.name][incoming.index].relations[table.self.name],
|
|
654
|
+
relations,
|
|
655
|
+
];
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
db.tables[this.self.name][incoming.index].relations[table.self.name] = relations;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
await this.put(this.self.base_name, db);
|
|
664
|
+
return db.tables[this.self.name][incoming.index];
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Save table into a Jsondb instance
|
|
668
|
+
* -----------------------------
|
|
669
|
+
* @type .save(schema)=> Promise(object)
|
|
670
|
+
* @example
|
|
671
|
+
await PollTable.save(poll)
|
|
672
|
+
*/
|
|
673
|
+
async save(incoming) {
|
|
674
|
+
// db.tables[this.self.name] = db.tables[this.self.name].sort(
|
|
675
|
+
// (a, b) => a.index - b.index
|
|
676
|
+
// );
|
|
677
|
+
const db = await this.get(this.self.base_name);
|
|
678
|
+
db.last_access_time = Date();
|
|
679
|
+
if (typeof incoming.index !== "number") {
|
|
680
|
+
incoming = this.validator(incoming, db.tables[this.self.name]);
|
|
681
|
+
if (this.self.relations && !incoming.relations) {
|
|
682
|
+
incoming.relations = {};
|
|
683
|
+
}
|
|
684
|
+
db.Entities[this.self.name].last_index += 1;
|
|
685
|
+
incoming.index = db.Entities[this.self.name].last_index;
|
|
686
|
+
db.tables[this.self.name].push(incoming);
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
db.tables[this.self.name][incoming.index] = incoming;
|
|
690
|
+
}
|
|
691
|
+
await this.put(this.self.base_name, db);
|
|
692
|
+
return incoming;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Save table into a Jsondb instance
|
|
696
|
+
* -----------------------------
|
|
697
|
+
* @type .remove(schema)=> Promise(object)
|
|
698
|
+
* @example
|
|
699
|
+
await PollTable.remove(poll)
|
|
700
|
+
*/
|
|
701
|
+
async remove(entity) {
|
|
702
|
+
const db = await this.get(this.self.base_name);
|
|
703
|
+
db.last_access_time = Date();
|
|
704
|
+
// db.tables[this.self.name].splice(entity.index, 1);
|
|
705
|
+
db.tables[this.self.name][entity.index] = null;
|
|
706
|
+
await this.put(this.self.base_name, db);
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Save table into a Jsondb instance
|
|
711
|
+
* -----------------------------
|
|
712
|
+
* @type .count(schema)=> Promise(number)
|
|
713
|
+
* @example
|
|
714
|
+
await PollTable.count(poll)
|
|
715
|
+
*/
|
|
716
|
+
async count() {
|
|
717
|
+
const db = await this.get(this.self.base_name);
|
|
718
|
+
db.last_access_time = Date();
|
|
719
|
+
return db.tables[this.self.name].length;
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Save table into a Jsondb instance
|
|
723
|
+
* -----------------------------
|
|
724
|
+
* @type .getAll()=> Promise(object[])
|
|
725
|
+
* @example
|
|
726
|
+
await PollTable.getAll()
|
|
727
|
+
*/
|
|
728
|
+
async getAll() {
|
|
729
|
+
const db = await this.get(this.self.base_name);
|
|
730
|
+
db.last_access_time = Date();
|
|
731
|
+
return db.tables[this.self.name];
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* get entities with any of the values specifiled from a Jsondb instance
|
|
735
|
+
* -----------------------------
|
|
736
|
+
* @type .getWhereAny({prop: value}, number | undefind)=> Promise(object)
|
|
737
|
+
* @example
|
|
738
|
+
await PollTable.getWhereAny({name: "friday", age: 121, class: "senior"}) // gets all
|
|
739
|
+
await PollTable.getWhereAny({email: "fridaymaxtour@gmail.com"}, 2) // gets 2 if they are up to two
|
|
740
|
+
*/
|
|
741
|
+
async getWhereAny(props, number) {
|
|
742
|
+
const results = [];
|
|
743
|
+
let all;
|
|
744
|
+
const db = await this.get(this.self.base_name);
|
|
745
|
+
db.last_access_time = Date();
|
|
746
|
+
all = db.tables[this.self.name];
|
|
747
|
+
for (let i = 0; i < all.length; i++) {
|
|
748
|
+
const element = all[i];
|
|
749
|
+
for (const [k, v] of Object.entries(props)) {
|
|
750
|
+
if (element[k] && element[k] === v) {
|
|
751
|
+
results.push(element);
|
|
752
|
+
if (typeof number === "number" && results.length === number) {
|
|
753
|
+
return results;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return results;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* get entities with the given prop of type "string" where the values specifiled is included
|
|
762
|
+
* -----------------------------
|
|
763
|
+
* @type .getWhereAnyPropsIncludes({prop: value}, number | undefind)=> Promise(object)
|
|
764
|
+
*
|
|
765
|
+
* @example prop must be type string!
|
|
766
|
+
*
|
|
767
|
+
await PollTable.getWhereAnyPropsIncludes({name: "fri"}) // gets all
|
|
768
|
+
await PollTable.getWhereAnyPropsIncludes({name: "fri"}, 2) // gets 2 if they are up to two
|
|
769
|
+
*/
|
|
770
|
+
async getWhereAnyPropsIncludes(props, number) {
|
|
771
|
+
const results = [];
|
|
772
|
+
const db = await this.get(this.self.base_name);
|
|
773
|
+
db.last_access_time = Date();
|
|
774
|
+
const all = db.tables[this.self.name];
|
|
775
|
+
for (let i = 0; i < all.length; i++) {
|
|
776
|
+
const element = all[i];
|
|
777
|
+
for (const [k, v] of Object.entries(props)) {
|
|
778
|
+
if (element[k] && typeof v === "string" && element[k].includes(v)) {
|
|
779
|
+
results.push(element);
|
|
780
|
+
if (typeof number === "number" && results.length === number) {
|
|
781
|
+
return results;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return results;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* get an entity with the values specifiled from a Jsondb instance
|
|
790
|
+
* -----------------------------
|
|
791
|
+
* @type .getOne({prop: value})=> Promise(object)
|
|
792
|
+
* @example
|
|
793
|
+
|
|
794
|
+
await PollTable.getOne({email: "fridaymaxtour@gamail.com"}) // gets one
|
|
795
|
+
|
|
796
|
+
*/
|
|
797
|
+
async getOne(props) {
|
|
798
|
+
let results = null;
|
|
799
|
+
const db = await this.get(this.self.base_name);
|
|
800
|
+
db.last_access_time = Date();
|
|
801
|
+
const all = db.tables[this.self.name];
|
|
802
|
+
for (let i = 0; i < all.length; i++) {
|
|
803
|
+
const element = all[i];
|
|
804
|
+
for (const [k, v] of Object.entries(props)) {
|
|
805
|
+
if (element[k] && element[k] === v) {
|
|
806
|
+
results = element;
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return results;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const JSONDBConnection = class {
|
|
815
|
+
constructor(Entities, keys) {
|
|
816
|
+
this.Entities = Entities;
|
|
817
|
+
this.keys = keys;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Get a table from JSONDB
|
|
821
|
+
*------------------------
|
|
822
|
+
* @example
|
|
823
|
+
*
|
|
824
|
+
*
|
|
825
|
+
const details = {
|
|
826
|
+
password: "password",
|
|
827
|
+
username: "jsondb_username",
|
|
828
|
+
};
|
|
829
|
+
// getting connection instance into JSONDB
|
|
830
|
+
const connection = await database.createJSONDBConnection(details);
|
|
831
|
+
// getting a table
|
|
832
|
+
const MessageTable = connection.getTable("Message");
|
|
833
|
+
* */
|
|
834
|
+
getTable(table_name) {
|
|
835
|
+
for (const [tableName, table] of Object.entries(this.Entities)) {
|
|
836
|
+
if (table_name === tableName) {
|
|
837
|
+
return new JSONDBTableWrapper(table, this.keys);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return undefined;
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
/**
|
|
844
|
+
* Create a new JSONDB object
|
|
845
|
+
*------------------------
|
|
846
|
+
* @class
|
|
847
|
+
|
|
848
|
+
* const database = new JSONDB()
|
|
849
|
+
*
|
|
850
|
+
* Creates a new JSONDB object
|
|
851
|
+
*
|
|
852
|
+
* .
|
|
853
|
+
* */
|
|
854
|
+
class JSONDB {
|
|
855
|
+
constructor() {
|
|
856
|
+
this.DB_NAME = "";
|
|
857
|
+
this.username = "";
|
|
858
|
+
this.encrypted = false;
|
|
859
|
+
this.initialised = false;
|
|
860
|
+
this.time_created = Date();
|
|
861
|
+
this.version = JSONDBversion;
|
|
862
|
+
this.last_access_time = "";
|
|
863
|
+
this.visuality = "";
|
|
864
|
+
this.Entities = {};
|
|
865
|
+
this.tables = {};
|
|
866
|
+
}
|
|
867
|
+
async getDB(name) {
|
|
868
|
+
if (!isNode) {
|
|
869
|
+
return JSON.parse(localStorage.getItem(name));
|
|
870
|
+
}
|
|
871
|
+
const data = await fs.readFile(_dirname + "/" + name + ".json", {
|
|
872
|
+
encoding: "utf-8",
|
|
873
|
+
});
|
|
874
|
+
if (data) {
|
|
875
|
+
return JSON.parse(data);
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
throw new Error("JSONDB: error failed to retrieve entities from database ");
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Schema constructor for Jsondb
|
|
883
|
+
* -----------------------------
|
|
884
|
+
*
|
|
885
|
+
* name @type string
|
|
886
|
+
*
|
|
887
|
+
* columns @type object {
|
|
888
|
+
*
|
|
889
|
+
* type > @type any of number > string > boolean > blob and must be specified
|
|
890
|
+
*
|
|
891
|
+
* nullable @type bolean true > false default false
|
|
892
|
+
*
|
|
893
|
+
* unique @type bolean true > false default false
|
|
894
|
+
*
|
|
895
|
+
* }
|
|
896
|
+
*
|
|
897
|
+
* relations @type object {
|
|
898
|
+
*
|
|
899
|
+
* target: entity schema @type object,
|
|
900
|
+
*
|
|
901
|
+
* attachment_name: @type string,
|
|
902
|
+
*
|
|
903
|
+
* type : @type string should be "many" or "one"
|
|
904
|
+
*
|
|
905
|
+
* }
|
|
906
|
+
*
|
|
907
|
+
*
|
|
908
|
+
*
|
|
909
|
+
* @example
|
|
910
|
+
*
|
|
911
|
+
* const MessageSchema = database.schema({
|
|
912
|
+
name: "Message",
|
|
913
|
+
columns: {
|
|
914
|
+
vote: {
|
|
915
|
+
type: "number",
|
|
916
|
+
},
|
|
917
|
+
time: {
|
|
918
|
+
type: "string",
|
|
919
|
+
nullable: true,
|
|
920
|
+
},
|
|
921
|
+
value: {
|
|
922
|
+
type: "string",
|
|
923
|
+
},
|
|
924
|
+
},
|
|
925
|
+
});
|
|
926
|
+
*
|
|
927
|
+
* const PollSchema = new JSONDB.schema({
|
|
928
|
+
name: "Poll",
|
|
929
|
+
columns: {
|
|
930
|
+
value: {
|
|
931
|
+
type: "varchar",
|
|
932
|
+
},
|
|
933
|
+
},
|
|
934
|
+
relations: {
|
|
935
|
+
Message: {
|
|
936
|
+
target: Message,
|
|
937
|
+
type: "many-to-one",
|
|
938
|
+
},
|
|
939
|
+
},
|
|
940
|
+
});
|
|
941
|
+
*/
|
|
942
|
+
schema(schema_configuration_object) {
|
|
943
|
+
return new schema(schema_configuration_object, {
|
|
944
|
+
validateColumns: this.validateColumns,
|
|
945
|
+
validateRelations: this.validateRelations,
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Create a new JSONDB instance
|
|
950
|
+
*------------------------
|
|
951
|
+
* @example
|
|
952
|
+
* // creates a JSONDB object
|
|
953
|
+
* const Database = new JSONDB()
|
|
954
|
+
* // database configuration object
|
|
955
|
+
* const config = {
|
|
956
|
+
DB_NAME: "my db",
|
|
957
|
+
password: "password",
|
|
958
|
+
username: "jsondb_username",
|
|
959
|
+
encrypted: false,
|
|
960
|
+
}
|
|
961
|
+
// Creates a new JSONDB instance
|
|
962
|
+
* Database.init(config)
|
|
963
|
+
* */
|
|
964
|
+
async init(config) {
|
|
965
|
+
console.log(`\x1B[32m JSONDB version ${JSONDBversion} \x1B[39m`);
|
|
966
|
+
this.initialised = true;
|
|
967
|
+
this.DB_NAME = config.name;
|
|
968
|
+
this.password = config.password || "";
|
|
969
|
+
this.username = config.username || "";
|
|
970
|
+
this.encrypted = config.encrypted || false;
|
|
971
|
+
this.time_created = Date();
|
|
972
|
+
this.tables = {};
|
|
973
|
+
try {
|
|
974
|
+
let wasThere;
|
|
975
|
+
if (isNode) {
|
|
976
|
+
wasThere = this.getDB(config.name);
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
wasThere = localStorage.getItem(config.name);
|
|
980
|
+
}
|
|
981
|
+
if (wasThere) {
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
catch (error) { }
|
|
986
|
+
if (!config.password) {
|
|
987
|
+
throw new Error("JSONDB: error password is empty ");
|
|
988
|
+
}
|
|
989
|
+
if (!config.username) {
|
|
990
|
+
throw new Error("JSONDB: error username is empty ");
|
|
991
|
+
}
|
|
992
|
+
if (isNode) {
|
|
993
|
+
await fs.writeFile(config.name + ".json", JSON.stringify(this), "utf-8");
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
let db = JSON.stringify(this);
|
|
997
|
+
localStorage.setItem(config.name, db);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Create secure connection a Jsondb instance
|
|
1002
|
+
* -----------------------------
|
|
1003
|
+
* @example
|
|
1004
|
+
*
|
|
1005
|
+
* const details = {
|
|
1006
|
+
password: "password",
|
|
1007
|
+
username: "jsondb_username",
|
|
1008
|
+
};
|
|
1009
|
+
const connection = await database.createJSONDBConnection(details);
|
|
1010
|
+
*/
|
|
1011
|
+
async createJSONDBConnection(details) {
|
|
1012
|
+
if (!this.initialised) {
|
|
1013
|
+
throw new Error("JSONDB: you haven't create a JSONDB instance yet");
|
|
1014
|
+
}
|
|
1015
|
+
if (details.username !== this.username ||
|
|
1016
|
+
details.password !== this.password) {
|
|
1017
|
+
throw new Error("JSONDB: Access Denied");
|
|
1018
|
+
}
|
|
1019
|
+
const connection = await this.getDB(this.DB_NAME);
|
|
1020
|
+
connection.last_access_time = Date();
|
|
1021
|
+
return new JSONDBConnection(connection.Entities);
|
|
1022
|
+
}
|
|
1023
|
+
validateRelations(relations) {
|
|
1024
|
+
const types = ["many", "one"];
|
|
1025
|
+
for (const relation in relations) {
|
|
1026
|
+
const value = relations[relation];
|
|
1027
|
+
if (typeof value.target !== "object") {
|
|
1028
|
+
throw new Error("JSONDB: wrong relationship target type given " +
|
|
1029
|
+
value.target +
|
|
1030
|
+
" should be object only");
|
|
1031
|
+
}
|
|
1032
|
+
if (!types.includes(value.type)) {
|
|
1033
|
+
throw new Error("JSONDB: wrong relationship type given " +
|
|
1034
|
+
value.type +
|
|
1035
|
+
" should be many or one");
|
|
1036
|
+
}
|
|
1037
|
+
if (value.cascade && typeof value.cascade !== "boolean") {
|
|
1038
|
+
throw new Error("JSONDB: wrong cascade value given " +
|
|
1039
|
+
value.cascade +
|
|
1040
|
+
" should be true or false");
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
validateColumns(columns) {
|
|
1045
|
+
const types = ["number", "string", "boolean", "blob"];
|
|
1046
|
+
for (const column in columns) {
|
|
1047
|
+
const value = columns[column];
|
|
1048
|
+
if (column) {
|
|
1049
|
+
if (!types.includes(value.type)) {
|
|
1050
|
+
throw new Error("JSONDB: wrong data type given " +
|
|
1051
|
+
value.type +
|
|
1052
|
+
" only number, string, boolean and blob are accepted");
|
|
1053
|
+
}
|
|
1054
|
+
if (value.unique && typeof value.unique !== "boolean") {
|
|
1055
|
+
throw new Error("JSONDB: wrong unique value given " +
|
|
1056
|
+
value.unique +
|
|
1057
|
+
" should be true or false");
|
|
1058
|
+
}
|
|
1059
|
+
if (value.nullable && typeof value.nullable !== "boolean") {
|
|
1060
|
+
throw new Error("JSONDB: wrong nullable value given " +
|
|
1061
|
+
value.nullable +
|
|
1062
|
+
" should be true or false");
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Assemble Entities into Jsondb
|
|
1069
|
+
* -----------------------------
|
|
1070
|
+
* @example
|
|
1071
|
+
*
|
|
1072
|
+
* const MessageSchema = database.schema({
|
|
1073
|
+
name: "Message",
|
|
1074
|
+
columns: {
|
|
1075
|
+
vote: {
|
|
1076
|
+
type: "number",
|
|
1077
|
+
},
|
|
1078
|
+
time: {
|
|
1079
|
+
type: "string",
|
|
1080
|
+
nullable: true,
|
|
1081
|
+
},
|
|
1082
|
+
value: {
|
|
1083
|
+
type: "string",
|
|
1084
|
+
},
|
|
1085
|
+
},
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
database.assemble([MessageSchema]);
|
|
1089
|
+
*
|
|
1090
|
+
*/
|
|
1091
|
+
async assemble(allEntities) {
|
|
1092
|
+
if (!this.initialised) {
|
|
1093
|
+
throw new Error("JSONDB: you haven't create a JSONDB instance yet");
|
|
1094
|
+
}
|
|
1095
|
+
try {
|
|
1096
|
+
const wasThere = await this.getDB(this.DB_NAME);
|
|
1097
|
+
if (wasThere) {
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
catch (error) { }
|
|
1102
|
+
if (!Array.isArray(allEntities) || typeof allEntities[0] !== "object") {
|
|
1103
|
+
throw new Error("JSONDB: invalid entity array list, can't be assembled");
|
|
1104
|
+
}
|
|
1105
|
+
for (let i = 0; i < allEntities.length; i++) {
|
|
1106
|
+
this.Entities[allEntities[i].name] = allEntities[i];
|
|
1107
|
+
this.Entities[allEntities[i].name].base_name = this.DB_NAME;
|
|
1108
|
+
this.tables[allEntities[i].name] = [];
|
|
1109
|
+
}
|
|
1110
|
+
if (isNode) {
|
|
1111
|
+
await fs.writeFile(this.DB_NAME + ".json", JSON.stringify(this), "utf-8");
|
|
1112
|
+
}
|
|
1113
|
+
else {
|
|
1114
|
+
localStorage.setItem(this.DB_NAME, JSON.stringify(this));
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* @exports
|
|
1120
|
+
*/
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
*
|
|
1124
|
+
* little Axios request handler
|
|
1125
|
+
* ----------------------
|
|
1126
|
+
* supports files upload
|
|
1127
|
+
* ----------------------
|
|
1128
|
+
* @param {string} url
|
|
1129
|
+
* @param {object?} data?
|
|
1130
|
+
* @param {(object | function) ?} header or callback?
|
|
1131
|
+
* @param {function?} callback only?
|
|
1132
|
+
* @return void
|
|
1133
|
+
*/
|
|
1134
|
+
async function littleAxios(url, callback, data, header) {
|
|
1135
|
+
if (!callback && typeof header === "function") {
|
|
1136
|
+
callback = header;
|
|
1137
|
+
}
|
|
1138
|
+
if (typeof url !== "string") {
|
|
1139
|
+
throw new Error("Cradova err : little Axios invalid url " + url);
|
|
1140
|
+
}
|
|
1141
|
+
const ajax = new XMLHttpRequest();
|
|
1142
|
+
let formData = new FormData();
|
|
1143
|
+
const method = data && typeof data !== "object" ? "GET" : "POST";
|
|
1144
|
+
ajax.addEventListener("load", async function (res) {
|
|
1145
|
+
console.log(res.target);
|
|
1146
|
+
callback(res.target);
|
|
1147
|
+
});
|
|
1148
|
+
if (data) {
|
|
1149
|
+
for (const key in data) {
|
|
1150
|
+
const value = data[key];
|
|
1151
|
+
formData.append(key, value);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
ajax.addEventListener("error", (e) => {
|
|
1155
|
+
return callback(e);
|
|
1156
|
+
});
|
|
1157
|
+
ajax.open(method, url, true);
|
|
1158
|
+
ajax.send(formData);
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* An fetch based fetcher
|
|
1162
|
+
* ----------------------
|
|
1163
|
+
*
|
|
1164
|
+
* @param url string
|
|
1165
|
+
* @param method string
|
|
1166
|
+
* @param headers object
|
|
1167
|
+
* @param data object
|
|
1168
|
+
* @returns any
|
|
1169
|
+
*/
|
|
1170
|
+
async function fetcher(url, method = "GET", headers, data) {
|
|
1171
|
+
return await fetch(url, {
|
|
1172
|
+
headers,
|
|
1173
|
+
method,
|
|
1174
|
+
body: JSON.stringify(data),
|
|
1175
|
+
}).catch((_err) => {
|
|
1176
|
+
return {
|
|
1177
|
+
async text() {
|
|
1178
|
+
return {
|
|
1179
|
+
message: JSON.stringify(`${method} ${url} net::ERR_FAILED`),
|
|
1180
|
+
};
|
|
1181
|
+
},
|
|
1182
|
+
};
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function loadCradovaUICss(seconds = 0.3) {
|
|
1187
|
+
const css = `:root {
|
|
1188
|
+
--animation-timing: ${seconds + ""}s;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.CRADOVA-UI-CIRCLE-OUT {
|
|
1192
|
+
animation: circle-out var(--animation-timing) ease forwards;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
.CRADOVA-UI-CIRCLE-IN {
|
|
1196
|
+
animation: circle-in var(--animation-timing) ease forwards;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
.CRADOVA-UI-SCALE-IN {
|
|
1200
|
+
animation: scale-in var(--animation-timing) ease forwards;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
.CRADOVA-UI-SCALE-OUT {
|
|
1204
|
+
animation: scale-out var(--animation-timing) ease forwards;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
.CRADOVA-UI-SLIDE-RIGHT {
|
|
1208
|
+
animation: slide-right var(--animation-timing) ease forwards;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
.CRADOVA-UI-SLIDE-LEFT {
|
|
1212
|
+
animation: slide-left var(--animation-timing) ease forwards;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
.CRADOVA-UI-SLIDE-UP {
|
|
1216
|
+
animation: slide-up var(--animation-timing) ease forwards;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
.CRADOVA-UI-SLIDE-DOWN {
|
|
1220
|
+
animation: slide-down var(--animation-timing) ease forwards;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/* not done yet */
|
|
1224
|
+
|
|
1225
|
+
.CRADOVA-UI-FADE-IN {
|
|
1226
|
+
animation: fade-in var(--animation-timing) ease forwards;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
.CRADOVA-UI-FADE-OUT {
|
|
1230
|
+
animation: fade-out var(--animation-timing) ease forwards;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/* ultility classes */
|
|
1234
|
+
|
|
1235
|
+
.CIRCLE-OUT {
|
|
1236
|
+
animation: fadeoff var(--animation-timing) ease forwards;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
.CIRCLE-IN {
|
|
1240
|
+
animation: fadein var(--animation-timing) ease forwards;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/* animations */
|
|
1244
|
+
|
|
1245
|
+
@keyframes circle-out {
|
|
1246
|
+
from {
|
|
1247
|
+
border-radius: 49%;
|
|
1248
|
+
/* width: 100%; */
|
|
1249
|
+
/* height: 100%; */
|
|
1250
|
+
margin: auto;
|
|
1251
|
+
margin-top: 40%;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
to {
|
|
1255
|
+
border-radius: 100%;
|
|
1256
|
+
/* width: 0%; */
|
|
1257
|
+
/* height: 0%; */
|
|
1258
|
+
margin: auto;
|
|
1259
|
+
margin-top: 50%;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
@keyframes circle-in {
|
|
1264
|
+
from {
|
|
1265
|
+
border-radius: 100%;
|
|
1266
|
+
/* width: 0%; */
|
|
1267
|
+
/* height: 0%; */
|
|
1268
|
+
margin: auto;
|
|
1269
|
+
margin-top: 40%;
|
|
1270
|
+
}
|
|
1271
|
+
to {
|
|
1272
|
+
border-radius: 0%;
|
|
1273
|
+
/* width: 100vw; */
|
|
1274
|
+
/* height: 100vh; */
|
|
1275
|
+
margin: auto;
|
|
1276
|
+
margin-top: 0%;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
@keyframes scale-in {
|
|
1281
|
+
from {
|
|
1282
|
+
transform: scale(1);
|
|
1283
|
+
}
|
|
1284
|
+
to {
|
|
1285
|
+
transform: scale(0);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
@keyframes scale-out {
|
|
1291
|
+
from {
|
|
1292
|
+
transform: scale(0);
|
|
1293
|
+
}
|
|
1294
|
+
to {
|
|
1295
|
+
transform: scale(1);
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
@keyframes slide-right {
|
|
1300
|
+
from {
|
|
1301
|
+
margin-left: -140%;
|
|
1302
|
+
}
|
|
1303
|
+
to {
|
|
1304
|
+
margin-left: 0%;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
@keyframes slide-left {
|
|
1309
|
+
from {
|
|
1310
|
+
margin-left: 140%;
|
|
1311
|
+
}
|
|
1312
|
+
to {
|
|
1313
|
+
margin-left: 0%;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
@keyframes slide-up {
|
|
1318
|
+
from {
|
|
1319
|
+
margin-top: 140%;
|
|
1320
|
+
}
|
|
1321
|
+
to {
|
|
1322
|
+
margin-top: 0%;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
@keyframes slide-down {
|
|
1327
|
+
from {
|
|
1328
|
+
margin-top: -140%;
|
|
1329
|
+
}
|
|
1330
|
+
to {
|
|
1331
|
+
margin-top: 0%;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
@keyframes fade-in {
|
|
1336
|
+
from {
|
|
1337
|
+
opacity: 0;
|
|
1338
|
+
}
|
|
1339
|
+
to {
|
|
1340
|
+
opacity: 1;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
@keyframes fade-out {
|
|
1345
|
+
from {
|
|
1346
|
+
opacity: 1;
|
|
1347
|
+
}
|
|
1348
|
+
to {
|
|
1349
|
+
opacity: 0;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
/* ultility animations */
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
@keyframes fadeoff {
|
|
1357
|
+
50%{
|
|
1358
|
+
opacity: 0;
|
|
1359
|
+
}
|
|
1360
|
+
100% {
|
|
1361
|
+
opacity: 0;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
@keyframes fadein {
|
|
1366
|
+
0% {
|
|
1367
|
+
opacity: 0;
|
|
1368
|
+
}
|
|
1369
|
+
70%{
|
|
1370
|
+
opacity: 1;
|
|
1371
|
+
}
|
|
1372
|
+
100% {
|
|
1373
|
+
opacity: 1;
|
|
1374
|
+
}
|
|
1375
|
+
} `;
|
|
1376
|
+
const style = document.createElement("style");
|
|
1377
|
+
style.innerHTML = css;
|
|
1378
|
+
document.head.appendChild(style);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// the global dispatcher
|
|
1382
|
+
function cradovaDispatchtrack(nodes /*NodeListOf<Element>*/, stateID, state) {
|
|
1383
|
+
const updated = [];
|
|
1384
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
1385
|
+
const element = nodes[i];
|
|
1386
|
+
// abort re-rendering if the state is not for this element
|
|
1387
|
+
if (!element.stateID || element.stateID !== stateID) {
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
updated.push(element);
|
|
1391
|
+
if (typeof state === "object") {
|
|
1392
|
+
// updating the element's state
|
|
1393
|
+
for (const key in state) {
|
|
1394
|
+
// updating element styling
|
|
1395
|
+
if (key === "style") {
|
|
1396
|
+
for (const [k, v] of Object.entries(state[key])) {
|
|
1397
|
+
element.style[k] = v;
|
|
1398
|
+
}
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
if (element.style[key] && key !== "src") {
|
|
1402
|
+
element.style[key] = state[key];
|
|
1403
|
+
continue;
|
|
1404
|
+
}
|
|
1405
|
+
if (element.style[key] === "" && key !== "src") {
|
|
1406
|
+
element.style[key] = state[key];
|
|
1407
|
+
continue;
|
|
1408
|
+
}
|
|
1409
|
+
if (typeof element[key] === "function") {
|
|
1410
|
+
element[key]();
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
// updating element's inner text
|
|
1414
|
+
if (key === "text") {
|
|
1415
|
+
element.innerText = state[key];
|
|
1416
|
+
continue;
|
|
1417
|
+
}
|
|
1418
|
+
// setting element dimesion to full screen
|
|
1419
|
+
if (key === "fullscreen") {
|
|
1420
|
+
if (state[key]) {
|
|
1421
|
+
fullScreen(element).set();
|
|
1422
|
+
}
|
|
1423
|
+
else {
|
|
1424
|
+
fullScreen(element).exist();
|
|
1425
|
+
}
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1428
|
+
// adding class name to element
|
|
1429
|
+
if (key === "class" && typeof state[key] === "string") {
|
|
1430
|
+
const classes = state[key].split(" ");
|
|
1431
|
+
for (let i = 0; i < classes.length; i++) {
|
|
1432
|
+
if (classes[i]) {
|
|
1433
|
+
element.classList.add(classes[i]);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
// console.log(element.className, "cl");
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
// toggling element class
|
|
1440
|
+
if (key === "toggleclass") {
|
|
1441
|
+
element.classList.toggle(state[key]);
|
|
1442
|
+
// console.log(element.className, "tc");
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
//removing element class
|
|
1446
|
+
if (key === "removeclass") {
|
|
1447
|
+
element.classList.remove(state[key]);
|
|
1448
|
+
// console.log(element.className, "rm");
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1451
|
+
//removing element class
|
|
1452
|
+
if (key === "remove") {
|
|
1453
|
+
element.parentElement?.remove(element);
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1456
|
+
// changing the element children tree
|
|
1457
|
+
if (key === "tree") {
|
|
1458
|
+
if (typeof state[key] === "function") {
|
|
1459
|
+
state[key] = state[key]();
|
|
1460
|
+
}
|
|
1461
|
+
if (typeof state[key] === "function") {
|
|
1462
|
+
state[key] = state[key]();
|
|
1463
|
+
}
|
|
1464
|
+
if (Array.isArray(state[key])) {
|
|
1465
|
+
throw new TypeError("cradova err invalid element, should be a single element or parent element from cradova");
|
|
1466
|
+
}
|
|
1467
|
+
if (!(state[key] instanceof HTMLElement)) {
|
|
1468
|
+
console.error("cradova err wrong element type: can't create element state on " +
|
|
1469
|
+
state[key]);
|
|
1470
|
+
throw new TypeError("cradova err invalid element, should be a html element from cradova");
|
|
1471
|
+
}
|
|
1472
|
+
// destroy the component tree
|
|
1473
|
+
element.innerHTML = "";
|
|
1474
|
+
// rebuild the component tree
|
|
1475
|
+
// console.log(state[key]);
|
|
1476
|
+
element.append(state[key]);
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
element[key] = state[key];
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
return updated;
|
|
1484
|
+
}
|
|
1485
|
+
/**
|
|
1486
|
+
* Send a new state to specified element with stateID
|
|
1487
|
+
*
|
|
1488
|
+
* @param stateID
|
|
1489
|
+
* @param state
|
|
1490
|
+
* @returns element(s)
|
|
1491
|
+
*/
|
|
1492
|
+
function dispatch(stateID, state) {
|
|
1493
|
+
// TODO: this will be updated to use data-stateid soon
|
|
1494
|
+
// speed test still going on
|
|
1495
|
+
const nodes = document.querySelectorAll(".cra_child_doc");
|
|
1496
|
+
let updated = [];
|
|
1497
|
+
if (typeof state === "undefined" && typeof stateID === "object") {
|
|
1498
|
+
for (const [id, eachState] of Object.entries(stateID)) {
|
|
1499
|
+
const node = cradovaDispatchtrack(nodes, id, eachState);
|
|
1500
|
+
updated.push(...node);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
else {
|
|
1504
|
+
if (typeof stateID === "string") {
|
|
1505
|
+
const node = cradovaDispatchtrack(nodes, stateID, state);
|
|
1506
|
+
updated.push(...node);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
return updated;
|
|
1510
|
+
}
|
|
1511
|
+
// for making the dom elements fulscreen
|
|
1512
|
+
function fullScreen(e) {
|
|
1513
|
+
return {
|
|
1514
|
+
set() {
|
|
1515
|
+
e.requestFullscreen().catch((err) => {
|
|
1516
|
+
throw err;
|
|
1517
|
+
});
|
|
1518
|
+
},
|
|
1519
|
+
exist() {
|
|
1520
|
+
document.exitFullscreen();
|
|
1521
|
+
},
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
/*
|
|
1525
|
+
*** HOW TO USE ***
|
|
1526
|
+
|
|
1527
|
+
u("#container").fullscreen().toggle()
|
|
1528
|
+
u("#container").fullscreen().exist()
|
|
1529
|
+
u("#container").fullscreen().set()
|
|
1530
|
+
*/
|
|
1531
|
+
|
|
1532
|
+
/**
|
|
1533
|
+
* @param {number} num
|
|
1534
|
+
* @returns uuid
|
|
1535
|
+
*/
|
|
1536
|
+
function uuid(num = 10) {
|
|
1537
|
+
function dec2hex(dec) {
|
|
1538
|
+
return dec.toString(16).padStart(2, "0");
|
|
1539
|
+
}
|
|
1540
|
+
function generateId(len) {
|
|
1541
|
+
len = Math.round(len);
|
|
1542
|
+
return Array.from(crypto.getRandomValues(new Uint8Array(len || 10)), dec2hex).join("");
|
|
1543
|
+
}
|
|
1544
|
+
return generateId(num);
|
|
1545
|
+
}
|
|
1546
|
+
function PromptBeforeLeave() {
|
|
1547
|
+
window.addEventListener("beforeunload", (ev) => {
|
|
1548
|
+
const e = ev || window.event;
|
|
1549
|
+
if (e) {
|
|
1550
|
+
e.preventDefault();
|
|
1551
|
+
e.returnValue = "";
|
|
1552
|
+
}
|
|
1553
|
+
return "";
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
Write CSS media in javascript
|
|
1558
|
+
|
|
1559
|
+
@example
|
|
1560
|
+
|
|
1561
|
+
media("min-width: 790px",
|
|
1562
|
+
["#container",
|
|
1563
|
+
{
|
|
1564
|
+
width: "100%",
|
|
1565
|
+
height: "100%",
|
|
1566
|
+
"background-color": "#0000"
|
|
1567
|
+
}],
|
|
1568
|
+
|
|
1569
|
+
["#header",
|
|
1570
|
+
{
|
|
1571
|
+
width: "100%",
|
|
1572
|
+
height: "20%",
|
|
1573
|
+
"background-color": "#fff"
|
|
1574
|
+
}]
|
|
1575
|
+
)
|
|
1576
|
+
*/
|
|
1577
|
+
function media(value, ...properties) {
|
|
1578
|
+
/* This is for creating css
|
|
1579
|
+
@media styles using javascipt*/
|
|
1580
|
+
const styS = "@media only screen and (" +
|
|
1581
|
+
value +
|
|
1582
|
+
") " +
|
|
1583
|
+
"{" +
|
|
1584
|
+
`
|
|
1585
|
+
`, styE = "}" +
|
|
1586
|
+
`
|
|
1587
|
+
`;
|
|
1588
|
+
let style = " ", aniSty = " ";
|
|
1589
|
+
const proplen = properties.length;
|
|
1590
|
+
let totalAnimation, Animation = " ";
|
|
1591
|
+
const animationStep = (num) => {
|
|
1592
|
+
style = " ";
|
|
1593
|
+
for (const [k, v] of Object.entries(properties[num][1])) {
|
|
1594
|
+
style +=
|
|
1595
|
+
"" +
|
|
1596
|
+
k +
|
|
1597
|
+
": " +
|
|
1598
|
+
v +
|
|
1599
|
+
";" +
|
|
1600
|
+
`
|
|
1601
|
+
`;
|
|
1602
|
+
}
|
|
1603
|
+
aniSty +=
|
|
1604
|
+
"" +
|
|
1605
|
+
properties[num][0] +
|
|
1606
|
+
"{" +
|
|
1607
|
+
`
|
|
1608
|
+
` +
|
|
1609
|
+
style +
|
|
1610
|
+
"}" +
|
|
1611
|
+
`
|
|
1612
|
+
`;
|
|
1613
|
+
return aniSty;
|
|
1614
|
+
};
|
|
1615
|
+
for (let i = 0; i < proplen; i++) {
|
|
1616
|
+
Animation += animationStep(i);
|
|
1617
|
+
}
|
|
1618
|
+
let aniStyleTag = document.querySelector("style");
|
|
1619
|
+
if (aniStyleTag === null) {
|
|
1620
|
+
aniStyleTag = document.createElement("style");
|
|
1621
|
+
}
|
|
1622
|
+
aniStyleTag.media = "screen";
|
|
1623
|
+
totalAnimation =
|
|
1624
|
+
aniStyleTag.innerHTML +
|
|
1625
|
+
`
|
|
1626
|
+
|
|
1627
|
+
`;
|
|
1628
|
+
totalAnimation += styS + Animation + styE;
|
|
1629
|
+
aniStyleTag.innerHTML = totalAnimation;
|
|
1630
|
+
document.head.append(aniStyleTag);
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
Write CSS styles in Javascript
|
|
1634
|
+
@example
|
|
1635
|
+
|
|
1636
|
+
css("#container",
|
|
1637
|
+
{
|
|
1638
|
+
height: "100%",
|
|
1639
|
+
height: "100%",
|
|
1640
|
+
background-color: "#ff9800"
|
|
1641
|
+
})
|
|
1642
|
+
|
|
1643
|
+
css(".btn:hover",
|
|
1644
|
+
{
|
|
1645
|
+
height: "100%",
|
|
1646
|
+
height: "100%",
|
|
1647
|
+
background-color: "#ff9800"
|
|
1648
|
+
})
|
|
1649
|
+
|
|
1650
|
+
*/
|
|
1651
|
+
function css(indentifier, properties) {
|
|
1652
|
+
/*This is for creating
|
|
1653
|
+
css styles using javascipt*/
|
|
1654
|
+
const styS = "" +
|
|
1655
|
+
indentifier +
|
|
1656
|
+
"" +
|
|
1657
|
+
"{" +
|
|
1658
|
+
`
|
|
1659
|
+
`;
|
|
1660
|
+
const styE = "}" +
|
|
1661
|
+
`
|
|
1662
|
+
`;
|
|
1663
|
+
let style = "", totalStyle = "";
|
|
1664
|
+
for (const [k, v] of Object.entries(properties)) {
|
|
1665
|
+
style +=
|
|
1666
|
+
"" +
|
|
1667
|
+
k +
|
|
1668
|
+
": " +
|
|
1669
|
+
v +
|
|
1670
|
+
";" +
|
|
1671
|
+
`
|
|
1672
|
+
`;
|
|
1673
|
+
}
|
|
1674
|
+
let styleTag = document.querySelector("style");
|
|
1675
|
+
if (styleTag !== null) {
|
|
1676
|
+
totalStyle += styleTag.innerHTML;
|
|
1677
|
+
totalStyle += styS + style + styE;
|
|
1678
|
+
styleTag.innerHTML = totalStyle;
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
styleTag = document.createElement("style");
|
|
1682
|
+
totalStyle +=
|
|
1683
|
+
styleTag.innerHTML +
|
|
1684
|
+
`
|
|
1685
|
+
|
|
1686
|
+
`;
|
|
1687
|
+
totalStyle += styS + style + styE;
|
|
1688
|
+
styleTag.innerHTML = totalStyle;
|
|
1689
|
+
document.head.append(styleTag);
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
Write animation value in javascript
|
|
1693
|
+
|
|
1694
|
+
@example
|
|
1695
|
+
|
|
1696
|
+
animate("popanimation",
|
|
1697
|
+
["from",
|
|
1698
|
+
{
|
|
1699
|
+
transform: "scale3D(2)" ,
|
|
1700
|
+
height: "10%",
|
|
1701
|
+
"background-color": "#0000"
|
|
1702
|
+
}],
|
|
1703
|
+
|
|
1704
|
+
["to",
|
|
1705
|
+
{
|
|
1706
|
+
transform: "scale3D(1)" ,
|
|
1707
|
+
height: "100%",
|
|
1708
|
+
"background-color": "#ff9800"
|
|
1709
|
+
}]
|
|
1710
|
+
)
|
|
1711
|
+
|
|
1712
|
+
*/
|
|
1713
|
+
function animate(indentifier, ...properties) {
|
|
1714
|
+
/*This is for creating css
|
|
1715
|
+
animations using javascipt*/
|
|
1716
|
+
const styS = "@keyframes " +
|
|
1717
|
+
indentifier +
|
|
1718
|
+
" " +
|
|
1719
|
+
"{" +
|
|
1720
|
+
`
|
|
1721
|
+
`, styE = "}" +
|
|
1722
|
+
`
|
|
1723
|
+
`, proplen = properties.length;
|
|
1724
|
+
let style = " ", aniSty = " ", Animation = " ", totalAnimation = null;
|
|
1725
|
+
const animationStep = (num) => {
|
|
1726
|
+
style = " ";
|
|
1727
|
+
for (const [k, v] of Object.entries(properties[num][1])) {
|
|
1728
|
+
style +=
|
|
1729
|
+
"" +
|
|
1730
|
+
k +
|
|
1731
|
+
": " +
|
|
1732
|
+
v +
|
|
1733
|
+
";" +
|
|
1734
|
+
`
|
|
1735
|
+
`;
|
|
1736
|
+
}
|
|
1737
|
+
aniSty +=
|
|
1738
|
+
"" +
|
|
1739
|
+
properties[num][0] +
|
|
1740
|
+
"{" +
|
|
1741
|
+
`
|
|
1742
|
+
` +
|
|
1743
|
+
style +
|
|
1744
|
+
"}" +
|
|
1745
|
+
`
|
|
1746
|
+
`;
|
|
1747
|
+
return aniSty;
|
|
1748
|
+
};
|
|
1749
|
+
for (let i = 0; i < proplen; i++) {
|
|
1750
|
+
Animation += animationStep(i);
|
|
1751
|
+
}
|
|
1752
|
+
let aniStyleTag = document.querySelector("style");
|
|
1753
|
+
if (aniStyleTag === null) {
|
|
1754
|
+
aniStyleTag = document.createElement("style");
|
|
1755
|
+
}
|
|
1756
|
+
aniStyleTag.media = "screen";
|
|
1757
|
+
totalAnimation =
|
|
1758
|
+
aniStyleTag.innerHTML +
|
|
1759
|
+
`
|
|
1760
|
+
|
|
1761
|
+
`;
|
|
1762
|
+
totalAnimation += styS + Animation + styE;
|
|
1763
|
+
aniStyleTag.innerHTML = totalAnimation;
|
|
1764
|
+
document.head.append(aniStyleTag);
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1767
|
+
*
|
|
1768
|
+
* @param {expession} condition
|
|
1769
|
+
* @param {function} callback
|
|
1770
|
+
*/
|
|
1771
|
+
function assert(condition, callback) {
|
|
1772
|
+
if (condition) {
|
|
1773
|
+
return callback(condition);
|
|
1774
|
+
}
|
|
1775
|
+
// That didn't went well
|
|
1776
|
+
return " ";
|
|
1777
|
+
}
|
|
1778
|
+
const ls = {};
|
|
1779
|
+
ls.store = (name, value) => {
|
|
1780
|
+
localStorage.setItem(name, JSON.stringify(value));
|
|
1781
|
+
};
|
|
1782
|
+
ls.retrieve = (name) => {
|
|
1783
|
+
return localStorage.getItem(name);
|
|
1784
|
+
};
|
|
1785
|
+
ls.remove = (name) => {
|
|
1786
|
+
localStorage.removeItem(name);
|
|
1787
|
+
};
|
|
1788
|
+
ls.getKey = (index) => {
|
|
1789
|
+
return window.localStorage.key(index);
|
|
1790
|
+
};
|
|
1791
|
+
ls.clear = () => {
|
|
1792
|
+
localStorage.clear();
|
|
1793
|
+
};
|
|
1794
|
+
class list {
|
|
1795
|
+
constructor(component) {
|
|
1796
|
+
this.#stateID = uuid();
|
|
1797
|
+
this.#parentElement = null;
|
|
1798
|
+
this.#component = component;
|
|
1799
|
+
}
|
|
1800
|
+
#component;
|
|
1801
|
+
#stateID;
|
|
1802
|
+
#parentElement;
|
|
1803
|
+
build(datas) {
|
|
1804
|
+
if (!datas[0]) {
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
const elements = [];
|
|
1808
|
+
for (let i = 0; i < datas.length; i++) {
|
|
1809
|
+
const data = datas[i];
|
|
1810
|
+
const chtml = this.#component(data);
|
|
1811
|
+
const element = chtml({ stateID: this.#stateID });
|
|
1812
|
+
elements.push(element);
|
|
1813
|
+
}
|
|
1814
|
+
return elements;
|
|
1815
|
+
}
|
|
1816
|
+
update(datas) {
|
|
1817
|
+
if (!datas[0]) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
if (!this.#parentElement) {
|
|
1821
|
+
// only for the first call
|
|
1822
|
+
this.#parentElement = dispatch(this.#stateID, {
|
|
1823
|
+
display: "none",
|
|
1824
|
+
})[0].parentElement;
|
|
1825
|
+
}
|
|
1826
|
+
dispatch(this.#stateID, { remove: true });
|
|
1827
|
+
if (!this.#parentElement) {
|
|
1828
|
+
throw new Error("cannot update list");
|
|
1829
|
+
}
|
|
1830
|
+
for (let i = 0; i < datas.length; i++) {
|
|
1831
|
+
const data = datas[i];
|
|
1832
|
+
const chtml = this.#component(data);
|
|
1833
|
+
const element = chtml({ stateID: this.#stateID });
|
|
1834
|
+
this.#parentElement?.append(element);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* Save values to memory
|
|
1841
|
+
* get them when needed
|
|
1842
|
+
*/
|
|
1843
|
+
class memory {
|
|
1844
|
+
constructor(lastMemory) {
|
|
1845
|
+
this.#mem = {};
|
|
1846
|
+
this.#string = "cradova-memory-cache-key";
|
|
1847
|
+
if (lastMemory) {
|
|
1848
|
+
this.#mem = lastMemory;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
#mem;
|
|
1852
|
+
#string;
|
|
1853
|
+
get(key) {
|
|
1854
|
+
if (this.#mem[key]) {
|
|
1855
|
+
return this.#mem[key];
|
|
1856
|
+
}
|
|
1857
|
+
return null;
|
|
1858
|
+
}
|
|
1859
|
+
set(key, value) {
|
|
1860
|
+
this.#mem[key] = value;
|
|
1861
|
+
}
|
|
1862
|
+
load() {
|
|
1863
|
+
let memory = localStorage.getItem(this.#string);
|
|
1864
|
+
if (!memory)
|
|
1865
|
+
return false;
|
|
1866
|
+
memory = JSON.parse(memory);
|
|
1867
|
+
for (const mem in memory) {
|
|
1868
|
+
this.set(mem, memory[mem]);
|
|
1869
|
+
}
|
|
1870
|
+
return true;
|
|
1871
|
+
}
|
|
1872
|
+
save() {
|
|
1873
|
+
const memory = {};
|
|
1874
|
+
for (const mem in this.#mem) {
|
|
1875
|
+
memory[mem] = this.#mem[mem];
|
|
1876
|
+
}
|
|
1877
|
+
for (const val in memory) {
|
|
1878
|
+
if (val) {
|
|
1879
|
+
localStorage.setItem(this.#string, JSON.stringify(memory));
|
|
1880
|
+
break;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
const Init = function (self) {
|
|
1887
|
+
const Wrapper = document.createElement("div");
|
|
1888
|
+
Wrapper.className = "Cradova-app-wrappper";
|
|
1889
|
+
Wrapper.id = "app-wrapper";
|
|
1890
|
+
// style can be over written by another css file
|
|
1891
|
+
css(".Cradova-app-wrappper", {
|
|
1892
|
+
display: "flex",
|
|
1893
|
+
"align-items": "center",
|
|
1894
|
+
"justify-content": "center",
|
|
1895
|
+
"flex-direction": "column",
|
|
1896
|
+
width: "100%",
|
|
1897
|
+
});
|
|
1898
|
+
Wrapper.stateID = "Cradova-app-wrappper-id";
|
|
1899
|
+
document.body.append(Wrapper);
|
|
1900
|
+
const lastMemory = window.localStorage.getItem("cradova-local-memory");
|
|
1901
|
+
if (lastMemory) {
|
|
1902
|
+
self.memory = new memory(JSON.parse(lastMemory));
|
|
1903
|
+
}
|
|
1904
|
+
else {
|
|
1905
|
+
self.memory = new memory(null);
|
|
1906
|
+
}
|
|
1907
|
+
return Wrapper;
|
|
1908
|
+
};
|
|
1909
|
+
|
|
1910
|
+
/*
|
|
1911
|
+
|
|
1912
|
+
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
|
1913
|
+
"
|
|
1914
|
+
" ██████╗ ██████╗ █████ ██████╗ ███████╗ ██╗ ██╗ █████
|
|
1915
|
+
" ██╔════╝ ██╔══██╗ ██╔═╗██ █ ██ ██╔═════╝ ██║ ██║ ██╔═╗██
|
|
1916
|
+
" ██║ ██████╔╝ ██████╗ █ ██ ██║ ██ ██║ ██║ ██████╗
|
|
1917
|
+
" ██║ ██╔══██ ██║ ██║ █ ██ ██║ ██ ╚██╗ ██╔╝ ██║ ██
|
|
1918
|
+
" ╚██████╗ ██║ ██║ ██║ ██║ ███████╔╝ ███████ ╚████╔╝ ██║ ██║
|
|
1919
|
+
" ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚════╝ ╚═══╝ ╚═╝ ╚═╝
|
|
1920
|
+
" JSONDB inside
|
|
1921
|
+
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
|
1922
|
+
|
|
1923
|
+
" =============================================================================
|
|
1924
|
+
" By Friday Candour
|
|
1925
|
+
" -----------------------------------------------------------------------------
|
|
1926
|
+
" =============================================================================
|
|
1927
|
+
" Cradova FrameWork
|
|
1928
|
+
" @version 1.*.*
|
|
1929
|
+
" -----------------------------------------------------------------------------
|
|
1930
|
+
" License: Apache V2
|
|
1931
|
+
" -----------------------------------------------------------------------------
|
|
1932
|
+
" fridaymaxtour@gmail.com ...
|
|
1933
|
+
" =============================================================================
|
|
1934
|
+
|
|
1935
|
+
Apache License
|
|
1936
|
+
Version 2.0, January 2004
|
|
1937
|
+
http://www.apache.org/licenses/
|
|
1938
|
+
|
|
1939
|
+
Copyright 2022 Friday Candour. All Rights Reserved.
|
|
1940
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1941
|
+
you may not use this file except in compliance with the License.
|
|
1942
|
+
You may obtain a copy of the License at
|
|
1943
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
1944
|
+
Unless required by applicable law or agreed to in writing, software
|
|
1945
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1946
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1947
|
+
See the License for the specific language governing permissions and
|
|
1948
|
+
limitations under the License.
|
|
1949
|
+
*/
|
|
1950
|
+
const make = function (txx) {
|
|
1951
|
+
if (Array.isArray(txx)) {
|
|
1952
|
+
txx = txx[0].trim();
|
|
1953
|
+
}
|
|
1954
|
+
if (!txx) {
|
|
1955
|
+
return {
|
|
1956
|
+
tag: "div",
|
|
1957
|
+
Classes: undefined,
|
|
1958
|
+
ID: undefined,
|
|
1959
|
+
innerValue: undefined,
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
let tag;
|
|
1963
|
+
const itemsPurifier = (impure, items) => {
|
|
1964
|
+
const pureItems = [];
|
|
1965
|
+
for (let i = 0; i < items.length; i++) {
|
|
1966
|
+
if (items[i].includes(impure)) {
|
|
1967
|
+
items[i] = items[i].split(impure)[0];
|
|
1968
|
+
}
|
|
1969
|
+
pureItems.push(items[i]);
|
|
1970
|
+
}
|
|
1971
|
+
return pureItems;
|
|
1972
|
+
};
|
|
1973
|
+
let textContent;
|
|
1974
|
+
tag = txx.trim()[0] === "|" && "p";
|
|
1975
|
+
if (txx.includes("|")) {
|
|
1976
|
+
textContent = txx.split("|")[1];
|
|
1977
|
+
txx = txx.split("|")[0] && txx.split("|")[0];
|
|
1978
|
+
}
|
|
1979
|
+
const classes = itemsPurifier("#", txx.split("."));
|
|
1980
|
+
const ids = itemsPurifier(".", txx.split("#"));
|
|
1981
|
+
if (!tag) {
|
|
1982
|
+
tag = classes.shift();
|
|
1983
|
+
}
|
|
1984
|
+
if (!tag) {
|
|
1985
|
+
tag = ids.shift();
|
|
1986
|
+
}
|
|
1987
|
+
if (!tag) {
|
|
1988
|
+
tag = "div";
|
|
1989
|
+
}
|
|
1990
|
+
if (!txx.includes(".") && !txx.includes("#")) {
|
|
1991
|
+
tag = txx;
|
|
1992
|
+
ids.length = 0;
|
|
1993
|
+
classes.length = 0;
|
|
1994
|
+
}
|
|
1995
|
+
let ID = ids[1] ? ids[1].trim() : null;
|
|
1996
|
+
const className = classes.join(" ");
|
|
1997
|
+
let innerValue;
|
|
1998
|
+
if (textContent) {
|
|
1999
|
+
innerValue = textContent;
|
|
2000
|
+
}
|
|
2001
|
+
return { tag, className, ID, innerValue };
|
|
2002
|
+
};
|
|
2003
|
+
/**
|
|
2004
|
+
* Creates new cradova HTML element
|
|
2005
|
+
* @example
|
|
2006
|
+
* format for static _`p| am a p tag` // or _`p.class| am a p tag` or _`p#id| am a p tag` or _`p.class#id| am a p tag`
|
|
2007
|
+
* format for dynamic _(
|
|
2008
|
+
* "p| am a p tag" // or "p.class| am a p tag" or "p#id| am a p tag" or "p.class#id| am a p tag"
|
|
2009
|
+
* , {
|
|
2010
|
+
* //props like
|
|
2011
|
+
* text: "am a p tag",
|
|
2012
|
+
* style: {
|
|
2013
|
+
* color: "blue"
|
|
2014
|
+
* }
|
|
2015
|
+
* },
|
|
2016
|
+
* // place other children here like span
|
|
2017
|
+
* _`span| am a span tag like so`, // this is a static child
|
|
2018
|
+
* _("span| am a span tag like so", {style: {color: "brown"}}) // this is a dynamic child
|
|
2019
|
+
* )
|
|
2020
|
+
* @param {...any} element_initials
|
|
2021
|
+
* @returns function | HTMLElement
|
|
2022
|
+
*
|
|
2023
|
+
* static elements cannot be given props nor children nor state but dynamic can
|
|
2024
|
+
*
|
|
2025
|
+
* and static are useful too
|
|
2026
|
+
*/
|
|
2027
|
+
const _ = (...element_initials) => {
|
|
2028
|
+
let properties, childrens = [], beforeMount;
|
|
2029
|
+
if (typeof element_initials[1] == "object" &&
|
|
2030
|
+
!(element_initials[1] instanceof HTMLElement && !element_initials[1].tagName)) {
|
|
2031
|
+
properties = element_initials[1];
|
|
2032
|
+
if (properties?.beforeMount) {
|
|
2033
|
+
beforeMount = properties["beforeMount"];
|
|
2034
|
+
}
|
|
2035
|
+
if (element_initials.length > 2) {
|
|
2036
|
+
childrens = element_initials.slice(2, element_initials.length);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
else {
|
|
2040
|
+
if (element_initials[1] instanceof HTMLElement ||
|
|
2041
|
+
typeof element_initials[1] === "function") {
|
|
2042
|
+
childrens = element_initials.slice(1, element_initials.length);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
if (element_initials[0].raw) {
|
|
2046
|
+
// getting the value of static cradova calls
|
|
2047
|
+
element_initials[0] = element_initials[0]["raw"][0];
|
|
2048
|
+
}
|
|
2049
|
+
// verifing the children array
|
|
2050
|
+
function identify(element_initials) {
|
|
2051
|
+
if (typeof element_initials !== "object") {
|
|
2052
|
+
element_initials = [element_initials];
|
|
2053
|
+
}
|
|
2054
|
+
const initials = make(element_initials[0]);
|
|
2055
|
+
// TODO: tag debugger
|
|
2056
|
+
// const { tag, className, ID, innerValue } = initials;
|
|
2057
|
+
// if (tag === "div" && properties?.style?.pp === "o") {
|
|
2058
|
+
// // console.log(properties.beforeMount);
|
|
2059
|
+
// properties.beforeMount();
|
|
2060
|
+
// }
|
|
2061
|
+
/**
|
|
2062
|
+
*
|
|
2063
|
+
* --- Cradova Element Initials ---
|
|
2064
|
+
* --------------------------------
|
|
2065
|
+
*
|
|
2066
|
+
* Note: this element has not been initialised!
|
|
2067
|
+
*
|
|
2068
|
+
* add to a parent element or call this return fuction
|
|
2069
|
+
*
|
|
2070
|
+
* .
|
|
2071
|
+
*/
|
|
2072
|
+
return (...incoming) => {
|
|
2073
|
+
/*
|
|
2074
|
+
*
|
|
2075
|
+
* --- Cradova Element Initials ---
|
|
2076
|
+
* --------------------------------
|
|
2077
|
+
*
|
|
2078
|
+
* Note: this element has not been initialised!
|
|
2079
|
+
*
|
|
2080
|
+
* add to a parent element or call this return fuction
|
|
2081
|
+
*
|
|
2082
|
+
* .
|
|
2083
|
+
*/
|
|
2084
|
+
let childrens2rd = [], props = {}, text;
|
|
2085
|
+
for (let i = 0; i < incoming.length; i++) {
|
|
2086
|
+
if (typeof incoming[i] === "function" ||
|
|
2087
|
+
incoming[i] instanceof HTMLElement
|
|
2088
|
+
// ||
|
|
2089
|
+
// incoming[i].tagName
|
|
2090
|
+
) {
|
|
2091
|
+
childrens2rd.push(incoming[i]);
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
// if (
|
|
2095
|
+
// !incoming[i]
|
|
2096
|
+
// ) {
|
|
2097
|
+
// console.log(incoming[i]);
|
|
2098
|
+
// continue;
|
|
2099
|
+
// }
|
|
2100
|
+
//
|
|
2101
|
+
if (
|
|
2102
|
+
// !incoming[i].tagName &&
|
|
2103
|
+
!(incoming[i] instanceof HTMLElement) &&
|
|
2104
|
+
!Array.isArray(incoming[i]) &&
|
|
2105
|
+
typeof incoming[i] === "object" &&
|
|
2106
|
+
!incoming[i].tagName) {
|
|
2107
|
+
if (incoming[i].beforeMount) {
|
|
2108
|
+
beforeMount = incoming[i]["beforeMount"];
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2111
|
+
if (incoming[i].composedPath) {
|
|
2112
|
+
continue;
|
|
2113
|
+
}
|
|
2114
|
+
props = incoming[i];
|
|
2115
|
+
continue;
|
|
2116
|
+
}
|
|
2117
|
+
//
|
|
2118
|
+
if (typeof incoming[i] === "string") {
|
|
2119
|
+
text = incoming[i];
|
|
2120
|
+
continue;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
if (childrens.length) {
|
|
2124
|
+
childrens2rd.push(...childrens);
|
|
2125
|
+
}
|
|
2126
|
+
let element;
|
|
2127
|
+
try {
|
|
2128
|
+
element = document.createElement(initials.tag.trim());
|
|
2129
|
+
}
|
|
2130
|
+
catch (error) {
|
|
2131
|
+
throw new TypeError("cradova err invalid tag given " + initials.tag);
|
|
2132
|
+
}
|
|
2133
|
+
if (!element) {
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
if (initials.className) {
|
|
2137
|
+
element.className = initials.className.trim();
|
|
2138
|
+
}
|
|
2139
|
+
if (initials.ID) {
|
|
2140
|
+
element.id = initials.ID.trim();
|
|
2141
|
+
}
|
|
2142
|
+
if (initials.innerValue) {
|
|
2143
|
+
element.append(initials.innerValue);
|
|
2144
|
+
}
|
|
2145
|
+
for (const prop in properties) {
|
|
2146
|
+
if (prop === "style") {
|
|
2147
|
+
for (const [k, v] of Object.entries(properties[prop])) {
|
|
2148
|
+
element.style[k] = v;
|
|
2149
|
+
}
|
|
2150
|
+
continue;
|
|
2151
|
+
}
|
|
2152
|
+
if (element.style[prop] === "" && prop !== "src") {
|
|
2153
|
+
element.style[prop] = properties[prop];
|
|
2154
|
+
continue;
|
|
2155
|
+
}
|
|
2156
|
+
if (prop === "class" && typeof properties[prop] === "string") {
|
|
2157
|
+
const classes = properties[prop].split(" ");
|
|
2158
|
+
for (let i = 0; i < classes.length; i++) {
|
|
2159
|
+
if (classes[i]) {
|
|
2160
|
+
element.classList.add(classes[i]);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
continue;
|
|
2164
|
+
}
|
|
2165
|
+
if (prop === "text") {
|
|
2166
|
+
element.innerText = properties[prop];
|
|
2167
|
+
continue;
|
|
2168
|
+
}
|
|
2169
|
+
try {
|
|
2170
|
+
element[prop] = properties[prop];
|
|
2171
|
+
}
|
|
2172
|
+
catch (error) {
|
|
2173
|
+
throw new Error("cradova err invalid props " +
|
|
2174
|
+
prop +
|
|
2175
|
+
" for this element type with value " +
|
|
2176
|
+
properties[prop]);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
//
|
|
2180
|
+
// dynamic props
|
|
2181
|
+
// over-rides props that appear in the first level
|
|
2182
|
+
if (props && typeof props === "object" && !Array.isArray(props)) {
|
|
2183
|
+
for (const prop in props) {
|
|
2184
|
+
if (prop === "style") {
|
|
2185
|
+
for (const [k, v] of Object.entries(props[prop])) {
|
|
2186
|
+
element.style[k] = v;
|
|
2187
|
+
}
|
|
2188
|
+
continue;
|
|
2189
|
+
}
|
|
2190
|
+
if (prop === "text" && typeof props[prop] === "string") {
|
|
2191
|
+
element.innerText = props[prop];
|
|
2192
|
+
continue;
|
|
2193
|
+
}
|
|
2194
|
+
if (prop === "class" && typeof props[prop] === "string") {
|
|
2195
|
+
element.classList.add(props[prop]);
|
|
2196
|
+
continue;
|
|
2197
|
+
}
|
|
2198
|
+
if (prop === "beforeMount") {
|
|
2199
|
+
beforeMount = props["beforeMount"];
|
|
2200
|
+
continue;
|
|
2201
|
+
}
|
|
2202
|
+
if (prop === "fullscreen") {
|
|
2203
|
+
if (properties[prop]) {
|
|
2204
|
+
fullScreen(element).set();
|
|
2205
|
+
}
|
|
2206
|
+
else {
|
|
2207
|
+
fullScreen(element).exist();
|
|
2208
|
+
}
|
|
2209
|
+
continue;
|
|
2210
|
+
}
|
|
2211
|
+
try {
|
|
2212
|
+
element[prop] = props[prop];
|
|
2213
|
+
}
|
|
2214
|
+
catch (error) {
|
|
2215
|
+
// console.log(element);
|
|
2216
|
+
// console.log(props);
|
|
2217
|
+
console.error(error);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
if (childrens2rd && childrens2rd[0]) {
|
|
2222
|
+
//
|
|
2223
|
+
for (let i = 0; i < childrens2rd.length; i++) {
|
|
2224
|
+
if (typeof childrens2rd[i] === "function") {
|
|
2225
|
+
const child = childrens2rd[i]();
|
|
2226
|
+
element.append(child);
|
|
2227
|
+
if (child.afterMount) {
|
|
2228
|
+
child.afterMount(child);
|
|
2229
|
+
child.afterMount = undefined;
|
|
2230
|
+
}
|
|
2231
|
+
continue;
|
|
2232
|
+
}
|
|
2233
|
+
if (Array.isArray(childrens2rd[i])) {
|
|
2234
|
+
const arrCX = childrens2rd[i];
|
|
2235
|
+
const arrSET = [];
|
|
2236
|
+
for (let p = 0; p < arrCX.length; p++) {
|
|
2237
|
+
if (!(arrCX[p] instanceof HTMLElement) &&
|
|
2238
|
+
typeof arrCX[p] !== "function" &&
|
|
2239
|
+
!Array.isArray(arrCX[p])) {
|
|
2240
|
+
throw new TypeError("cradova err invalid children list, should be a html element from cradova " +
|
|
2241
|
+
arrCX[p]);
|
|
2242
|
+
}
|
|
2243
|
+
arrSET.push(arrCX[p]);
|
|
2244
|
+
}
|
|
2245
|
+
//
|
|
2246
|
+
childrens2rd = [
|
|
2247
|
+
...childrens2rd.slice(0, i + 1),
|
|
2248
|
+
...arrSET,
|
|
2249
|
+
...childrens2rd.slice(i + 1, childrens2rd.length),
|
|
2250
|
+
];
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2253
|
+
const child = childrens2rd[i];
|
|
2254
|
+
element.append(child);
|
|
2255
|
+
if (child.afterMount) {
|
|
2256
|
+
child.afterMount(child);
|
|
2257
|
+
child.afterMount = undefined;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
if (text) {
|
|
2262
|
+
element.append(text);
|
|
2263
|
+
}
|
|
2264
|
+
// TODO: this will be updated to use data-stateid soon
|
|
2265
|
+
// spped test still going on
|
|
2266
|
+
if (element.stateID) {
|
|
2267
|
+
// adding cradova dynamic signature
|
|
2268
|
+
element.classList.add("cra_child_doc");
|
|
2269
|
+
}
|
|
2270
|
+
if (beforeMount) {
|
|
2271
|
+
beforeMount(element);
|
|
2272
|
+
}
|
|
2273
|
+
return element;
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
if (typeof element_initials[0] !== "string") {
|
|
2277
|
+
return () => "empty cradova call";
|
|
2278
|
+
}
|
|
2279
|
+
const CradovaElemet = identify(element_initials);
|
|
2280
|
+
if (!CradovaElemet) {
|
|
2281
|
+
throw new Error("Cradova err invalid element initials " + element_initials);
|
|
2282
|
+
}
|
|
2283
|
+
return CradovaElemet;
|
|
2284
|
+
};
|
|
2285
|
+
Init(_);
|
|
2286
|
+
/**
|
|
2287
|
+
* Create element and get a callback to update their state
|
|
2288
|
+
* no need to manage stateIDs
|
|
2289
|
+
* ----------------------------------------------------------------
|
|
2290
|
+
*
|
|
2291
|
+
* @param element_initials
|
|
2292
|
+
* @param props
|
|
2293
|
+
* @returns
|
|
2294
|
+
*/
|
|
2295
|
+
function $(element_initials = "div", props = {}) {
|
|
2296
|
+
props.stateID = uuid();
|
|
2297
|
+
const element = _(element_initials, props);
|
|
2298
|
+
return [element, dispatch.bind(null, props.stateID)];
|
|
2299
|
+
}
|
|
2300
|
+
const controls = function () {
|
|
2301
|
+
const svg = `<svg width="20" height="20" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2302
|
+
<path d="M4.49975 5.625C4.3402 5.6242 4.18282 5.58788 4.03904 5.5187C3.89526 5.44951 3.76869 5.34919 3.6685 5.225L1.03725 2.0375C0.8835 1.84561 0.786745 1.61438 0.758014 1.37017C0.729283 1.12596 0.769733 0.878589 0.874753 0.65625C0.959928 0.463017 1.09892 0.298383 1.27514 0.182014C1.45136 0.0656449 1.65734 0.00245816 1.8685 0H7.131C7.34216 0.00245816 7.54815 0.0656449 7.72437 0.182014C7.90058 0.298383 8.03958 0.463017 8.12475 0.65625C8.22977 0.878589 8.27023 1.12596 8.24149 1.37017C8.21276 1.61438 8.11601 1.84561 7.96226 2.0375L5.331 5.225C5.23082 5.34919 5.10424 5.44951 4.96047 5.5187C4.81669 5.58788 4.65931 5.6242 4.49975 5.625Z" fill="#2c3e50"/>
|
|
2303
|
+
</svg>
|
|
2304
|
+
`;
|
|
2305
|
+
const icon = (styles) => _("div", { ...styles, innerHTML: svg });
|
|
2306
|
+
const constr = _("div", {
|
|
2307
|
+
display: "flex",
|
|
2308
|
+
position: "fixed",
|
|
2309
|
+
alignContent: "center",
|
|
2310
|
+
justifyContent: "space-around",
|
|
2311
|
+
flexDirection: "row",
|
|
2312
|
+
width: "80px",
|
|
2313
|
+
top: "4px",
|
|
2314
|
+
right: "4px",
|
|
2315
|
+
backgroundColor: "#fff",
|
|
2316
|
+
transform: "rotate(0deg)",
|
|
2317
|
+
border: "aqua 2px solid",
|
|
2318
|
+
borderRadius: "6px",
|
|
2319
|
+
}, icon({
|
|
2320
|
+
transform: "rotate(90deg)",
|
|
2321
|
+
/* border: "red 2px solid", */ onclick() {
|
|
2322
|
+
window.history.back();
|
|
2323
|
+
},
|
|
2324
|
+
}), icon({
|
|
2325
|
+
transform: "rotate(270deg)",
|
|
2326
|
+
/* border: "red 2px solid", */ onclick() {
|
|
2327
|
+
window.history.forward();
|
|
2328
|
+
},
|
|
2329
|
+
}));
|
|
2330
|
+
const cont = constr();
|
|
2331
|
+
if (cont) {
|
|
2332
|
+
document.body.append(cont);
|
|
2333
|
+
}
|
|
2334
|
+
};
|
|
2335
|
+
function register(modules) {
|
|
2336
|
+
for (let i = 0; i < modules.length; i++) {
|
|
2337
|
+
_[modules[i]] = modules[i];
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
register([
|
|
2341
|
+
fragment,
|
|
2342
|
+
swipe,
|
|
2343
|
+
Store,
|
|
2344
|
+
Router,
|
|
2345
|
+
Screen,
|
|
2346
|
+
JSONDB,
|
|
2347
|
+
fetcher,
|
|
2348
|
+
littleAxios,
|
|
2349
|
+
loadCradovaUICss,
|
|
2350
|
+
assert,
|
|
2351
|
+
animate,
|
|
2352
|
+
PromptBeforeLeave,
|
|
2353
|
+
css,
|
|
2354
|
+
media,
|
|
2355
|
+
ls,
|
|
2356
|
+
list,
|
|
2357
|
+
$,
|
|
2358
|
+
controls,
|
|
2359
|
+
]);
|
|
2360
|
+
for (const key in _) {
|
|
2361
|
+
console.log(key);
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
return _;
|
|
2365
|
+
|
|
2366
|
+
}));
|