simplyview 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,4 @@
1
1
  (() => {
2
- var __defProp = Object.defineProperty;
3
- var __export = (target, all) => {
4
- for (var name in all)
5
- __defProp(target, name, { get: all[name], enumerable: true });
6
- };
7
-
8
2
  // src/activate.mjs
9
3
  var listeners = /* @__PURE__ */ new Map();
10
4
  var activate = {
@@ -66,10 +60,6 @@
66
60
  });
67
61
 
68
62
  // src/action.mjs
69
- var action_exports = {};
70
- __export(action_exports, {
71
- actions: () => actions
72
- });
73
63
  function actions(options) {
74
64
  if (options.app) {
75
65
  const actionHandler = {
@@ -84,10 +74,6 @@
84
74
  }
85
75
 
86
76
  // src/route.mjs
87
- var route_exports = {};
88
- __export(route_exports, {
89
- routes: () => routes
90
- });
91
77
  function routes(options) {
92
78
  return new SimplyRoute(options);
93
79
  }
@@ -95,13 +81,15 @@
95
81
  constructor(options = {}) {
96
82
  this.root = options.root || "/";
97
83
  this.app = options.app;
84
+ this.addMissingSlash = !!options.addMissingSlash;
85
+ this.matchExact = !!options.matchExact;
98
86
  this.clear();
99
87
  if (options.routes) {
100
88
  this.load(options.routes);
101
89
  }
102
90
  }
103
91
  load(routes2) {
104
- parseRoutes(routes2, this.routeInfo);
92
+ parseRoutes(routes2, this.routeInfo, this.matchExact);
105
93
  }
106
94
  clear() {
107
95
  this.routeInfo = [];
@@ -129,13 +117,22 @@
129
117
  path = getPath(path);
130
118
  for (let route of this.routeInfo) {
131
119
  matches = route.match.exec(path);
120
+ if (this.addMissingSlash && !matches?.length) {
121
+ if (path && path[path.length - 1] != "/") {
122
+ matches = route.match.exec(path + "/");
123
+ if (matches) {
124
+ path += "/";
125
+ history.replaceState({}, "", getURL(path));
126
+ }
127
+ }
128
+ }
132
129
  if (matches && matches.length) {
133
130
  var params = {};
134
- route.params.forEach((key, i) => {
131
+ route.params.forEach((key, i2) => {
135
132
  if (key == "*") {
136
133
  key = "remainder";
137
134
  }
138
- params[key] = matches[i + 1];
135
+ params[key] = matches[i2 + 1];
139
136
  });
140
137
  Object.assign(params, options);
141
138
  args.route = route;
@@ -147,9 +144,6 @@
147
144
  return args.result;
148
145
  }
149
146
  }
150
- if (path && path[path.length - 1] != "/") {
151
- return this.match(path + "/", options);
152
- }
153
147
  return false;
154
148
  }
155
149
  runListeners(action, params) {
@@ -176,7 +170,7 @@
176
170
  this.match(getPath(document.location.pathname, this.root));
177
171
  }
178
172
  });
179
- globalThis.document.addEventListener("click", (evt) => {
173
+ this.app.container.addEventListener("click", (evt) => {
180
174
  if (evt.ctrlKey) {
181
175
  return;
182
176
  }
@@ -195,10 +189,11 @@
195
189
  if (this.has(path)) {
196
190
  let params = this.runListeners("goto", { path });
197
191
  if (params.path) {
198
- this.goto(params.path);
192
+ if (this.goto(params.path)) {
193
+ evt.preventDefault();
194
+ return false;
195
+ }
199
196
  }
200
- evt.preventDefault();
201
- return false;
202
197
  }
203
198
  }
204
199
  });
@@ -259,11 +254,13 @@
259
254
  }
260
255
  return root + path;
261
256
  }
262
- function getRegexpFromRoute(route) {
257
+ function getRegexpFromRoute(route, exact = false) {
258
+ if (exact) {
259
+ return new RegExp("^" + route.replace(/:\w+/g, "([^/]+)").replace(/:\*/, "(.*)") + "(\\?|$)");
260
+ }
263
261
  return new RegExp("^" + route.replace(/:\w+/g, "([^/]+)").replace(/:\*/, "(.*)"));
264
262
  }
265
- function parseRoutes(routes2) {
266
- let routeInfo = [];
263
+ function parseRoutes(routes2, routeInfo, exact = false) {
267
264
  const paths = Object.keys(routes2);
268
265
  const matchParams = /:(\w+|\*)/g;
269
266
  for (let path of paths) {
@@ -276,7 +273,7 @@
276
273
  }
277
274
  } while (matches);
278
275
  routeInfo.push({
279
- match: getRegexpFromRoute(path),
276
+ match: getRegexpFromRoute(path, exact),
280
277
  params,
281
278
  action: routes2[path]
282
279
  });
@@ -285,10 +282,6 @@
285
282
  }
286
283
 
287
284
  // src/command.mjs
288
- var command_exports = {};
289
- __export(command_exports, {
290
- commands: () => commands
291
- });
292
285
  var SimplyCommands = class {
293
286
  constructor(options = {}) {
294
287
  if (!options.app) {
@@ -311,7 +304,7 @@
311
304
  return;
312
305
  }
313
306
  const shouldContinue = this[command.name].call(options.app, command.source, command.value);
314
- if (shouldContinue === false) {
307
+ if (shouldContinue !== true) {
315
308
  evt.preventDefault();
316
309
  evt.stopPropagation();
317
310
  return false;
@@ -409,11 +402,14 @@
409
402
  ];
410
403
 
411
404
  // src/key.mjs
412
- var key_exports = {};
413
- __export(key_exports, {
414
- keys: () => keys
405
+ var KEY = Object.freeze({
406
+ Compose: 229,
407
+ Control: 17,
408
+ Meta: 224,
409
+ Alt: 18,
410
+ Shift: 16
415
411
  });
416
- var SimplyKeys = class {
412
+ var SimplyKey = class {
417
413
  constructor(options = {}) {
418
414
  if (!options.app) {
419
415
  options.app = {};
@@ -423,7 +419,7 @@
423
419
  }
424
420
  Object.assign(this, options.keys);
425
421
  const keyHandler = (e) => {
426
- if (e.isComposing || e.keyCode === 229) {
422
+ if (e.isComposing || e.keyCode === KEY.Compose) {
427
423
  return;
428
424
  }
429
425
  if (e.defaultPrevented) {
@@ -436,845 +432,88 @@
436
432
  if (e.target.closest("[data-simply-keyboard]")) {
437
433
  selectedKeyboard = e.target.closest("[data-simply-keyboard]").dataset.simplyKeyboard;
438
434
  }
439
- let key = "";
440
- if (e.ctrlKey && e.keyCode != 17) {
441
- key += "Control+";
442
- }
443
- if (e.metaKey && e.keyCode != 224) {
444
- key += "Meta+";
445
- }
446
- if (e.altKey && e.keyCode != 18) {
447
- key += "Alt+";
448
- }
449
- if (e.shiftKey && e.keyCode != 16) {
450
- key += "Shift+";
451
- }
452
- key += e.key;
453
- if (this[selectedKeyboard] && this[selectedKeyboard][key]) {
454
- let keyboard = this[selectedKeyboard];
455
- keyboard[key].call(options.app, e);
456
- }
457
- };
458
- options.app.container.addEventListener("keydown", keyHandler);
459
- }
460
- };
461
- function keys(options = {}) {
462
- return new SimplyKeys(options);
463
- }
464
-
465
- // src/state.mjs
466
- var state_exports = {};
467
- __export(state_exports, {
468
- batch: () => batch,
469
- clockEffect: () => clockEffect,
470
- destroy: () => destroy,
471
- effect: () => effect,
472
- signal: () => signal,
473
- throttledEffect: () => throttledEffect,
474
- untracked: () => untracked
475
- });
476
- var iterate = Symbol("iterate");
477
- if (!Symbol.xRay) {
478
- Symbol.xRay = Symbol("xRay");
479
- }
480
- var signalHandler = {
481
- get: (target, property, receiver) => {
482
- if (property === Symbol.xRay) {
483
- return target;
484
- }
485
- const value = target?.[property];
486
- notifyGet(receiver, property);
487
- if (typeof value === "function") {
488
- if (Array.isArray(target)) {
489
- return (...args) => {
490
- let l = target.length;
491
- let result = value.apply(receiver, args);
492
- if (l != target.length) {
493
- notifySet(receiver, makeContext("length", { was: l, now: target.length }));
494
- }
495
- return result;
496
- };
497
- } else if (target instanceof Set || target instanceof Map) {
498
- return (...args) => {
499
- let s = target.size;
500
- let result = value.apply(target, args);
501
- if (s != target.size) {
502
- notifySet(receiver, makeContext("size", { was: s, now: target.size }));
503
- }
504
- if (["set", "add", "clear", "delete"].includes(property)) {
505
- notifySet(receiver, makeContext({ entries: {}, forEach: {}, has: {}, keys: {}, values: {}, [Symbol.iterator]: {} }));
506
- }
507
- return result;
508
- };
509
- } else if (target instanceof HTMLElement || target instanceof Number || target instanceof String || target instanceof Boolean) {
510
- return value.bind(target);
511
- } else {
512
- return value.bind(receiver);
513
- }
514
- }
515
- if (value && typeof value == "object") {
516
- return signal(value);
517
- }
518
- return value;
519
- },
520
- set: (target, property, value, receiver) => {
521
- value = value?.[Symbol.xRay] || value;
522
- let current = target[property];
523
- if (current !== value) {
524
- target[property] = value;
525
- notifySet(receiver, makeContext(property, { was: current, now: value }));
526
- }
527
- if (typeof current === "undefined") {
528
- notifySet(receiver, makeContext(iterate, {}));
529
- }
530
- return true;
531
- },
532
- has: (target, property) => {
533
- let receiver = signals.get(target);
534
- if (receiver) {
535
- notifyGet(receiver, property);
536
- }
537
- return Object.hasOwn(target, property);
538
- },
539
- deleteProperty: (target, property) => {
540
- if (typeof target[property] !== "undefined") {
541
- let current = target[property];
542
- delete target[property];
543
- let receiver = signals.get(target);
544
- notifySet(receiver, makeContext(property, { delete: true, was: current }));
545
- }
546
- return true;
547
- },
548
- defineProperty: (target, property, descriptor) => {
549
- if (typeof target[property] === "undefined") {
550
- let receiver = signals.get(target);
551
- notifySet(receiver, makeContext(iterate, {}));
552
- }
553
- return Object.defineProperty(target, property, descriptor);
554
- },
555
- ownKeys: (target) => {
556
- let receiver = signals.get(target);
557
- notifyGet(receiver, iterate);
558
- return Reflect.ownKeys(target);
559
- }
560
- };
561
- var signals = /* @__PURE__ */ new WeakMap();
562
- function signal(v) {
563
- if (!signals.has(v)) {
564
- signals.set(v, new Proxy(v, signalHandler));
565
- }
566
- return signals.get(v);
567
- }
568
- var batchedListeners = /* @__PURE__ */ new Set();
569
- var batchMode = 0;
570
- function notifySet(self, context = {}) {
571
- let listeners2 = [];
572
- context.forEach((change, property) => {
573
- let propListeners = getListeners(self, property);
574
- if (propListeners?.length) {
575
- for (let listener of propListeners) {
576
- addContext(listener, makeContext(property, change));
577
- }
578
- listeners2 = listeners2.concat(propListeners);
579
- }
580
- });
581
- listeners2 = new Set(listeners2.filter(Boolean));
582
- if (listeners2) {
583
- if (batchMode) {
584
- batchedListeners = batchedListeners.union(listeners2);
585
- } else {
586
- const currentEffect = computeStack[computeStack.length - 1];
587
- for (let listener of Array.from(listeners2)) {
588
- if (listener != currentEffect && listener?.needsUpdate) {
589
- listener();
590
- }
591
- clearContext(listener);
592
- }
593
- }
594
- }
595
- }
596
- function makeContext(property, change) {
597
- let context = /* @__PURE__ */ new Map();
598
- if (typeof property === "object") {
599
- for (let prop in property) {
600
- context.set(prop, property[prop]);
601
- }
602
- } else {
603
- context.set(property, change);
604
- }
605
- return context;
606
- }
607
- function addContext(listener, context) {
608
- if (!listener.context) {
609
- listener.context = context;
610
- } else {
611
- context.forEach((change, property) => {
612
- listener.context.set(property, change);
613
- });
614
- }
615
- listener.needsUpdate = true;
616
- }
617
- function clearContext(listener) {
618
- delete listener.context;
619
- delete listener.needsUpdate;
620
- }
621
- function notifyGet(self, property) {
622
- let currentCompute = computeStack[computeStack.length - 1];
623
- if (currentCompute) {
624
- setListeners(self, property, currentCompute);
625
- }
626
- }
627
- var listenersMap = /* @__PURE__ */ new WeakMap();
628
- var computeMap = /* @__PURE__ */ new WeakMap();
629
- function getListeners(self, property) {
630
- let listeners2 = listenersMap.get(self);
631
- return listeners2 ? Array.from(listeners2.get(property) || []) : [];
632
- }
633
- function setListeners(self, property, compute) {
634
- if (!listenersMap.has(self)) {
635
- listenersMap.set(self, /* @__PURE__ */ new Map());
636
- }
637
- let listeners2 = listenersMap.get(self);
638
- if (!listeners2.has(property)) {
639
- listeners2.set(property, /* @__PURE__ */ new Set());
640
- }
641
- listeners2.get(property).add(compute);
642
- if (!computeMap.has(compute)) {
643
- computeMap.set(compute, /* @__PURE__ */ new Map());
644
- }
645
- let connectedSignals = computeMap.get(compute);
646
- if (!connectedSignals.has(property)) {
647
- connectedSignals.set(property, /* @__PURE__ */ new Set());
648
- }
649
- connectedSignals.get(property).add(self);
650
- }
651
- function clearListeners(compute) {
652
- let connectedSignals = computeMap.get(compute);
653
- if (connectedSignals) {
654
- connectedSignals.forEach((property) => {
655
- property.forEach((s) => {
656
- let listeners2 = listenersMap.get(s);
657
- if (listeners2.has(property)) {
658
- listeners2.get(property).delete(compute);
659
- }
660
- });
661
- });
662
- }
663
- }
664
- var computeStack = [];
665
- var effectStack = [];
666
- var effectMap = /* @__PURE__ */ new WeakMap();
667
- var signalStack = [];
668
- function effect(fn) {
669
- if (effectStack.findIndex((f) => fn == f) !== -1) {
670
- throw new Error("Recursive update() call", { cause: fn });
671
- }
672
- effectStack.push(fn);
673
- let connectedSignal = signals.get(fn);
674
- if (!connectedSignal) {
675
- connectedSignal = signal({
676
- current: null
677
- });
678
- signals.set(fn, connectedSignal);
679
- }
680
- const computeEffect = function computeEffect2() {
681
- if (signalStack.findIndex((s) => s == connectedSignal) !== -1) {
682
- throw new Error("Cyclical dependency in update() call", { cause: fn });
683
- }
684
- clearListeners(computeEffect2);
685
- computeStack.push(computeEffect2);
686
- signalStack.push(connectedSignal);
687
- let result;
688
- try {
689
- result = fn(computeEffect2, computeStack, signalStack);
690
- } finally {
691
- computeStack.pop();
692
- signalStack.pop();
693
- if (result instanceof Promise) {
694
- result.then((result2) => {
695
- connectedSignal.current = result2;
696
- });
697
- } else {
698
- connectedSignal.current = result;
699
- }
700
- }
701
- };
702
- computeEffect.fn = fn;
703
- effectMap.set(connectedSignal, computeEffect);
704
- computeEffect();
705
- return connectedSignal;
706
- }
707
- function destroy(connectedSignal) {
708
- const computeEffect = effectMap.get(connectedSignal)?.deref();
709
- if (!computeEffect) {
710
- return;
711
- }
712
- clearListeners(computeEffect);
713
- let fn = computeEffect.fn;
714
- signals.remove(fn);
715
- effectMap.delete(connectedSignal);
716
- }
717
- function batch(fn) {
718
- batchMode++;
719
- let result;
720
- try {
721
- result = fn();
722
- } finally {
723
- if (result instanceof Promise) {
724
- result.then(() => {
725
- batchMode--;
726
- if (!batchMode) {
727
- runBatchedListeners();
435
+ let keyCombination = [];
436
+ if (e.ctrlKey && e.keyCode != KEY.Control) {
437
+ keyCombination.push("Control");
438
+ }
439
+ if (e.metaKey && e.keyCode != KEY.Meta) {
440
+ keyCombination.push("Meta");
441
+ }
442
+ if (e.altKey && e.keyCode != KEY.Alt) {
443
+ keyCombination.push("Alt");
444
+ }
445
+ if (e.shiftKey && e.keyCode != KEY.Shift) {
446
+ keyCombination.push("Shift");
447
+ }
448
+ keyCombination.push(e.key.toLowerCase());
449
+ let keyboards = [];
450
+ let keyboardElement = event.target.closest("[data-simply-keyboard]");
451
+ while (keyboardElement) {
452
+ keyboards.push(keyboardElement.dataset.simplyKeyboard);
453
+ keyboardElement = keyboardElement.parentNode.closest("[data-simply-keyboard]");
454
+ }
455
+ keyboards.push("");
456
+ let keyboard, subkeyboard;
457
+ let separators = ["+", "-"];
458
+ for (i in keyboards) {
459
+ keyboard = keyboards[i];
460
+ if (keyboard == "") {
461
+ subkeyboard = "default";
462
+ } else {
463
+ subkeyboard = keyboard;
464
+ keyboard += ".";
728
465
  }
729
- });
730
- } else {
731
- batchMode--;
732
- if (!batchMode) {
733
- runBatchedListeners();
734
- }
735
- }
736
- }
737
- return result;
738
- }
739
- function runBatchedListeners() {
740
- let copyBatchedListeners = Array.from(batchedListeners);
741
- batchedListeners = /* @__PURE__ */ new Set();
742
- const currentEffect = computeStack[computeStack.length - 1];
743
- for (let listener of copyBatchedListeners) {
744
- if (listener != currentEffect && listener?.needsUpdate) {
745
- listener();
746
- }
747
- clearContext(listener);
748
- }
749
- }
750
- function throttledEffect(fn, throttleTime) {
751
- if (effectStack.findIndex((f) => fn == f) !== -1) {
752
- throw new Error("Recursive update() call", { cause: fn });
753
- }
754
- effectStack.push(fn);
755
- let connectedSignal = signals.get(fn);
756
- if (!connectedSignal) {
757
- connectedSignal = signal({
758
- current: null
759
- });
760
- signals.set(fn, connectedSignal);
761
- }
762
- let throttled = false;
763
- let hasChange = true;
764
- const computeEffect = function computeEffect2() {
765
- if (signalStack.findIndex((s) => s == connectedSignal) !== -1) {
766
- throw new Error("Cyclical dependency in update() call", { cause: fn });
767
- }
768
- if (throttled && throttled > Date.now()) {
769
- hasChange = true;
770
- return;
771
- }
772
- clearListeners(computeEffect2);
773
- computeStack.push(computeEffect2);
774
- signalStack.push(connectedSignal);
775
- let result;
776
- try {
777
- result = fn(computeEffect2, computeStack, signalStack);
778
- } finally {
779
- hasChange = false;
780
- computeStack.pop();
781
- signalStack.pop();
782
- if (result instanceof Promise) {
783
- result.then((result2) => {
784
- connectedSignal.current = result2;
785
- });
786
- } else {
787
- connectedSignal.current = result;
788
- }
789
- }
790
- throttled = Date.now() + throttleTime;
791
- globalThis.setTimeout(() => {
792
- if (hasChange) {
793
- computeEffect2();
794
- }
795
- }, throttleTime);
796
- };
797
- computeEffect();
798
- return connectedSignal;
799
- }
800
- function clockEffect(fn, clock) {
801
- let connectedSignal = signals.get(fn);
802
- if (!connectedSignal) {
803
- connectedSignal = signal({
804
- current: null
805
- });
806
- signals.set(fn, connectedSignal);
807
- }
808
- let lastTick = -1;
809
- let hasChanged = true;
810
- const computeEffect = function computeEffect2() {
811
- if (lastTick < clock.time) {
812
- if (hasChanged) {
813
- clearListeners(computeEffect2);
814
- computeStack.push(computeEffect2);
815
- lastTick = clock.time;
816
- let result;
817
- try {
818
- result = fn(computeEffect2, computeStack);
819
- } finally {
820
- computeStack.pop();
821
- if (result instanceof Promise) {
822
- result.then((result2) => {
823
- connectedSignal.current = result2;
824
- });
825
- } else {
826
- connectedSignal.current = result;
466
+ for (let separator of separators) {
467
+ let keyString = keyCombination.join(separator);
468
+ if (this[subkeyboard] && typeof this[subkeyboard][keyString] == "function") {
469
+ let _continue = this[subkeyboard][keyString].call(this[subkeyboard], e);
470
+ if (!_continue) {
471
+ e.preventDefault();
472
+ return;
473
+ }
827
474
  }
828
- hasChanged = false;
829
- }
830
- } else {
831
- lastTick = clock.time;
832
- }
833
- } else {
834
- hasChanged = true;
835
- }
836
- };
837
- computeEffect();
838
- return connectedSignal;
839
- }
840
- function untracked(fn) {
841
- const remember = computeStack.slice();
842
- computeStack = [];
843
- try {
844
- return fn();
845
- } finally {
846
- computeStack = remember;
847
- }
848
- }
849
-
850
- // src/bind.mjs
851
- var SimplyBind = class {
852
- constructor(options) {
853
- this.bindings = /* @__PURE__ */ new Map();
854
- const defaultOptions = {
855
- container: document.body,
856
- attribute: "data-bind",
857
- transformers: [],
858
- defaultTransformers: [defaultTransformer]
859
- };
860
- if (!options?.root) {
861
- throw new Error("bind needs at least options.root set");
862
- }
863
- this.options = Object.assign({}, defaultOptions, options);
864
- const attribute = this.options.attribute;
865
- const render = (el) => {
866
- this.bindings.set(el, throttledEffect(() => {
867
- const context = {
868
- templates: el.querySelectorAll(":scope > template"),
869
- path: this.getBindingPath(el)
870
- };
871
- context.value = getValueByPath(this.options.root, context.path);
872
- context.element = el;
873
- runTransformers(context);
874
- }, 100));
875
- };
876
- const runTransformers = (context) => {
877
- let transformers = this.options.defaultTransformers || [];
878
- if (context.element.dataset.transform) {
879
- context.element.dataset.transform.split(" ").filter(Boolean).forEach((t) => {
880
- if (this.options.transformers[t]) {
881
- transformers.push(this.options.transformers[t]);
882
- } else {
883
- console.warn("No transformer with name " + t + " configured", { cause: context.element });
475
+ if (typeof this[subkeyboard + keyString] == "function") {
476
+ let _continue = this[subkeyboard + keyString].call(this, e);
477
+ if (!_continue) {
478
+ e.preventDefault();
479
+ return;
480
+ }
884
481
  }
885
- });
886
- }
887
- let next;
888
- for (let transformer of transformers) {
889
- next = /* @__PURE__ */ ((next2, transformer2) => {
890
- return (context2) => {
891
- return transformer2.call(this, context2, next2);
892
- };
893
- })(next, transformer);
894
- }
895
- next(context);
896
- };
897
- const applyBindings = (bindings2) => {
898
- for (let bindingEl of bindings2) {
899
- render(bindingEl);
900
- }
901
- };
902
- const updateBindings = (changes) => {
903
- for (const change of changes) {
904
- if (change.type == "childList" && change.addedNodes) {
905
- for (let node of change.addedNodes) {
906
- if (node instanceof HTMLElement) {
907
- let bindings2 = Array.from(node.querySelectorAll(`[${attribute}]`));
908
- if (node.matches(`[${attribute}]`)) {
909
- bindings2.unshift(node);
910
- }
911
- if (bindings2.length) {
912
- applyBindings(bindings2);
913
- }
482
+ if (this[selectedKeyboard] && this[selectedKeyboard][keyString]) {
483
+ let targets = options.app.container.querySelectorAll('[data-simply-accesskey="' + keyboard + keyString + '"]');
484
+ if (targets.length) {
485
+ targets.forEach((t) => t.click());
486
+ e.preventDefault();
914
487
  }
915
488
  }
916
489
  }
917
490
  }
918
491
  };
919
- this.observer = new MutationObserver((changes) => {
920
- updateBindings(changes);
921
- });
922
- this.observer.observe(options.container, {
923
- subtree: true,
924
- childList: true
925
- });
926
- const bindings = this.options.container.querySelectorAll("[" + this.options.attribute + "]:not(template)");
927
- if (bindings.length) {
928
- applyBindings(bindings);
929
- }
930
- }
931
- /**
932
- * Finds the first matching template and creates a new DocumentFragment
933
- * with the correct data bind attributes in it (prepends the current path)
934
- */
935
- applyTemplate(context) {
936
- const path = context.path;
937
- const templates = context.templates;
938
- const list = context.list;
939
- const index = context.index;
940
- const parent = context.parent;
941
- const value = list ? list[index] : context.value;
942
- let template = this.findTemplate(templates, value);
943
- if (!template) {
944
- let result = new DocumentFragment();
945
- result.innerHTML = "<!-- no matching template -->";
946
- return result;
947
- }
948
- let clone = template.content.cloneNode(true);
949
- if (!clone.children?.length) {
950
- throw new Error("template must contain a single html element", { cause: template });
951
- }
952
- if (clone.children.length > 1) {
953
- throw new Error("template must contain a single root node", { cause: template });
954
- }
955
- const bindings = clone.querySelectorAll("[" + this.options.attribute + "]");
956
- const attribute = this.options.attribute;
957
- for (let binding of bindings) {
958
- const bind2 = binding.getAttribute(attribute);
959
- if (bind2.substring(0, "#root.".length) == "#root.") {
960
- binding.setAttribute(attribute, bind2.substring("#root.".length));
961
- } else if (bind2 == "#value" && index != null) {
962
- binding.setAttribute(attribute, path + "." + index);
963
- } else if (index != null) {
964
- binding.setAttribute(attribute, path + "." + index + "." + bind2);
965
- } else {
966
- binding.setAttribute(attribute, parent + "." + bind2);
967
- }
968
- }
969
- if (typeof index !== "undefined") {
970
- clone.children[0].setAttribute(attribute + "-key", index);
971
- }
972
- clone.children[0].$bindTemplate = template;
973
- return clone;
974
- }
975
- getBindingPath(el) {
976
- return el.getAttribute(this.options.attribute);
977
- }
978
- /**
979
- * Finds the first template from an array of templates that
980
- * matches the given value.
981
- */
982
- findTemplate(templates, value) {
983
- const templateMatches = (t) => {
984
- let path = this.getBindingPath(t);
985
- let currentItem;
986
- if (path) {
987
- if (path.substr(0, 6) == "#root.") {
988
- currentItem = getValueByPath(this.options.root, path);
989
- } else {
990
- currentItem = getValueByPath(value, path);
991
- }
992
- } else {
993
- currentItem = value;
994
- }
995
- const strItem = "" + currentItem;
996
- let matches = t.getAttribute(this.options.attribute + "-match");
997
- if (matches) {
998
- if (matches === "#empty" && !currentItem) {
999
- return t;
1000
- } else if (matches === "#notempty" && currentItem) {
1001
- return t;
1002
- }
1003
- if (strItem.match(matches)) {
1004
- return t;
1005
- }
1006
- }
1007
- if (!matches) {
1008
- return t;
1009
- }
1010
- };
1011
- let template = Array.from(templates).find(templateMatches);
1012
- let rel = template?.getAttribute("rel");
1013
- if (rel) {
1014
- let replacement = document.querySelector("template#" + rel);
1015
- if (!replacement) {
1016
- throw new Error("Could not find template with id " + rel);
1017
- }
1018
- template = replacement;
1019
- }
1020
- return template;
1021
- }
1022
- destroy() {
1023
- this.bindings.forEach((binding) => {
1024
- destroy(binding);
1025
- });
1026
- this.bindings = /* @__PURE__ */ new Map();
1027
- this.observer.disconnect();
492
+ options.app.container.addEventListener("keydown", keyHandler);
1028
493
  }
1029
494
  };
1030
- function bind(options) {
1031
- return new SimplyBind(options);
1032
- }
1033
- function matchValue(a, b) {
1034
- if (a == "#empty" && !b) {
1035
- return true;
1036
- }
1037
- if (b == "#empty" && !a) {
1038
- return true;
1039
- }
1040
- if ("" + a == "" + b) {
1041
- return true;
1042
- }
1043
- return false;
1044
- }
1045
- function getValueByPath(root, path) {
1046
- let parts = path.split(".");
1047
- let curr = root;
1048
- let part, prevPart;
1049
- while (parts.length && curr) {
1050
- part = parts.shift();
1051
- if (part == "#key") {
1052
- return prevPart;
1053
- } else if (part == "#value") {
1054
- return curr;
1055
- } else if (part == "#root") {
1056
- curr = root;
1057
- } else {
1058
- part = decodeURIComponent(part);
1059
- curr = curr[part];
1060
- prevPart = part;
1061
- }
1062
- }
1063
- return curr;
1064
- }
1065
- function defaultTransformer(context) {
1066
- const el = context.element;
1067
- const templates = context.templates;
1068
- const templatesCount = templates.length;
1069
- const path = context.path;
1070
- const value = context.value;
1071
- const attribute = this.options.attribute;
1072
- if (Array.isArray(value) && templates?.length) {
1073
- transformArrayByTemplates.call(this, context);
1074
- } else if (typeof value == "object" && templates?.length) {
1075
- transformObjectByTemplates.call(this, context);
1076
- } else if (templates?.length) {
1077
- transformLiteralByTemplates.call(this, context);
1078
- } else if (el.tagName == "INPUT") {
1079
- transformInput.call(this, context);
1080
- } else if (el.tagName == "BUTTON") {
1081
- transformButton.call(this, context);
1082
- } else if (el.tagName == "SELECT") {
1083
- transformSelect.call(this, context);
1084
- } else if (el.tagName == "A") {
1085
- transformAnchor.call(this, context);
1086
- } else {
1087
- transformElement.call(this, context);
1088
- }
1089
- return context;
1090
- }
1091
- function transformArrayByTemplates(context) {
1092
- const el = context.element;
1093
- const templates = context.templates;
1094
- const templatesCount = templates.length;
1095
- const path = context.path;
1096
- const value = context.value;
1097
- const attribute = this.options.attribute;
1098
- let items = el.querySelectorAll(":scope > [" + attribute + "-key]");
1099
- let lastKey = 0;
1100
- let skipped = 0;
1101
- context.list = value;
1102
- for (let item2 of items) {
1103
- let currentKey = parseInt(item2.getAttribute(attribute + "-key"));
1104
- if (currentKey > lastKey) {
1105
- context.index = lastKey;
1106
- el.insertBefore(this.applyTemplate(context), item2);
1107
- } else if (currentKey < lastKey) {
1108
- item2.remove();
1109
- } else {
1110
- let bindings = Array.from(item2.querySelectorAll(`[${attribute}]`));
1111
- if (item2.matches(`[${attribute}]`)) {
1112
- bindings.unshift(item2);
1113
- }
1114
- let needsReplacement = bindings.find((b) => {
1115
- let databind = b.getAttribute(attribute);
1116
- return databind.substr(0, 5) !== "#root" && databind.substr(0, path.length) !== path;
1117
- });
1118
- if (!needsReplacement) {
1119
- if (item2.$bindTemplate) {
1120
- let newTemplate = this.findTemplate(templates, value[lastKey]);
1121
- if (newTemplate != item2.$bindTemplate) {
1122
- needsReplacement = true;
1123
- if (!newTemplate) {
1124
- skipped++;
1125
- }
1126
- }
1127
- }
1128
- }
1129
- if (needsReplacement) {
1130
- context.index = lastKey;
1131
- el.replaceChild(this.applyTemplate(context), item2);
1132
- }
1133
- }
1134
- lastKey++;
1135
- if (lastKey >= value.length) {
1136
- break;
1137
- }
1138
- }
1139
- items = el.querySelectorAll(":scope > [" + attribute + "-key]");
1140
- let length = items.length + skipped;
1141
- if (length > value.length) {
1142
- while (length > value.length) {
1143
- let child = el.querySelectorAll(":scope > :not(template)")?.[length - 1];
1144
- child?.remove();
1145
- length--;
1146
- }
1147
- } else if (length < value.length) {
1148
- while (length < value.length) {
1149
- context.index = length;
1150
- el.appendChild(this.applyTemplate(context));
1151
- length++;
1152
- }
1153
- }
1154
- }
1155
- function transformObjectByTemplates(context) {
1156
- const el = context.element;
1157
- const templates = context.templates;
1158
- const templatesCount = templates.length;
1159
- const path = context.path;
1160
- const value = context.value;
1161
- const attribute = this.options.attribute;
1162
- context.list = value;
1163
- let items = Array.from(el.querySelectorAll(":scope > [" + attribute + "-key]"));
1164
- for (let key in context.list) {
1165
- context.index = key;
1166
- let item2 = items.shift();
1167
- if (!item2) {
1168
- el.appendChild(this.applyTemplate(context));
1169
- continue;
1170
- }
1171
- if (item2.getAttribute[attribute + "-key"] != key) {
1172
- items.unshift(item2);
1173
- let outOfOrderItem = el.querySelector(":scope > [" + attribute + '-key="' + key + '"]');
1174
- if (!outOfOrderItem) {
1175
- let clone = this.applyTemplate(context);
1176
- if (clone.firstElementChild) {
1177
- el.insertBefore(clone, item2);
1178
- }
1179
- continue;
1180
- } else {
1181
- el.insertBefore(outOfOrderItem, item2);
1182
- item2 = outOfOrderItem;
1183
- items = items.filter((i) => i != outOfOrderItem);
1184
- }
1185
- }
1186
- let newTemplate = this.findTemplate(templates, value[key]);
1187
- if (newTemplate != item2.$bindTemplate) {
1188
- let clone = this.applyTemplate(context);
1189
- el.replaceChild(clone, item2);
1190
- }
1191
- }
1192
- while (items.length) {
1193
- item = items.shift();
1194
- item.remove();
1195
- }
1196
- }
1197
- function transformLiteralByTemplates(context) {
1198
- const el = context.element;
1199
- const templates = context.templates;
1200
- const value = context.value;
1201
- const attribute = this.options.attribute;
1202
- const rendered = el.querySelector(":scope > :not(template)");
1203
- const template = this.findTemplate(templates, value);
1204
- context.parent = el.parentElement?.closest(`[${attribute}]`)?.getAttribute(attribute) || "#root";
1205
- if (rendered) {
1206
- if (template) {
1207
- if (rendered?.$bindTemplate != template) {
1208
- const clone = this.applyTemplate(context);
1209
- el.replaceChild(clone, rendered);
1210
- }
1211
- } else {
1212
- el.removeChild(rendered);
1213
- }
1214
- } else if (template) {
1215
- const clone = this.applyTemplate(context);
1216
- el.appendChild(clone);
1217
- }
495
+ function keys(options = {}) {
496
+ return new SimplyKey(options);
1218
497
  }
1219
- function transformInput(context) {
1220
- const el = context.element;
1221
- const value = context.value;
1222
- if (el.type == "checkbox" || el.type == "radio") {
1223
- if (matchValue(el.value, value)) {
1224
- el.checked = true;
498
+
499
+ // src/view.mjs
500
+ function view(options) {
501
+ if (options.app) {
502
+ options.app.view = options.view || {};
503
+ const load = () => {
504
+ const data = options.app.view;
505
+ const path = globalThis.editor.data.getDataPath(options.app.container || document.body);
506
+ options.app.view = globalThis.editor.currentData[path];
507
+ Object.assign(options.app.view, data);
508
+ };
509
+ if (globalThis.editor && globalThis.editor.currentData) {
510
+ load();
1225
511
  } else {
1226
- el.checked = false;
1227
- }
1228
- } else if (!matchValue(el.value, value)) {
1229
- el.value = "" + value;
1230
- }
1231
- }
1232
- function transformButton(context) {
1233
- const el = context.element;
1234
- const value = context.value;
1235
- if (!matchValue(el.value, value)) {
1236
- el.value = "" + value;
1237
- }
1238
- }
1239
- function transformSelect(context) {
1240
- const el = context.element;
1241
- const value = context.value;
1242
- if (el.multiple) {
1243
- if (Array.isArray(value)) {
1244
- for (let option of el.options) {
1245
- if (value.indexOf(option.value) === false) {
1246
- option.selected = false;
1247
- } else {
1248
- option.selected = true;
1249
- }
1250
- }
512
+ document.addEventListener("simply-content-loaded", load);
1251
513
  }
514
+ return options.app.view;
1252
515
  } else {
1253
- let option = el.options.find((o) => matchValue(o.value, value));
1254
- if (option) {
1255
- option.selected = true;
1256
- }
1257
- }
1258
- }
1259
- function transformAnchor(context) {
1260
- const el = context.element;
1261
- const value = context.value;
1262
- if (value?.innerHTML && !matchValue(el.innerHTML, value.innerHTML)) {
1263
- el.innerHTML = "" + value.innerHTML;
1264
- }
1265
- if (value?.href && !matchValue(el.href, value.href)) {
1266
- el.href = "" + value.href;
1267
- }
1268
- }
1269
- function transformElement(context) {
1270
- const el = context.element;
1271
- const value = context.value;
1272
- if (!matchValue(el.innerHTML, value)) {
1273
- if (typeof value == "undefined" || value == null) {
1274
- el.innerHTML = "";
1275
- } else {
1276
- el.innerHTML = "" + value;
1277
- }
516
+ return options.view;
1278
517
  }
1279
518
  }
1280
519
 
@@ -1282,30 +521,29 @@
1282
521
  var SimplyApp = class {
1283
522
  constructor(options = {}) {
1284
523
  this.container = options.container || document.body;
1285
- if (!options.state) {
1286
- options.state = {};
1287
- }
1288
- this.state = signal(options.state);
1289
- if (options.commands) {
1290
- this.commands = commands({ app: this, container: this.container, commands: options.commands });
1291
- }
1292
- if (options.keys) {
1293
- this.keys = keys({ app: this, keys: options.keys });
1294
- }
1295
- if (options.routes) {
1296
- this.routes = routes({ app: this, routes: options.routes });
1297
- }
1298
- if (options.actions) {
1299
- this.actions = actions({ app: this, actions: options.actions });
1300
- }
1301
- let bindOptions = { container: this.container, root: this.state };
1302
- if (options.defaultTransformers) {
1303
- bindOptions.defaultTransformers = options.defaultTransformers;
1304
- }
1305
- if (options.transformers) {
1306
- bindOptions.transformers = options.transformers;
524
+ for (let key in options) {
525
+ switch (key) {
526
+ case "commands":
527
+ this.commands = commands({ app: this, container: this.container, commands: options.commands });
528
+ break;
529
+ case "keys":
530
+ case "keyboard":
531
+ this.keys = keys({ app: this, keys: options.keys });
532
+ break;
533
+ case "routes":
534
+ this.routes = routes({ app: this, routes: options.routes });
535
+ break;
536
+ case "actions":
537
+ this.actions = actions({ app: this, actions: options.actions });
538
+ break;
539
+ case "view":
540
+ this.view = view({ app: this, view: options.view });
541
+ break;
542
+ default:
543
+ this[key] = options[key];
544
+ break;
545
+ }
1307
546
  }
1308
- this.bind = bind(bindOptions);
1309
547
  }
1310
548
  };
1311
549
  function app(options = {}) {
@@ -1362,7 +600,7 @@
1362
600
  var waitForPreviousScripts = async () => {
1363
601
  return new Promise(function(resolve) {
1364
602
  var next = globalThis.document.createElement("script");
1365
- next.src = "javascript:document.dispatchEvent(new Event('simply-include-next'))";
603
+ next.src = "https://cdn.jsdelivr.net/gh/simplyedit/simplyview/dist/simply.include.next.js";
1366
604
  next.async = false;
1367
605
  globalThis.document.addEventListener("simply-include-next", () => {
1368
606
  head.removeChild(next);
@@ -1424,17 +662,19 @@
1424
662
  }
1425
663
  let scriptsFragment = globalThis.document.createDocumentFragment();
1426
664
  const scripts = fragment.querySelectorAll("script");
1427
- for (let script of scripts) {
1428
- let placeholder = globalThis.document.createComment(script.src || "inline script");
1429
- script.parentNode.insertBefore(placeholder, script);
1430
- script.dataset.simplyLocation = scriptLocations.length;
1431
- scriptLocations.push(placeholder);
1432
- scriptsFragment.appendChild(script);
665
+ if (scripts.length) {
666
+ for (let script of scripts) {
667
+ let placeholder = globalThis.document.createComment(script.src || "inline script");
668
+ script.parentNode.insertBefore(placeholder, script);
669
+ script.dataset.simplyLocation = scriptLocations.length;
670
+ scriptLocations.push(placeholder);
671
+ scriptsFragment.appendChild(script);
672
+ }
673
+ globalThis.setTimeout(function() {
674
+ include.scripts(Array.from(scriptsFragment.children), link ? link.href : globalThis.location.href);
675
+ }, 10);
1433
676
  }
1434
677
  link.parentNode.insertBefore(fragment, link ? link : null);
1435
- globalThis.setTimeout(function() {
1436
- include.scripts(scriptsFragment.childNodes, link ? link.href : globalThis.location.href);
1437
- }, 10);
1438
678
  }
1439
679
  };
1440
680
  var included = {};
@@ -1482,157 +722,16 @@
1482
722
  observe();
1483
723
  handleChanges2();
1484
724
 
1485
- // src/model.mjs
1486
- var model_exports = {};
1487
- __export(model_exports, {
1488
- columns: () => columns,
1489
- filter: () => filter,
1490
- model: () => model,
1491
- paging: () => paging,
1492
- sort: () => sort
1493
- });
1494
- var SimplyModel = class {
1495
- /**
1496
- * Creates a new datamodel, with a state property that contains
1497
- * all the data passed to this constructor
1498
- * @param state Object with all the data for this model
1499
- */
1500
- constructor(state) {
1501
- this.state = signal(state);
1502
- if (!this.state.options) {
1503
- this.state.options = {};
1504
- }
1505
- this.effects = [{ current: state.data }];
1506
- this.view = signal(state.data);
1507
- }
1508
- /**
1509
- * Adds an effect to run whenever a signal it depends on
1510
- * changes. this.state is the usual signal.
1511
- * The `fn` function param is not itself an effect, but must return
1512
- * and effect function. `fn` takes one param, which is the data signal.
1513
- * This signal will always have at least a `current` property.
1514
- * The result of the effect function is pushed on to the this.effects
1515
- * list. And the last effect added is set as this.view
1516
- */
1517
- addEffect(fn) {
1518
- const dataSignal = this.effects[this.effects.length - 1];
1519
- this.view = fn.call(this, dataSignal);
1520
- this.effects.push(this.view);
1521
- }
1522
- };
1523
- function model(options) {
1524
- return new SimplyModel(options);
1525
- }
1526
- function sort(options = {}) {
1527
- return function(data) {
1528
- this.state.options.sort = Object.assign({
1529
- direction: "asc",
1530
- sortBy: null,
1531
- sortFn: (a, b) => {
1532
- const sort2 = this.state.options.sort;
1533
- const sortBy = sort2.sortBy;
1534
- if (!sort2.sortBy) {
1535
- return 0;
1536
- }
1537
- const larger = sort2.direction == "asc" ? 1 : -1;
1538
- const smaller = sort2.direction == "asc" ? -1 : 1;
1539
- if (typeof a?.[sortBy] === "undefined") {
1540
- if (typeof b?.[sortBy] === "undefined") {
1541
- return 0;
1542
- }
1543
- return larger;
1544
- }
1545
- if (typeof b?.[sortBy] === "undefined") {
1546
- return smaller;
1547
- }
1548
- if (a[sortBy] < b[sortBy]) {
1549
- return smaller;
1550
- } else if (a[sortBy] > b[sortBy]) {
1551
- return larger;
1552
- } else {
1553
- return 0;
1554
- }
1555
- }
1556
- }, options);
1557
- return effect(() => {
1558
- const sort2 = this.state.options.sort;
1559
- if (sort2?.sortBy && sort2?.direction) {
1560
- return data.current.toSorted(sort2?.sortFn);
1561
- }
1562
- return data.current;
1563
- });
1564
- };
1565
- }
1566
- function paging(options = {}) {
1567
- return function(data) {
1568
- this.state.options.paging = Object.assign({
1569
- page: 1,
1570
- pageSize: 20,
1571
- max: 1
1572
- }, options);
1573
- return effect(() => {
1574
- return batch(() => {
1575
- const paging2 = this.state.options.paging;
1576
- if (!paging2.pageSize) {
1577
- paging2.pageSize = 20;
1578
- }
1579
- paging2.max = Math.ceil(this.state.data.length / paging2.pageSize);
1580
- paging2.page = Math.max(1, Math.min(paging2.max, paging2.page));
1581
- const start = (paging2.page - 1) * paging2.pageSize;
1582
- const end = start + paging2.pageSize;
1583
- return data.current.slice(start, end);
1584
- });
1585
- });
1586
- };
1587
- }
1588
- function filter(options) {
1589
- if (!options?.name || typeof options.name !== "string") {
1590
- throw new Error("filter requires options.name to be a string");
1591
- }
1592
- if (!options.matches || typeof options.matches !== "function") {
1593
- throw new Error("filter requires options.matches to be a function");
1594
- }
1595
- return function(data) {
1596
- this.state.options[options.name] = options;
1597
- return effect(() => {
1598
- if (this.state.options[options.name].enabled) {
1599
- return data.filter(this.state.options.matches);
1600
- }
1601
- });
1602
- };
1603
- }
1604
- function columns(options = {}) {
1605
- if (!options || typeof options !== "object" || Object.keys(options).length === 0) {
1606
- throw new Error("columns requires options to be an object with at least one property");
1607
- }
1608
- return function(data) {
1609
- this.state.options.columns = options;
1610
- return effect(() => {
1611
- return data.current.map((input) => {
1612
- let result = {};
1613
- for (let key of Object.keys(this.state.options.columns)) {
1614
- if (!this.state.options.columns[key].hidden) {
1615
- result[key] = input[key];
1616
- }
1617
- }
1618
- return result;
1619
- });
1620
- });
1621
- };
1622
- }
1623
-
1624
725
  // src/everything.mjs
1625
726
  var simply = {
1626
727
  activate,
1627
- action: action_exports,
728
+ action: actions,
1628
729
  app,
1629
- bind,
1630
- command: command_exports,
730
+ command: commands,
1631
731
  include,
1632
- key: key_exports,
1633
- model: model_exports,
1634
- route: route_exports,
1635
- state: state_exports
732
+ key: keys,
733
+ route: routes,
734
+ view
1636
735
  };
1637
736
  window.simply = simply;
1638
737
  var everything_default = simply;