j-templates 7.0.89 → 7.0.90

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.
@@ -145,42 +145,36 @@ exports.DOMNodeConfig = {
145
145
  target.replaceChildren(...children);
146
146
  },
147
147
  reconcileChildren(target, children) {
148
- if (!target.firstChild) {
149
- for (let x = 0; x < children.length; x++) {
150
- const child = getHTMLNode(children[x]);
151
- target.appendChild(child);
152
- }
153
- return;
154
- }
155
- if (children.length === 0) {
156
- target.replaceChildren();
157
- return;
148
+ let lastChild = null;
149
+ switch (children.length) {
150
+ case 0:
151
+ target.replaceChildren();
152
+ break;
153
+ case 1:
154
+ lastChild = getHTMLNode(children[0], target.firstChild);
155
+ if (target.firstChild !== lastChild)
156
+ target.insertBefore(lastChild, target.firstChild);
157
+ break;
158
+ default:
159
+ let actualChild = target.firstChild;
160
+ let nextChild = null;
161
+ let x = 0;
162
+ for (; x < children.length; x++) {
163
+ nextChild = getHTMLNode(children[x], actualChild);
164
+ while (actualChild && nextChild !== actualChild) {
165
+ const nextSibling = actualChild.nextSibling;
166
+ target.removeChild(actualChild);
167
+ actualChild = nextSibling;
168
+ }
169
+ if (actualChild)
170
+ actualChild = actualChild.nextSibling;
171
+ else
172
+ target.appendChild(nextChild);
173
+ }
174
+ lastChild = nextChild;
175
+ break;
158
176
  }
159
- let actualNode = target.firstChild;
160
- let x = 0;
161
- let removed = false;
162
- for (; actualNode && x < children.length; x++) {
163
- const child = children[x];
164
- let expectedNode = getHTMLNode(child, actualNode);
165
- if (!removed && actualNode !== expectedNode) {
166
- const remove = actualNode;
167
- actualNode = actualNode.nextSibling;
168
- target.removeChild(remove);
169
- removed = true;
170
- }
171
- if (actualNode !== expectedNode) {
172
- target.insertBefore(expectedNode, actualNode);
173
- }
174
- else {
175
- actualNode = actualNode.nextSibling;
176
- removed = false;
177
- }
178
- }
179
- while (target.lastChild && target.lastChild !== children[x - 1])
177
+ while (target.lastChild !== lastChild)
180
178
  target.removeChild(target.lastChild);
181
- for (; x < children.length; x++) {
182
- const child = getHTMLNode(children[x]);
183
- target.appendChild(child);
184
- }
185
179
  },
186
180
  };
@@ -58,10 +58,12 @@ function UnwrapProxy(value, type = (0, json_2.JsonType)(value)) {
58
58
  const keys = Object.keys(value);
59
59
  for (let x = 0; x < keys.length; x++)
60
60
  value[keys[x]] = UnwrapProxy(value[keys[x]]);
61
+ break;
61
62
  }
62
63
  case "array": {
63
64
  for (let x = 0; x < value.length; x++)
64
65
  value[x] = UnwrapProxy(value[x]);
66
+ break;
65
67
  }
66
68
  }
67
69
  return value;
@@ -17,6 +17,7 @@ interface IDynamicObservableScope<T> {
17
17
  type: "dynamic";
18
18
  /** Whether the scope's getter function is async */
19
19
  async: boolean;
20
+ pending: Promise<T> | null;
20
21
  /** Whether updates are batched (true) or immediate (false) */
21
22
  greedy: boolean;
22
23
  /** Whether the scope needs recomputation */
@@ -145,20 +146,22 @@ export declare namespace ObservableScope {
145
146
  * @param scope The scope to watch for changes.
146
147
  * @param callback Function to invoke when the scope's value changes.
147
148
  */
148
- function Watch<T>(scope: IObservableScope<T>, callback: EmitterCallback<[IObservableScope<T>]>): void;
149
+ function Watch<T>(scope: IObservableScope<T>, callback: (scope: IObservableScope<T>) => void): void;
149
150
  /**
150
151
  * Unsubscribes from changes on a dynamic scope.
151
152
  * @template T The type of value stored in the scope.
152
153
  * @param scope The scope to stop watching.
153
154
  * @param callback The callback function to remove.
154
155
  */
155
- function Unwatch<T>(scope: IObservableScope<T>, callback: EmitterCallback<[IObservableScope<T>]>): void;
156
+ function Unwatch<T>(scope: IObservableScope<T>, callback: (scope: IObservableScope<T>) => void): void;
156
157
  /**
157
158
  * Registers a callback to be invoked when the scope is destroyed.
158
159
  * @param scope The scope to monitor for destruction.
159
160
  * @param callback Function to invoke when the scope is destroyed.
160
161
  */
161
- function OnDestroyed(scope: IObservableScope<unknown>, callback: EmitterCallback): void;
162
+ function OnDestroyed(scope: IObservableScope<unknown>, callback: {
163
+ (): void;
164
+ }): void;
162
165
  /**
163
166
  * Marks a scope as dirty, triggering recomputation on next access or batch.
164
167
  * @param scope The scope to mark for update.
@@ -19,6 +19,7 @@ function CreateDynamicScope(getFunction, greedy, value) {
19
19
  const scope = {
20
20
  type: "dynamic",
21
21
  async,
22
+ pending: null,
22
23
  greedy: greedy || async,
23
24
  dirty: false,
24
25
  destroyed: false,
@@ -80,7 +81,7 @@ function OnSetQueued(scope) {
80
81
  * @returns true if the scope is static or destroyed, false if queued or non-greedy scope emitted.
81
82
  */
82
83
  function OnSet() {
83
- if (this.dirty)
84
+ if (this.dirty || this.destroyed)
84
85
  return;
85
86
  this.dirty = true;
86
87
  if (this.greedy) {
@@ -229,10 +230,11 @@ function ExecuteScope(scope) {
229
230
  for (const key in calcScopes)
230
231
  DestroyScope(calcScopes[key]);
231
232
  if (scope.async) {
232
- const promise = state.value;
233
+ const promise = (scope.pending = state.value);
233
234
  promise.then(function (result) {
234
- if (state.value !== promise)
235
+ if (scope.destroyed || scope.pending !== promise)
235
236
  return;
237
+ scope.pending = null;
236
238
  scope.value = result;
237
239
  emitter_1.Emitter.Emit(scope.emitter, scope);
238
240
  });
@@ -256,10 +258,12 @@ function ExecuteFunction(callback, greedy, allowStatic) {
256
258
  scope.scopes = state.nextCalc;
257
259
  UpdateEmitters(scope, state);
258
260
  if (async) {
259
- const promise = state.value;
261
+ console.log("resolving async scope");
262
+ const promise = (scope.pending = state.value);
260
263
  promise.then(function (result) {
261
- if (state.value !== promise)
264
+ if (scope.destroyed || scope.pending !== promise)
262
265
  return;
266
+ scope.pending = null;
263
267
  scope.value = result;
264
268
  emitter_1.Emitter.Emit(scope.emitter, scope);
265
269
  });
@@ -419,12 +423,13 @@ function DestroyAllScopes(scopes) {
419
423
  function DestroyScope(scope) {
420
424
  if (!scope || scope.type === "static")
421
425
  return;
422
- emitter_1.Emitter.Clear(scope.emitter);
423
426
  for (const key in scope.scopes)
424
427
  DestroyScope(scope.scopes[key]);
428
+ // Emitter.Destroy(scope.emitter);
429
+ emitter_1.Emitter.DestroyCallback(scope.setCallback);
425
430
  if (scope.emitters !== null)
426
431
  for (let x = 0; x < scope.emitters.length; x++)
427
- emitter_1.Emitter.Remove(scope.emitters[x], scope.setCallback);
432
+ emitter_1.Emitter.Compact(scope.emitters[x]);
428
433
  scope.value = undefined;
429
434
  scope.scopes = null;
430
435
  scope.emitters = null;
@@ -2,6 +2,8 @@ export type EmitterCallback<T extends readonly any[] = any[]> = (...args: T) =>
2
2
  export type Emitter = EmitterCallback[];
3
3
  export declare namespace Emitter {
4
4
  function Create(): Emitter;
5
+ function Compact(emitter: Emitter): void;
6
+ function DestroyCallback(callback: EmitterCallback): void;
5
7
  function On(emitter: Emitter, callback: EmitterCallback): void;
6
8
  function Emit(emitter: Emitter, ...args: any[]): void;
7
9
  function Remove(emitter: Emitter, callback: EmitterCallback): void;
package/Utils/emitter.js CHANGED
@@ -3,18 +3,93 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Emitter = void 0;
4
4
  var Emitter;
5
5
  (function (Emitter) {
6
+ const destroyed = new WeakSet();
7
+ // const pendingCompactEmitters = new Set<Emitter>();
8
+ let pendingCompactEmitters = [];
6
9
  function Create() {
7
10
  return [];
8
11
  }
9
12
  Emitter.Create = Create;
13
+ const scheduleCallback = typeof requestIdleCallback !== "undefined"
14
+ ? requestIdleCallback
15
+ : setTimeout;
16
+ function Compact(emitter) {
17
+ if (emitter.length === 0)
18
+ return;
19
+ pendingCompactEmitters.push(emitter);
20
+ ScheduleCompact();
21
+ }
22
+ Emitter.Compact = Compact;
23
+ let compactScheduled = false;
24
+ function ScheduleCompact() {
25
+ if (compactScheduled)
26
+ return;
27
+ compactScheduled = true;
28
+ scheduleCallback(PerformCompact);
29
+ }
30
+ function PerformCompact() {
31
+ compactScheduled = false;
32
+ let compactEmitters = pendingCompactEmitters;
33
+ pendingCompactEmitters = [];
34
+ const processedEmitters = new Set();
35
+ for (let x = 0; x < compactEmitters.length; x++)
36
+ if (!processedEmitters.has(compactEmitters[x])) {
37
+ CompactEmitter(compactEmitters[x]);
38
+ processedEmitters.add(compactEmitters[x]);
39
+ }
40
+ }
41
+ /* export function Compact(emitter: Emitter) {
42
+ if (emitter.length === 0 || destroyed.has(emitter)) {
43
+ return;
44
+ }
45
+
46
+ if (pendingCompactEmitters.size === 0) {
47
+ scheduleCallback(CompactPending);
48
+ }
49
+
50
+ pendingCompactEmitters.add(emitter);
51
+ }
52
+
53
+ function CompactPending() {
54
+ if (pendingCompactEmitters.size === 0) return;
55
+
56
+ const pending = Array.from(pendingCompactEmitters);
57
+ pendingCompactEmitters.clear();
58
+
59
+ for (let x = 0; x < pending.length; x++)
60
+ if (!destroyed.has(pending[x])) CompactEmitter(pending[x]);
61
+ } */
62
+ function CompactEmitter(emitter) {
63
+ let count = 0;
64
+ for (; count < emitter.length &&
65
+ (emitter[count] === null || destroyed.has(emitter[count])); count++) { }
66
+ if (count > 0)
67
+ emitter.splice(0, count);
68
+ let writePos = 0;
69
+ for (let x = 0; x < emitter.length; x++) {
70
+ if (emitter[x] !== null && !destroyed.has(emitter[x]))
71
+ emitter[writePos++] = emitter[x];
72
+ }
73
+ if (writePos < emitter.length)
74
+ emitter.splice(writePos);
75
+ }
76
+ function DestroyCallback(callback) {
77
+ destroyed.add(callback);
78
+ }
79
+ Emitter.DestroyCallback = DestroyCallback;
80
+ /* export function Destroy(emitter: Emitter) {
81
+ pendingCompactEmitters.delete(emitter);
82
+ destroyed.add(emitter);
83
+ } */
10
84
  function On(emitter, callback) {
11
85
  emitter.push(callback);
12
86
  }
13
87
  Emitter.On = On;
14
88
  function Emit(emitter, ...args) {
89
+ // pendingCompactEmitters.delete(emitter);
15
90
  let writePos = 0;
16
91
  for (let x = 0; x < emitter.length; x++) {
17
- if (emitter[x] !== null) {
92
+ if (emitter[x] !== null && !destroyed.has(emitter[x])) {
18
93
  emitter[x](...args);
19
94
  emitter[writePos++] = emitter[x];
20
95
  }
@@ -25,11 +100,16 @@ var Emitter;
25
100
  Emitter.Emit = Emit;
26
101
  function Remove(emitter, callback) {
27
102
  const index = emitter.indexOf(callback);
28
- if (index >= 0)
103
+ if (index >= 0) {
29
104
  emitter[index] = null;
105
+ Compact(emitter);
106
+ }
30
107
  }
31
108
  Emitter.Remove = Remove;
32
109
  function Clear(emitter) {
110
+ if (emitter.length === 0)
111
+ return;
112
+ // pendingCompactEmitters.delete(emitter);
33
113
  emitter.splice(0);
34
114
  }
35
115
  Emitter.Clear = Clear;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "j-templates",
3
- "version": "7.0.89",
3
+ "version": "7.0.90",
4
4
  "description": "j-templates",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/TypesInCode/jTemplates",