simplyview 2.1.1 → 3.0.1

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,2088 +1,1658 @@
1
- /**
2
- * simply.observe
3
- * This component lets you observe changes in a json compatible data structure
4
- * It doesn't support linking the same object multiple times
5
- * It doesn't register deletion of properties using the delete keyword, assign
6
- * null to the property instead.
7
- * It doesn't register addition of new properties.
8
- * It doesn't register directly assigning new entries in an array on a previously
9
- * non-existant index.
10
- *
11
- * usage:
12
- *
13
- * (function) simply.observe( (object) model, (string) path, (function) callback)
14
- *
15
- * var model = { foo: { bar: 'baz' } };
16
- * var removeObserver = simply.observe(model, 'foo.bar', function(value, sourcePath) {
17
- * console.log(sourcePath+': '+value);
18
- * };
19
- *
20
- * The function returns a function that removes the observer when called.
21
- *
22
- * The component can observe in place changes in arrays, either by changing
23
- * an item in a specific index, by calling methods on the array that change
24
- * the array in place or by reassigning the array with a new value.
25
- *
26
- * The sourcePath contains the exact entry that was changed, the value is the
27
- * value for the path passed to simply.observe.
28
- * If an array method was called that changes the array in place, the sourcePath
29
- * also contains that method and its arguments JSON serialized.
30
- *
31
- * sourcePath parts are always seperated with '.', even for array indexes.
32
- * so if foo = [ 'bar' ], the path to 'bar' would be 'foo.0'
33
- */
34
-
35
- /*
36
- FIXME: child properties added after initial observe() call aren't added to the
37
- childListeners. onMissingChildren can't then find them.
38
- TODO: onMissingChildren must loop through all fields to get only the direct child
39
- properties for a given parent, keep seperate index for this?
40
- */
41
-
42
- (function (global) {
43
- 'use strict';
44
-
45
- var changeListeners = new WeakMap();
46
- var parentListeners = new WeakMap();
47
- var childListeners = new WeakMap();
48
- var changesSignalled = {};
49
- var observersPaused = 0;
50
-
51
- function signalChange(model, path, value, sourcePath) {
52
- if (observersPaused) {
53
- return;
54
- }
55
-
56
- sourcePath = sourcePath ? sourcePath : path;
57
- changesSignalled = {};
58
-
59
- var signalRecursion = function(model, path, value, sourcePath) {
60
- if (changeListeners.has(model) && changeListeners.get(model)[path]) {
61
- // changeListeners[model][path] contains callback methods
62
- changeListeners.get(model)[path].forEach(function(callback) {
63
- changesSignalled[path] = true;
64
- callback(value, sourcePath);
65
- });
66
- }
67
- };
68
-
69
- //TODO: check if this is correct
70
- //previous version only triggered parentListeners when no changeListeners were
71
- //triggered. that created problems with arrays. make an exhaustive unit test.
72
- signalRecursion(model, path, value, sourcePath);
73
-
74
- if (parentListeners.has(model) && parentListeners.get(model)[path]) {
75
- // parentListeners[model][path] contains child paths to signal change on
76
- // if a parent object is changed, this signals the change to the child objects
77
- parentListeners.get(model)[path].forEach(function(childPath) {
78
- if (!changesSignalled[childPath]) {
79
- var value = getByPath(model, childPath);
80
- if (value) {
81
- attach(model, childPath);
82
- }
83
- signalRecursion(model, childPath, value, sourcePath);
84
- changesSignalled[childPath] = true;
85
- }
86
- });
87
- }
88
-
89
- if (childListeners.has(model) && childListeners.get(model)[path]) {
90
- // childListeners[model][path] contains parent paths to signal change on
91
- // if a child object is changed, this signals the change to the parent objects
92
- childListeners.get(model)[path].forEach(function(parentPath) {
93
- if (!changesSignalled[parentPath]) {
94
- var value = getByPath(model, parentPath);
95
- signalRecursion(model, parentPath, value, sourcePath);
96
- changesSignalled[parentPath] = true;
97
- // check if the parent object still has this child property
98
- //FIXME: add a setter trigger here to restore observers once the child property get set again
99
-
100
- }
101
- });
102
- }
103
-
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
+ // src/activate.mjs
9
+ var listeners = /* @__PURE__ */ new Map();
10
+ var activate = {
11
+ addListener: (name, callback) => {
12
+ if (!listeners.has(name)) {
13
+ listeners.set(name, []);
14
+ }
15
+ listeners.get(name).push(callback);
16
+ initialCall(name);
17
+ },
18
+ removeListener: (name, callback) => {
19
+ if (!listeners.has(name)) {
20
+ return false;
21
+ }
22
+ listeners.set(name, listeners.get(name).filter((listener) => {
23
+ return listener != callback;
24
+ }));
104
25
  }
105
-
106
- function getByPath(model, path) {
107
- var parts = path.split('.');
108
- var curr = model;
109
- do {
110
- curr = curr[parts.shift()];
111
- } while (parts.length && curr);
112
- return curr;
26
+ };
27
+ function initialCall(name) {
28
+ const nodes = document.querySelectorAll('[data-simply-activate="' + name + '"]');
29
+ if (nodes) {
30
+ for (let node of nodes) {
31
+ callListeners(node);
32
+ }
113
33
  }
114
-
115
- function parent(path) {
116
- var parts = path.split('.');
117
- parts.pop();
118
- return parts.join('.');
34
+ }
35
+ function callListeners(node) {
36
+ const activate2 = node?.dataset?.simplyActivate;
37
+ if (activate2 && listeners.has(activate2)) {
38
+ for (let callback of listeners.get(activate2)) {
39
+ callback.call(node);
40
+ }
119
41
  }
120
-
121
- function head(path) {
122
- return path.split('.').shift();
42
+ }
43
+ function handleChanges(changes) {
44
+ let activateNodes = [];
45
+ for (let change of changes) {
46
+ if (change.type == "childList") {
47
+ for (let node of change.addedNodes) {
48
+ if (node.querySelectorAll) {
49
+ var toActivate = Array.from(node.querySelectorAll("[data-simply-activate]"));
50
+ if (node.matches("[data-simply-activate]")) {
51
+ toActivate.push(node);
52
+ }
53
+ activateNodes = activateNodes.concat(toActivate);
54
+ }
55
+ }
56
+ }
123
57
  }
124
-
125
- function onParents(model, path, callback) {
126
- var parent = '';
127
- var parentOb = model;
128
- var parents = path.split('.');
129
- do {
130
- var head = parents.shift();
131
- if (parentOb && typeof parentOb[head] != 'undefined') {
132
- callback(parentOb, head, (parent ? parent + '.' + head : head));
133
- parentOb = parentOb[head];
134
- }
135
- parent = (parent ? parent + '.' + head : head );
136
- } while (parents.length);
58
+ for (let node of activateNodes) {
59
+ callListeners(node);
137
60
  }
138
-
139
- function onChildren(model, path, callback) {
140
- var onChildObjects = function(object, path, callback) {
141
- if (typeof object != 'object' || object == null) {
142
- return;
143
- }
144
- if (Array.isArray(object)) {
145
- return;
146
- }
147
- // register the current keys
148
- Object.keys(object).forEach(function(key) {
149
- callback(object, key, path+'.'+key);
150
- onChildObjects(object[key], path+'.'+key, callback);
151
- });
152
- };
153
- var parent = getByPath(model, path);
154
- onChildObjects(parent, path, callback);
61
+ }
62
+ var observer = new MutationObserver(handleChanges);
63
+ observer.observe(document, {
64
+ subtree: true,
65
+ childList: true
66
+ });
67
+
68
+ // src/action.mjs
69
+ var action_exports = {};
70
+ __export(action_exports, {
71
+ actions: () => actions
72
+ });
73
+ function actions(options) {
74
+ if (options.app) {
75
+ const actionHandler = {
76
+ get: (target, property) => {
77
+ return target[property].bind(options.app);
78
+ }
79
+ };
80
+ return new Proxy(options.actions, actionHandler);
81
+ } else {
82
+ return options;
155
83
  }
156
-
157
- function onMissingChildren(model, path, callback) {
158
- var allChildren = Object.keys(childListeners.get(model) || []).filter(function(childPath) {
159
- return childPath.substr(0, path.length)==path && childPath.length>path.length;
160
- });
161
- if (!allChildren.length) {
162
- return;
163
- }
164
- var object = getByPath(model, path);
165
- var keysSeen = {};
166
- allChildren.forEach(function(childPath) {
167
- var key = head(childPath.substr(path.length+1));
168
- if (typeof object[key] == 'undefined') {
169
- if (!keysSeen[key]) {
170
- callback(object, key, path+'.'+key);
171
- keysSeen[key] = true;
172
- }
173
- } else {
174
- onMissingChildren(model, path+'.'+key, callback);
175
- }
176
- });
84
+ }
85
+
86
+ // src/route.mjs
87
+ var route_exports = {};
88
+ __export(route_exports, {
89
+ routes: () => routes
90
+ });
91
+ function routes(options) {
92
+ return new SimplyRoute(options);
93
+ }
94
+ var SimplyRoute = class {
95
+ constructor(options = {}) {
96
+ this.root = options.root || "/";
97
+ this.app = options.app;
98
+ this.clear();
99
+ if (options.routes) {
100
+ this.load(options.routes);
101
+ }
177
102
  }
178
-
179
- function addChangeListener(model, path, callback) {
180
- if (!changeListeners.has(model)) {
181
- changeListeners.set(model, {});
103
+ load(routes2) {
104
+ parseRoutes(routes2, this.routeInfo);
105
+ }
106
+ clear() {
107
+ this.routeInfo = [];
108
+ this.listeners = {
109
+ match: {},
110
+ call: {},
111
+ finish: {}
112
+ };
113
+ }
114
+ match(path, options) {
115
+ let args = {
116
+ path,
117
+ options
118
+ };
119
+ args = this.runListeners("match", args);
120
+ path = args.path ? args.path : path;
121
+ let matches;
122
+ if (!path) {
123
+ if (this.match(document.location.pathname + document.location.hash)) {
124
+ return true;
125
+ } else {
126
+ return this.match(document.location.pathname);
127
+ }
128
+ }
129
+ path = getPath(path);
130
+ for (let route of this.routeInfo) {
131
+ matches = route.match.exec(path);
132
+ if (matches && matches.length) {
133
+ var params = {};
134
+ route.params.forEach((key, i) => {
135
+ if (key == "*") {
136
+ key = "remainder";
137
+ }
138
+ params[key] = matches[i + 1];
139
+ });
140
+ Object.assign(params, options);
141
+ args.route = route;
142
+ args.params = params;
143
+ args = this.runListeners("call", args);
144
+ params = args.params ? args.params : params;
145
+ args.result = route.action.call(route, params);
146
+ this.runListeners("finish", args);
147
+ return args.result;
148
+ }
149
+ }
150
+ if (path && path[path.length - 1] != "/") {
151
+ return this.match(path + "/", options);
152
+ }
153
+ return false;
154
+ }
155
+ runListeners(action, params) {
156
+ if (!Object.keys(this.listeners[action])) {
157
+ return;
158
+ }
159
+ Object.keys(this.listeners[action]).forEach((route) => {
160
+ var routeRe = getRegexpFromRoute(route);
161
+ if (routeRe.exec(params.path)) {
162
+ var result;
163
+ for (let callback of this.listeners[action][route]) {
164
+ result = callback.call(this.app, params);
165
+ if (result) {
166
+ params = result;
167
+ }
168
+ }
169
+ }
170
+ });
171
+ return params;
172
+ }
173
+ handleEvents() {
174
+ globalThis.addEventListener("popstate", () => {
175
+ if (this.match(getPath(document.location.pathname + document.location.hash, this.root)) === false) {
176
+ this.match(getPath(document.location.pathname, this.root));
182
177
  }
183
- if (!changeListeners.get(model)[path]) {
184
- changeListeners.get(model)[path] = [];
178
+ });
179
+ globalThis.document.addEventListener("click", (evt) => {
180
+ if (evt.ctrlKey) {
181
+ return;
185
182
  }
186
- changeListeners.get(model)[path].push(callback);
187
-
188
- if (!parentListeners.has(model)) {
189
- parentListeners.set(model, {});
183
+ if (evt.which != 1) {
184
+ return;
190
185
  }
191
- var parentPath = parent(path);
192
- onParents(model, parentPath, function(parentOb, key, currPath) {
193
- if (!parentListeners.get(model)[currPath]) {
194
- parentListeners.get(model)[currPath] = [];
195
- }
196
- parentListeners.get(model)[currPath].push(path);
197
- });
198
-
199
- if (!childListeners.has(model)) {
200
- childListeners.set(model, {});
186
+ var link = evt.target;
187
+ while (link && link.tagName != "A") {
188
+ link = link.parentElement;
189
+ }
190
+ if (link && link.pathname && link.hostname == globalThis.location.hostname && !link.link && !link.dataset.simplyCommand) {
191
+ let path = getPath(link.pathname + link.hash, this.root);
192
+ if (!this.has(path)) {
193
+ path = getPath(link.pathname, this.root);
194
+ }
195
+ if (this.has(path)) {
196
+ let params = this.runListeners("goto", { path });
197
+ if (params.path) {
198
+ this.goto(params.path);
199
+ }
200
+ evt.preventDefault();
201
+ return false;
202
+ }
201
203
  }
202
- onChildren(model, path, function(childOb, key, currPath) {
203
- if (!childListeners.get(model)[currPath]) {
204
- childListeners.get(model)[currPath] = [];
205
- }
206
- childListeners.get(model)[currPath].push(path);
207
- });
204
+ });
208
205
  }
209
-
210
- function removeChangeListener(model, path, callback) {
211
- if (!changeListeners.has(model)) {
212
- return;
213
- }
214
- if (changeListeners.get(model)[path]) {
215
- changeListeners.get(model)[path] = changeListeners.get(model)[path].filter(function(f) {
216
- return f != callback;
217
- });
218
- }
206
+ goto(path) {
207
+ history.pushState({}, "", getURL(path));
208
+ return this.match(path);
219
209
  }
220
-
221
- function pauseObservers() {
222
- observersPaused++;
210
+ has(path) {
211
+ path = getPath(path, this.root);
212
+ for (let route of this.routeInfo) {
213
+ var matches = route.match.exec(path);
214
+ if (matches && matches.length) {
215
+ return true;
216
+ }
217
+ }
218
+ return false;
223
219
  }
224
-
225
- function resumeObservers() {
226
- observersPaused--;
220
+ addListener(action, route, callback) {
221
+ if (["goto", "match", "call", "finish"].indexOf(action) == -1) {
222
+ throw new Error("Unknown action " + action);
223
+ }
224
+ if (!this.listeners[action][route]) {
225
+ this.listeners[action][route] = [];
226
+ }
227
+ this.listeners[action][route].push(callback);
227
228
  }
228
-
229
- function attach(model, path, options) {
230
-
231
- var attachArray = function(object, path) {
232
- var desc = Object.getOwnPropertyDescriptor(object, 'push');
233
- if (!desc || desc.configurable) {
234
- for (var f of ['push','pop','reverse','shift','sort','splice','unshift','copyWithin']) {
235
- (function(f) {
236
- try {
237
- Object.defineProperty(object, f, {
238
- value: function() {
239
- pauseObservers();
240
- var result = Array.prototype[f].apply(this, arguments);
241
- attach(model, path);
242
- var args = [].slice.call(arguments).map(function(arg) {
243
- return JSON.stringify(arg);
244
- });
245
- resumeObservers();
246
- signalChange(model, path, this, path+'.'+f+'('+args.join(',')+')');
247
- return result;
248
- },
249
- readable: false,
250
- enumerable: false,
251
- configurable: false
252
- });
253
- } catch(e) {
254
- console.error('simply.observer: Error: Couldn\'t redefine array method '+f+' on '+path, e);
255
- }
256
- }(f));
257
- }
258
- for (var i=0, l=object.length; i<l; i++) {
259
- //FIXME: options becomes undefined here somewhere
260
- // if (options.skipArray) {
261
- addSetter(object, i, path+'.'+i);
262
- // } else {
263
- // attach(model, path+'.'+i, options);
264
- // }
265
- }
266
- }
267
- };
268
-
269
- var addSetTrigger = function(object, key, currPath) {
270
- Object.defineProperty(object, key, {
271
- set: function(value) {
272
- addSetter(object, key, currPath);
273
- object[key] = value;
274
- },
275
- configurable: true,
276
- readable: false,
277
- enumerable: false
278
- });
279
- };
280
-
281
- var addSetter = function(object, key, currPath) {
282
- if (Object.getOwnPropertyDescriptor(object, key).configurable) {
283
- // assume object keys are only unconfigurable if the
284
- // following code has already been run on this property
285
- var _value = object[key];
286
- Object.defineProperty(object, key, {
287
- set: function(value) {
288
- _value = value;
289
- signalChange(model, currPath, value);
290
- if (value!=null) {
291
- onChildren(model, currPath, addSetter);
292
- onMissingChildren(model, currPath, addSetTrigger);
293
- }
294
- },
295
- get: function() {
296
- return _value;
297
- },
298
- configurable: false,
299
- readable: true,
300
- enumerable: true
301
- });
302
- }
303
- if (Array.isArray(object[key])) {
304
- attachArray(object[key], currPath, options);
305
- }
306
- };
307
-
308
- onParents(model, path, addSetter);
309
- onChildren(model, path, addSetter);
229
+ removeListener(action, route, callback) {
230
+ if (["match", "call", "finish"].indexOf(action) == -1) {
231
+ throw new Error("Unknown action " + action);
232
+ }
233
+ if (!this.listeners[action][route]) {
234
+ return;
235
+ }
236
+ this.listeners[action][route] = this.listeners[action][route].filter((listener) => {
237
+ return listener != callback;
238
+ });
310
239
  }
311
-
312
- // FIXME: if you remove a key by reassigning the parent object
313
- // and then assign that missing key a new value
314
- // the observer doesn't get triggered
315
- // var model = { foo: { bar: 'baz' } };
316
- // simply.observer(model, 'foo.bar', ...)
317
- // model.foo = { }
318
- // model.foo.bar = 'zab'; // this should trigger the observer but doesn't
319
-
320
- var observe = function(model, path, callback, options) {
321
- if (!path) {
322
- var keys = Object.keys(model);
323
- keys.forEach(function(key) {
324
- attach(model, key, options);
325
- addChangeListener(model, key, callback);
326
- });
327
- return function() {
328
- keys.forEach(function(key) {
329
- removeChangeListener(model, key, callback);
330
- });
331
- };
332
- } else {
333
- attach(model, path, options);
334
- addChangeListener(model, path, callback);
335
- return function() {
336
- removeChangeListener(model, path, callback);
337
- };
338
- }
339
- };
340
-
341
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
342
- module.exports = observe;
343
- } else {
344
- if (!global.simply) {
345
- global.simply = {};
346
- }
347
- global.simply.observe = observe;
240
+ init(options) {
241
+ if (options.root) {
242
+ this.root = options.root;
243
+ }
348
244
  }
349
- })(this);(function(global) {
350
- 'use strict';
351
-
352
- var render = function(options) {
353
- if (!options) {
354
- options = {};
355
- }
356
- options = Object.assign({
357
- attribute: 'data-simply-field',
358
- selector: '[data-simply-field]',
359
- twoway: true,
360
- model: {}
361
- }, options);
362
-
363
- options.fieldTypes = Object.assign({
364
- '*': {
365
- set: function(value) {
366
- this.innerHTML = value;
367
- },
368
- get: function() {
369
- return this.innerHTML;
370
- }
371
- },
372
- 'input,textarea,select': {
373
- init: function(binding) {
374
- this.addEventListener('input', function() {
375
- if (binding.observing) {
376
- this.dispatchEvent(new Event('simply.bind.update', {
377
- bubbles: true,
378
- cancelable: true
379
- }));
380
- }
381
- });
382
- },
383
- set: function(value) {
384
- this.value = value;
385
- },
386
- get: function() {
387
- return this.value;
388
- }
389
- },
390
- 'input[type=radio]': {
391
- init: function(binding) {
392
- this.addEventListener('change', function() {
393
- if (binding.observing) {
394
- this.dispatchEvent(new Event('simply.bind.update', {
395
- bubbles: true,
396
- cancelable: true
397
- }));
398
- }
399
- });
400
- },
401
- set: function(value) {
402
- this.checked = (value==this.value);
403
- },
404
- get: function() {
405
- var checked;
406
- if (this.form) {
407
- return this.form[this.name].value;
408
- } else if (checked=document.body.querySelector('input[name="'+this.name+'"][checked]')) {
409
- return checked.value;
410
- } else {
411
- return null;
412
- }
413
- }
414
- },
415
- 'input[type=checkbox]': {
416
- init: function(binding) {
417
- this.addEventListener('change', function() {
418
- if (binding.observing) {
419
- this.dispatchEvent(new Event('simply.bind.update', {
420
- bubbles: true,
421
- cancelable: true
422
- }));
423
- }
424
- });
425
- },
426
- set: function(value) {
427
- this.checked = (value.checked);
428
- this.value = value.value;
429
- },
430
- get: function() {
431
- return {
432
- checked: this.checked,
433
- value: this.value
434
- };
435
- }
436
- },
437
- 'select[multiple]': {
438
- init: function(binding) {
439
- this.addEventListener('change', function() {
440
- if (binding.observing) {
441
- this.dispatchEvent(new Event('simply.bind.update', {
442
- bubbles: true,
443
- cancelable: true
444
- }));
445
- }
446
- });
447
- },
448
- set: function(value) {
449
- for (var i=0,l=this.options.length;i<l;i++) {
450
- this.options[i].selected = (value.indexOf(this.options[i].value)>=0);
451
- }
452
- },
453
- get: function() {
454
- return this.value;
455
- }
456
- },
457
- // '[data-simply-content="template"]': {
458
- // allowNesting: true
459
- // },
460
- }, options.fieldTypes);
461
-
462
- return options;
463
- };
464
-
465
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
466
- module.exports = render;
467
- } else {
468
- if (!global.simply) {
469
- global.simply = {};
470
- }
471
- global.simply.render = render;
245
+ };
246
+ function getPath(path, root = "/") {
247
+ if (path.substring(0, root.length) == root || root[root.length - 1] == "/" && path.length == root.length - 1 && path == root.substring(0, path.length)) {
248
+ path = path.substring(root.length);
472
249
  }
473
- })(this);
474
- (function(global) {
475
- 'us strict';
476
-
477
- var path = {
478
- get: function(model, path) {
479
- if (!path) {
480
- return model;
481
- }
482
- return path.split('.').reduce(function(acc, name) {
483
- return (acc && acc[name] ? acc[name] : null);
484
- }, model);
485
- },
486
- set: function(model, path, value) {
487
- var lastName = simply.path.pop(path);
488
- var parentPath = simply.path.parent(path);
489
- var parentOb = simply.path.get(model, parentPath);
490
- parentOb[lastName] = value;
491
- },
492
- pop: function(path) {
493
- return path.split('.').pop();
494
- },
495
- push: function(path, name) {
496
- return (path ? path + '.' : '') + name;
497
- },
498
- parent: function(path) {
499
- var p = path.split('.');
500
- p.pop();
501
- return p.join('.');
502
- },
503
- parents: function(path) {
504
- var result = [];
505
- path.split('.').reduce(function(acc, name) {
506
- acc.push( (acc.length ? acc[acc.length-1] + '.' : '') + name );
507
- return acc;
508
- },result);
509
- return result;
510
- }
511
- };
512
-
513
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
514
- module.exports = path;
515
- } else {
516
- if (!global.simply) {
517
- global.simply = {};
518
- }
519
- global.simply.path = path;
250
+ if (path[0] != "/" && path[0] != "#") {
251
+ path = "/" + path;
520
252
  }
521
- })(this);
522
- (function(global) {
523
- 'use strict';
524
-
525
- var routeInfo = [];
526
- var listeners = {
527
- goto: {},
528
- match: {},
529
- call: {},
530
- finish: {}
531
- };
532
-
533
- function getRegexpFromRoute(route) {
534
- return new RegExp('^'+route.replace(/:\w+/g, '([^/]+)').replace(/:\*/, '(.*)'));
253
+ return path;
254
+ }
255
+ function getURL(path, root) {
256
+ path = getPath(path, root);
257
+ if (root[root.length - 1] === "/" && path[0] === "/") {
258
+ path = path.substring(1);
535
259
  }
536
-
537
- function parseRoutes(routes) {
538
- var paths = Object.keys(routes);
539
- var matchParams = /:(\w+|\*)/g;
540
- var matches, params, path;
541
- for (var i=0; i<paths.length; i++) {
542
- path = paths[i];
543
- matches = [];
544
- params = [];
545
- do {
546
- matches = matchParams.exec(path);
547
- if (matches) {
548
- params.push(matches[1]);
549
- }
550
- } while(matches);
551
- routeInfo.push({
552
- match: getRegexpFromRoute(path),
553
- params: params,
554
- action: routes[path]
555
- });
556
- }
260
+ return root + path;
261
+ }
262
+ function getRegexpFromRoute(route) {
263
+ return new RegExp("^" + route.replace(/:\w+/g, "([^/]+)").replace(/:\*/, "(.*)"));
264
+ }
265
+ function parseRoutes(routes2) {
266
+ let routeInfo = [];
267
+ const paths = Object.keys(routes2);
268
+ const matchParams = /:(\w+|\*)/g;
269
+ for (let path of paths) {
270
+ let matches = [];
271
+ let params = [];
272
+ do {
273
+ matches = matchParams.exec(path);
274
+ if (matches) {
275
+ params.push(matches[1]);
276
+ }
277
+ } while (matches);
278
+ routeInfo.push({
279
+ match: getRegexpFromRoute(path),
280
+ params,
281
+ action: routes2[path]
282
+ });
557
283
  }
558
-
559
- var linkHandler = function(evt) {
560
- if (evt.ctrlKey) {
561
- return;
562
- }
563
- if (evt.which != 1) {
564
- return; // not a 'left' mouse click
565
- }
566
- var link = evt.target;
567
- while (link && link.tagName!='A') {
568
- link = link.parentElement;
569
- }
570
- if (link
571
- && link.pathname
572
- && link.hostname==global.location.hostname
573
- && !link.link
574
- && !link.dataset.simplyCommand
575
- ) {
576
- let path = getPath(link.pathname+link.hash);
577
- if ( !route.has(path) ) {
578
- path = getPath(link.pathname);
579
- }
580
- if ( route.has(path) ) {
581
- let params = runListeners('goto', { path: path});
582
- if (params.path) {
583
- route.goto(params.path);
584
- }
585
- evt.preventDefault();
586
- return false;
587
- }
588
- }
589
- };
590
-
591
- var options = {
592
- root: '/'
593
- };
594
-
595
- var getPath = function(path) {
596
- if (path.substring(0,options.root.length)==options.root
597
- ||
598
- ( options.root[options.root.length-1]=='/'
599
- && path.length==(options.root.length-1)
600
- && path == options.root.substring(0,path.length)
601
- )
602
- ) {
603
- path = path.substring(options.root.length);
604
- }
605
- if (path[0]!='/' && path[0]!='#') {
606
- path = '/'+path;
607
- }
608
- return path;
609
- };
610
-
611
- var getUrl = function(path) {
612
- path = getPath(path);
613
- if (options.root[options.root.length-1]==='/' && path[0]==='/') {
614
- path = path.substring(1);
615
- }
616
- return options.root + path;
617
- };
618
-
619
- function runListeners(action, params) {
620
- if (!Object.keys(listeners[action])) {
621
- return;
622
- }
623
- Object.keys(listeners[action]).forEach(function(route) {
624
- var routeRe = getRegexpFromRoute(route);
625
- if (routeRe.exec(params.path)) {
626
- var result;
627
- listeners[action][route].forEach(function(callback) {
628
- result = callback.call(global, params);
629
- if (result) {
630
- params = result;
631
- }
632
- });
633
- }
634
- });
635
- return params;
284
+ return routeInfo;
285
+ }
286
+
287
+ // src/command.mjs
288
+ var command_exports = {};
289
+ __export(command_exports, {
290
+ commands: () => commands
291
+ });
292
+ var SimplyCommands = class {
293
+ constructor(options = {}) {
294
+ if (!options.app) {
295
+ options.app = {};
296
+ }
297
+ if (!options.app.container) {
298
+ options.app.container = document.body;
299
+ }
300
+ this.$handlers = options.handlers || defaultHandlers;
301
+ if (options.commands) {
302
+ Object.assign(this, options.commands);
303
+ }
304
+ const commandHandler = (evt) => {
305
+ const command = getCommand(evt, this.$handlers);
306
+ if (!command) {
307
+ return;
308
+ }
309
+ if (!this[command.name]) {
310
+ console.error("simply.command: undefined command " + command.name, command.source);
311
+ return;
312
+ }
313
+ const shouldContinue = this[command.name].call(options.app, command.source, command.value);
314
+ if (shouldContinue === false) {
315
+ evt.preventDefault();
316
+ evt.stopPropagation();
317
+ return false;
318
+ }
319
+ };
320
+ options.app.container.addEventListener("click", commandHandler);
321
+ options.app.container.addEventListener("submit", commandHandler);
322
+ options.app.container.addEventListener("change", commandHandler);
323
+ options.app.container.addEventListener("input", commandHandler);
636
324
  }
637
-
638
- var route = {
639
- handleEvents: function() {
640
- global.addEventListener('popstate', function() {
641
- if (route.match(getPath(document.location.pathname + document.location.hash)) === false) {
642
- route.match(getPath(document.location.pathname));
643
- }
644
- });
645
- global.document.addEventListener('click', linkHandler);
646
- },
647
- load: function(routes) {
648
- parseRoutes(routes);
649
- },
650
- clear: function() {
651
- routeInfo = [];
652
- listeners = {
653
- match: {},
654
- call: {},
655
- finish: {}
325
+ };
326
+ function commands(options = {}) {
327
+ return new SimplyCommands(options);
328
+ }
329
+ function getCommand(evt, handlers) {
330
+ var el = evt.target.closest("[data-simply-command]");
331
+ if (el) {
332
+ for (let handler of handlers) {
333
+ if (el.matches(handler.match)) {
334
+ if (handler.check(el, evt)) {
335
+ return {
336
+ name: el.dataset.simplyCommand,
337
+ source: el,
338
+ value: handler.get(el)
656
339
  };
657
- },
658
- match: function(path, options) {
659
- var args = {
660
- path: path,
661
- options: options
662
- };
663
- args = runListeners('match',args);
664
- path = args.path ? args.path : path;
665
-
666
- var matches;
667
- if (!path) {
668
- if (route.match(document.location.pathname+document.location.hash)) {
669
- return true;
670
- } else {
671
- return route.match(document.location.pathname);
672
- }
673
- }
674
- path = getPath(path);
675
- for ( var i=0; i<routeInfo.length; i++) {
676
- matches = routeInfo[i].match.exec(path);
677
- if (!matches || !matches.length) {
678
- if (path && path[path.length-1]!='/') {
679
- matches = routeInfo[i].match.exec(path+'/');
680
- if (matches) {
681
- path+='/';
682
- history.replaceState({}, '', getUrl(path));
683
- }
684
- }
685
- }
686
- if (matches && matches.length) {
687
- var params = {};
688
- routeInfo[i].params.forEach(function(key, i) {
689
- if (key=='*') {
690
- key = 'remainder';
691
- }
692
- params[key] = matches[i+1];
693
- });
694
- Object.assign(params, options);
695
- args.route = route;
696
- args.params = params;
697
- args = runListeners('call', args);
698
- params = args.params ? args.params : params;
699
- args.result = routeInfo[i].action.call(route, params);
700
- runListeners('finish', args);
701
- return args.result;
702
- }
703
- }
704
- return false;
705
- },
706
- goto: function(path) {
707
- history.pushState({},'',getUrl(path));
708
- return route.match(path);
709
- },
710
- has: function(path) {
711
- path = getPath(path);
712
- for ( var i=0; i<routeInfo.length; i++) {
713
- var matches = routeInfo[i].match.exec(path);
714
- if (matches && matches.length) {
715
- return true;
716
- }
717
- }
718
- return false;
719
- },
720
- addListener: function(action, route, callback) {
721
- if (['goto','match','call','finish'].indexOf(action)==-1) {
722
- throw new Error('Unknown action '+action);
723
- }
724
- if (!listeners[action][route]) {
725
- listeners[action][route] = [];
726
- }
727
- listeners[action][route].push(callback);
728
- },
729
- removeListener: function(action, route, callback) {
730
- if (['match','call','finish'].indexOf(action)==-1) {
731
- throw new Error('Unknown action '+action);
732
- }
733
- if (!listeners[action][route]) {
734
- return;
735
- }
736
- listeners[action][route] = listeners[action][route].filter(function(listener) {
737
- return listener != callback;
738
- });
739
- },
740
- init: function(params) {
741
- if (params.root) {
742
- options.root = params.root;
743
- }
744
- }
745
- };
746
-
747
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
748
- module.exports = route;
749
- } else {
750
- if (!global.simply) {
751
- global.simply = {};
340
+ }
341
+ return null;
752
342
  }
753
- global.simply.route = route;
343
+ }
754
344
  }
755
- })(this);
756
- (function(global) {
757
- 'use strict';
758
-
759
- var listeners = {};
760
-
761
- var activate = {
762
- addListener: function(name, callback) {
763
- if (!listeners[name]) {
764
- listeners[name] = [];
345
+ return null;
346
+ }
347
+ var defaultHandlers = [
348
+ {
349
+ match: "input,select,textarea",
350
+ get: function(el) {
351
+ if (el.tagName === "SELECT" && el.multiple) {
352
+ let values = [];
353
+ for (let option of el.options) {
354
+ if (option.selected) {
355
+ values.push(option.value);
356
+ }
357
+ }
358
+ return values;
359
+ }
360
+ return el.dataset.simplyValue || el.value;
361
+ },
362
+ check: function(el, evt) {
363
+ return evt.type == "change" || el.dataset.simplyImmediate && evt.type == "input";
364
+ }
365
+ },
366
+ {
367
+ match: "a,button",
368
+ get: function(el) {
369
+ return el.dataset.simplyValue || el.href || el.value;
370
+ },
371
+ check: function(el, evt) {
372
+ return evt.type == "click" && evt.ctrlKey == false && evt.button == 0;
373
+ }
374
+ },
375
+ {
376
+ match: "form",
377
+ get: function(el) {
378
+ let data = {};
379
+ for (let input of Array.from(el.elements)) {
380
+ if (input.tagName == "INPUT" && (input.type == "checkbox" || input.type == "radio")) {
381
+ if (!input.checked) {
382
+ return;
383
+ }
384
+ }
385
+ if (data[input.name] && !Array.isArray(data[input.name])) {
386
+ data[input.name] = [data[input.name]];
387
+ }
388
+ if (Array.isArray(data[input.name])) {
389
+ data[input.name].push(input.value);
390
+ } else {
391
+ data[input.name] = input.value;
392
+ }
393
+ }
394
+ return data;
395
+ },
396
+ check: function(el, evt) {
397
+ return evt.type == "submit";
398
+ }
399
+ },
400
+ {
401
+ match: "*",
402
+ get: function(el) {
403
+ return el.dataset.simplyValue;
404
+ },
405
+ check: function(el, evt) {
406
+ return evt.type == "click" && evt.ctrlKey == false && evt.button == 0;
407
+ }
408
+ }
409
+ ];
410
+
411
+ // src/key.mjs
412
+ var key_exports = {};
413
+ __export(key_exports, {
414
+ keys: () => keys
415
+ });
416
+ var SimplyKeys = class {
417
+ constructor(options = {}) {
418
+ if (!options.app) {
419
+ options.app = {};
420
+ }
421
+ if (!options.app.container) {
422
+ options.app.container = document.body;
423
+ }
424
+ Object.assign(this, options.keys);
425
+ const keyHandler = (e) => {
426
+ if (e.isComposing || e.keyCode === 229) {
427
+ return;
428
+ }
429
+ if (e.defaultPrevented) {
430
+ return;
431
+ }
432
+ if (!e.target) {
433
+ return;
434
+ }
435
+ let selectedKeyboard = "default";
436
+ if (e.target.closest("[data-simply-keyboard]")) {
437
+ selectedKeyboard = e.target.closest("[data-simply-keyboard]").dataset.simplyKeyboard;
438
+ }
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 }));
765
494
  }
766
- listeners[name].push(callback);
767
- initialCall(name);
768
- },
769
- removeListener: function(name, callback) {
770
- if (!listeners[name]) {
771
- return false;
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 }));
772
503
  }
773
- listeners[name] = listeners[name].filter(function(listener) {
774
- return listener!=callback;
775
- });
776
- }
777
- };
778
-
779
- var initialCall = function(name) {
780
- var nodes = document.querySelectorAll('[data-simply-activate="'+name+'"]');
781
- if (nodes) {
782
- [].forEach.call(nodes, function(node) {
783
- callListeners(node);
784
- });
785
- }
786
- };
787
-
788
- var callListeners = function(node) {
789
- if (node && node.dataset.simplyActivate
790
- && listeners[node.dataset.simplyActivate]
791
- ) {
792
- listeners[node.dataset.simplyActivate].forEach(function(callback) {
793
- callback.call(node);
794
- });
795
- }
796
- };
797
-
798
- var handleChanges = function(changes) {
799
- var activateNodes = [];
800
- for (var change of changes) {
801
- if (change.type=='childList') {
802
- [].forEach.call(change.addedNodes, function(node) {
803
- if (node.querySelectorAll) {
804
- var toActivate = [].slice.call(node.querySelectorAll('[data-simply-activate]'));
805
- if (node.matches('[data-simply-activate]')) {
806
- toActivate.push(node);
807
- }
808
- activateNodes = activateNodes.concat(toActivate);
809
- }
810
- });
504
+ if (["set", "add", "clear", "delete"].includes(property)) {
505
+ notifySet(receiver, makeContext({ entries: {}, forEach: {}, has: {}, keys: {}, values: {}, [Symbol.iterator]: {} }));
811
506
  }
812
- }
813
- if (activateNodes.length) {
814
- activateNodes.forEach(function(node) {
815
- callListeners(node);
816
- });
817
- }
818
- };
819
-
820
- var observer = new MutationObserver(handleChanges);
821
- observer.observe(document, {
822
- subtree: true,
823
- childList: true
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
+ }
824
580
  });
825
-
826
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
827
- module.exports = activate;
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
+ }
828
602
  } else {
829
- if (!global.simply) {
830
- global.simply = {};
831
- }
832
- global.simply.activate = activate;
603
+ context.set(property, change);
833
604
  }
834
- })(this);
835
- (function(global) {
836
- 'use strict';
837
-
838
- var knownCollections = {};
839
-
840
- var collect = {
841
- addListener: function(name, callback) {
842
- if (!knownCollections[name]) {
843
- knownCollections[name] = [];
844
- }
845
- if (knownCollections[name].indexOf(callback) == -1) {
846
- knownCollections[name].push(callback);
847
- }
848
- },
849
- removeListener: function(name, callback) {
850
- if (knownCollections[name]) {
851
- var index = knownCollections[name].indexOf(callback);
852
- if (index>=0) {
853
- knownCollections[name].splice(index, 1);
854
- }
855
- }
856
- },
857
- update: function(element, value) {
858
- element.value = value;
859
- element.dispatchEvent(new Event('change', {
860
- bubbles: true,
861
- cancelable: true
862
- }));
863
- }
864
- };
865
-
866
- function findCollection(el) {
867
- while (el && !el.dataset.simplyCollection) {
868
- el = el.parentElement;
869
- }
870
- return el;
871
- }
872
-
873
- global.addEventListener('change', function(evt) {
874
- var root = null;
875
- var name = '';
876
- if (evt.target.dataset.simplyElement) {
877
- root = findCollection(evt.target);
878
- if (root && root.dataset) {
879
- name = root.dataset.simplyCollection;
880
- }
881
- }
882
- if (name && knownCollections[name]) {
883
- var inputs = root.querySelectorAll('[data-simply-element]');
884
- var elements = [].reduce.call(inputs, function(elements, input) {
885
- elements[input.dataset.simplyElement] = input;
886
- return elements;
887
- }, {});
888
- for (var i=knownCollections[name].length-1; i>=0; i--) {
889
- var result = knownCollections[name][i].call(evt.target.form, elements);
890
- if (result === false) {
891
- break;
892
- }
893
- }
894
- }
895
- }, true);
896
-
897
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
898
- module.exports = collect;
605
+ return context;
606
+ }
607
+ function addContext(listener, context) {
608
+ if (!listener.context) {
609
+ listener.context = context;
899
610
  } else {
900
- if (!global.simply) {
901
- global.simply = {};
902
- }
903
- global.simply.collect = collect;
611
+ context.forEach((change, property) => {
612
+ listener.context.set(property, change);
613
+ });
904
614
  }
905
-
906
- })(this);
907
- (function(global) {
908
- 'use strict';
909
-
910
- var defaultCommands = {
911
- 'simply-hide': function(el, value) {
912
- var target = this.app.get(value);
913
- if (target) {
914
- this.action('simply-hide',target);
915
- }
916
- },
917
- 'simply-show': function(el, value) {
918
- var target = this.app.get(value);
919
- if (target) {
920
- this.action('simply-show',target);
921
- }
922
- },
923
- 'simply-select': function(el, value) {
924
- var group = el.dataset.simplyGroup;
925
- var target = this.app.get(value);
926
- var targetGroup = (target ? target.dataset.simplyGroup : null);
927
- this.action('simply-select', el, group, target, targetGroup);
928
- },
929
- 'simply-toggle-select': function(el, value) {
930
- var group = el.dataset.simplyGroup;
931
- var target = this.app.get(value);
932
- var targetGroup = (target ? target.dataset.simplyTarget : null);
933
- this.action('simply-toggle-select',el,group,target,targetGroup);
934
- },
935
- 'simply-toggle-class': function(el, value) {
936
- var target = this.app.get(el.dataset.simplyTarget);
937
- this.action('simply-toggle-class',el,value,target);
938
- },
939
- 'simply-deselect': function(el, value) {
940
- var target = this.app.get(value);
941
- this.action('simply-deselect',el,target);
942
- },
943
- 'simply-fullscreen': function(el, value) {
944
- var target = this.app.get(value);
945
- this.action('simply-fullscreen',target);
946
- }
947
- };
948
-
949
-
950
- var handlers = [
951
- {
952
- match: 'input,select,textarea',
953
- get: function(el) {
954
- if (el.tagName==='SELECT' && el.multiple) {
955
- var values = [], opt;
956
- for (var i=0,l=el.options.length;i<l;i++) {
957
- var opt = el.options[i];
958
- if (opt.selected) {
959
- values.push(opt.value);
960
- }
961
- }
962
- return values;
963
- }
964
- return el.dataset.simplyValue || el.value;
965
- },
966
- check: function(el, evt) {
967
- return evt.type=='change' || (el.dataset.simplyImmediate && evt.type=='input');
968
- }
969
- },
970
- {
971
- match: 'a,button',
972
- get: function(el) {
973
- return el.dataset.simplyValue || el.href || el.value;
974
- },
975
- check: function(el,evt) {
976
- return evt.type=='click' && evt.ctrlKey==false && evt.button==0;
977
- }
978
- },
979
- {
980
- match: 'form',
981
- get: function(el) {
982
- var data = {};
983
- [].forEach.call(el.elements, function(el) {
984
- if (el.tagName=='INPUT' && (el.type=='checkbox' || el.type=='radio')) {
985
- if (!el.checked) {
986
- return;
987
- }
988
- }
989
- if (data[el.name] && !Array.isArray(data[el.name])) {
990
- data[el.name] = [data[el.name]];
991
- }
992
- if (Array.isArray(data[el.name])) {
993
- data[el.name].push(el.value);
994
- } else {
995
- data[el.name] = el.value;
996
- }
997
- });
998
- return data;//new FormData(el);
999
- },
1000
- check: function(el,evt) {
1001
- return evt.type=='submit';
1002
- }
1003
- }
1004
- ];
1005
-
1006
- var fallbackHandler = {
1007
- get: function(el) {
1008
- return el.dataset.simplyValue;
1009
- },
1010
- check: function(el, evt) {
1011
- return evt.type=='click' && evt.ctrlKey==false && evt.button==0;
1012
- }
1013
- };
1014
-
1015
- function getCommand(evt) {
1016
- var el = evt.target.closest('[data-simply-command]');
1017
- if (el) {
1018
- var matched = false;
1019
- for (var i=handlers.length-1; i>=0; i--) {
1020
- if (el.matches(handlers[i].match)) {
1021
- matched = true;
1022
- if (handlers[i].check(el, evt)) {
1023
- return {
1024
- name: el.dataset.simplyCommand,
1025
- source: el,
1026
- value: handlers[i].get(el)
1027
- };
1028
- }
1029
- }
1030
- }
1031
- if (!matched && fallbackHandler.check(el,evt)) {
1032
- return {
1033
- name: el.dataset.simplyCommand,
1034
- source: el,
1035
- value: fallbackHandler.get(el)
1036
- };
1037
- }
1038
- }
1039
- return null;
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);
1040
625
  }
1041
-
1042
- var command = function(app, inCommands) {
1043
-
1044
- var commands = Object.create(defaultCommands);
1045
- for (var i in inCommands) {
1046
- commands[i] = inCommands[i];
1047
- }
1048
-
1049
- commands.app = app;
1050
-
1051
- commands.action = function(name) {
1052
- var params = Array.prototype.slice.call(arguments);
1053
- params.shift();
1054
- return app.actions[name].apply(app.actions,params);
1055
- };
1056
-
1057
- commands.call = function(name) {
1058
- var params = Array.prototype.slice.call(arguments);
1059
- params.shift();
1060
- return this[name].apply(this,params);
1061
- };
1062
-
1063
- commands.appendHandler = function(handler) {
1064
- handlers.push(handler);
1065
- };
1066
-
1067
- commands.prependHandler = function(handler) {
1068
- handlers.unshift(handler);
1069
- };
1070
-
1071
- var commandHandler = function(evt) {
1072
- var command = getCommand(evt);
1073
- if ( command ) {
1074
- if (!commands[command.name]) {
1075
- console.error('simply.command: undefined command '+command.name, command.source);
1076
- } else {
1077
- commands.call(command.name, command.source, command.value);
1078
- evt.preventDefault();
1079
- evt.stopPropagation();
1080
- return false;
1081
- }
1082
- }
1083
- };
1084
-
1085
- app.container.addEventListener('click', commandHandler);
1086
- app.container.addEventListener('submit', commandHandler);
1087
- app.container.addEventListener('change', commandHandler);
1088
- app.container.addEventListener('input', commandHandler);
1089
-
1090
- return commands;
1091
- };
1092
-
1093
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1094
- module.exports = command;
1095
- } else {
1096
- if (!global.simply) {
1097
- global.simply = {};
1098
- }
1099
- global.simply.command = command;
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());
1100
636
  }
1101
-
1102
- })(this);
1103
- (function(global) {
1104
- 'use strict';
1105
-
1106
- function keyboard(app, config) {
1107
- var keys = config;
1108
-
1109
- if (!app) {
1110
- app = {};
1111
- }
1112
- if (!app.container) {
1113
- app.container = document.body;
1114
- }
1115
- app.container.addEventListener('keydown', (e) => {
1116
- if (e.isComposing || e.keyCode === 229) {
1117
- return;
1118
- }
1119
- if (e.defaultPrevented) {
1120
- return;
1121
- }
1122
- if (!e.target) {
1123
- return;
1124
- }
1125
-
1126
- let selectedKeyboard = 'default';
1127
- if (e.target.closest('[data-simply-keyboard]')) {
1128
- selectedKeyboard = e.target.closest('[data-simply-keyboard]').dataset.simplyKeyboard;
1129
- }
1130
- let key = '';
1131
- if (e.ctrlKey && e.keyCode!=17) {
1132
- key+='Control+';
1133
- }
1134
- if (e.metaKey && e.keyCode!=224) {
1135
- key+='Meta+';
1136
- }
1137
- if (e.altKey && e.keyCode!=18) {
1138
- key+='Alt+';
1139
- }
1140
- if (e.shiftKey && e.keyCode!=16) {
1141
- key+='Shift+';
1142
- }
1143
- key+=e.key;
1144
-
1145
- if (keys[selectedKeyboard] && keys[selectedKeyboard][key]) {
1146
- let keyboard = keys[selectedKeyboard]
1147
- keyboard.app = app;
1148
- keyboard[key].call(keyboard,e);
1149
- }
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
+ }
1150
660
  });
1151
-
1152
- return keys;
661
+ });
1153
662
  }
1154
-
1155
-
1156
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1157
- module.exports = keyboard;
1158
- } else {
1159
- if (!global.simply) {
1160
- global.simply = {};
1161
- }
1162
- global.simply.keyboard = keyboard;
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 });
1163
671
  }
1164
- })(this);
1165
- (function(global) {
1166
- 'use strict';
1167
-
1168
- var defaultActions = {
1169
- 'simply-hide': function(el) {
1170
- el.classList.remove('simply-visible');
1171
- return Promise.resolve();
1172
- },
1173
- 'simply-show': function(el) {
1174
- el.classList.add('simply-visible');
1175
- return Promise.resolve();
1176
- },
1177
- 'simply-select': function(el,group,target,targetGroup) {
1178
- if (group) {
1179
- this.call('simply-deselect', this.app.container.querySelectorAll('[data-simply-group='+group+']'));
1180
- }
1181
- el.classList.add('simply-selected');
1182
- if (target) {
1183
- this.call('simply-select',target,targetGroup);
1184
- }
1185
- return Promise.resolve();
1186
- },
1187
- 'simply-toggle-select': function(el,group,target,targetGroup) {
1188
- if (!el.classList.contains('simply-selected')) {
1189
- this.call('simply-select',el,group,target,targetGroup);
1190
- } else {
1191
- this.call('simply-deselect',el,target);
1192
- }
1193
- return Promise.resolve();
1194
- },
1195
- 'simply-toggle-class': function(el,className,target) {
1196
- if (!target) {
1197
- target = el;
1198
- }
1199
- return Promise.resolve(target.classList.toggle(className));
1200
- },
1201
- 'simply-deselect': function(el,target) {
1202
- if ( typeof el.length=='number' && typeof el.item=='function') {
1203
- el = Array.prototype.slice.call(el);
1204
- }
1205
- if ( Array.isArray(el) ) {
1206
- for (var i=0,l=el.length; i<l; i++) {
1207
- this.call('simply-deselect',el[i],target);
1208
- target = null;
1209
- }
1210
- } else {
1211
- el.classList.remove('simply-selected');
1212
- if (target) {
1213
- this.call('simply-deselect',target);
1214
- }
1215
- }
1216
- return Promise.resolve();
1217
- },
1218
- 'simply-fullscreen': function(target) {
1219
- var methods = {
1220
- 'requestFullscreen':{exit:'exitFullscreen',event:'fullscreenchange',el:'fullscreenElement'},
1221
- 'webkitRequestFullScreen':{exit:'webkitCancelFullScreen',event:'webkitfullscreenchange',el:'webkitFullscreenElement'},
1222
- 'msRequestFullscreen':{exit:'msExitFullscreen',event:'MSFullscreenChange',el:'msFullscreenElement'},
1223
- 'mozRequestFullScreen':{exit:'mozCancelFullScreen',event:'mozfullscreenchange',el:'mozFullScreenElement'}
1224
- };
1225
- for ( var i in methods ) {
1226
- if ( typeof document.documentElement[i] != 'undefined' ) {
1227
- var requestMethod = i;
1228
- var cancelMethod = methods[i].exit;
1229
- var event = methods[i].event;
1230
- var element = methods[i].el;
1231
- break;
1232
- }
1233
- }
1234
- if ( !requestMethod ) {
1235
- return;
1236
- }
1237
- if (!target.classList.contains('simply-fullscreen')) {
1238
- target.classList.add('simply-fullscreen');
1239
- target[requestMethod]();
1240
- var exit = function() {
1241
- if ( !document[element] ) {
1242
- target.classList.remove('simply-fullscreen');
1243
- document.removeEventListener(event,exit);
1244
- }
1245
- };
1246
- document.addEventListener(event,exit);
1247
- } else {
1248
- target.classList.remove('simply-fullscreen');
1249
- document[cancelMethod]();
1250
- }
1251
- return Promise.resolve();
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;
1252
699
  }
700
+ }
1253
701
  };
1254
-
1255
- var action = function(app, inActions) {
1256
- var actions = Object.create(defaultActions);
1257
- for ( var i in inActions ) {
1258
- actions[i] = inActions[i];
1259
- }
1260
-
1261
- actions.app = app;
1262
- actions.call = function(name) {
1263
- var params = Array.prototype.slice.call(arguments);
1264
- params.shift();
1265
- return this[name].apply(this, params);
1266
- };
1267
- return actions;
1268
- };
1269
-
1270
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1271
- module.exports = action;
1272
- } else {
1273
- if (!global.simply) {
1274
- global.simply = {};
1275
- }
1276
- global.simply.action = action;
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;
1277
711
  }
1278
-
1279
- })(this);
1280
- (function(global) {
1281
- 'use strict';
1282
-
1283
- var resize = function(app, config) {
1284
- if (!config) {
1285
- config = {};
1286
- }
1287
- if (!config.sizes) {
1288
- config.sizes = {
1289
- 'simply-tiny' : 0,
1290
- 'simply-xsmall' : 480,
1291
- 'simply-small' : 768,
1292
- 'simply-medium' : 992,
1293
- 'simply-large' : 1200
1294
- };
1295
- }
1296
-
1297
- var lastSize = 0;
1298
- function resizeSniffer() {
1299
- var size = app.container.getBoundingClientRect().width;
1300
- if ( lastSize==size ) {
1301
- return;
1302
- }
1303
- lastSize = size;
1304
- var sizes = Object.keys(config.sizes);
1305
- var match = sizes.pop();
1306
- while (match) {
1307
- if ( size<config.sizes[match] ) {
1308
- if ( app.container.classList.contains(match)) {
1309
- app.container.classList.remove(match);
1310
- }
1311
- } else {
1312
- if ( !app.container.classList.contains(match) ) {
1313
- app.container.classList.add(match);
1314
- match = sizes.pop(); // skip to next match to remove these
1315
- }
1316
- break;
1317
- }
1318
- match = sizes.pop();
1319
- }
1320
- while (match) {
1321
- if ( app.container.classList.contains(match)) {
1322
- app.container.classList.remove(match);
1323
- }
1324
- match = sizes.pop();
1325
- }
1326
- var toolbars = app.container.querySelectorAll('.simply-toolbar');
1327
- [].forEach.call(toolbars, function(toolbar) {
1328
- toolbar.style.transform = '';
1329
- });
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();
728
+ }
729
+ });
730
+ } else {
731
+ batchMode--;
732
+ if (!batchMode) {
733
+ runBatchedListeners();
1330
734
  }
1331
-
1332
- if ( global.attachEvent ) {
1333
- app.container.attachEvent('onresize', resizeSniffer);
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
+ });
1334
786
  } else {
1335
- global.setInterval(resizeSniffer, 200);
787
+ connectedSignal.current = result;
1336
788
  }
1337
-
1338
- if ( simply.toolbar ) {
1339
- var toolbars = app.container.querySelectorAll('.simply-toolbar');
1340
- [].forEach.call(toolbars, function(toolbar) {
1341
- simply.toolbar.init(toolbar);
1342
- if (simply.toolbar.scroll) {
1343
- simply.toolbar.scroll(toolbar);
1344
- }
1345
- });
789
+ }
790
+ throttled = Date.now() + throttleTime;
791
+ globalThis.setTimeout(() => {
792
+ if (hasChange) {
793
+ computeEffect2();
1346
794
  }
1347
-
1348
- return resizeSniffer;
795
+ }, throttleTime);
1349
796
  };
1350
-
1351
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1352
- module.exports = resize;
1353
- } else {
1354
- if (!global.simply) {
1355
- global.simply = {};
1356
- }
1357
- global.simply.resize = resize;
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);
1358
807
  }
1359
- })(this);(function (global) {
1360
- 'use strict';
1361
-
1362
- var throttle = function( callbackFunction, intervalTime ) {
1363
- var eventId = 0;
1364
- return function() {
1365
- var myArguments = arguments;
1366
- var me = this;
1367
- if ( eventId ) {
1368
- return;
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
+ });
1369
825
  } else {
1370
- eventId = global.setTimeout( function() {
1371
- callbackFunction.apply(me, myArguments);
1372
- eventId = 0;
1373
- }, intervalTime );
826
+ connectedSignal.current = result;
1374
827
  }
1375
- };
1376
- };
1377
-
1378
- var runWhenIdle = (function() {
1379
- if (global.requestIdleCallback) {
1380
- return function(callback) {
1381
- global.requestIdleCallback(callback, {timeout: 500});
1382
- };
1383
- }
1384
- return global.requestAnimationFrame;
1385
- })();
1386
-
1387
- var rebaseHref = function(relative, base) {
1388
- let url = new URL(relative, base)
1389
- if (include.cacheBuster) {
1390
- url.searchParams.set('cb',include.cacheBuster)
828
+ hasChanged = false;
829
+ }
830
+ } else {
831
+ lastTick = clock.time;
1391
832
  }
1392
- return url.href
833
+ } else {
834
+ hasChanged = true;
835
+ }
1393
836
  };
1394
-
1395
- var observer, loaded = {};
1396
- var head = global.document.querySelector('head');
1397
- var currentScript = global.document.currentScript;
1398
- if (!currentScript) {
1399
- var getScriptURL = (function() {
1400
- var scripts = document.getElementsByTagName('script');
1401
- var index = scripts.length - 1;
1402
- var myScript = scripts[index];
1403
- return function() { return myScript.src; };
1404
- })();
1405
- var currentScriptURL = getScriptURL();
1406
- } else {
1407
- var currentScriptURL = currentScript.src;
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;
1408
847
  }
1409
-
1410
- var waitForPreviousScripts = function() {
1411
- // because of the async=false attribute, this script will run after
1412
- // the previous scripts have been loaded and run
1413
- // simply.include.next.js only fires the simply-next-script event
1414
- // that triggers the Promise.resolve method
1415
- return new Promise(function(resolve) {
1416
- var next = global.document.createElement('script');
1417
- next.src = rebaseHref('simply.include.next.js', currentScriptURL);
1418
- next.async = false;
1419
- global.document.addEventListener('simply-include-next', function() {
1420
- head.removeChild(next);
1421
- resolve();
1422
- }, { once: true, passive: true});
1423
- head.appendChild(next);
1424
- });
1425
- };
1426
-
1427
- var scriptLocations = [];
1428
-
1429
- var include = {
1430
- cacheBuster: null,
1431
- scripts: function(scripts, base) {
1432
- var arr = [];
1433
- for(var i = scripts.length; i--; arr.unshift(scripts[i]));
1434
- var importScript = function() {
1435
- var script = arr.shift();
1436
- if (!script) {
1437
- return;
1438
- }
1439
- var attrs = [].map.call(script.attributes, function(attr) {
1440
- return attr.name;
1441
- });
1442
- var clone = global.document.createElement('script');
1443
- attrs.forEach(function(attr) {
1444
- clone.setAttribute(attr, script.getAttribute(attr));
1445
- });
1446
- clone.removeAttribute('data-simply-location');
1447
- if (!clone.src) {
1448
- // this is an inline script, so copy the content and wait for previous scripts to run
1449
- clone.innerHTML = script.innerHTML;
1450
- waitForPreviousScripts()
1451
- .then(function() {
1452
- var node = scriptLocations[script.dataset.simplyLocation];
1453
- node.parentNode.insertBefore(clone, node);
1454
- node.parentNode.removeChild(node);
1455
- importScript();
1456
- });
1457
- } else {
1458
- clone.src = rebaseHref(clone.src, base);
1459
- if (!clone.hasAttribute('async') && !clone.hasAttribute('defer')) {
1460
- clone.async = false; //important! do not use clone.setAttribute('async', false) - it has no effect
1461
- }
1462
- var node = scriptLocations[script.dataset.simplyLocation];
1463
- node.parentNode.insertBefore(clone, node);
1464
- node.parentNode.removeChild(node);
1465
- loaded[clone.src]=true;
1466
- importScript();
1467
- }
1468
- };
1469
- if (arr.length) {
1470
- importScript();
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 });
1471
884
  }
1472
- },
1473
- html: function(html, link) {
1474
- var fragment = global.document.createRange().createContextualFragment(html);
1475
- var stylesheets = fragment.querySelectorAll('link[rel="stylesheet"],style');
1476
- // add all stylesheets to head
1477
- [].forEach.call(stylesheets, function(stylesheet) {
1478
- if (stylesheet.href) {
1479
- stylesheet.href = rebaseHref(stylesheet.href, link.href);
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);
1480
910
  }
1481
- head.appendChild(stylesheet);
1482
- });
1483
- // remove the scripts from the fragment, as they will not run in the
1484
- // order in which they are defined
1485
- var scriptsFragment = global.document.createDocumentFragment();
1486
- // FIXME: this loses the original position of the script
1487
- // should add a placeholder so we can reinsert the clone
1488
- var scripts = fragment.querySelectorAll('script');
1489
- [].forEach.call(scripts, function(script) {
1490
- var placeholder = global.document.createComment(script.src || 'inline script');
1491
- script.parentNode.insertBefore(placeholder, script);
1492
- script.dataset.simplyLocation = scriptLocations.length;
1493
- scriptLocations.push(placeholder);
1494
- scriptsFragment.appendChild(script);
1495
- });
1496
- // add the remainder before the include link
1497
- link.parentNode.insertBefore(fragment, link ? link : null);
1498
- global.setTimeout(function() {
1499
- if (global.editor && global.editor.data && fragment.querySelector('[data-simply-field],[data-simply-list]')) {
1500
- //TODO: remove this dependency and let simply.bind listen for dom node insertions (and simply-edit.js use simply.bind)
1501
- global.editor.data.apply(global.editor.currentData, global.document);
911
+ if (bindings2.length) {
912
+ applyBindings(bindings2);
1502
913
  }
1503
- simply.include.scripts(scriptsFragment.childNodes, link ? link.href : global.location.href );
1504
- }, 10);
1505
- }
1506
- };
1507
-
1508
- var included = {};
1509
- var includeLinks = function(links) {
1510
- // mark them as in progress, so handleChanges doesn't find them again
1511
- var remainingLinks = [].reduce.call(links, function(remainder, link) {
1512
- if (link.rel=='simply-include-once' && included[link.href]) {
1513
- link.parentNode.removeChild(link);
1514
- } else {
1515
- included[link.href]=true;
1516
- link.rel = 'simply-include-loading';
1517
- remainder.push(link);
914
+ }
1518
915
  }
1519
- return remainder;
1520
- }, []);
1521
- [].forEach.call(remainingLinks, function(link) {
1522
- if (!link.href) {
1523
- return;
1524
- }
1525
- // fetch the html
1526
- fetch(link.href)
1527
- .then(function(response) {
1528
- if (response.ok) {
1529
- console.log('simply-include: loaded '+link.href);
1530
- return response.text();
1531
- } else {
1532
- console.log('simply-include: failed to load '+link.href);
1533
- }
1534
- })
1535
- .then(function(html) {
1536
- // if succesfull import the html
1537
- simply.include.html(html, link);
1538
- // remove the include link
1539
- link.parentNode.removeChild(link);
1540
- });
1541
- });
1542
- };
1543
-
1544
- var handleChanges = throttle(function() {
1545
- runWhenIdle(function() {
1546
- var links = global.document.querySelectorAll('link[rel="simply-include"],link[rel="simply-include-once"]');
1547
- if (links.length) {
1548
- includeLinks(links);
1549
- }
1550
- });
1551
- });
1552
-
1553
- var observe = function() {
1554
- observer = new MutationObserver(handleChanges);
1555
- observer.observe(global.document, {
1556
- subtree: true,
1557
- childList: true,
1558
- });
1559
- };
1560
-
1561
- observe();
1562
- handleChanges(); // check if there are include links in the dom already
1563
-
1564
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1565
- module.exports = include;
1566
- } else {
1567
- if (!global.simply) {
1568
- global.simply = {};
916
+ }
1569
917
  }
1570
- global.simply.include = include;
918
+ };
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
+ }
1571
930
  }
1572
-
1573
-
1574
- })(this);
1575
- (function(global) {
1576
- 'use strict';
1577
- var view = function(app, view) {
1578
-
1579
- app.view = view || {};
1580
-
1581
- var load = function() {
1582
- var data = app.view;
1583
- var path = global.editor.data.getDataPath(app.container);
1584
- app.view = global.editor.currentData[path];
1585
- Object.keys(data).forEach(function(key) {
1586
- app.view[key] = data[key];
1587
- });
1588
- };
1589
-
1590
- if (global.editor && global.editor.currentData) {
1591
- load();
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);
1592
965
  } else {
1593
- global.document.addEventListener('simply-content-loaded', function() {
1594
- load();
1595
- });
1596
- }
1597
-
1598
- return app.view;
1599
- };
1600
-
1601
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1602
- module.exports = view;
1603
- } else {
1604
- if (!global.simply) {
1605
- global.simply = {};
1606
- }
1607
- global.simply.view = view;
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;
1608
974
  }
1609
- })(this);
1610
- (function(global) {
1611
- 'use strict';
1612
-
1613
- function etag() {
1614
- let d = '';
1615
- while (d.length < 32) d += Math.random().toString(16).substr(2);
1616
- const vr = ((parseInt(d.substr(16, 1), 16) & 0x3) | 0x8).toString(16);
1617
- return `${d.substr(0, 8)}-${d.substr(8, 4)}-4${d.substr(13, 3)}-${vr}${d.substr(17, 3)}-${d.substr(20, 12)}`;
1618
- }
1619
-
1620
- function ViewModel(name, data, options) {
1621
- this.name = name;
1622
- this.data = data || [];
1623
- this.data.etag = etag();
1624
- this.view = {
1625
- options: {},
1626
- data: [] //Array.from(this.data).slice()
1627
- };
1628
- this.options = options || {};
1629
- this.plugins = {
1630
- start: [],
1631
- select: [],
1632
- order: [],
1633
- render: [],
1634
- finish: []
1635
- };
975
+ getBindingPath(el) {
976
+ return el.getAttribute(this.options.attribute);
1636
977
  }
1637
-
1638
- ViewModel.prototype.update = function(params) {
1639
- if (!params) {
1640
- params = {};
1641
- }
1642
- if (params.data) {
1643
- // this.data is a reference to the data passed, so that any changes in it will get applied
1644
- // to the original
1645
- this.data = params.data;
1646
- this.data.etag = etag()
1647
- }
1648
- // the view is a shallow copy of the array, so that changes in sort order and filtering
1649
- // won't get applied to the original, but databindings on its children will still work
1650
- this.view.data = Array.from(this.data).slice();
1651
- this.view.data.etag = this.data.etag;
1652
- let data = this.view.data;
1653
- let plugins = this.plugins.start.concat(this.plugins.select, this.plugins.order, this.plugins.render, this.plugins.finish);
1654
- plugins.forEach(plugin => {
1655
- data = plugin.call(this, params, data);
1656
- if (!data) {
1657
- data = this.view.data;
1658
- }
1659
- this.view.data = data
1660
- });
1661
-
1662
- if (global.editor) {
1663
- global.editor.addDataSource(this.name,{
1664
- load: function(el, callback) {
1665
- callback(self.view.data);
1666
- }
1667
- });
1668
- updateDataSource(this.name);
1669
- }
1670
- };
1671
-
1672
- ViewModel.prototype.addPlugin = function(pipe, plugin) {
1673
- if (typeof this.plugins[pipe] == 'undefined') {
1674
- throw new Error('Unknown pipeline '+pipe);
1675
- }
1676
- this.plugins[pipe].push(plugin);
1677
- };
1678
-
1679
- ViewModel.prototype.removePlugin = function(pipe, plugin) {
1680
- if (typeof this.plugins[pipe] == 'undefined') {
1681
- throw new Error('Unknown pipeline '+pipe);
1682
- }
1683
- this.plugins[pipe] = this.plugins[pipe].filter(function(p) {
1684
- return p != plugin;
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
+ if (currentItem) {
1009
+ return t;
1010
+ }
1011
+ }
1012
+ };
1013
+ let template = Array.from(templates).find(templateMatches);
1014
+ let rel = template?.getAttribute("rel");
1015
+ if (rel) {
1016
+ let replacement = document.querySelector("template#" + rel);
1017
+ if (!replacement) {
1018
+ throw new Error("Could not find template with id " + rel);
1019
+ }
1020
+ template = replacement;
1021
+ }
1022
+ return template;
1023
+ }
1024
+ destroy() {
1025
+ this.bindings.forEach((binding) => {
1026
+ destroy(binding);
1027
+ });
1028
+ this.bindings = /* @__PURE__ */ new Map();
1029
+ this.observer.disconnect();
1030
+ }
1031
+ };
1032
+ function bind(options) {
1033
+ return new SimplyBind(options);
1034
+ }
1035
+ function matchValue(a, b) {
1036
+ if (a == "#empty" && !b) {
1037
+ return true;
1038
+ }
1039
+ if (b == "#empty" && !a) {
1040
+ return true;
1041
+ }
1042
+ if ("" + a == "" + b) {
1043
+ return true;
1044
+ }
1045
+ return false;
1046
+ }
1047
+ function getValueByPath(root, path) {
1048
+ let parts = path.split(".");
1049
+ let curr = root;
1050
+ let part, prevPart;
1051
+ while (parts.length && curr) {
1052
+ part = parts.shift();
1053
+ if (part == "#key") {
1054
+ return prevPart;
1055
+ } else if (part == "#value") {
1056
+ return curr;
1057
+ } else if (part == "#root") {
1058
+ curr = root;
1059
+ } else {
1060
+ part = decodeURIComponent(part);
1061
+ curr = curr[part];
1062
+ prevPart = part;
1063
+ }
1064
+ }
1065
+ return curr;
1066
+ }
1067
+ function defaultTransformer(context) {
1068
+ const el = context.element;
1069
+ const templates = context.templates;
1070
+ const templatesCount = templates.length;
1071
+ const path = context.path;
1072
+ const value = context.value;
1073
+ const attribute = this.options.attribute;
1074
+ if (Array.isArray(value) && templates?.length) {
1075
+ transformArrayByTemplates.call(this, context);
1076
+ } else if (typeof value == "object" && templates?.length) {
1077
+ transformObjectByTemplates.call(this, context);
1078
+ } else if (templates?.length) {
1079
+ transformLiteralByTemplates.call(this, context);
1080
+ } else if (el.tagName == "INPUT") {
1081
+ transformInput.call(this, context);
1082
+ } else if (el.tagName == "BUTTON") {
1083
+ transformButton.call(this, context);
1084
+ } else if (el.tagName == "SELECT") {
1085
+ transformSelect.call(this, context);
1086
+ } else if (el.tagName == "A") {
1087
+ transformAnchor.call(this, context);
1088
+ } else {
1089
+ transformElement.call(this, context);
1090
+ }
1091
+ return context;
1092
+ }
1093
+ function transformArrayByTemplates(context) {
1094
+ const el = context.element;
1095
+ const templates = context.templates;
1096
+ const templatesCount = templates.length;
1097
+ const path = context.path;
1098
+ const value = context.value;
1099
+ const attribute = this.options.attribute;
1100
+ let items = el.querySelectorAll(":scope > [" + attribute + "-key]");
1101
+ let lastKey = 0;
1102
+ let skipped = 0;
1103
+ context.list = value;
1104
+ for (let item of items) {
1105
+ let currentKey = parseInt(item.getAttribute(attribute + "-key"));
1106
+ if (currentKey > lastKey) {
1107
+ context.index = lastKey;
1108
+ el.insertBefore(this.applyTemplate(context), item);
1109
+ } else if (currentKey < lastKey) {
1110
+ item.remove();
1111
+ } else {
1112
+ let bindings = Array.from(item.querySelectorAll(`[${attribute}]`));
1113
+ if (item.matches(`[${attribute}]`)) {
1114
+ bindings.unshift(item);
1115
+ }
1116
+ let needsReplacement = bindings.find((b) => {
1117
+ let databind = b.getAttribute(attribute);
1118
+ return databind.substr(0, 5) !== "#root" && databind.substr(0, path.length) !== path;
1685
1119
  });
1686
- };
1687
-
1688
- var updateDataSource = function(name) {
1689
- global.document.querySelectorAll('[data-simply-data="'+name+'"]').forEach(function(list) {
1690
- global.editor.list.applyDataSource(list, name);
1120
+ if (!needsReplacement) {
1121
+ if (item.$bindTemplate) {
1122
+ let newTemplate = this.findTemplate(templates, value[lastKey]);
1123
+ if (newTemplate != item.$bindTemplate) {
1124
+ needsReplacement = true;
1125
+ if (!newTemplate) {
1126
+ skipped++;
1127
+ }
1128
+ }
1129
+ }
1130
+ }
1131
+ if (needsReplacement) {
1132
+ context.index = lastKey;
1133
+ el.replaceChild(this.applyTemplate(context), item);
1134
+ }
1135
+ }
1136
+ lastKey++;
1137
+ if (lastKey >= value.length) {
1138
+ break;
1139
+ }
1140
+ }
1141
+ items = el.querySelectorAll(":scope > [" + attribute + "-key]");
1142
+ let length = items.length + skipped;
1143
+ if (length > value.length) {
1144
+ while (length > value.length) {
1145
+ let child = el.querySelectorAll(":scope > :not(template)")?.[length - 1];
1146
+ child?.remove();
1147
+ length--;
1148
+ }
1149
+ } else if (length < value.length) {
1150
+ while (length < value.length) {
1151
+ context.index = length;
1152
+ el.appendChild(this.applyTemplate(context));
1153
+ length++;
1154
+ }
1155
+ }
1156
+ }
1157
+ function transformObjectByTemplates(context) {
1158
+ const el = context.element;
1159
+ const templates = context.templates;
1160
+ const templatesCount = templates.length;
1161
+ const path = context.path;
1162
+ const value = context.value;
1163
+ const attribute = this.options.attribute;
1164
+ context.list = value;
1165
+ let list = Object.entries(value);
1166
+ let items = el.querySelectorAll(":scope > [" + attribute + "-key]");
1167
+ let current = 0;
1168
+ let skipped = 0;
1169
+ for (let item of items) {
1170
+ if (current >= list.length) {
1171
+ break;
1172
+ }
1173
+ let key = list[current][0];
1174
+ current++;
1175
+ let keypath = path + "." + key;
1176
+ let needsReplacement;
1177
+ const databind = item.getAttribute(attribute);
1178
+ if (databind && databind.substr(0, keypath.length) != keypath) {
1179
+ needsReplacement = true;
1180
+ } else {
1181
+ let bindings = Array.from(item.querySelectorAll(`[${attribute}]`));
1182
+ needsReplacement = bindings.find((b) => {
1183
+ const db = b.getAttribute(attribute);
1184
+ return db.substr(0, 5) !== "#root" && db.substr(0, keypath.length) !== keypath;
1691
1185
  });
1692
- };
1693
-
1694
- var createSort = function(options) {
1695
- var defaultOptions = {
1696
- name: 'sort',
1697
- getSort: function(params) {
1698
- return Array.prototype.sort;
1699
- }
1700
- };
1701
- options = Object.assign(defaultOptions, options || {});
1702
-
1703
- return function(params) {
1704
- this.options[options.name] = options;
1705
- if (params[options.name]) {
1706
- options = Object.assign(options, params[options.name]);
1707
- }
1708
- this.view.data.sort(options.getSort.call(this, options));
1709
- };
1710
- };
1711
-
1712
- var createPaging = function(options) {
1713
- var defaultOptions = {
1714
- name: 'paging',
1715
- page: 1,
1716
- pageSize: 100,
1717
- max: 1,
1718
- prev: 0,
1719
- next: 0
1720
- };
1721
- options = Object.assign(defaultOptions, options || {});
1722
-
1723
- return function(params) {
1724
- this.options[options.name] = options;
1725
- if (this.view.data) {
1726
- options.max = Math.max(1, Math.ceil(Array.from(this.view.data).length / options.pageSize));
1727
- } else {
1728
- options.max = 1;
1729
- }
1730
- if (this.view.changed) {
1731
- options.page = 1; // reset to page 1 when something in the view data has changed
1732
- }
1733
- if (params[options.name]) {
1734
- options = Object.assign(options, params[options.name]);
1735
- }
1736
- options.page = Math.max(1, Math.min(options.max, options.page)); // clamp page nr
1737
- options.prev = options.page - 1; // calculate previous page, 0 is allowed
1738
- if (options.page<options.max) {
1739
- options.next = options.page + 1;
1740
- } else {
1741
- options.next = 0; // no next page
1742
- }
1743
-
1744
- var start = (options.page - 1) * options.pageSize;
1745
- var end = start + options.pageSize;
1746
-
1747
- this.view.data = this.view.data.slice(start, end);
1748
- };
1749
- };
1750
-
1751
- var createFilter = function(options) {
1752
- var defaultOptions = {
1753
- name: 'filter',
1754
- label: 'A filter',
1755
- getMatch: function(entry) {
1756
- return false;
1757
- }
1758
- };
1759
- options = Object.assign(defaultOptions, options || {});
1760
- if (options.init) {
1761
- options.init.call(this, options);
1762
- }
1763
- return function(params) {
1764
- this.options[options.name] = options;
1765
- if (params[options.name]) {
1766
- options = Object.assign(options, params[options.name]);
1767
- }
1768
- var match = options.getMatch.call(this, options);
1769
- if (match) {
1770
- options.enabled = true;
1771
- this.view.data = this.view.data.filter(match);
1772
- } else if (options.enabled) {
1773
- options.enabled = false;
1774
- }
1775
- }
1186
+ if (!needsReplacement) {
1187
+ if (item.$bindTemplate) {
1188
+ let newTemplate = this.findTemplate(templates, value[key]);
1189
+ if (newTemplate != item.$bindTemplate) {
1190
+ needsReplacement = true;
1191
+ if (!newTemplate) {
1192
+ skipped++;
1193
+ }
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+ if (needsReplacement) {
1199
+ context.index = key;
1200
+ let clone = this.applyTemplate(context);
1201
+ el.replaceChild(clone, item);
1202
+ }
1776
1203
  }
1777
-
1778
- var viewmodel = {
1779
- create: function(name, data, options) {
1780
- return new ViewModel(name, data, options);
1781
- },
1782
- createFilter: createFilter,
1783
- createSort: createSort,
1784
- createPaging: createPaging,
1785
- updateDataSource: updateDataSource,
1786
- etag
1787
- };
1788
-
1789
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1790
- module.exports = viewmodel;
1204
+ items = el.querySelectorAll(":scope > [" + attribute + "-key]");
1205
+ let length = items.length + skipped;
1206
+ if (length > list.length) {
1207
+ while (length > list.length) {
1208
+ let child = el.querySelectorAll(":scope > :not(template)")?.[length - 1];
1209
+ child?.remove();
1210
+ length--;
1211
+ }
1212
+ } else if (length < list.length) {
1213
+ while (length < list.length) {
1214
+ context.index = list[length][0];
1215
+ el.appendChild(this.applyTemplate(context));
1216
+ length++;
1217
+ }
1218
+ }
1219
+ }
1220
+ function transformLiteralByTemplates(context) {
1221
+ const el = context.element;
1222
+ const templates = context.templates;
1223
+ const value = context.value;
1224
+ const attribute = this.options.attribute;
1225
+ const rendered = el.querySelector(":scope > :not(template)");
1226
+ const template = this.findTemplate(templates, value);
1227
+ context.parent = el.parentElement?.closest(`[${attribute}]`)?.getAttribute(attribute) || "#root";
1228
+ if (rendered) {
1229
+ if (template) {
1230
+ if (rendered?.$bindTemplate != template) {
1231
+ const clone = this.applyTemplate(context);
1232
+ el.replaceChild(clone, rendered);
1233
+ }
1234
+ } else {
1235
+ el.removeChild(rendered);
1236
+ }
1237
+ } else if (template) {
1238
+ const clone = this.applyTemplate(context);
1239
+ el.appendChild(clone);
1240
+ }
1241
+ }
1242
+ function transformInput(context) {
1243
+ const el = context.element;
1244
+ const value = context.value;
1245
+ if (el.type == "checkbox" || el.type == "radio") {
1246
+ if (matchValue(el.value, value)) {
1247
+ el.checked = true;
1248
+ } else {
1249
+ el.checked = false;
1250
+ }
1251
+ } else if (!matchValue(el.value, value)) {
1252
+ el.value = "" + value;
1253
+ }
1254
+ }
1255
+ function transformButton(context) {
1256
+ const el = context.element;
1257
+ const value = context.value;
1258
+ if (!matchValue(el.value, value)) {
1259
+ el.value = "" + value;
1260
+ }
1261
+ }
1262
+ function transformSelect(context) {
1263
+ const el = context.element;
1264
+ const value = context.value;
1265
+ if (el.multiple) {
1266
+ if (Array.isArray(value)) {
1267
+ for (let option of el.options) {
1268
+ if (value.indexOf(option.value) === false) {
1269
+ option.selected = false;
1270
+ } else {
1271
+ option.selected = true;
1272
+ }
1273
+ }
1274
+ }
1791
1275
  } else {
1792
- if (!global.simply) {
1793
- global.simply = {};
1794
- }
1795
- global.simply.viewmodel = viewmodel;
1276
+ let option = el.options.find((o) => matchValue(o.value, value));
1277
+ if (option) {
1278
+ option.selected = true;
1279
+ }
1796
1280
  }
1797
-
1798
- })(this);(function(global) {
1799
- 'use strict';
1800
-
1801
- var api = {
1802
- /**
1803
- * Returns a Proxy object that translates property access to a URL in the api
1804
- * and method calls to a fetch on that URL.
1805
- * @param options: a list of options for fetch(),
1806
- * see the 'init' parameter at https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters
1807
- * additionally:
1808
- * - baseURL: (required) the endpoint of the API
1809
- * - path: the current path in the API, is appended to the baseURL
1810
- * - verbs: list of http verbs to allow as methods, default ['get','post']
1811
- * - handlers.fetch: alternative fetch method
1812
- * - handlers.result: alternative getResult method
1813
- * - handlers.error: alternative error method
1814
- * - user (and password): if set, a basic authentication header will be added
1815
- * - paramsFormat: either 'formData', 'json' or 'search'. Default is search.
1816
- * - responseFormat: test, formData, blob, json, arrayBuffer or unbuffered. Default is json.
1817
- * @return Proxy
1818
- */
1819
- proxy: function(options) {
1820
- var cache = () => {};
1821
- cache.$options = Object.assign({}, options);
1822
- return new Proxy( cache, getApiHandler(cache.$options) );
1823
- },
1824
-
1825
- /**
1826
- * Fetches the options.baseURL using the fetch api and returns a promise
1827
- * Extra options in addition to those of global.fetch():
1828
- * - user (and password): if set, a basic authentication header will be added
1829
- * - paramsFormat: either 'formData', 'json' or 'search'
1830
- * By default params, if set, will be added to the baseURL as searchParams
1831
- * @param method one of the http verbs, e.g. get, post, etc.
1832
- * @param options the options for fetch(), with some additions
1833
- * @param params the parameters to send with the request, as javascript/json data
1834
- * @return Promise
1835
- */
1836
- fetch: function(method, params, options) {
1837
- if (!options.url) {
1838
- if (!options.baseURL) {
1839
- throw new Error('No url or baseURL in options object');
1840
- }
1841
- while (options.baseURL[options.baseURL.length-1]=='/') {
1842
- options.baseURL = options.baseURL.substr(0, options.baseURL.length-1);
1843
- }
1844
- var url = new URL(options.baseURL+options.path);
1845
- } else {
1846
- var url = options.url;
1847
- }
1848
- var fetchOptions = Object.assign({}, options);
1849
- if (!fetchOptions.headers) {
1850
- fetchOptions.headers = {};
1851
- }
1852
- if (params) {
1853
- if (method=='GET') {
1854
- var paramsFormat = 'search';
1855
- } else {
1856
- var paramsFormat = options.paramsFormat;
1857
- }
1858
- switch(paramsFormat) {
1859
- case 'formData':
1860
- var formData = new FormData();
1861
- for (const name in params) {
1862
- formData.append(name, params[name]);
1863
- }
1864
- if (!fetchOptions.headers['Content-Type']) {
1865
- fetchOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded';
1866
- }
1867
- break;
1868
- case 'json':
1869
- var formData = JSON.stringify(params);
1870
- if (!fetchOptions.headers['Content-Type']) {
1871
- fetchOptions.headers['Content-Type'] = 'application/json';
1872
- }
1873
- break;
1874
- case 'search':
1875
- var searchParams = url.searchParams; //new URLSearchParams(url.search.slice(1));
1876
- for (const name in params) {
1877
- searchParams.set(name, params[name]);
1878
- }
1879
- url.search = searchParams.toString();
1880
- break;
1881
- default:
1882
- throw Error('Unknown options.paramsFormat '+options.paramsFormat+'. Select one of formData, json or search.');
1883
- break;
1884
- }
1885
- }
1886
- if (formData) {
1887
- fetchOptions.body = formData
1888
- }
1889
- if (options.user) {
1890
- fetchOptions.headers['Authorization'] = 'Basic '+btoa(options.user+':'+options.password);
1891
- }
1892
- fetchOptions.method = method.toUpperCase();
1893
- var fetchURL = url.toString()
1894
- return fetch(fetchURL, fetchOptions);
1895
- },
1896
- /**
1897
- * Creates a function to call one or more graphql queries
1898
- */
1899
- graphqlQuery: function(url, query, options) {
1900
- options = Object.assign({ paramsFormat: 'json', url: url, responseFormat: 'json' }, options);
1901
- return function(params, operationName) {
1902
- let postParams = {
1903
- query: query
1904
- };
1905
- if (operationName) {
1906
- postParams.operationName = operationName;
1907
- }
1908
- postParams.variables = params || {};
1909
- return simply.api.fetch('POST', postParams, options )
1910
- .then(function(response) {
1911
- return simply.api.getResult(response, options);
1912
- });
1913
- }
1914
- },
1915
- /**
1916
- * Handles the response and returns a Promise with the response data as specified
1917
- * @param response Response
1918
- * @param options
1919
- * - responseFormat: one of 'text', 'formData', 'blob', 'arrayBuffer', 'unbuffered' or 'json'.
1920
- * The default is json.
1921
- */
1922
- getResult: function(response, options) {
1923
- if (response.ok) {
1924
- switch(options.responseFormat) {
1925
- case 'text':
1926
- return response.text();
1927
- break;
1928
- case 'formData':
1929
- return response.formData();
1930
- break;
1931
- case 'blob':
1932
- return response.blob();
1933
- break;
1934
- case 'arrayBuffer':
1935
- return response.arrayBuffer();
1936
- break;
1937
- case 'unbuffered':
1938
- return response.body;
1939
- break;
1940
- case 'json':
1941
- default:
1942
- return response.json();
1943
- break;
1944
- }
1945
- } else {
1946
- throw {
1947
- status: response.status,
1948
- message: response.statusText,
1949
- response: response
1950
- }
1951
- }
1952
- },
1953
- logError: function(error, options) {
1954
- console.error(error.status, error.message);
1955
- }
1281
+ }
1282
+ function transformAnchor(context) {
1283
+ const el = context.element;
1284
+ const value = context.value;
1285
+ if (value?.innerHTML && !matchValue(el.innerHTML, value.innerHTML)) {
1286
+ el.innerHTML = "" + value.innerHTML;
1956
1287
  }
1957
-
1958
- var defaultOptions = {
1959
- path: '',
1960
- responseFormat: 'json',
1961
- paramsFormat: 'search',
1962
- verbs: ['get','post'],
1963
- handlers: {
1964
- fetch: api.fetch,
1965
- result: api.getResult,
1966
- error: api.logError
1967
- }
1288
+ if (value?.href && !matchValue(el.href, value.href)) {
1289
+ el.href = "" + value.href;
1290
+ }
1291
+ }
1292
+ function transformElement(context) {
1293
+ const el = context.element;
1294
+ const value = context.value;
1295
+ if (!matchValue(el.innerHTML, value)) {
1296
+ el.innerHTML = "" + value;
1297
+ }
1298
+ }
1299
+
1300
+ // src/app.mjs
1301
+ var SimplyApp = class {
1302
+ constructor(options = {}) {
1303
+ this.container = options.container || document.body;
1304
+ if (!options.state) {
1305
+ options.state = {};
1306
+ }
1307
+ this.state = signal(options.state);
1308
+ if (options.commands) {
1309
+ this.commands = commands({ app: this, container: this.container, commands: options.commands });
1310
+ }
1311
+ if (options.keys) {
1312
+ this.keys = keys({ app: this, keys: options.keys });
1313
+ }
1314
+ if (options.routes) {
1315
+ this.routes = routes({ app: this, routes: options.routes });
1316
+ }
1317
+ if (options.actions) {
1318
+ this.actions = actions({ app: this, actions: options.actions });
1319
+ }
1320
+ let bindOptions = { container: this.container, root: this.state };
1321
+ if (options.defaultTransformers) {
1322
+ bindOptions.defaultTransformers = options.defaultTransformers;
1323
+ }
1324
+ if (options.transformers) {
1325
+ bindOptions.transformers = options.transformers;
1326
+ }
1327
+ this.bind = bind(bindOptions);
1328
+ }
1329
+ };
1330
+ function app(options = {}) {
1331
+ return new SimplyApp(options);
1332
+ }
1333
+
1334
+ // src/include.mjs
1335
+ function throttle(callbackFunction, intervalTime) {
1336
+ let eventId = 0;
1337
+ return () => {
1338
+ const myArguments = arguments;
1339
+ if (eventId) {
1340
+ return;
1341
+ } else {
1342
+ eventId = globalThis.setTimeout(() => {
1343
+ callbackFunction.apply(this, myArguments);
1344
+ eventId = 0;
1345
+ }, intervalTime);
1346
+ }
1968
1347
  };
1969
-
1970
- function cd(path, name) {
1971
- name = name.replace(/\//g,'');
1972
- if (!path.length || path[path.length-1]!=='/') {
1973
- path+='/';
1974
- }
1975
- return path+encodeURIComponent(name);
1348
+ }
1349
+ var runWhenIdle = (() => {
1350
+ if (globalThis.requestIdleCallback) {
1351
+ return (callback) => {
1352
+ globalThis.requestIdleCallback(callback, { timeout: 500 });
1353
+ };
1976
1354
  }
1977
-
1978
- function fetchChain(prop, params) {
1979
- var options = this;
1980
- return this.handlers.fetch
1981
- .call(this, prop, params, options)
1982
- .then(function(res) {
1983
- return options.handlers.result.call(options, res, options);
1984
- })
1985
- .catch(function(error) {
1986
- return options.handlers.error.call(options, error, options);
1987
- });
1355
+ return globalThis.requestAnimationFrame;
1356
+ })();
1357
+ function rebaseHref(relative, base) {
1358
+ let url = new URL(relative, base);
1359
+ if (include.cacheBuster) {
1360
+ url.searchParams.set("cb", include.cacheBuster);
1988
1361
  }
1989
-
1990
- function getApiHandler(options) {
1991
- options.handlers = Object.assign({}, defaultOptions.handlers, options.handlers);
1992
- options = Object.assign({}, defaultOptions, options);
1993
-
1994
- return {
1995
- get: function(cache, prop) {
1996
- if (!cache[prop]) {
1997
- if (options.verbs.indexOf(prop)!=-1) {
1998
- cache[prop] = function(params) {
1999
- return fetchChain.call(options, prop, params);
2000
- }
2001
- } else {
2002
- cache[prop] = api.proxy(Object.assign({}, options, {
2003
- path: cd(options.path, prop)
2004
- }));
2005
- }
2006
- }
2007
- return cache[prop];
2008
- },
2009
- apply: function(cache, thisArg, params) {
2010
- return fetchChain.call(options, 'get', params[0] ? params[0] : null)
2011
- }
2012
- }
1362
+ return url.href;
1363
+ }
1364
+ var observer2;
1365
+ var loaded = {};
1366
+ var head = globalThis.document.querySelector("head");
1367
+ var currentScript = globalThis.document.currentScript;
1368
+ var getScriptURL;
1369
+ var currentScriptURL;
1370
+ if (!currentScript) {
1371
+ getScriptURL = (() => {
1372
+ var scripts = document.getElementsByTagName("script");
1373
+ var index = scripts.length - 1;
1374
+ var myScript = scripts[index];
1375
+ return () => myScript.src;
1376
+ })();
1377
+ currentScriptURL = getScriptURL();
1378
+ } else {
1379
+ currentScriptURL = currentScript.src;
1380
+ }
1381
+ var waitForPreviousScripts = async () => {
1382
+ return new Promise(function(resolve) {
1383
+ var next = globalThis.document.createElement("script");
1384
+ next.src = rebaseHref("simply.include.next.js", currentScriptURL);
1385
+ next.async = false;
1386
+ globalThis.document.addEventListener("simply-include-next", () => {
1387
+ head.removeChild(next);
1388
+ resolve();
1389
+ }, { once: true, passive: true });
1390
+ head.appendChild(next);
1391
+ });
1392
+ };
1393
+ var scriptLocations = [];
1394
+ var include = {
1395
+ cacheBuster: null,
1396
+ scripts: (scripts, base) => {
1397
+ let arr = scripts.slice();
1398
+ const importScript = () => {
1399
+ const script = arr.shift();
1400
+ if (!script) {
1401
+ return;
1402
+ }
1403
+ const attrs = [].map.call(script.attributes, (attr) => {
1404
+ return attr.name;
1405
+ });
1406
+ let clone = globalThis.document.createElement("script");
1407
+ for (const attr of attrs) {
1408
+ clone.setAttribute(attr, script.getAttribute(attr));
1409
+ }
1410
+ clone.removeAttribute("data-simply-location");
1411
+ if (!clone.src) {
1412
+ clone.innerHTML = script.innerHTML;
1413
+ waitForPreviousScripts().then(() => {
1414
+ const node = scriptLocations[script.dataset.simplyLocation];
1415
+ node.parentNode.insertBefore(clone, node);
1416
+ node.parentNode.removeChild(node);
1417
+ importScript();
1418
+ });
1419
+ } else {
1420
+ clone.src = rebaseHref(clone.src, base);
1421
+ if (!clone.hasAttribute("async") && !clone.hasAttribute("defer")) {
1422
+ clone.async = false;
1423
+ }
1424
+ const node = scriptLocations[script.dataset.simplyLocation];
1425
+ node.parentNode.insertBefore(clone, node);
1426
+ node.parentNode.removeChild(node);
1427
+ loaded[clone.src] = true;
1428
+ importScript();
1429
+ }
1430
+ };
1431
+ if (arr.length) {
1432
+ importScript();
1433
+ }
1434
+ },
1435
+ html: (html, link) => {
1436
+ let fragment = globalThis.document.createRange().createContextualFragment(html);
1437
+ const stylesheets = fragment.querySelectorAll('link[rel="stylesheet"],style');
1438
+ for (let stylesheet of stylesheets) {
1439
+ if (stylesheet.href) {
1440
+ stylesheet.href = rebaseHref(stylesheet.href, link.href);
1441
+ }
1442
+ head.appendChild(stylesheet);
1443
+ }
1444
+ let scriptsFragment = globalThis.document.createDocumentFragment();
1445
+ const scripts = fragment.querySelectorAll("script");
1446
+ for (let script of scripts) {
1447
+ let placeholder = globalThis.document.createComment(script.src || "inline script");
1448
+ script.parentNode.insertBefore(placeholder, script);
1449
+ script.dataset.simplyLocation = scriptLocations.length;
1450
+ scriptLocations.push(placeholder);
1451
+ scriptsFragment.appendChild(script);
1452
+ }
1453
+ link.parentNode.insertBefore(fragment, link ? link : null);
1454
+ globalThis.setTimeout(function() {
1455
+ include.scripts(scriptsFragment.childNodes, link ? link.href : globalThis.location.href);
1456
+ }, 10);
2013
1457
  }
2014
-
2015
-
2016
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
2017
- module.exports = api;
2018
- } else {
2019
- if (!global.simply) {
2020
- global.simply = {};
2021
- }
2022
- global.simply.api = api;
1458
+ };
1459
+ var included = {};
1460
+ var includeLinks = async (links) => {
1461
+ let remainingLinks = [].reduce.call(links, (remainder, link) => {
1462
+ if (link.rel == "simply-include-once" && included[link.href]) {
1463
+ link.parentNode.removeChild(link);
1464
+ } else {
1465
+ included[link.href] = true;
1466
+ link.rel = "simply-include-loading";
1467
+ remainder.push(link);
1468
+ }
1469
+ return remainder;
1470
+ }, []);
1471
+ for (let link of remainingLinks) {
1472
+ if (!link.href) {
1473
+ return;
1474
+ }
1475
+ const response = await fetch(link.href);
1476
+ if (!response.ok) {
1477
+ console.log("simply-include: failed to load " + link.href);
1478
+ continue;
1479
+ }
1480
+ console.log("simply-include: loaded " + link.href);
1481
+ const html = await response.text();
1482
+ include.html(html, link);
1483
+ link.parentNode.removeChild(link);
2023
1484
  }
2024
-
2025
- })(this);
2026
- (function(global) {
2027
- 'use strict';
2028
-
2029
- var app = function(options) {
2030
- if (!options) {
2031
- options = {};
2032
- }
2033
- if (!options.container) {
2034
- console.warn('No simply.app application container element specified, using document.body.');
2035
- }
2036
-
2037
- function simplyApp(options) {
2038
- if (!options) {
2039
- options = {};
2040
- }
2041
- if ( options.routes ) {
2042
- simply.route.load(options.routes);
2043
- if (options.routeEvents) {
2044
- Object.keys(options.routeEvents).forEach(function(action) {
2045
- Object.keys(options.routeEvents[action]).forEach(function(route) {
2046
- options.routeEvents[action][route].forEach(function(callback) {
2047
- simply.route.addListener(action, route, callback);
2048
- });
2049
- });
2050
- });
2051
- }
2052
- simply.route.handleEvents();
2053
- global.setTimeout(function() {
2054
- simply.route.match(global.location.pathname+global.location.hash);
2055
- });
2056
- }
2057
- this.container = options.container || document.body;
2058
- this.keyboard = simply.keyboard ? simply.keyboard(this, options.keyboard || {}) : false;
2059
- this.actions = simply.action ? simply.action(this, options.actions) : false;
2060
- this.commands = simply.command ? simply.command(this, options.commands) : false;
2061
- this.resize = simply.resize ? simply.resize(this, options.resize) : false;
2062
- this.view = simply.view ? simply.view(this, options.view) : false;
2063
- if (!(global.editor && global.editor.field) && simply.bind) {
2064
- // skip simplyview databinding if SimplyEdit is loaded
2065
- options.bind = simply.render(options.bind || {});
2066
- options.bind.model = this.view;
2067
- options.bind.container = this.container;
2068
- this.bind = options.bindings = simply.bind(options.bind);
2069
- }
2070
- }
2071
-
2072
- simplyApp.prototype.get = function(id) {
2073
- return this.container.querySelector('[data-simply-id='+id+']') || document.getElementById(id);
2074
- };
2075
-
2076
- return new simplyApp(options);
1485
+ };
1486
+ var handleChanges2 = throttle(() => {
1487
+ runWhenIdle(() => {
1488
+ var links = globalThis.document.querySelectorAll('link[rel="simply-include"],link[rel="simply-include-once"]');
1489
+ if (links.length) {
1490
+ includeLinks(links);
1491
+ }
1492
+ });
1493
+ });
1494
+ var observe = () => {
1495
+ observer2 = new MutationObserver(handleChanges2);
1496
+ observer2.observe(globalThis.document, {
1497
+ subtree: true,
1498
+ childList: true
1499
+ });
1500
+ };
1501
+ observe();
1502
+ handleChanges2();
1503
+
1504
+ // src/model.mjs
1505
+ var model_exports = {};
1506
+ __export(model_exports, {
1507
+ columns: () => columns,
1508
+ filter: () => filter,
1509
+ model: () => model,
1510
+ paging: () => paging,
1511
+ sort: () => sort
1512
+ });
1513
+ var SimplyModel = class {
1514
+ /**
1515
+ * Creates a new datamodel, with a state property that contains
1516
+ * all the data passed to this constructor
1517
+ * @param state Object with all the data for this model
1518
+ */
1519
+ constructor(state) {
1520
+ this.state = signal(state);
1521
+ if (!this.state.options) {
1522
+ this.state.options = {};
1523
+ }
1524
+ this.effects = [{ current: state.data }];
1525
+ this.view = signal(state.data);
1526
+ }
1527
+ /**
1528
+ * Adds an effect to run whenever a signal it depends on
1529
+ * changes. this.state is the usual signal.
1530
+ * The `fn` function param is not itself an effect, but must return
1531
+ * and effect function. `fn` takes one param, which is the data signal.
1532
+ * This signal will always have at least a `current` property.
1533
+ * The result of the effect function is pushed on to the this.effects
1534
+ * list. And the last effect added is set as this.view
1535
+ */
1536
+ addEffect(fn) {
1537
+ const dataSignal = this.effects[this.effects.length - 1];
1538
+ this.view = fn.call(this, dataSignal);
1539
+ this.effects.push(this.view);
1540
+ }
1541
+ };
1542
+ function model(options) {
1543
+ return new SimplyModel(options);
1544
+ }
1545
+ function sort(options = {}) {
1546
+ return function(data) {
1547
+ this.state.options.sort = Object.assign({
1548
+ direction: "asc",
1549
+ sortBy: null,
1550
+ sortFn: (a, b) => {
1551
+ const sort2 = this.state.options.sort;
1552
+ const sortBy = sort2.sortBy;
1553
+ if (!sort2.sortBy) {
1554
+ return 0;
1555
+ }
1556
+ const larger = sort2.direction == "asc" ? 1 : -1;
1557
+ const smaller = sort2.direction == "asc" ? -1 : 1;
1558
+ if (typeof a?.[sortBy] === "undefined") {
1559
+ if (typeof b?.[sortBy] === "undefined") {
1560
+ return 0;
1561
+ }
1562
+ return larger;
1563
+ }
1564
+ if (typeof b?.[sortBy] === "undefined") {
1565
+ return smaller;
1566
+ }
1567
+ if (a[sortBy] < b[sortBy]) {
1568
+ return smaller;
1569
+ } else if (a[sortBy] > b[sortBy]) {
1570
+ return larger;
1571
+ } else {
1572
+ return 0;
1573
+ }
1574
+ }
1575
+ }, options);
1576
+ return effect(() => {
1577
+ const sort2 = this.state.options.sort;
1578
+ if (sort2?.sortBy && sort2?.direction) {
1579
+ return data.current.toSorted(sort2?.sortFn);
1580
+ }
1581
+ return data.current;
1582
+ });
2077
1583
  };
2078
-
2079
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
2080
- module.exports = app;
2081
- } else {
2082
- if (!global.simply) {
2083
- global.simply = {};
1584
+ }
1585
+ function paging(options = {}) {
1586
+ return function(data) {
1587
+ this.state.options.paging = Object.assign({
1588
+ page: 1,
1589
+ pageSize: 20,
1590
+ max: 1
1591
+ }, options);
1592
+ return effect(() => {
1593
+ return batch(() => {
1594
+ const paging2 = this.state.options.paging;
1595
+ if (!paging2.pageSize) {
1596
+ paging2.pageSize = 20;
1597
+ }
1598
+ paging2.max = Math.ceil(this.state.data.length / paging2.pageSize);
1599
+ paging2.page = Math.max(1, Math.min(paging2.max, paging2.page));
1600
+ const start = (paging2.page - 1) * paging2.pageSize;
1601
+ const end = start + paging2.pageSize;
1602
+ return data.current.slice(start, end);
1603
+ });
1604
+ });
1605
+ };
1606
+ }
1607
+ function filter(options) {
1608
+ if (!options?.name || typeof options.name !== "string") {
1609
+ throw new Error("filter requires options.name to be a string");
1610
+ }
1611
+ if (!options.matches || typeof options.matches !== "function") {
1612
+ throw new Error("filter requires options.matches to be a function");
1613
+ }
1614
+ return function(data) {
1615
+ this.state.options[options.name] = options;
1616
+ return effect(() => {
1617
+ if (this.state.options[options.name].enabled) {
1618
+ return data.filter(this.state.options.matches);
2084
1619
  }
2085
- global.simply.app = app;
1620
+ });
1621
+ };
1622
+ }
1623
+ function columns(options = {}) {
1624
+ if (!options || typeof options !== "object" || Object.keys(options).length === 0) {
1625
+ throw new Error("columns requires options to be an object with at least one property");
2086
1626
  }
2087
-
2088
- })(this);
1627
+ return function(data) {
1628
+ this.state.options.columns = options;
1629
+ return effect(() => {
1630
+ return data.current.map((input) => {
1631
+ let result = {};
1632
+ for (let key of Object.keys(this.state.options.columns)) {
1633
+ if (!this.state.options.columns[key].hidden) {
1634
+ result[key] = input[key];
1635
+ }
1636
+ }
1637
+ return result;
1638
+ });
1639
+ });
1640
+ };
1641
+ }
1642
+
1643
+ // src/everything.mjs
1644
+ var simply = {
1645
+ activate,
1646
+ action: action_exports,
1647
+ app,
1648
+ bind,
1649
+ command: command_exports,
1650
+ include,
1651
+ key: key_exports,
1652
+ model: model_exports,
1653
+ route: route_exports,
1654
+ state: state_exports
1655
+ };
1656
+ window.simply = simply;
1657
+ var everything_default = simply;
1658
+ })();