jaxs 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.env +1 -0
  2. package/.parcel-cache/6f14daf302269614-BundleGraph-0 +0 -0
  3. package/.parcel-cache/7d3d872b02d671a6-AssetGraph-0 +0 -0
  4. package/.parcel-cache/8e029bb14f8992df-RequestGraph-0 +0 -0
  5. package/.parcel-cache/a5394978e4ece10b-AssetGraph-0 +0 -0
  6. package/.parcel-cache/b5686051ae060930.txt +2 -0
  7. package/.parcel-cache/data.mdb +0 -0
  8. package/.parcel-cache/lock.mdb +0 -0
  9. package/README.md +15 -0
  10. package/bun.lockb +0 -0
  11. package/bundle.ts +5 -0
  12. package/bunfig.toml +0 -0
  13. package/cypress/e2e/add-remove-nested-children.cy.js +39 -0
  14. package/cypress/e2e/add-remove-root-children.cy.js +37 -0
  15. package/cypress/jaxs-apps/add-remove-nested-children.html +12 -0
  16. package/cypress/jaxs-apps/add-remove-nested-children.jsx +84 -0
  17. package/cypress/jaxs-apps/add-remove-root-children.html +15 -0
  18. package/cypress/jaxs-apps/add-remove-root-children.jsx +53 -0
  19. package/cypress/jaxs-apps/dist/add-remove-nested-children.afcab974.js +717 -0
  20. package/cypress/jaxs-apps/dist/add-remove-nested-children.afcab974.js.map +1 -0
  21. package/cypress/jaxs-apps/dist/add-remove-nested-children.html +12 -0
  22. package/cypress/jaxs-apps/dist/add-remove-root-children.3bb9b3f5.js +1682 -0
  23. package/cypress/jaxs-apps/dist/add-remove-root-children.3bb9b3f5.js.map +1 -0
  24. package/cypress/jaxs-apps/dist/add-remove-root-children.fbb4ec9b.js +706 -0
  25. package/cypress/jaxs-apps/dist/add-remove-root-children.fbb4ec9b.js.map +1 -0
  26. package/cypress/jaxs-apps/dist/add-remove-root-children.html +15 -0
  27. package/cypress/support/commands.js +25 -0
  28. package/cypress/support/e2e.js +20 -0
  29. package/cypress.config.js +10 -0
  30. package/dist/jaxs.js +1154 -0
  31. package/package.json +38 -0
  32. package/src/app.ts +64 -0
  33. package/src/debugging.js +5 -0
  34. package/src/jaxs.ts +8 -0
  35. package/src/jsx.js +27 -0
  36. package/src/messageBus.ts +70 -0
  37. package/src/navigation/findHref.js +10 -0
  38. package/src/navigation/routeState.js +15 -0
  39. package/src/navigation/setupHistory.js +38 -0
  40. package/src/navigation/setupNavigation.js +25 -0
  41. package/src/navigation.ts +2 -0
  42. package/src/rendering/change/compile.ts +1 -0
  43. package/src/rendering/change/instructions/attributes.ts +78 -0
  44. package/src/rendering/change/instructions/children.ts +128 -0
  45. package/src/rendering/change/instructions/element.ts +42 -0
  46. package/src/rendering/change/instructions/events.ts +51 -0
  47. package/src/rendering/change/instructions/generate.ts +122 -0
  48. package/src/rendering/change/instructions/idMap.js +55 -0
  49. package/src/rendering/change/instructions/node.ts +38 -0
  50. package/src/rendering/change/instructions/text.ts +10 -0
  51. package/src/rendering/change.ts +131 -0
  52. package/src/rendering/dom/attributesAndEvents.ts +33 -0
  53. package/src/rendering/dom/create.ts +68 -0
  54. package/src/rendering/templates/bound.js +55 -0
  55. package/src/rendering/templates/children.ts +91 -0
  56. package/src/rendering/templates/root.ts +55 -0
  57. package/src/rendering/templates/tag.ts +70 -0
  58. package/src/rendering/templates/text.ts +17 -0
  59. package/src/state/equality.js +36 -0
  60. package/src/state/stores.js +63 -0
  61. package/src/state/testingTypes.js +6 -0
  62. package/src/state.js +89 -0
  63. package/src/types.ts +149 -0
  64. package/src/views/conditionals.jsx +18 -0
  65. package/src/views/link.jsx +5 -0
  66. package/src/views.js +7 -0
  67. package/tsconfig.json +26 -0
package/dist/jaxs.js ADDED
@@ -0,0 +1,1154 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, {
5
+ get: all[name],
6
+ enumerable: true,
7
+ configurable: true,
8
+ set: (newValue) => all[name] = () => newValue
9
+ });
10
+ };
11
+
12
+ // src/rendering/dom/create.ts
13
+ var setAttributesOnElement = (element, attributes) => {
14
+ for (const key in attributes) {
15
+ if (key === "__self")
16
+ continue;
17
+ if (key === "value") {
18
+ element.value = attributes[key];
19
+ } else {
20
+ element.setAttribute(key, attributes[key]);
21
+ }
22
+ }
23
+ };
24
+ var setEventsOnElement = (element, events, publish) => {
25
+ const eventMaps = {};
26
+ for (const domEvent in events) {
27
+ const eventName = events[domEvent];
28
+ const listener = (event) => publish(eventName, event);
29
+ element.addEventListener(domEvent, listener);
30
+ eventMaps[domEvent] = {
31
+ domEvent,
32
+ busEvent: eventName,
33
+ listener
34
+ };
35
+ }
36
+ element.eventMaps = eventMaps;
37
+ };
38
+ var createNode = (type, document) => {
39
+ document = document || window.document;
40
+ return document.createElement(type);
41
+ };
42
+ var createTextNode = (value, document) => {
43
+ document = document || window.document;
44
+ return document.createTextNode(value);
45
+ };
46
+ var createDecoratedNode = (type, attributes, events, renderKit) => {
47
+ const dom = createNode(type, renderKit.document);
48
+ setAttributesOnElement(dom, attributes);
49
+ setEventsOnElement(dom, events, renderKit.publish);
50
+ return dom;
51
+ };
52
+
53
+ // src/rendering/dom/attributesAndEvents.ts
54
+ var separateAttrsAndEvents = (combined, defaultValue = "") => {
55
+ const attributes = {};
56
+ const events = {};
57
+ for (const key in combined) {
58
+ const value = combined[key];
59
+ if (key.match(/^on.+/i)) {
60
+ const eventKey = key.slice(2).toLowerCase();
61
+ events[eventKey] = value;
62
+ } else {
63
+ attributes[key] = normalizeValueForKey(combined, key, defaultValue);
64
+ }
65
+ }
66
+ return {
67
+ attributes,
68
+ events
69
+ };
70
+ };
71
+ var normalizeValueForKey = (object, key, defaultValue = "") => {
72
+ if (object[key] === undefined)
73
+ return defaultValue;
74
+ return object[key];
75
+ };
76
+
77
+ // src/rendering/templates/text.ts
78
+ class TextTemplate {
79
+ value;
80
+ constructor(content) {
81
+ this.value = content.toString();
82
+ }
83
+ render(renderKit) {
84
+ const textNode = createTextNode(this.value, renderKit.document);
85
+ if (!textNode)
86
+ return [];
87
+ textNode.__jsx = "TextNode";
88
+ return [textNode];
89
+ }
90
+ }
91
+
92
+ // src/rendering/templates/children.ts
93
+ var ensureArray = (children) => {
94
+ if (Array.isArray(children)) {
95
+ return children.flat();
96
+ }
97
+ if (!children) {
98
+ return [];
99
+ }
100
+ return [children];
101
+ };
102
+ var recursiveRender = (children, renderKit, rendered = []) => children.reduce(renderReducer(renderKit), rendered).flat();
103
+ var renderReducer = (renderKit) => (aggregate, view) => {
104
+ if (!view)
105
+ return aggregate;
106
+ if (Array.isArray(view)) {
107
+ const dom = recursiveRender(view, renderKit, aggregate);
108
+ return dom;
109
+ }
110
+ view.render(renderKit).forEach((template) => aggregate.push(template));
111
+ return aggregate;
112
+ };
113
+ var replaceTextNodes = (child) => {
114
+ if (isTextNode(child)) {
115
+ return textNode(child);
116
+ }
117
+ return child;
118
+ };
119
+ var isTextNode = (child) => {
120
+ return typeof child === "string" || typeof child === "number";
121
+ };
122
+ var textNode = (content) => {
123
+ return new TextTemplate(content);
124
+ };
125
+
126
+ class Children {
127
+ collection;
128
+ parentElement;
129
+ constructor(jsxChildren) {
130
+ this.collection = ensureArray(jsxChildren);
131
+ this.collection = this.collection.map(replaceTextNodes);
132
+ this.collection = this.collection.flat();
133
+ }
134
+ render(renderKit, parentElement) {
135
+ this.parentElement = parentElement;
136
+ const dom = this.generateDom(renderKit);
137
+ this.attachToParent(dom);
138
+ return dom;
139
+ }
140
+ generateDom(renderKit) {
141
+ return recursiveRender(this.collection, renderKit);
142
+ }
143
+ attachToParent(dom) {
144
+ if (this.parentElement === undefined)
145
+ return;
146
+ const parent = this.parentElement;
147
+ dom.forEach((node) => parent.appendChild(node));
148
+ }
149
+ }
150
+
151
+ // src/rendering/templates/tag.ts
152
+ class Tag {
153
+ type;
154
+ events;
155
+ attributes;
156
+ children;
157
+ constructor(tagType, combinedAttributes, children2) {
158
+ this.type = tagType;
159
+ const { events, attributes } = separateAttrsAndEvents(combinedAttributes);
160
+ this.events = events;
161
+ this.attributes = attributes;
162
+ this.children = new Children(children2);
163
+ }
164
+ render(renderKit) {
165
+ const dom = this.generateDom(renderKit);
166
+ if (!dom)
167
+ return [];
168
+ this.children.render(renderKit, dom);
169
+ return [dom];
170
+ }
171
+ generateDom(renderKit) {
172
+ const node = createDecoratedNode(this.type, this.attributes, this.events, renderKit);
173
+ node.__jsx = this.key();
174
+ return node;
175
+ }
176
+ key() {
177
+ return this.attributes.key || this.source() || this.createKey();
178
+ }
179
+ source() {
180
+ if (this.attributes.__source) {
181
+ const { fileName, lineNumber, columnNumber } = this.attributes.__source;
182
+ return `${fileName}:${lineNumber}:${columnNumber}`;
183
+ }
184
+ }
185
+ createKey() {
186
+ const id = this.attributes.id ? `#${this.attributes.id}` : "";
187
+ const type = this.attributes.type ? `[type=${this.attributes.type}]` : "";
188
+ const name = this.attributes.name ? `[name=${this.attributes.name}]` : "";
189
+ return `${this.type}${id}${type}${name}`;
190
+ }
191
+ }
192
+
193
+ // src/jsx.js
194
+ var ensureChildrenArray = (maybeChildren, attributes) => maybeChildren || attributes.children || [];
195
+ var packageAttributes = (maybeAttributes, maybeChildren) => {
196
+ const attributes = maybeAttributes || {};
197
+ const children3 = ensureChildrenArray(maybeChildren, attributes);
198
+ attributes.children = attributes.children || children3;
199
+ return attributes;
200
+ };
201
+ var jsx = (type, attributes, ...children3) => {
202
+ if (typeof type === "string") {
203
+ return new Tag(type, attributes, children3);
204
+ }
205
+ return type(packageAttributes(attributes, children3));
206
+ };
207
+ jsx.fragment = (attributes, maybeChildren) => {
208
+ const children3 = ensureChildrenArray(maybeChildren, attributes);
209
+ return new Children(children3);
210
+ };
211
+ var jsx_default = jsx;
212
+ // src/rendering/templates/root.ts
213
+ class Root {
214
+ template;
215
+ selector;
216
+ renderKit;
217
+ dom;
218
+ parentElement;
219
+ constructor(template, selector, renderKit) {
220
+ this.template = template;
221
+ this.selector = selector;
222
+ this.renderKit = renderKit;
223
+ this.dom = [];
224
+ this.parentElement = null;
225
+ }
226
+ renderAndAttach(renderKit) {
227
+ this.parentElement = this.getParentElement();
228
+ this.dom = this.render({ ...renderKit, parent: this.parentElement });
229
+ if (this.parentElement) {
230
+ this.attach();
231
+ }
232
+ }
233
+ render(renderKit) {
234
+ return this.template.render(renderKit);
235
+ }
236
+ attach() {
237
+ this.parentElement && (this.parentElement.innerHTML = "");
238
+ this.dom.forEach((element) => {
239
+ this.parentElement && this.parentElement.appendChild(element);
240
+ });
241
+ }
242
+ getParentElement() {
243
+ return this.renderKit.document.querySelector(this.selector);
244
+ }
245
+ }
246
+ var render = (template, selector, renderKit) => {
247
+ const root = new Root(template, selector, renderKit);
248
+ root.renderAndAttach(renderKit);
249
+ return root;
250
+ };
251
+ // src/state/testingTypes.js
252
+ var isBoolean = (value) => typeof value === "boolean";
253
+ var isNumber = (value) => typeof value === "number";
254
+ var isString = (value) => typeof value === "string";
255
+ var isArray = (value) => Array.isArray(value);
256
+ var isObject = (value) => value !== null && !isArray(value) && typeof value === "object";
257
+
258
+ // src/state/equality.js
259
+ var areElementEqual = (oldValue, newValue) => oldValue === newValue;
260
+ var keyLengthSame = (oldValue, newValue) => Object.keys(oldValue).length === Object.keys(newValue).length;
261
+ var areObjectsEqual = (oldValue, newValue) => {
262
+ if (!(isObject(oldValue) && isObject(newValue)))
263
+ return false;
264
+ if (!keyLengthSame(oldValue, newValue))
265
+ return false;
266
+ Object.keys(oldValue).every((key) => {
267
+ const oldInnerValue = oldValue[key];
268
+ const newInnerValue = newValue[key];
269
+ return areEqual(oldInnerValue, newInnerValue);
270
+ });
271
+ };
272
+ var areArraysEqual = (oldValue, newValue) => {
273
+ if (!(isArray(oldValue) && isArray(newValue)))
274
+ return false;
275
+ if (oldValue.length !== newValue.length)
276
+ return false;
277
+ oldValue.every((oldInnerValue, index) => {
278
+ const newInnerValue = newValue[index];
279
+ return areEqual(oldInnerValue, newInnerValue);
280
+ });
281
+ };
282
+ var areEqual = (oldValue, newValue) => {
283
+ if (isObject(oldValue))
284
+ return areObjectsEqual(oldValue, newValue);
285
+ if (isArray(oldValue))
286
+ return areArraysEqual(oldValue, newValue);
287
+ return areElementEqual(oldValue, newValue);
288
+ };
289
+
290
+ // src/state/stores.js
291
+ class GeneralStore {
292
+ nullEvent = {};
293
+ constructor({ name, value, parent }) {
294
+ this.name = name;
295
+ this.value = value;
296
+ this.parent = parent;
297
+ this.initialState = value;
298
+ }
299
+ update(newValue) {
300
+ if (this.isEqual(newValue))
301
+ return;
302
+ this.value = newValue;
303
+ return this.parent.publish(this.event());
304
+ }
305
+ reset() {
306
+ this.update(this.initialState);
307
+ }
308
+ isEqual(newValue) {
309
+ return areElementEqual(this.value, newValue);
310
+ }
311
+ event() {
312
+ return {
313
+ name: this.name,
314
+ value: this.value
315
+ };
316
+ }
317
+ }
318
+
319
+ class BooleanStore extends GeneralStore {
320
+ toggle() {
321
+ this.update(!this.value);
322
+ }
323
+ }
324
+
325
+ class NumericStore extends GeneralStore {
326
+ }
327
+
328
+ class StringStore extends GeneralStore {
329
+ }
330
+
331
+ class ListStore extends GeneralStore {
332
+ isEqual(newValue) {
333
+ return areArraysEqual(this.value, newValue);
334
+ }
335
+ push(newValue) {
336
+ const value = [...this.value, newValue];
337
+ this.update(value);
338
+ }
339
+ }
340
+
341
+ class RecordStore extends GeneralStore {
342
+ isEqual(newValue) {
343
+ return areObjectsEqual(this.value, newValue);
344
+ }
345
+ }
346
+
347
+ // src/state.js
348
+ var eventPrefix = "state-change";
349
+ var eventName = (name) => `${eventPrefix}:${name}`;
350
+
351
+ class State {
352
+ constructor(publish) {
353
+ this.publisher = publish;
354
+ this.stores = {};
355
+ this.events = [];
356
+ this.transacting = false;
357
+ }
358
+ create(name, value) {
359
+ const StoreClass = this.storeTypeFor(value);
360
+ const store = new StoreClass({ name, value, parent: this });
361
+ this.addStore(name, store);
362
+ }
363
+ add(store) {
364
+ const name = store.name;
365
+ this.addStore(name, store);
366
+ }
367
+ getStore(name) {
368
+ return this.stores[name];
369
+ }
370
+ addStore(name, store) {
371
+ this.stores[name] = store;
372
+ this[name] = store;
373
+ }
374
+ storeTypeFor(value) {
375
+ if (isArray(value))
376
+ return ListStore;
377
+ if (isObject(value))
378
+ return RecordStore;
379
+ if (isNumber(value))
380
+ return NumericStore;
381
+ if (isBoolean(value))
382
+ return BooleanStore;
383
+ if (isString(value))
384
+ return StringStore;
385
+ return GeneralStore;
386
+ }
387
+ publish(event) {
388
+ this.events.push(event);
389
+ if (!this.transacting)
390
+ this.publishAll();
391
+ }
392
+ publishAll() {
393
+ const publishedStores = [];
394
+ this.events.reverse().forEach((event) => {
395
+ const { name, value } = event;
396
+ if (!publishedStores.includes(name)) {
397
+ publishedStores.push(name);
398
+ this.publisher(`${eventPrefix}:${name}`, value);
399
+ }
400
+ });
401
+ this.events = [];
402
+ }
403
+ transaction(setter) {
404
+ this.transacting = true;
405
+ setter(this);
406
+ this.transacting = false;
407
+ this.publishAll();
408
+ }
409
+ value() {
410
+ return Object.keys(this.stores).reduce((valueObject, key) => {
411
+ valueObject[key] = this.stores[key].value;
412
+ return valueObject;
413
+ }, {});
414
+ }
415
+ }
416
+
417
+ // src/types.ts
418
+ var ChangeInstructions;
419
+ (function(ChangeInstructions2) {
420
+ ChangeInstructions2[ChangeInstructions2["removeNode"] = 0] = "removeNode";
421
+ ChangeInstructions2[ChangeInstructions2["insertNode"] = 1] = "insertNode";
422
+ ChangeInstructions2[ChangeInstructions2["replaceNode"] = 2] = "replaceNode";
423
+ ChangeInstructions2[ChangeInstructions2["removeAttribute"] = 3] = "removeAttribute";
424
+ ChangeInstructions2[ChangeInstructions2["addAttribute"] = 4] = "addAttribute";
425
+ ChangeInstructions2[ChangeInstructions2["updateAttribute"] = 5] = "updateAttribute";
426
+ ChangeInstructions2[ChangeInstructions2["removeEvent"] = 6] = "removeEvent";
427
+ ChangeInstructions2[ChangeInstructions2["addEvent"] = 7] = "addEvent";
428
+ ChangeInstructions2[ChangeInstructions2["updateEvent"] = 8] = "updateEvent";
429
+ ChangeInstructions2[ChangeInstructions2["changeValue"] = 9] = "changeValue";
430
+ ChangeInstructions2[ChangeInstructions2["changeText"] = 10] = "changeText";
431
+ })(ChangeInstructions || (ChangeInstructions = {}));
432
+
433
+ // src/rendering/change/instructions/generate.ts
434
+ var changeText = (source, target) => ({
435
+ source,
436
+ target,
437
+ type: ChangeInstructions.changeText,
438
+ data: {}
439
+ });
440
+ var replaceNode = (source, target) => ({
441
+ source,
442
+ target,
443
+ type: ChangeInstructions.replaceNode,
444
+ data: {}
445
+ });
446
+ var removeAttribute = (source, target, data) => ({
447
+ source,
448
+ target,
449
+ data,
450
+ type: ChangeInstructions.removeAttribute
451
+ });
452
+ var addAttribute = (source, target, data) => ({
453
+ source,
454
+ target,
455
+ data,
456
+ type: ChangeInstructions.addAttribute
457
+ });
458
+ var updateAttribute = (source, target, data) => ({
459
+ source,
460
+ target,
461
+ data,
462
+ type: ChangeInstructions.updateAttribute
463
+ });
464
+ var removeEvent = (source, target, data) => ({
465
+ source,
466
+ target,
467
+ data,
468
+ type: ChangeInstructions.removeEvent
469
+ });
470
+ var addEvent = (source, target, data) => ({
471
+ source,
472
+ target,
473
+ data,
474
+ type: ChangeInstructions.addEvent
475
+ });
476
+ var updateEvent = (source, target, data) => ({
477
+ source,
478
+ target,
479
+ data,
480
+ type: ChangeInstructions.updateEvent
481
+ });
482
+ var removeNode = (source) => ({
483
+ source,
484
+ target: source,
485
+ type: ChangeInstructions.removeNode,
486
+ data: {}
487
+ });
488
+ var insertNode = (target, data) => ({
489
+ target,
490
+ source: target,
491
+ type: ChangeInstructions.insertNode,
492
+ data
493
+ });
494
+ var changeValue = (source, target, data) => ({
495
+ source,
496
+ target,
497
+ type: ChangeInstructions.changeValue,
498
+ data
499
+ });
500
+
501
+ // src/rendering/change/instructions/idMap.js
502
+ var nullMatch = { index: -1 };
503
+
504
+ class IdMap {
505
+ constructor() {
506
+ this.map = {};
507
+ }
508
+ populate(list) {
509
+ list.forEach((element, i) => {
510
+ const id = element.__jsx;
511
+ if (id) {
512
+ this.map[id] = this.map[id] || [];
513
+ this.map[id].push({
514
+ element,
515
+ index: i
516
+ });
517
+ }
518
+ });
519
+ }
520
+ pullMatch(element) {
521
+ const id = element && element.__jsx;
522
+ if (!id)
523
+ return nullMatch;
524
+ if (!(this.map[id] && this.map[id].length))
525
+ return nullMatch;
526
+ return this.map[id].shift();
527
+ }
528
+ clear(element) {
529
+ const id = element && element.__jsx;
530
+ if (!(id && this.map[id] && this.map[id].length))
531
+ return;
532
+ const matches = this.map[id];
533
+ this.map[id] = matches.reduce((collection, possibleMatch) => {
534
+ if (possibleMatch.element !== element)
535
+ collection.push(possibleMatch);
536
+ return collection;
537
+ }, []);
538
+ }
539
+ check(element) {
540
+ const id = element && element.__jsx;
541
+ if (!(id && this.map[id]))
542
+ return false;
543
+ return this.map[id].length > 0;
544
+ }
545
+ remaining() {
546
+ return Object.values(this.map).flat();
547
+ }
548
+ }
549
+ var createIdMap = (list) => {
550
+ const map = new IdMap;
551
+ map.populate(list);
552
+ return map;
553
+ };
554
+
555
+ // src/rendering/change/instructions/attributes.ts
556
+ var compileForAttributes = (source, target) => {
557
+ const instructions = [];
558
+ const sourceAttributes = source.attributes;
559
+ const sourceLength = sourceAttributes.length;
560
+ const targetAttributes = target.attributes;
561
+ const targetLength = targetAttributes.length;
562
+ let index;
563
+ let innerIndex;
564
+ let matchingAttribute;
565
+ for (index = 0;index < sourceLength; index++) {
566
+ matchingAttribute = null;
567
+ const sourceAttribute = sourceAttributes.item(index);
568
+ if (!sourceAttribute)
569
+ continue;
570
+ for (innerIndex = 0;innerIndex < targetLength; innerIndex++) {
571
+ const targetAttribute = targetAttributes.item(innerIndex);
572
+ if (!targetAttribute)
573
+ continue;
574
+ if (sourceAttribute.name == targetAttribute.name) {
575
+ matchingAttribute = targetAttribute;
576
+ break;
577
+ }
578
+ }
579
+ if (!matchingAttribute) {
580
+ instructions.push(removeAttribute(source, target, { name: sourceAttribute.name }));
581
+ } else if (sourceAttribute.value !== matchingAttribute.value) {
582
+ instructions.push(updateAttribute(source, target, {
583
+ name: sourceAttribute.name,
584
+ value: matchingAttribute.value
585
+ }));
586
+ }
587
+ }
588
+ for (index = 0;index < targetLength; index++) {
589
+ matchingAttribute = null;
590
+ const targetAttribute = targetAttributes.item(index);
591
+ if (!targetAttribute)
592
+ continue;
593
+ for (innerIndex = 0;innerIndex < sourceLength; innerIndex++) {
594
+ const sourceAttribute = sourceAttributes.item(innerIndex);
595
+ if (!sourceAttribute)
596
+ continue;
597
+ if (sourceAttribute.name == targetAttribute.name) {
598
+ matchingAttribute = sourceAttribute;
599
+ break;
600
+ }
601
+ }
602
+ if (!matchingAttribute) {
603
+ instructions.push(addAttribute(source, target, {
604
+ name: targetAttribute.name,
605
+ value: targetAttribute.value
606
+ }));
607
+ }
608
+ }
609
+ return instructions;
610
+ };
611
+
612
+ // src/rendering/change/instructions/events.ts
613
+ var compileForEvents = (source, target) => {
614
+ const instructions = [];
615
+ const sourceEventMaps = source.eventMaps;
616
+ const targetEventMaps = target.eventMaps;
617
+ const sourceDomEvents = Object.keys(sourceEventMaps);
618
+ const targetDomEvents = Object.keys(targetEventMaps);
619
+ sourceDomEvents.forEach((domEvent) => {
620
+ const sourceEventMap = sourceEventMaps[domEvent];
621
+ const targetEventMap = targetEventMaps[domEvent];
622
+ if (!targetEventMap) {
623
+ instructions.push(removeEvent(source, target, {
624
+ name: sourceEventMap.domEvent,
625
+ value: sourceEventMap.listener
626
+ }));
627
+ } else if (targetEventMap.busEvent !== sourceEventMap.busEvent) {
628
+ instructions.push(updateEvent(source, target, {
629
+ name: domEvent,
630
+ targetValue: targetEventMap.listener,
631
+ sourceValue: sourceEventMap.listener
632
+ }));
633
+ }
634
+ });
635
+ targetDomEvents.forEach((domEvent) => {
636
+ const sourceEventMap = sourceEventMaps[domEvent];
637
+ const targetEventMap = targetEventMaps[domEvent];
638
+ if (!sourceEventMap) {
639
+ instructions.push(addEvent(source, target, {
640
+ name: targetEventMap.domEvent,
641
+ value: targetEventMap.listener
642
+ }));
643
+ }
644
+ });
645
+ return instructions;
646
+ };
647
+
648
+ // src/rendering/change/instructions/element.ts
649
+ var compileForElement = (source, target) => {
650
+ const attributeInstructions = compileForAttributes(source, target);
651
+ const eventInstructions = compileForEvents(source, target);
652
+ const valueInstructions = compileForInputValue(source, target);
653
+ return attributeInstructions.concat(eventInstructions).concat(valueInstructions);
654
+ };
655
+ var compileForInputValue = (sourceElement, targetElement) => {
656
+ const instructions = [];
657
+ if (sourceElement.tagName !== "INPUT") {
658
+ return instructions;
659
+ }
660
+ const source = sourceElement;
661
+ const target = targetElement;
662
+ if (source.value !== target.value) {
663
+ instructions.push(changeValue(source, target, { name: "value", value: target.value }));
664
+ }
665
+ return instructions;
666
+ };
667
+
668
+ // src/rendering/change/instructions/text.ts
669
+ var compileForText = (source, target) => {
670
+ if (source.textContent !== target.textContent) {
671
+ return [changeText(source, target)];
672
+ }
673
+ return [];
674
+ };
675
+
676
+ // src/rendering/change/instructions/node.ts
677
+ var NodeTypes;
678
+ (function(NodeTypes2) {
679
+ NodeTypes2[NodeTypes2["ElementNode"] = 1] = "ElementNode";
680
+ NodeTypes2[NodeTypes2["TextNode"] = 3] = "TextNode";
681
+ })(NodeTypes || (NodeTypes = {}));
682
+ var compileForNodeGenerator = (compileForCollection) => (source, target) => {
683
+ let instructions = [];
684
+ if (source.nodeType === NodeTypes.ElementNode) {
685
+ const sourceElement = source;
686
+ const targetElement = target;
687
+ const baseInstructions = compileForElement(sourceElement, targetElement);
688
+ const childrenInstructions = compileForCollection(sourceElement.childNodes, targetElement.childNodes, sourceElement);
689
+ instructions = baseInstructions.concat(childrenInstructions);
690
+ } else if (source.nodeType === NodeTypes.TextNode) {
691
+ instructions = compileForText(source, target);
692
+ }
693
+ return instructions;
694
+ };
695
+
696
+ // src/debugging.js
697
+ var debug = (...message) => {
698
+ if (process.env.DEBUG === "true") {
699
+ console.log(...message);
700
+ }
701
+ };
702
+
703
+ // src/rendering/change/instructions/children.ts
704
+ var compileChildren = (sourceList, targetList, parent) => {
705
+ const baseInstructions = [];
706
+ const length = largerLength(sourceList, targetList);
707
+ const sourceMap = createIdMap(sourceList);
708
+ const targetMap = createIdMap(targetList);
709
+ const nodesPairsToDiff = [];
710
+ let index = 0;
711
+ for (;index < length; index++) {
712
+ const source = sourceList[index];
713
+ const target = targetList[index];
714
+ debug("\n", "loop index", index, source && source.__jsx, target && target.__jsx);
715
+ if (target && targetMap.check(target)) {
716
+ debug("target", target.__jsx, "index", index);
717
+ const matchingSource = sourceMap.pullMatch(target);
718
+ targetMap.clear(target);
719
+ if (matchingSource.element) {
720
+ debug("matching source found for target");
721
+ if (matchingSource.index !== index) {
722
+ debug("moving source", matchingSource.element.__jsx, index);
723
+ baseInstructions.push(insertNode(matchingSource.element, { parent, index }));
724
+ }
725
+ debug("updating to match target", matchingSource.element.__jsx, matchingSource.element.classList, target.__jsx, target.classList);
726
+ nodesPairsToDiff.push({
727
+ source: matchingSource.element,
728
+ target
729
+ });
730
+ } else if (source) {
731
+ debug("NO matching source for target but source in slot", source.__jsx);
732
+ if (targetMap.check(source)) {
733
+ debug("adding", target.__jsx, "at", index);
734
+ baseInstructions.push(insertNode(target, { parent, index }));
735
+ } else {
736
+ debug("replacing", source.__jsx, target.__jsx, "at", index);
737
+ sourceMap.clear(source);
738
+ baseInstructions.push(replaceNode(source, target));
739
+ }
740
+ } else {
741
+ debug("adding target to end", target.__jsx);
742
+ baseInstructions.push(insertNode(target, { parent, index }));
743
+ }
744
+ } else if (source) {
745
+ const matchingSource = sourceMap.pullMatch(source);
746
+ if (matchingSource.element) {
747
+ debug("removing", source.__jsx);
748
+ baseInstructions.push(removeNode(source));
749
+ }
750
+ }
751
+ }
752
+ sourceMap.remaining().forEach(({ element: element2 }) => {
753
+ debug("removing", element2.__jsx);
754
+ baseInstructions.push(removeNode(element2));
755
+ });
756
+ const nodeInstructions = nodesPairsToDiff.reduce((collection, { source, target }) => {
757
+ return collection.concat(compileForNode(source, target));
758
+ }, []);
759
+ return baseInstructions.concat(nodeInstructions).sort(instructionsSorter);
760
+ };
761
+ var instructionsSorter = (left, right) => {
762
+ if (left.type > right.type)
763
+ return 1;
764
+ if (left.type < right.type)
765
+ return -1;
766
+ return 0;
767
+ };
768
+ var largerLength = (sourceList, targetList) => {
769
+ const sourceLength = Array.from(sourceList).length;
770
+ const targetLength = Array.from(targetList).length;
771
+ return sourceLength > targetLength ? sourceLength : targetLength;
772
+ };
773
+ var compileForNode = compileForNodeGenerator(compileChildren);
774
+ // src/rendering/change.ts
775
+ var change = (source, target, parent) => {
776
+ const instructions = compileChildren(source, target, parent);
777
+ debug("instructions", instructions.map((instruction) => instruction.type));
778
+ instructions.forEach((instruction) => {
779
+ performInstruction(instruction);
780
+ });
781
+ };
782
+ var performInstruction = (instruction) => {
783
+ const performer = performers[instruction.type] || noop;
784
+ performer(instruction);
785
+ };
786
+ var noop = (_instruction) => {
787
+ };
788
+ var changeText2 = (instruction) => {
789
+ const { source, target } = instruction;
790
+ source.nodeValue = target.textContent;
791
+ };
792
+ var removeNode2 = (instruction) => {
793
+ const { source } = instruction;
794
+ source.remove();
795
+ debug("removeNode called on", source.nodeName);
796
+ };
797
+ var insertNode2 = (instruction) => {
798
+ const { target, data } = instruction;
799
+ const { parent, index } = data;
800
+ const sibling = parent.childNodes[index];
801
+ if (!sibling) {
802
+ parent.appendChild(target);
803
+ } else if (sibling && sibling !== target) {
804
+ parent.insertBefore(target, sibling);
805
+ }
806
+ };
807
+ var replaceNode2 = (instruction) => {
808
+ const { source, target } = instruction;
809
+ source.replaceWith(target);
810
+ debug("replaceNode called on", source.nodeName, "with", target.nodeName);
811
+ debug("parent", source.parentElement);
812
+ };
813
+ var removeAttribute2 = (instruction) => {
814
+ const { source, data } = instruction;
815
+ const { name } = data;
816
+ source.removeAttribute(name);
817
+ };
818
+ var addAttribute2 = (instruction) => {
819
+ const { source, data } = instruction;
820
+ const { name, value } = data;
821
+ source.setAttribute(name, value);
822
+ };
823
+ var updateAttribute2 = (instruction) => {
824
+ addAttribute2(instruction);
825
+ };
826
+ var removeEvent2 = (instruction) => {
827
+ const data = instruction.data;
828
+ const source = instruction.source;
829
+ const { name, value } = data;
830
+ source.removeEventListener(name, value);
831
+ };
832
+ var addEvent2 = (instruction) => {
833
+ const data = instruction.data;
834
+ const source = instruction.source;
835
+ const { name, value } = data;
836
+ source.addEventListener(name, value);
837
+ };
838
+ var updateEvent2 = (instruction) => {
839
+ const data = instruction.data;
840
+ const source = instruction.source;
841
+ const { name, sourceValue, targetValue } = data;
842
+ source.removeEventListener(name, sourceValue);
843
+ source.addEventListener(name, targetValue);
844
+ };
845
+ var changeValue2 = (instruction) => {
846
+ const data = instruction.data;
847
+ const source = instruction.source;
848
+ const { value } = data;
849
+ source.value = value;
850
+ };
851
+ var performers = {
852
+ [ChangeInstructions.changeText]: changeText2,
853
+ [ChangeInstructions.removeNode]: removeNode2,
854
+ [ChangeInstructions.insertNode]: insertNode2,
855
+ [ChangeInstructions.replaceNode]: replaceNode2,
856
+ [ChangeInstructions.removeAttribute]: removeAttribute2,
857
+ [ChangeInstructions.addAttribute]: addAttribute2,
858
+ [ChangeInstructions.updateAttribute]: updateAttribute2,
859
+ [ChangeInstructions.removeEvent]: removeEvent2,
860
+ [ChangeInstructions.addEvent]: addEvent2,
861
+ [ChangeInstructions.updateEvent]: updateEvent2,
862
+ [ChangeInstructions.changeValue]: changeValue2
863
+ };
864
+
865
+ // src/rendering/templates/bound.js
866
+ class Bound {
867
+ constructor(TemplateClass, viewModel, subscriptions, attributes2) {
868
+ this.TemplateClass = TemplateClass;
869
+ this.viewModel = viewModel;
870
+ this.attributes = attributes2 || {};
871
+ this.subscriptions = subscriptions;
872
+ this.dom = [];
873
+ }
874
+ render(renderKit) {
875
+ this.parentElement = renderKit.parent;
876
+ this.renderKit = renderKit;
877
+ this.subscribeForRerender();
878
+ this.dom = this._render(renderKit);
879
+ return this.dom;
880
+ }
881
+ _render(renderKit) {
882
+ const props = {
883
+ ...this.attributes,
884
+ ...this.viewModel(renderKit.state.value())
885
+ };
886
+ const template = this.TemplateClass(props);
887
+ const dom = !template ? [] : template.render(renderKit);
888
+ return dom;
889
+ }
890
+ rerender() {
891
+ if (!this.parentElement) {
892
+ this.parentElement = this.dom[0] && this.dom[0].parentElement;
893
+ }
894
+ const newDom = this._render(this.renderKit);
895
+ change(this.dom, newDom, this.parentElement);
896
+ if (this.parentElement) {
897
+ this.dom = Array.from(this.parentElement.childNodes);
898
+ }
899
+ }
900
+ subscribeForRerender() {
901
+ this.subscriptions.forEach((storeName) => {
902
+ this.renderKit.subscribe(eventName(storeName), () => this.rerender());
903
+ });
904
+ }
905
+ }
906
+ var bind = ({ Template, viewModel, subscriptions }) => {
907
+ subscriptions = subscriptions || [];
908
+ return (attributes2) => new Bound(Template, viewModel, subscriptions, attributes2);
909
+ };
910
+ // src/messageBus.ts
911
+ class MessageBus {
912
+ listeners;
913
+ options;
914
+ constructor() {
915
+ this.options = {};
916
+ this.listeners = {};
917
+ }
918
+ subscribe(eventName2, listener) {
919
+ this.ensureListenerCollection(eventName2);
920
+ this.listeners[eventName2].push(listener);
921
+ }
922
+ publish(eventName2, payload) {
923
+ const listeners = this.listeners[eventName2];
924
+ if (!listeners)
925
+ return false;
926
+ listeners.forEach((listener) => {
927
+ listener(payload, this.buildListenerKit(eventName2));
928
+ });
929
+ return true;
930
+ }
931
+ ensureListenerCollection(eventName2) {
932
+ if (this.listeners[eventName2])
933
+ return;
934
+ this.listeners[eventName2] = [];
935
+ }
936
+ buildListenerKit(eventName2) {
937
+ return {
938
+ eventName: eventName2,
939
+ publish: this.publish.bind(this),
940
+ ...this.options
941
+ };
942
+ }
943
+ addListenerOptions(options) {
944
+ this.options = {
945
+ ...this.options,
946
+ ...options
947
+ };
948
+ }
949
+ }
950
+ var createBus = () => {
951
+ const bus = new MessageBus;
952
+ const publish = bus.publish.bind(bus);
953
+ const subscribe = bus.subscribe.bind(bus);
954
+ return {
955
+ bus,
956
+ publish,
957
+ subscribe
958
+ };
959
+ };
960
+ // src/navigation/routeState.js
961
+ var createRouteState = (state2) => {
962
+ const store = new RecordStore({
963
+ name: "route",
964
+ value: {
965
+ host: "",
966
+ path: "",
967
+ query: ""
968
+ },
969
+ parent: state2
970
+ });
971
+ state2.add(store);
972
+ };
973
+
974
+ // src/navigation/setupHistory.js
975
+ var locationChangeEvent = "locationChange";
976
+ var routeChangeEvent = "routeChange";
977
+ var extractQueryParams = (queryString) => {
978
+ return queryString.replace(/^\?/, "").split("&").reduce((aggregate, pairString) => {
979
+ if (!pairString)
980
+ return aggregate;
981
+ const pair = pairString.split("=");
982
+ aggregate[pair[0]] = pair[1];
983
+ return aggregate;
984
+ }, {});
985
+ };
986
+ var onLocationChange = (_payload, { publish, state: state2 }) => {
987
+ const { host, pathname, search } = window.location;
988
+ const path = pathname;
989
+ const query = extractQueryParams(search);
990
+ state2.route.update({
991
+ host,
992
+ path,
993
+ query
994
+ });
995
+ publish(routeChangeEvent, { host, path, query });
996
+ };
997
+ var setupHistory = (app) => {
998
+ const { publish, subscribe, state: state2 } = app;
999
+ createRouteState(state2);
1000
+ window.addEventListener("popstate", () => publish(locationChangeEvent));
1001
+ subscribe(locationChangeEvent, onLocationChange);
1002
+ };
1003
+
1004
+ // src/navigation/findHref.js
1005
+ var findHref = (node2) => {
1006
+ if (!node2 || !node2.getAttribute)
1007
+ return "";
1008
+ while (!node2.getAttribute("href")) {
1009
+ node2 = node2.parentNode;
1010
+ if (!node2 || !node2.getAttribute)
1011
+ return "";
1012
+ }
1013
+ return node2.getAttribute("href");
1014
+ };
1015
+
1016
+ // src/navigation/setupNavigation.js
1017
+ var linkNavigationEvent = "goToHref";
1018
+ var programmaticNavigationEvent = "navigate";
1019
+ var navigate = (path, { publish }) => {
1020
+ window.history.pushState(null, "", path);
1021
+ publish(locationChangeEvent);
1022
+ };
1023
+ var onLinkClick = (domEvent, { publish }) => {
1024
+ if (!domEvent || !domEvent.target)
1025
+ return;
1026
+ domEvent.preventDefault();
1027
+ const href = findHref(domEvent.target);
1028
+ navigate(href, { publish });
1029
+ };
1030
+ var setupNavigation = (app) => {
1031
+ const { subscribe } = app;
1032
+ subscribe(linkNavigationEvent, onLinkClick);
1033
+ subscribe(programmaticNavigationEvent, navigate);
1034
+ };
1035
+
1036
+ // src/app.ts
1037
+ var setupBus = (app) => {
1038
+ const { publish, subscribe, bus } = createBus();
1039
+ app.publish = publish;
1040
+ app.subscribe = subscribe;
1041
+ app.bus = bus;
1042
+ };
1043
+ var setupState = (app) => {
1044
+ const state3 = new State(app.publish);
1045
+ app.state = state3;
1046
+ };
1047
+ var connectBusToState = (app) => {
1048
+ const { bus } = app;
1049
+ bus.addListenerOptions({ state: app.state });
1050
+ };
1051
+ var setupRenderKit = (app, document) => {
1052
+ app.renderKit = {
1053
+ publish: app.publish,
1054
+ subscribe: app.subscribe,
1055
+ state: app.state,
1056
+ document
1057
+ };
1058
+ };
1059
+ var triggerRoute = (app) => {
1060
+ const publish = app.publish;
1061
+ setTimeout(() => {
1062
+ publish(locationChangeEvent, null);
1063
+ }, 0);
1064
+ };
1065
+ var addRender = (app) => {
1066
+ app.render = (template, selector) => {
1067
+ return render(template, selector, app.renderKit);
1068
+ };
1069
+ };
1070
+ var createApp = (document = window.document) => {
1071
+ const app = {};
1072
+ setupBus(app);
1073
+ setupState(app);
1074
+ connectBusToState(app);
1075
+ setupRenderKit(app, document);
1076
+ setupHistory(app);
1077
+ setupNavigation(app);
1078
+ triggerRoute(app);
1079
+ addRender(app);
1080
+ return app;
1081
+ };
1082
+ // src/navigation.ts
1083
+ var exports_navigation = {};
1084
+ __export(exports_navigation, {
1085
+ setupNavigation: () => {
1086
+ {
1087
+ return setupNavigation;
1088
+ }
1089
+ },
1090
+ setupHistory: () => {
1091
+ {
1092
+ return setupHistory;
1093
+ }
1094
+ }
1095
+ });
1096
+ // src/views/conditionals.jsx
1097
+ var exports_conditionals = {};
1098
+ __export(exports_conditionals, {
1099
+ Unless: () => {
1100
+ {
1101
+ return Unless;
1102
+ }
1103
+ },
1104
+ IfElse: () => {
1105
+ {
1106
+ return IfElse;
1107
+ }
1108
+ },
1109
+ If: () => {
1110
+ {
1111
+ return If;
1112
+ }
1113
+ }
1114
+ });
1115
+ var If = ({ condition, children: children3 }) => {
1116
+ if (!condition)
1117
+ return;
1118
+ return jsx_default(jsx_default.fragment, null, children3);
1119
+ };
1120
+ var Unless = ({ condition, children: children3 }) => {
1121
+ if (condition)
1122
+ return;
1123
+ return jsx_default(jsx_default.fragment, null, children3);
1124
+ };
1125
+ var IfElse = ({ condition, children: children3 }) => {
1126
+ const [first, ...rest] = children3;
1127
+ if (condition)
1128
+ return jsx_default(jsx_default.fragment, null, first);
1129
+ return jsx_default(jsx_default.fragment, null, rest);
1130
+ };
1131
+
1132
+ // src/views/link.jsx
1133
+ var Link = ({ children: children3, ...props }) => {
1134
+ return jsx_default("a", {
1135
+ ...props,
1136
+ onClick: "goToHref"
1137
+ }, children3);
1138
+ };
1139
+
1140
+ // src/views.js
1141
+ var views = {
1142
+ ...exports_conditionals,
1143
+ Link
1144
+ };
1145
+ export {
1146
+ views,
1147
+ render,
1148
+ exports_navigation as navigation,
1149
+ jsx_default as jsx,
1150
+ createBus,
1151
+ createApp,
1152
+ bind,
1153
+ State
1154
+ };