structured-fw 0.7.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.
Files changed (129) hide show
  1. package/Config.ts +47 -0
  2. package/LICENSE +21 -0
  3. package/README.md +332 -0
  4. package/app/Types.ts +1 -0
  5. package/app/models/README.md +9 -0
  6. package/app/routes/README.md +19 -0
  7. package/app/views/README.md +1 -0
  8. package/app/views/layout.html +1 -0
  9. package/bin/structured +114 -0
  10. package/build/Config.d.ts +2 -0
  11. package/build/Config.js +31 -0
  12. package/build/app/Types.d.ts +1 -0
  13. package/build/app/Types.js +1 -0
  14. package/build/app/models/Users.d.ts +0 -0
  15. package/build/app/models/Users.js +1 -0
  16. package/build/app/routes/Auth.d.ts +0 -0
  17. package/build/app/routes/Auth.js +1 -0
  18. package/build/app/routes/Test.d.ts +2 -0
  19. package/build/app/routes/Test.js +101 -0
  20. package/build/app/routes/Todo.d.ts +0 -0
  21. package/build/app/routes/Todo.js +1 -0
  22. package/build/app/routes/Upload.d.ts +0 -0
  23. package/build/app/routes/Upload.js +1 -0
  24. package/build/app/routes/Validation.d.ts +2 -0
  25. package/build/app/routes/Validation.js +34 -0
  26. package/build/app/views/components/ClientImport/ClientImport.client.d.ts +2 -0
  27. package/build/app/views/components/ClientImport/ClientImport.client.js +4 -0
  28. package/build/app/views/components/ClientImport/Export.d.ts +1 -0
  29. package/build/app/views/components/ClientImport/Export.js +1 -0
  30. package/build/app/views/components/Conditionals/Conditionals.client.d.ts +2 -0
  31. package/build/app/views/components/Conditionals/Conditionals.client.js +43 -0
  32. package/build/app/views/components/FormTest/FormTestNested/FormTestNested.d.ts +8 -0
  33. package/build/app/views/components/FormTest/FormTestNested/FormTestNested.js +7 -0
  34. package/build/app/views/components/ModelsTest/ModelsTest.client.d.ts +2 -0
  35. package/build/app/views/components/ModelsTest/ModelsTest.client.js +5 -0
  36. package/build/app/views/components/MultipartForm/MultipartForm.client.d.ts +0 -0
  37. package/build/app/views/components/MultipartForm/MultipartForm.client.js +1 -0
  38. package/build/app/views/components/PassObject/PassObject.d.ts +10 -0
  39. package/build/app/views/components/PassObject/PassObject.js +10 -0
  40. package/build/app/views/components/PassObject/ReceiveObj/ReceiveObj.d.ts +6 -0
  41. package/build/app/views/components/PassObject/ReceiveObj/ReceiveObj.js +6 -0
  42. package/build/app/views/components/RedrawAbort/RedrawAbort.client.d.ts +2 -0
  43. package/build/app/views/components/RedrawAbort/RedrawAbort.client.js +6 -0
  44. package/build/app/views/components/RedrawAbort/RedrawAbort.d.ts +8 -0
  45. package/build/app/views/components/RedrawAbort/RedrawAbort.js +8 -0
  46. package/build/app/views/components/ServerSideContext/ServerSideContext.d.ts +7 -0
  47. package/build/app/views/components/ServerSideContext/ServerSideContext.js +10 -0
  48. package/build/assets/ts/Export.d.ts +1 -0
  49. package/build/assets/ts/Export.js +1 -0
  50. package/build/index.d.ts +1 -0
  51. package/build/index.js +3 -0
  52. package/build/system/Helpers.d.ts +3 -0
  53. package/build/system/Helpers.js +72 -0
  54. package/build/system/Symbols.d.ts +3 -0
  55. package/build/system/Symbols.js +3 -0
  56. package/build/system/Types.d.ts +171 -0
  57. package/build/system/Types.js +1 -0
  58. package/build/system/Util.d.ts +20 -0
  59. package/build/system/Util.js +336 -0
  60. package/build/system/client/App.d.ts +7 -0
  61. package/build/system/client/App.js +8 -0
  62. package/build/system/client/Client.d.ts +6 -0
  63. package/build/system/client/Client.js +9 -0
  64. package/build/system/client/ClientComponent.d.ts +68 -0
  65. package/build/system/client/ClientComponent.js +734 -0
  66. package/build/system/client/DataStore.d.ts +22 -0
  67. package/build/system/client/DataStore.js +64 -0
  68. package/build/system/client/DataStoreView.d.ts +19 -0
  69. package/build/system/client/DataStoreView.js +56 -0
  70. package/build/system/client/EventEmitter.d.ts +7 -0
  71. package/build/system/client/EventEmitter.js +31 -0
  72. package/build/system/client/Net.d.ts +13 -0
  73. package/build/system/client/Net.js +39 -0
  74. package/build/system/client/NetRequest.d.ts +13 -0
  75. package/build/system/client/NetRequest.js +45 -0
  76. package/build/system/server/Application.d.ts +31 -0
  77. package/build/system/server/Application.js +171 -0
  78. package/build/system/server/Component.d.ts +27 -0
  79. package/build/system/server/Component.js +249 -0
  80. package/build/system/server/Components.d.ts +12 -0
  81. package/build/system/server/Components.js +77 -0
  82. package/build/system/server/Cookies.d.ts +6 -0
  83. package/build/system/server/Cookies.js +19 -0
  84. package/build/system/server/Document.d.ts +24 -0
  85. package/build/system/server/Document.js +107 -0
  86. package/build/system/server/DocumentHead.d.ts +32 -0
  87. package/build/system/server/DocumentHead.js +118 -0
  88. package/build/system/server/FormValidation.d.ts +16 -0
  89. package/build/system/server/FormValidation.js +197 -0
  90. package/build/system/server/Handlebars.d.ts +11 -0
  91. package/build/system/server/Handlebars.js +34 -0
  92. package/build/system/server/Request.d.ts +21 -0
  93. package/build/system/server/Request.js +356 -0
  94. package/build/system/server/Session.d.ts +23 -0
  95. package/build/system/server/Session.js +114 -0
  96. package/build/system/server/dom/DOMFragment.d.ts +4 -0
  97. package/build/system/server/dom/DOMFragment.js +6 -0
  98. package/build/system/server/dom/DOMNode.d.ts +31 -0
  99. package/build/system/server/dom/DOMNode.js +110 -0
  100. package/build/system/server/dom/HTMLParser.d.ts +21 -0
  101. package/build/system/server/dom/HTMLParser.js +204 -0
  102. package/index.ts +4 -0
  103. package/package.json +31 -0
  104. package/system/Helpers.ts +97 -0
  105. package/system/Symbols.ts +6 -0
  106. package/system/Types.ts +234 -0
  107. package/system/Util.ts +488 -0
  108. package/system/client/App.ts +11 -0
  109. package/system/client/Client.ts +9 -0
  110. package/system/client/ClientComponent.ts +1117 -0
  111. package/system/client/DataStore.ts +101 -0
  112. package/system/client/DataStoreView.ts +82 -0
  113. package/system/client/EventEmitter.ts +38 -0
  114. package/system/client/Net.ts +58 -0
  115. package/system/client/NetRequest.ts +64 -0
  116. package/system/server/Application.ts +230 -0
  117. package/system/server/Component.ts +404 -0
  118. package/system/server/Components.ts +111 -0
  119. package/system/server/Cookies.ts +29 -0
  120. package/system/server/Document.ts +163 -0
  121. package/system/server/DocumentHead.ts +150 -0
  122. package/system/server/FormValidation.ts +231 -0
  123. package/system/server/Handlebars.ts +51 -0
  124. package/system/server/Request.ts +497 -0
  125. package/system/server/Session.ts +151 -0
  126. package/system/server/dom/DOMFragment.ts +7 -0
  127. package/system/server/dom/DOMNode.ts +140 -0
  128. package/system/server/dom/HTMLParser.ts +238 -0
  129. package/tsconfig.json +35 -0
@@ -0,0 +1,734 @@
1
+ import { attributeValueFromString, attributeValueToString, mergeDeep, objectEach, queryStringDecodedSetValue, toCamelCase } from '../Util.js';
2
+ import { DataStoreView } from './DataStoreView.js';
3
+ import { Net } from './Net.js';
4
+ import { NetRequest } from './NetRequest.js';
5
+ import { EventEmitter } from './EventEmitter.js';
6
+ export class ClientComponent extends EventEmitter {
7
+ constructor(parent, name, domNode, store, runInitializer = true) {
8
+ super();
9
+ this.children = [];
10
+ this.net = new Net();
11
+ this.initializerExecuted = false;
12
+ this.destroyed = false;
13
+ this.redrawRequest = null;
14
+ this.bound = [];
15
+ this.conditionals = [];
16
+ this.conditionalCallbacks = {};
17
+ this.conditionalClassNames = [];
18
+ this.refs = {};
19
+ this.refsArray = {};
20
+ this.loaded = false;
21
+ this.initializer = null;
22
+ this.data = {};
23
+ this.name = name;
24
+ this.domNode = domNode;
25
+ if (parent === null) {
26
+ this.isRoot = true;
27
+ this.root = this;
28
+ this.parent = this;
29
+ }
30
+ else {
31
+ this.isRoot = false;
32
+ this.root = parent.root;
33
+ this.parent = parent;
34
+ }
35
+ this.storeGlobal = store;
36
+ this.store = new DataStoreView(this.storeGlobal, this);
37
+ if (this.isRoot) {
38
+ this.init(runInitializer);
39
+ }
40
+ }
41
+ async init(runInitializer) {
42
+ const initializerExists = window.initializers !== undefined && this.name in window.initializers;
43
+ this.initRefs();
44
+ this.initData();
45
+ this.initModels();
46
+ this.initConditionals();
47
+ await this.initChildren();
48
+ this.promoteRefs();
49
+ this.store.onChange('*', () => {
50
+ this.updateConditionals(true);
51
+ });
52
+ if (runInitializer && initializerExists) {
53
+ await this.runInitializer();
54
+ }
55
+ if (this.conditionals.length > 0) {
56
+ if (!initializerExists) {
57
+ this.store.import(undefined, false, false);
58
+ }
59
+ this.updateConditionals(false);
60
+ }
61
+ if (this.data.deferred === true) {
62
+ this.setData('deferred', false, false);
63
+ this.redraw();
64
+ }
65
+ this.loaded = true;
66
+ this.emit('ready');
67
+ }
68
+ async runInitializer(isRedraw = false) {
69
+ const initializer = window.initializers[this.name];
70
+ if (!initializer) {
71
+ return;
72
+ }
73
+ if (!this.initializerExecuted && !this.destroyed) {
74
+ let initializerFunction = null;
75
+ if (typeof initializer === 'string') {
76
+ const AsyncFunction = async function () { }.constructor;
77
+ initializerFunction = new AsyncFunction('const init = ' + initializer + '; await init.apply(this, [...arguments]);');
78
+ }
79
+ else {
80
+ initializerFunction = initializer;
81
+ }
82
+ if (initializerFunction) {
83
+ this.initializer = initializerFunction;
84
+ await this.initializer.apply(this, [{
85
+ net: this.net,
86
+ isRedraw
87
+ }]);
88
+ }
89
+ }
90
+ this.initializerExecuted = true;
91
+ }
92
+ initData() {
93
+ for (let i = 0; i < this.domNode.attributes.length; i++) {
94
+ if (/^((number|string|boolean|object|any):)?data-[^\s]+/.test(this.domNode.attributes[i].name)) {
95
+ const value = this.domNode.attributes[i].value;
96
+ const attrData = attributeValueFromString(value);
97
+ if (typeof attrData === 'object') {
98
+ this.setData(attrData.key, attrData.value, false);
99
+ }
100
+ else {
101
+ const key = toCamelCase(this.domNode.attributes[i].name.substring(5));
102
+ this.setData(key, attrData, false);
103
+ }
104
+ }
105
+ }
106
+ }
107
+ pathData() {
108
+ let current = this;
109
+ const data = [];
110
+ do {
111
+ data.push(current.data);
112
+ if (current.isRoot || !current.data.use) {
113
+ break;
114
+ }
115
+ current = current.parent;
116
+ } while (true);
117
+ return data.reverse();
118
+ }
119
+ setData(key, value, updateStore = true) {
120
+ const dataKey = `data-${key}`;
121
+ this.domNode.setAttribute(dataKey, attributeValueToString(key, value));
122
+ const keyCamelCase = toCamelCase(key);
123
+ this.data[keyCamelCase] = value;
124
+ if (updateStore) {
125
+ this.store.set(keyCamelCase, value);
126
+ }
127
+ return this;
128
+ }
129
+ async initChildren(scope, callback) {
130
+ scope = scope || this.domNode;
131
+ const childInitPromises = [];
132
+ for (let i = 0; i < scope.childNodes.length; i++) {
133
+ const childNode = scope.childNodes[i];
134
+ if (childNode.nodeType == 1) {
135
+ if (childNode.hasAttribute(`data-${window.structuredClientConfig.componentNameAttribute}`)) {
136
+ const component = new ClientComponent(this, childNode.getAttribute(`data-${window.structuredClientConfig.componentNameAttribute}`) || '', childNode, this.storeGlobal);
137
+ this.children.push(component);
138
+ if (typeof callback === 'function') {
139
+ callback(component);
140
+ }
141
+ childInitPromises.push(component.init(true));
142
+ }
143
+ else {
144
+ childInitPromises.push(this.initChildren(childNode, callback));
145
+ }
146
+ }
147
+ }
148
+ await Promise.all(childInitPromises);
149
+ }
150
+ async redraw(data) {
151
+ if (window.structuredClientConfig.componentRender === false) {
152
+ console.error(`Can't redraw component, component rendering URL disabled`);
153
+ return;
154
+ }
155
+ if (this.destroyed) {
156
+ return;
157
+ }
158
+ if (data) {
159
+ objectEach(data, (key, val) => {
160
+ this.setData(key, val, false);
161
+ });
162
+ }
163
+ if (this.redrawRequest !== null) {
164
+ this.redrawRequest.abort();
165
+ this.redrawRequest = null;
166
+ }
167
+ const redrawRequest = new NetRequest('POST', window.structuredClientConfig.componentRender, {
168
+ 'content-type': 'application/json'
169
+ });
170
+ this.redrawRequest = redrawRequest.xhr;
171
+ const componentDataJSON = await redrawRequest.send(JSON.stringify({
172
+ component: this.name,
173
+ attributes: this.data,
174
+ unwrap: true
175
+ }));
176
+ this.redrawRequest = null;
177
+ if (componentDataJSON.length === 0) {
178
+ return;
179
+ }
180
+ this.loaded = false;
181
+ if (typeof this.onRedraw === 'function') {
182
+ this.onRedraw.apply(this);
183
+ }
184
+ this.unbindAll();
185
+ const childStoreChangeCallbacks = {};
186
+ Array.from(this.children).forEach((child) => {
187
+ childStoreChangeCallbacks[child.getData('componentId')] = child.store.onChangeCallbacks();
188
+ child.remove();
189
+ });
190
+ const componentData = JSON.parse(componentDataJSON);
191
+ this.domNode.innerHTML = componentData.html;
192
+ objectEach(componentData.data, (key, val) => {
193
+ this.setData(key, val, false);
194
+ });
195
+ for (const key in componentData.initializers) {
196
+ if (!window.initializers[key]) {
197
+ window.initializers[key] = componentData.initializers[key];
198
+ }
199
+ }
200
+ await this.initChildren(this.domNode, (childNew) => {
201
+ const childNewId = childNew.getData('componentId');
202
+ const existingChild = childNewId in childStoreChangeCallbacks;
203
+ if (existingChild) {
204
+ objectEach(childStoreChangeCallbacks[childNewId], (key, callbacks) => {
205
+ callbacks.forEach((callback) => {
206
+ childNew.store.onChange(key, callback);
207
+ });
208
+ });
209
+ }
210
+ });
211
+ this.refs = {};
212
+ this.refsArray = {};
213
+ this.conditionals = [];
214
+ this.initRefs();
215
+ this.initModels();
216
+ this.initConditionals();
217
+ this.promoteRefs();
218
+ if (this.initializer) {
219
+ this.initializerExecuted = false;
220
+ this.runInitializer(true);
221
+ }
222
+ this.updateConditionals(false);
223
+ this.loaded = true;
224
+ }
225
+ initConditionals(node) {
226
+ const isSelf = node === undefined;
227
+ if (node === undefined) {
228
+ node = this.domNode;
229
+ }
230
+ for (const attribute of node.attributes) {
231
+ if (attribute.name === 'data-if') {
232
+ this.conditionals.push(node);
233
+ }
234
+ if (attribute.name.startsWith('data-classname')) {
235
+ const className = attribute.name.substring(15);
236
+ this.conditionalClassNames.push({
237
+ element: node,
238
+ className
239
+ });
240
+ }
241
+ }
242
+ node.childNodes.forEach((child) => {
243
+ if (child.nodeType === 1 && (isSelf || !node?.hasAttribute(`data-${window.structuredClientConfig.componentNameAttribute}`))) {
244
+ this.initConditionals(child);
245
+ }
246
+ });
247
+ }
248
+ initRefs(node) {
249
+ const isSelf = node === undefined;
250
+ if (node === undefined) {
251
+ node = this.domNode;
252
+ }
253
+ if (node.hasAttribute('ref')) {
254
+ this.refs[node.getAttribute('ref') || 'undefined'] = node;
255
+ }
256
+ if (node.hasAttribute('array:ref')) {
257
+ const key = node.getAttribute('array:ref') || 'undefined';
258
+ if (!(key in this.refsArray)) {
259
+ this.refsArray[key] = [];
260
+ }
261
+ this.refsArray[key].push(node);
262
+ }
263
+ node.childNodes.forEach((child) => {
264
+ if (child.nodeType === 1 && (isSelf || !node?.hasAttribute(`data-${window.structuredClientConfig.componentNameAttribute}`))) {
265
+ this.initRefs(child);
266
+ }
267
+ });
268
+ }
269
+ initModels(node, modelNodes = []) {
270
+ const isSelf = node === undefined;
271
+ if (node === undefined) {
272
+ node = this.domNode;
273
+ }
274
+ const modelData = (node) => {
275
+ const field = node.getAttribute('data-model');
276
+ if (field) {
277
+ const isCheckbox = node.tagName === 'INPUT' && node.type === 'checkbox';
278
+ const valueRaw = isCheckbox ? node.checked : node.value;
279
+ const value = queryStringDecodedSetValue(field, valueRaw);
280
+ return value;
281
+ }
282
+ return {};
283
+ };
284
+ const update = (data) => {
285
+ objectEach(data, (key, val) => {
286
+ this.store.set(key, val);
287
+ });
288
+ };
289
+ if (node.hasAttribute('data-model') && (node.tagName === 'INPUT' || node.tagName === 'SELECT' || node.tagName === 'TEXTAREA')) {
290
+ modelNodes.push(node);
291
+ }
292
+ else {
293
+ node.childNodes.forEach((child) => {
294
+ if (child.nodeType === 1 && (isSelf || !node?.hasAttribute(`data-${window.structuredClientConfig.componentNameAttribute}`))) {
295
+ this.initModels(child, modelNodes);
296
+ }
297
+ });
298
+ }
299
+ if (isSelf) {
300
+ let data = {};
301
+ modelNodes.forEach((modelNode) => {
302
+ modelNode.addEventListener('input', () => {
303
+ let data = modelData(modelNode);
304
+ const key = Object.keys(data)[0];
305
+ if (typeof data[key] === 'object') {
306
+ const dataExisting = this.store.get(key);
307
+ if (dataExisting !== undefined) {
308
+ data = mergeDeep({}, { [key]: dataExisting }, data);
309
+ }
310
+ else {
311
+ data = mergeDeep({}, data);
312
+ }
313
+ }
314
+ update(data);
315
+ });
316
+ const field = modelNode.getAttribute('data-model');
317
+ if (field) {
318
+ const isCheckbox = modelNode.tagName === 'INPUT' && modelNode.type === 'checkbox';
319
+ const valueRaw = isCheckbox ? modelNode.checked : modelNode.value;
320
+ const value = queryStringDecodedSetValue(field, valueRaw);
321
+ data = mergeDeep(data, value);
322
+ }
323
+ });
324
+ update(data);
325
+ }
326
+ }
327
+ promoteRefs() {
328
+ this.children.forEach((child) => {
329
+ const ref = child.domNode.getAttribute('ref');
330
+ if (ref) {
331
+ this.refs[ref] = child;
332
+ }
333
+ const refArray = child.domNode.getAttribute('array:ref');
334
+ if (refArray !== null && refArray in this.refsArray) {
335
+ const nodeIndex = this.refsArray[refArray].indexOf(child.domNode);
336
+ if (nodeIndex > -1) {
337
+ this.refsArray[refArray].splice(nodeIndex, 1, child);
338
+ }
339
+ }
340
+ });
341
+ }
342
+ ref(refName) {
343
+ return this.refs[refName];
344
+ }
345
+ refArray(refName) {
346
+ return (this.refsArray[refName] || []);
347
+ }
348
+ execCondition(conditionRaw) {
349
+ const condition = conditionRaw.trim();
350
+ const isMethod = condition.endsWith(')');
351
+ if (isMethod) {
352
+ const parts = /^(!?)\s*([a-zA-Z]+[a-zA-Z0-9_]*)\(([^)]*)\)$/.exec(condition);
353
+ if (parts === null) {
354
+ console.error(`Could not parse condition ${condition}`);
355
+ return false;
356
+ }
357
+ const negated = parts[1] === '!';
358
+ const functionName = parts[2];
359
+ const args = parts[3].trim();
360
+ if (typeof this.conditionalCallbacks[functionName] !== 'function') {
361
+ console.warn(`No registered conditional callback '${functionName}'`);
362
+ return false;
363
+ }
364
+ const isTrue = this.conditionalCallbacks[functionName](args === '' ? undefined : eval(`(${args})`));
365
+ if (negated) {
366
+ return !isTrue;
367
+ }
368
+ return isTrue;
369
+ }
370
+ else {
371
+ const parts = /^(!)?\s*([a-zA-Z]+[a-zA-Z0-9_]*)\s*((?:==)|(?:===)|(?:!=)|(?:!==)|<|>|(?:<=)|(?:>=))?\s*([^=].+)?$/.exec(condition);
372
+ if (parts === null) {
373
+ console.error(`Could not parse condition ${condition}`);
374
+ return false;
375
+ }
376
+ const property = parts[2];
377
+ const value = this.store.get(property);
378
+ const isComparison = parts[3] !== undefined;
379
+ if (isComparison) {
380
+ let rightHandSide = null;
381
+ try {
382
+ rightHandSide = eval(`${parts[4]}`);
383
+ }
384
+ catch (e) {
385
+ rightHandSide = this.store.get(parts[4]);
386
+ }
387
+ const comparisonSymbol = parts[3];
388
+ if (comparisonSymbol === '==') {
389
+ return value == rightHandSide;
390
+ }
391
+ else if (comparisonSymbol === '===') {
392
+ return value === rightHandSide;
393
+ }
394
+ else if (comparisonSymbol === '!=') {
395
+ return value != rightHandSide;
396
+ }
397
+ else if (comparisonSymbol === '!==') {
398
+ return value !== rightHandSide;
399
+ }
400
+ else {
401
+ if (typeof value !== 'number') {
402
+ return false;
403
+ }
404
+ if (comparisonSymbol === '>') {
405
+ return value > rightHandSide;
406
+ }
407
+ else if (comparisonSymbol === '>=') {
408
+ return value >= rightHandSide;
409
+ }
410
+ else if (comparisonSymbol === '<') {
411
+ return value < rightHandSide;
412
+ }
413
+ else if (comparisonSymbol === '<=') {
414
+ return value <= rightHandSide;
415
+ }
416
+ }
417
+ return false;
418
+ }
419
+ else {
420
+ const negated = parts[1] === '!';
421
+ const isTrue = this.store.get(property);
422
+ if (negated) {
423
+ return !isTrue;
424
+ }
425
+ return !!isTrue;
426
+ }
427
+ }
428
+ }
429
+ conditionalCallback(name, callback, updateConditionals = true) {
430
+ this.conditionalCallbacks[name] = callback;
431
+ if (updateConditionals) {
432
+ this.updateConditionals(false);
433
+ }
434
+ }
435
+ updateConditionals(enableTransition) {
436
+ if (this.destroyed) {
437
+ return;
438
+ }
439
+ this.conditionals.forEach((node) => {
440
+ const condition = node.getAttribute('data-if');
441
+ if (typeof condition === 'string') {
442
+ const show = this.execCondition(condition);
443
+ if (show === true) {
444
+ this.show(node, enableTransition);
445
+ }
446
+ else {
447
+ this.hide(node, enableTransition);
448
+ }
449
+ }
450
+ });
451
+ this.conditionalClassNames.forEach((conditional) => {
452
+ const condition = conditional.element.getAttribute(`data-classname-${conditional.className}`);
453
+ if (typeof condition === 'string') {
454
+ const enableClassName = this.execCondition(condition);
455
+ if (enableClassName === true) {
456
+ conditional.element.classList.add(conditional.className);
457
+ }
458
+ else {
459
+ conditional.element.classList.remove(conditional.className);
460
+ }
461
+ }
462
+ });
463
+ }
464
+ async remove() {
465
+ if (!this.isRoot) {
466
+ const children = Array.from(this.children);
467
+ for (let i = 0; i < children.length; i++) {
468
+ await children[i].remove();
469
+ }
470
+ if (this.parent) {
471
+ this.parent.children.splice(this.parent.children.indexOf(this), 1);
472
+ }
473
+ this.domNode.parentElement?.removeChild(this.domNode);
474
+ await this.destroy();
475
+ }
476
+ }
477
+ parentFind(parentName) {
478
+ let parent = this.parent;
479
+ while (true) {
480
+ if (parent.name === parentName) {
481
+ return parent;
482
+ }
483
+ if (parent.isRoot) {
484
+ break;
485
+ }
486
+ parent = parent.parent;
487
+ }
488
+ return null;
489
+ }
490
+ find(componentName, recursive = true) {
491
+ for (let i = 0; i < this.children.length; i++) {
492
+ const child = this.children[i];
493
+ if (child.name == componentName) {
494
+ return child;
495
+ }
496
+ else {
497
+ if (recursive) {
498
+ const inChild = child.find(componentName, recursive);
499
+ if (inChild) {
500
+ return inChild;
501
+ }
502
+ }
503
+ }
504
+ }
505
+ return null;
506
+ }
507
+ query(componentName, recursive = true, results = []) {
508
+ for (let i = 0; i < this.children.length; i++) {
509
+ const child = this.children[i];
510
+ if (child.name == componentName) {
511
+ results.push(child);
512
+ }
513
+ else {
514
+ if (recursive) {
515
+ child.query(componentName, recursive, results);
516
+ }
517
+ }
518
+ }
519
+ return results;
520
+ }
521
+ async add(appendTo, componentName, data) {
522
+ if (window.structuredClientConfig.componentRender === false) {
523
+ console.error(`Can't add component, component rendering URL disabled`);
524
+ return null;
525
+ }
526
+ const container = typeof appendTo === 'string' ? this.domNode.querySelector(appendTo) : appendTo;
527
+ if (!(container instanceof HTMLElement)) {
528
+ throw new Error(`${this.name}.add() - appendTo selector not found within this component`);
529
+ }
530
+ const req = new NetRequest('POST', window.structuredClientConfig.componentRender, {
531
+ 'content-type': 'application/json'
532
+ });
533
+ const componentDataJSON = await req.send(JSON.stringify({
534
+ component: componentName,
535
+ attributes: data,
536
+ unwrap: false
537
+ }));
538
+ const res = JSON.parse(componentDataJSON);
539
+ for (let key in res.initializers) {
540
+ if (!window.initializers[key]) {
541
+ window.initializers[key] = res.initializers[key];
542
+ }
543
+ }
544
+ const tmpContainer = document.createElement('div');
545
+ tmpContainer.innerHTML = res.html;
546
+ const componentNode = tmpContainer.firstChild;
547
+ const component = new ClientComponent(this, componentName, componentNode, this.storeGlobal);
548
+ this.children.push(component);
549
+ await component.init(true);
550
+ container.appendChild(componentNode);
551
+ return component;
552
+ }
553
+ getData(key) {
554
+ if (!key) {
555
+ return this.data;
556
+ }
557
+ return this.data[key];
558
+ }
559
+ show(domNode, enableTransition = true) {
560
+ if (!enableTransition) {
561
+ domNode.style.display = '';
562
+ return;
563
+ }
564
+ if (domNode.style.display !== 'none') {
565
+ return;
566
+ }
567
+ const transitions = this.transitionAttributes(domNode).show;
568
+ const transitionsActive = Object.keys(transitions).filter((key) => {
569
+ return transitions[key] !== false;
570
+ }).reduce((prev, curr) => {
571
+ const key = curr;
572
+ prev[key] = transitions[key];
573
+ return prev;
574
+ }, {});
575
+ if (Object.keys(transitionsActive).length === 0) {
576
+ domNode.style.display = '';
577
+ return;
578
+ }
579
+ domNode.style.display = '';
580
+ const onTransitionEnd = (e) => {
581
+ domNode.style.opacity = '1';
582
+ domNode.style.transition = '';
583
+ domNode.style.transformOrigin = 'unset';
584
+ domNode.removeEventListener('transitionend', onTransitionEnd);
585
+ domNode.removeEventListener('transitioncancel', onTransitionEnd);
586
+ };
587
+ domNode.addEventListener('transitionend', onTransitionEnd);
588
+ domNode.addEventListener('transitioncancel', onTransitionEnd);
589
+ if (transitionsActive.slide) {
590
+ const transformOrigin = domNode.getAttribute('data-transform-origin-show') || '50% 0';
591
+ domNode.style.transformOrigin = transformOrigin;
592
+ const axis = this.transitionAxis(domNode, 'show');
593
+ domNode.style.transform = `scale${axis}(0.01)`;
594
+ domNode.style.transition = `transform ${transitionsActive.slide / 1000}s`;
595
+ setTimeout(() => {
596
+ domNode.style.transform = `scale${axis}(1)`;
597
+ }, 100);
598
+ }
599
+ if (transitionsActive.fade) {
600
+ domNode.style.opacity = '0';
601
+ domNode.style.transition = `opacity ${transitionsActive.fade / 1000}s`;
602
+ setTimeout(() => {
603
+ domNode.style.opacity = '1';
604
+ }, 100);
605
+ }
606
+ }
607
+ hide(domNode, enableTransition = true) {
608
+ if (!enableTransition) {
609
+ domNode.style.display = 'none';
610
+ return;
611
+ }
612
+ if (domNode.style.display === 'none') {
613
+ return;
614
+ }
615
+ const transitions = this.transitionAttributes(domNode).hide;
616
+ const transitionsActive = Object.keys(transitions).filter((key) => {
617
+ return transitions[key] !== false;
618
+ }).reduce((prev, curr) => {
619
+ const key = curr;
620
+ prev[key] = transitions[key];
621
+ return prev;
622
+ }, {});
623
+ if (Object.keys(transitionsActive).length === 0) {
624
+ domNode.style.display = 'none';
625
+ }
626
+ else {
627
+ const onTransitionEnd = (e) => {
628
+ domNode.style.display = 'none';
629
+ domNode.style.opacity = '1';
630
+ domNode.style.transition = '';
631
+ domNode.style.transformOrigin = 'unset';
632
+ domNode.removeEventListener('transitionend', onTransitionEnd);
633
+ domNode.removeEventListener('transitioncancel', onTransitionEnd);
634
+ };
635
+ domNode.addEventListener('transitionend', onTransitionEnd);
636
+ domNode.addEventListener('transitioncancel', onTransitionEnd);
637
+ if (transitionsActive.slide) {
638
+ const transformOrigin = domNode.getAttribute('data-transform-origin-hide') || '50% 100%';
639
+ domNode.style.transformOrigin = transformOrigin;
640
+ domNode.style.transition = `transform ${transitionsActive.slide / 1000}s ease`;
641
+ setTimeout(() => {
642
+ const axis = this.transitionAxis(domNode, 'hide');
643
+ domNode.style.transform = `scale${axis}(0.01)`;
644
+ }, 100);
645
+ }
646
+ if (transitionsActive.fade) {
647
+ domNode.style.opacity = '1';
648
+ domNode.style.transition = `opacity ${transitionsActive.fade / 1000}s`;
649
+ setTimeout(() => {
650
+ domNode.style.opacity = '0';
651
+ }, 100);
652
+ }
653
+ }
654
+ }
655
+ transitionAttributes(domNode) {
656
+ const transitions = {
657
+ show: {
658
+ slide: false,
659
+ fade: false
660
+ },
661
+ hide: {
662
+ slide: false,
663
+ fade: false
664
+ }
665
+ };
666
+ objectEach(transitions, (transitionEvent, transition) => {
667
+ objectEach(transition, (transitionType) => {
668
+ const attributeName = `data-transition-${transitionEvent}-${transitionType}`;
669
+ if (domNode.hasAttribute(attributeName)) {
670
+ const valueRaw = domNode.getAttribute(attributeName);
671
+ let value = false;
672
+ if (typeof valueRaw === 'string' && /^\d+$/.test(valueRaw)) {
673
+ value = parseInt(valueRaw);
674
+ }
675
+ transition[transitionType] = value;
676
+ }
677
+ });
678
+ });
679
+ return transitions;
680
+ }
681
+ transitionAxis(domNode, showHide) {
682
+ const attributeName = `data-transition-axis-${showHide}`;
683
+ if (!domNode.hasAttribute(attributeName)) {
684
+ return '';
685
+ }
686
+ let val = domNode.getAttribute(attributeName);
687
+ if (typeof val === 'string') {
688
+ val = val.trim().toUpperCase();
689
+ if (val.length > 0) {
690
+ val = val.substring(0, 1);
691
+ }
692
+ if (val != 'X' && val != 'Y') {
693
+ return '';
694
+ }
695
+ return val;
696
+ }
697
+ return '';
698
+ }
699
+ async destroy() {
700
+ if (this.redrawRequest) {
701
+ this.redrawRequest.abort();
702
+ this.redrawRequest = null;
703
+ }
704
+ if (typeof this.onDestroy === 'function') {
705
+ await this.onDestroy.apply(this);
706
+ }
707
+ this.store.destroy();
708
+ this.unbindAll();
709
+ this.conditionals = [];
710
+ this.conditionalClassNames = [];
711
+ this.conditionalCallbacks = {};
712
+ this.refs = {};
713
+ this.refsArray = {};
714
+ this.initializer = null;
715
+ this.data = {};
716
+ this.destroyed = true;
717
+ }
718
+ bind(element, event, callback) {
719
+ if (element instanceof HTMLElement) {
720
+ this.bound.push({
721
+ element,
722
+ event,
723
+ callback
724
+ });
725
+ element.addEventListener(event, callback);
726
+ }
727
+ }
728
+ unbindAll() {
729
+ this.bound.forEach((bound) => {
730
+ bound.element.removeEventListener(bound.event, bound.callback);
731
+ });
732
+ this.bound = [];
733
+ }
734
+ }