simplyview 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2100 +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
- }
340
+ }
341
+ return null;
744
342
  }
745
- };
746
-
747
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
748
- module.exports = route;
749
- } else {
750
- if (!global.simply) {
751
- global.simply = {};
752
- }
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();
1252
- }
1253
- };
1254
-
1255
- var action = function(app, inActions) {
1256
- var actions = Object.create(defaultActions);
1257
- for ( var i in inActions ) {
1258
- actions[i] = inActions[i];
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;
1259
699
  }
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;
700
+ }
1268
701
  };
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
- if (/^[a-z-]*:?\//.test(relative)) {
1389
- return relative; // absolute href, no need to rebase
1390
- }
1391
-
1392
- var stack = base.split('/'),
1393
- parts = relative.split('/');
1394
- stack.pop(); // remove current file name (or empty string)
1395
- for (var i=0; i<parts.length; i++) {
1396
- if (parts[i] == '.')
1397
- continue;
1398
- if (parts[i] == '..')
1399
- stack.pop();
1400
- else
1401
- stack.push(parts[i]);
828
+ hasChanged = false;
829
+ }
830
+ } else {
831
+ lastTick = clock.time;
1402
832
  }
1403
- return stack.join('/');
833
+ } else {
834
+ hasChanged = true;
835
+ }
1404
836
  };
1405
-
1406
- var observer, loaded = {};
1407
- var head = global.document.querySelector('head');
1408
- var currentScript = global.document.currentScript;
1409
- if (!currentScript) {
1410
- var getScriptURL = (function() {
1411
- var scripts = document.getElementsByTagName('script');
1412
- var index = scripts.length - 1;
1413
- var myScript = scripts[index];
1414
- return function() { return myScript.src; };
1415
- })();
1416
- var currentScriptURL = getScriptURL();
1417
- } else {
1418
- 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;
1419
847
  }
1420
-
1421
- var waitForPreviousScripts = function() {
1422
- // because of the async=false attribute, this script will run after
1423
- // the previous scripts have been loaded and run
1424
- // simply.include.next.js only fires the simply-next-script event
1425
- // that triggers the Promise.resolve method
1426
- return new Promise(function(resolve) {
1427
- var next = global.document.createElement('script');
1428
- next.src = rebaseHref('simply.include.next.js', currentScriptURL);
1429
- next.async = false;
1430
- global.document.addEventListener('simply-include-next', function() {
1431
- head.removeChild(next);
1432
- resolve();
1433
- }, { once: true, passive: true});
1434
- head.appendChild(next);
1435
- });
1436
- };
1437
-
1438
- var scriptLocations = [];
1439
-
1440
- var include = {
1441
- scripts: function(scripts, base) {
1442
- var arr = [];
1443
- for(var i = scripts.length; i--; arr.unshift(scripts[i]));
1444
- var importScript = function() {
1445
- var script = arr.shift();
1446
- if (!script) {
1447
- return;
1448
- }
1449
- var attrs = [].map.call(script.attributes, function(attr) {
1450
- return attr.name;
1451
- });
1452
- var clone = global.document.createElement('script');
1453
- attrs.forEach(function(attr) {
1454
- clone.setAttribute(attr, script.getAttribute(attr));
1455
- });
1456
- clone.removeAttribute('data-simply-location');
1457
- if (!clone.src) {
1458
- // this is an inline script, so copy the content and wait for previous scripts to run
1459
- clone.innerHTML = script.innerHTML;
1460
- waitForPreviousScripts()
1461
- .then(function() {
1462
- var node = scriptLocations[script.dataset.simplyLocation];
1463
- node.parentNode.insertBefore(clone, node);
1464
- node.parentNode.removeChild(node);
1465
- importScript();
1466
- });
1467
- } else {
1468
- clone.src = rebaseHref(clone.src, base);
1469
- if (!clone.hasAttribute('async') && !clone.hasAttribute('defer')) {
1470
- clone.async = false; //important! do not use clone.setAttribute('async', false) - it has no effect
1471
- }
1472
- var node = scriptLocations[script.dataset.simplyLocation];
1473
- node.parentNode.insertBefore(clone, node);
1474
- node.parentNode.removeChild(node);
1475
- loaded[clone.src]=true;
1476
- importScript();
1477
- }
1478
- };
1479
- if (arr.length) {
1480
- 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 });
1481
884
  }
1482
- },
1483
- html: function(html, link) {
1484
- var fragment = global.document.createRange().createContextualFragment(html);
1485
- var stylesheets = fragment.querySelectorAll('link[rel="stylesheet"],style');
1486
- // add all stylesheets to head
1487
- [].forEach.call(stylesheets, function(stylesheet) {
1488
- if (stylesheet.href) {
1489
- 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);
1490
910
  }
1491
- head.appendChild(stylesheet);
1492
- });
1493
- // remove the scripts from the fragment, as they will not run in the
1494
- // order in which they are defined
1495
- var scriptsFragment = global.document.createDocumentFragment();
1496
- // FIXME: this loses the original position of the script
1497
- // should add a placeholder so we can reinsert the clone
1498
- var scripts = fragment.querySelectorAll('script');
1499
- [].forEach.call(scripts, function(script) {
1500
- var placeholder = global.document.createComment(script.src || 'inline script');
1501
- script.parentNode.insertBefore(placeholder, script);
1502
- script.dataset.simplyLocation = scriptLocations.length;
1503
- scriptLocations.push(placeholder);
1504
- scriptsFragment.appendChild(script);
1505
- });
1506
- // add the remainder before the include link
1507
- link.parentNode.insertBefore(fragment, link ? link : null);
1508
- global.setTimeout(function() {
1509
- if (global.editor && global.editor.data && fragment.querySelector('[data-simply-field],[data-simply-list]')) {
1510
- //TODO: remove this dependency and let simply.bind listen for dom node insertions (and simply-edit.js use simply.bind)
1511
- global.editor.data.apply(global.editor.currentData, global.document);
911
+ if (bindings2.length) {
912
+ applyBindings(bindings2);
1512
913
  }
1513
- simply.include.scripts(scriptsFragment.childNodes, link ? link.href : global.location.href );
1514
- }, 10);
1515
- }
1516
- };
1517
-
1518
- var included = {};
1519
- var includeLinks = function(links) {
1520
- // mark them as in progress, so handleChanges doesn't find them again
1521
- var remainingLinks = [].reduce.call(links, function(remainder, link) {
1522
- if (link.rel=='simply-include-once' && included[link.href]) {
1523
- link.parentNode.removeChild(link);
1524
- } else {
1525
- included[link.href]=true;
1526
- link.rel = 'simply-include-loading';
1527
- remainder.push(link);
914
+ }
1528
915
  }
1529
- return remainder;
1530
- }, []);
1531
- [].forEach.call(remainingLinks, function(link) {
1532
- if (!link.href) {
1533
- return;
1534
- }
1535
- // fetch the html
1536
- fetch(link.href)
1537
- .then(function(response) {
1538
- if (response.ok) {
1539
- console.log('simply-include: loaded '+link.href);
1540
- return response.text();
1541
- } else {
1542
- console.log('simply-include: failed to load '+link.href);
1543
- }
1544
- })
1545
- .then(function(html) {
1546
- // if succesfull import the html
1547
- simply.include.html(html, link);
1548
- // remove the include link
1549
- link.parentNode.removeChild(link);
1550
- });
1551
- });
1552
- };
1553
-
1554
- var handleChanges = throttle(function() {
1555
- runWhenIdle(function() {
1556
- var links = global.document.querySelectorAll('link[rel="simply-include"],link[rel="simply-include-once"]');
1557
- if (links.length) {
1558
- includeLinks(links);
1559
- }
1560
- });
1561
- });
1562
-
1563
- var observe = function() {
1564
- observer = new MutationObserver(handleChanges);
1565
- observer.observe(global.document, {
1566
- subtree: true,
1567
- childList: true,
1568
- });
1569
- };
1570
-
1571
- observe();
1572
- handleChanges(); // check if there are include links in the dom already
1573
-
1574
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1575
- module.exports = include;
1576
- } else {
1577
- if (!global.simply) {
1578
- global.simply = {};
916
+ }
1579
917
  }
1580
- 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
+ }
1581
930
  }
1582
-
1583
-
1584
- })(this);
1585
- (function(global) {
1586
- 'use strict';
1587
- var view = function(app, view) {
1588
-
1589
- app.view = view || {};
1590
-
1591
- var load = function() {
1592
- var data = app.view;
1593
- var path = global.editor.data.getDataPath(app.container);
1594
- app.view = global.editor.currentData[path];
1595
- Object.keys(data).forEach(function(key) {
1596
- app.view[key] = data[key];
1597
- });
1598
- };
1599
-
1600
- if (global.editor && global.editor.currentData) {
1601
- 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);
1602
965
  } else {
1603
- global.document.addEventListener('simply-content-loaded', function() {
1604
- console.log('switching...')
1605
- load();
1606
- });
1607
- }
1608
-
1609
- return app.view;
1610
- };
1611
-
1612
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1613
- module.exports = view;
1614
- } else {
1615
- if (!global.simply) {
1616
- global.simply = {};
1617
- }
1618
- 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;
1619
974
  }
1620
- })(this);
1621
- (function(global) {
1622
- 'use strict';
1623
-
1624
- function etag() {
1625
- let d = '';
1626
- while (d.length < 32) d += Math.random().toString(16).substr(2);
1627
- const vr = ((parseInt(d.substr(16, 1), 16) & 0x3) | 0x8).toString(16);
1628
- return `${d.substr(0, 8)}-${d.substr(8, 4)}-4${d.substr(13, 3)}-${vr}${d.substr(17, 3)}-${d.substr(20, 12)}`;
1629
- }
1630
-
1631
- function ViewModel(name, data, options) {
1632
- this.name = name;
1633
- this.data = data || [];
1634
- this.data.etag = etag();
1635
- this.view = {
1636
- options: {},
1637
- data: [] //Array.from(this.data).slice()
1638
- };
1639
- this.options = options || {};
1640
- this.plugins = {
1641
- start: [],
1642
- select: [],
1643
- order: [],
1644
- render: [],
1645
- finish: []
1646
- };
975
+ getBindingPath(el) {
976
+ return el.getAttribute(this.options.attribute);
1647
977
  }
1648
-
1649
- ViewModel.prototype.update = function(params) {
1650
- if (!params) {
1651
- params = {};
1652
- }
1653
- if (params.data) {
1654
- // this.data is a reference to the data passed, so that any changes in it will get applied
1655
- // to the original
1656
- this.data = params.data;
1657
- this.data.etag = etag()
1658
- }
1659
- // the view is a shallow copy of the array, so that changes in sort order and filtering
1660
- // won't get applied to the original, but databindings on its children will still work
1661
- this.view.data = Array.from(this.data).slice();
1662
- this.view.data.etag = this.data.etag;
1663
- let data = this.view.data;
1664
- let plugins = this.plugins.start.concat(this.plugins.select, this.plugins.order, this.plugins.render, this.plugins.finish);
1665
- plugins.forEach(plugin => {
1666
- data = plugin.call(this, params, data);
1667
- if (!data) {
1668
- data = this.view.data;
1669
- }
1670
- this.view.data = data
1671
- });
1672
- this.view.data = data;
1673
-
1674
- if (global.editor) {
1675
- global.editor.addDataSource(this.name,{
1676
- load: function(el, callback) {
1677
- callback(self.view.data);
1678
- }
1679
- });
1680
- updateDataSource(this.name);
1681
- }
1682
- };
1683
-
1684
- ViewModel.prototype.addPlugin = function(pipe, plugin) {
1685
- if (typeof this.plugins[pipe] == 'undefined') {
1686
- throw new Error('Unknown pipeline '+pipe);
1687
- }
1688
- this.plugins[pipe].push(plugin);
1689
- };
1690
-
1691
- ViewModel.prototype.removePlugin = function(pipe, plugin) {
1692
- if (typeof this.plugins[pipe] == 'undefined') {
1693
- throw new Error('Unknown pipeline '+pipe);
1694
- }
1695
- this.plugins[pipe] = this.plugins[pipe].filter(function(p) {
1696
- 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;
1697
1119
  });
1698
- };
1699
-
1700
- var updateDataSource = function(name) {
1701
- global.document.querySelectorAll('[data-simply-data="'+name+'"]').forEach(function(list) {
1702
- 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;
1703
1185
  });
1704
- };
1705
-
1706
- var createSort = function(options) {
1707
- var defaultOptions = {
1708
- name: 'sort',
1709
- getSort: function(params) {
1710
- return Array.prototype.sort;
1711
- }
1712
- };
1713
- options = Object.assign(defaultOptions, options || {});
1714
-
1715
- return function(params) {
1716
- this.options[options.name] = options;
1717
- if (params[options.name]) {
1718
- options = Object.assign(options, params[options.name]);
1719
- }
1720
- this.view.data.sort(options.getSort.call(this, options));
1721
- };
1722
- };
1723
-
1724
- var createPaging = function(options) {
1725
- var defaultOptions = {
1726
- name: 'paging',
1727
- page: 1,
1728
- pageSize: 100,
1729
- max: 1,
1730
- prev: 0,
1731
- next: 0
1732
- };
1733
- options = Object.assign(defaultOptions, options || {});
1734
-
1735
- return function(params) {
1736
- this.options[options.name] = options;
1737
- if (this.view.data) {
1738
- options.max = Math.max(1, Math.ceil(Array.from(this.view.data).length / options.pageSize));
1739
- } else {
1740
- options.max = 1;
1741
- }
1742
- if (this.view.changed) {
1743
- options.page = 1; // reset to page 1 when something in the view data has changed
1744
- }
1745
- if (params[options.name]) {
1746
- options = Object.assign(options, params[options.name]);
1747
- }
1748
- options.page = Math.max(1, Math.min(options.max, options.page)); // clamp page nr
1749
- options.prev = options.page - 1; // calculate previous page, 0 is allowed
1750
- if (options.page<options.max) {
1751
- options.next = options.page + 1;
1752
- } else {
1753
- options.next = 0; // no next page
1754
- }
1755
-
1756
- var start = (options.page - 1) * options.pageSize;
1757
- var end = start + options.pageSize;
1758
-
1759
- this.view.data = this.view.data.slice(start, end);
1760
- };
1761
- };
1762
-
1763
- var createFilter = function(options) {
1764
- var defaultOptions = {
1765
- name: 'filter',
1766
- label: 'A filter',
1767
- getMatch: function(entry) {
1768
- return false;
1769
- }
1770
- };
1771
- options = Object.assign(defaultOptions, options || {});
1772
- if (options.init) {
1773
- options.init.call(this, options);
1774
- }
1775
- return function(params) {
1776
- this.options[options.name] = options;
1777
- if (params[options.name]) {
1778
- options = Object.assign(options, params[options.name]);
1779
- }
1780
- var match = options.getMatch.call(this, options);
1781
- if (match) {
1782
- options.enabled = true;
1783
- this.view.data = this.view.data.filter(match);
1784
- } else if (options.enabled) {
1785
- options.enabled = false;
1786
- }
1787
- }
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
+ }
1788
1203
  }
1789
-
1790
- var viewmodel = {
1791
- create: function(name, data, options) {
1792
- return new ViewModel(name, data, options);
1793
- },
1794
- createFilter: createFilter,
1795
- createSort: createSort,
1796
- createPaging: createPaging,
1797
- updateDataSource: updateDataSource,
1798
- etag
1799
- };
1800
-
1801
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1802
- 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
+ }
1803
1275
  } else {
1804
- if (!global.simply) {
1805
- global.simply = {};
1806
- }
1807
- global.simply.viewmodel = viewmodel;
1276
+ let option = el.options.find((o) => matchValue(o.value, value));
1277
+ if (option) {
1278
+ option.selected = true;
1279
+ }
1808
1280
  }
1809
-
1810
- })(this);(function(global) {
1811
- 'use strict';
1812
-
1813
- var api = {
1814
- /**
1815
- * Returns a Proxy object that translates property access to a URL in the api
1816
- * and method calls to a fetch on that URL.
1817
- * @param options: a list of options for fetch(),
1818
- * see the 'init' parameter at https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters
1819
- * additionally:
1820
- * - baseURL: (required) the endpoint of the API
1821
- * - path: the current path in the API, is appended to the baseURL
1822
- * - verbs: list of http verbs to allow as methods, default ['get','post']
1823
- * - handlers.fetch: alternative fetch method
1824
- * - handlers.result: alternative getResult method
1825
- * - handlers.error: alternative error method
1826
- * - user (and password): if set, a basic authentication header will be added
1827
- * - paramsFormat: either 'formData', 'json' or 'search'. Default is search.
1828
- * - responseFormat: test, formData, blob, json, arrayBuffer or unbuffered. Default is json.
1829
- * @return Proxy
1830
- */
1831
- proxy: function(options) {
1832
- var cache = () => {};
1833
- cache.$options = Object.assign({}, options);
1834
- return new Proxy( cache, getApiHandler(cache.$options) );
1835
- },
1836
-
1837
- /**
1838
- * Fetches the options.baseURL using the fetch api and returns a promise
1839
- * Extra options in addition to those of global.fetch():
1840
- * - user (and password): if set, a basic authentication header will be added
1841
- * - paramsFormat: either 'formData', 'json' or 'search'
1842
- * By default params, if set, will be added to the baseURL as searchParams
1843
- * @param method one of the http verbs, e.g. get, post, etc.
1844
- * @param options the options for fetch(), with some additions
1845
- * @param params the parameters to send with the request, as javascript/json data
1846
- * @return Promise
1847
- */
1848
- fetch: function(method, params, options) {
1849
- if (!options.url) {
1850
- if (!options.baseURL) {
1851
- throw new Error('No url or baseURL in options object');
1852
- }
1853
- while (options.baseURL[options.baseURL.length-1]=='/') {
1854
- options.baseURL = options.baseURL.substr(0, options.baseURL.length-1);
1855
- }
1856
- var url = new URL(options.baseURL+options.path);
1857
- } else {
1858
- var url = options.url;
1859
- }
1860
- var fetchOptions = Object.assign({}, options);
1861
- if (!fetchOptions.headers) {
1862
- fetchOptions.headers = {};
1863
- }
1864
- if (params) {
1865
- if (method=='GET') {
1866
- var paramsFormat = 'search';
1867
- } else {
1868
- var paramsFormat = options.paramsFormat;
1869
- }
1870
- switch(paramsFormat) {
1871
- case 'formData':
1872
- var formData = new FormData();
1873
- for (const name in params) {
1874
- formData.append(name, params[name]);
1875
- }
1876
- if (!fetchOptions.headers['Content-Type']) {
1877
- fetchOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded';
1878
- }
1879
- break;
1880
- case 'json':
1881
- var formData = JSON.stringify(params);
1882
- if (!fetchOptions.headers['Content-Type']) {
1883
- fetchOptions.headers['Content-Type'] = 'application/json';
1884
- }
1885
- break;
1886
- case 'search':
1887
- var searchParams = url.searchParams; //new URLSearchParams(url.search.slice(1));
1888
- for (const name in params) {
1889
- searchParams.set(name, params[name]);
1890
- }
1891
- url.search = searchParams.toString();
1892
- break;
1893
- default:
1894
- throw Error('Unknown options.paramsFormat '+options.paramsFormat+'. Select one of formData, json or search.');
1895
- break;
1896
- }
1897
- }
1898
- if (formData) {
1899
- fetchOptions.body = formData
1900
- }
1901
- if (options.user) {
1902
- fetchOptions.headers['Authorization'] = 'Basic '+btoa(options.user+':'+options.password);
1903
- }
1904
- fetchOptions.method = method.toUpperCase();
1905
- var fetchURL = url.toString()
1906
- return fetch(fetchURL, fetchOptions);
1907
- },
1908
- /**
1909
- * Creates a function to call one or more graphql queries
1910
- */
1911
- graphqlQuery: function(url, query, options) {
1912
- options = Object.assign({ paramsFormat: 'json', url: url, responseFormat: 'json' }, options);
1913
- return function(params, operationName) {
1914
- let postParams = {
1915
- query: query
1916
- };
1917
- if (operationName) {
1918
- postParams.operationName = operationName;
1919
- }
1920
- postParams.variables = params || {};
1921
- return simply.api.fetch('POST', postParams, options )
1922
- .then(function(response) {
1923
- return simply.api.getResult(response, options);
1924
- });
1925
- }
1926
- },
1927
- /**
1928
- * Handles the response and returns a Promise with the response data as specified
1929
- * @param response Response
1930
- * @param options
1931
- * - responseFormat: one of 'text', 'formData', 'blob', 'arrayBuffer', 'unbuffered' or 'json'.
1932
- * The default is json.
1933
- */
1934
- getResult: function(response, options) {
1935
- if (response.ok) {
1936
- switch(options.responseFormat) {
1937
- case 'text':
1938
- return response.text();
1939
- break;
1940
- case 'formData':
1941
- return response.formData();
1942
- break;
1943
- case 'blob':
1944
- return response.blob();
1945
- break;
1946
- case 'arrayBuffer':
1947
- return response.arrayBuffer();
1948
- break;
1949
- case 'unbuffered':
1950
- return response.body;
1951
- break;
1952
- case 'json':
1953
- default:
1954
- return response.json();
1955
- break;
1956
- }
1957
- } else {
1958
- throw {
1959
- status: response.status,
1960
- message: response.statusText,
1961
- response: response
1962
- }
1963
- }
1964
- },
1965
- logError: function(error, options) {
1966
- console.error(error.status, error.message);
1967
- }
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;
1968
1287
  }
1969
-
1970
- var defaultOptions = {
1971
- path: '',
1972
- responseFormat: 'json',
1973
- paramsFormat: 'search',
1974
- verbs: ['get','post'],
1975
- handlers: {
1976
- fetch: api.fetch,
1977
- result: api.getResult,
1978
- error: api.logError
1979
- }
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
+ }
1980
1347
  };
1981
-
1982
- function cd(path, name) {
1983
- name = name.replace(/\//g,'');
1984
- if (!path.length || path[path.length-1]!=='/') {
1985
- path+='/';
1986
- }
1987
- return path+encodeURIComponent(name);
1348
+ }
1349
+ var runWhenIdle = (() => {
1350
+ if (globalThis.requestIdleCallback) {
1351
+ return (callback) => {
1352
+ globalThis.requestIdleCallback(callback, { timeout: 500 });
1353
+ };
1988
1354
  }
1989
-
1990
- function fetchChain(prop, params) {
1991
- var options = this;
1992
- return this.handlers.fetch
1993
- .call(this, prop, params, options)
1994
- .then(function(res) {
1995
- return options.handlers.result.call(options, res, options);
1996
- })
1997
- .catch(function(error) {
1998
- return options.handlers.error.call(options, error, options);
1999
- });
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);
2000
1361
  }
2001
-
2002
- function getApiHandler(options) {
2003
- options.handlers = Object.assign({}, defaultOptions.handlers, options.handlers);
2004
- options = Object.assign({}, defaultOptions, options);
2005
-
2006
- return {
2007
- get: function(cache, prop) {
2008
- if (!cache[prop]) {
2009
- if (options.verbs.indexOf(prop)!=-1) {
2010
- cache[prop] = function(params) {
2011
- return fetchChain.call(options, prop, params);
2012
- }
2013
- } else {
2014
- cache[prop] = api.proxy(Object.assign({}, options, {
2015
- path: cd(options.path, prop)
2016
- }));
2017
- }
2018
- }
2019
- return cache[prop];
2020
- },
2021
- apply: function(cache, thisArg, params) {
2022
- return fetchChain.call(options, 'get', params[0] ? params[0] : null)
2023
- }
2024
- }
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);
2025
1457
  }
2026
-
2027
-
2028
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
2029
- module.exports = api;
2030
- } else {
2031
- if (!global.simply) {
2032
- global.simply = {};
2033
- }
2034
- 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);
2035
1484
  }
2036
-
2037
- })(this);
2038
- (function(global) {
2039
- 'use strict';
2040
-
2041
- var app = function(options) {
2042
- if (!options) {
2043
- options = {};
2044
- }
2045
- if (!options.container) {
2046
- console.warn('No simply.app application container element specified, using document.body.');
2047
- }
2048
-
2049
- function simplyApp(options) {
2050
- if (!options) {
2051
- options = {};
2052
- }
2053
- if ( options.routes ) {
2054
- simply.route.load(options.routes);
2055
- if (options.routeEvents) {
2056
- Object.keys(options.routeEvents).forEach(function(action) {
2057
- Object.keys(options.routeEvents[action]).forEach(function(route) {
2058
- options.routeEvents[action][route].forEach(function(callback) {
2059
- simply.route.addListener(action, route, callback);
2060
- });
2061
- });
2062
- });
2063
- }
2064
- simply.route.handleEvents();
2065
- global.setTimeout(function() {
2066
- simply.route.match(global.location.pathname+global.location.hash);
2067
- });
2068
- }
2069
- this.container = options.container || document.body;
2070
- this.keyboard = simply.keyboard ? simply.keyboard(this, options.keyboard || {}) : false;
2071
- this.actions = simply.action ? simply.action(this, options.actions) : false;
2072
- this.commands = simply.command ? simply.command(this, options.commands) : false;
2073
- this.resize = simply.resize ? simply.resize(this, options.resize) : false;
2074
- this.view = simply.view ? simply.view(this, options.view) : false;
2075
- if (!(global.editor && global.editor.field) && simply.bind) {
2076
- // skip simplyview databinding if SimplyEdit is loaded
2077
- options.bind = simply.render(options.bind || {});
2078
- options.bind.model = this.view;
2079
- options.bind.container = this.container;
2080
- this.bind = options.bindings = simply.bind(options.bind);
2081
- }
2082
- }
2083
-
2084
- simplyApp.prototype.get = function(id) {
2085
- return this.container.querySelector('[data-simply-id='+id+']') || document.getElementById(id);
2086
- };
2087
-
2088
- 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
+ });
2089
1583
  };
2090
-
2091
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
2092
- module.exports = app;
2093
- } else {
2094
- if (!global.simply) {
2095
- 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);
2096
1619
  }
2097
- 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");
2098
1626
  }
2099
-
2100
- })(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
+ })();