simplyview 3.0.1 → 3.0.3
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.
- package/README.md +2 -5
- package/dist/simply.app.js +85 -731
- package/dist/simply.app.min.js +1 -1
- package/dist/simply.app.min.js.map +4 -4
- package/dist/simply.everything.js +101 -1039
- package/dist/simply.everything.min.js +1 -1
- package/dist/simply.everything.min.js.map +4 -4
- package/package.json +10 -5
- package/src/app.mjs +3 -13
- package/src/bind.mjs +166 -89
- package/src/command.mjs +1 -1
- package/src/everything.mjs +6 -10
- package/src/include.mjs +13 -10
- package/src/key.mjs +74 -21
- package/src/route.mjs +3 -2
- package/src/view.mjs +20 -0
- package/src/changes.md +0 -18
- package/src/include.next.js +0 -1
package/src/bind.mjs
CHANGED
|
@@ -7,14 +7,28 @@ class SimplyBind {
|
|
|
7
7
|
container: document.body,
|
|
8
8
|
attribute: 'data-bind',
|
|
9
9
|
transformers: [],
|
|
10
|
-
defaultTransformers:
|
|
10
|
+
defaultTransformers: {
|
|
11
|
+
field: [defaultFieldTransformer],
|
|
12
|
+
list: [defaultListTransformer],
|
|
13
|
+
map: [defaultMapTransformer]
|
|
14
|
+
}
|
|
11
15
|
}
|
|
12
16
|
if (!options?.root) {
|
|
13
17
|
throw new Error('bind needs at least options.root set')
|
|
14
18
|
}
|
|
15
19
|
this.options = Object.assign({}, defaultOptions, options)
|
|
16
20
|
|
|
17
|
-
const attribute
|
|
21
|
+
const attribute = this.options.attribute
|
|
22
|
+
const bindAttributes = [attribute+'-field',attribute+'-list',attribute+'-map']
|
|
23
|
+
const bindSelector = `[${attribute}-field],[${attribute}-list],[${attribute}-map]`
|
|
24
|
+
|
|
25
|
+
const getBindingAttribute = (el) => {
|
|
26
|
+
const foundAttribute = bindAttributes.find(attr => el.hasAttribute(attr))
|
|
27
|
+
if (!foundAttribute) {
|
|
28
|
+
console.error('No matching attribute found',el)
|
|
29
|
+
}
|
|
30
|
+
return foundAttribute
|
|
31
|
+
}
|
|
18
32
|
|
|
19
33
|
// sets up the effect that updates the element if its
|
|
20
34
|
// data binding value changes
|
|
@@ -23,8 +37,9 @@ class SimplyBind {
|
|
|
23
37
|
this.bindings.set(el, throttledEffect(() => {
|
|
24
38
|
const context = {
|
|
25
39
|
templates: el.querySelectorAll(':scope > template'),
|
|
26
|
-
|
|
40
|
+
attribute: getBindingAttribute(el)
|
|
27
41
|
}
|
|
42
|
+
context.path = this.getBindingPath(el)
|
|
28
43
|
context.value = getValueByPath(this.options.root, context.path)
|
|
29
44
|
context.element = el
|
|
30
45
|
runTransformers(context)
|
|
@@ -36,7 +51,18 @@ class SimplyBind {
|
|
|
36
51
|
// each transformer can opt to call the next or not
|
|
37
52
|
// transformers should return the context object (possibly altered)
|
|
38
53
|
const runTransformers = (context) => {
|
|
39
|
-
let transformers
|
|
54
|
+
let transformers
|
|
55
|
+
switch(context.attribute) {
|
|
56
|
+
case this.options.attribute+'-field':
|
|
57
|
+
transformers = this.options.defaultTransformers.field || []
|
|
58
|
+
break
|
|
59
|
+
case this.options.attribute+'-list':
|
|
60
|
+
transformers = this.options.defaultTransformers.list || []
|
|
61
|
+
break
|
|
62
|
+
case this.options.attribute+'-map':
|
|
63
|
+
transformers = this.options.defaultTransformers.map || []
|
|
64
|
+
break
|
|
65
|
+
}
|
|
40
66
|
if (context.element.dataset.transform) {
|
|
41
67
|
context.element.dataset.transform.split(' ').filter(Boolean).forEach(t => {
|
|
42
68
|
if (this.options.transformers[t]) {
|
|
@@ -69,12 +95,13 @@ class SimplyBind {
|
|
|
69
95
|
// if any element is added, and has a data bind attribute
|
|
70
96
|
// it applies that data binding
|
|
71
97
|
const updateBindings = (changes) => {
|
|
98
|
+
const selector = `[${attribute}-field],[${attribute}-list],[${attribute}-map]`
|
|
72
99
|
for (const change of changes) {
|
|
73
100
|
if (change.type=="childList" && change.addedNodes) {
|
|
74
101
|
for (let node of change.addedNodes) {
|
|
75
102
|
if (node instanceof HTMLElement) {
|
|
76
|
-
let bindings = Array.from(node.querySelectorAll(
|
|
77
|
-
if (node.matches(
|
|
103
|
+
let bindings = Array.from(node.querySelectorAll(selector))
|
|
104
|
+
if (node.matches(selector)) {
|
|
78
105
|
bindings.unshift(node)
|
|
79
106
|
}
|
|
80
107
|
if (bindings.length) {
|
|
@@ -100,7 +127,11 @@ class SimplyBind {
|
|
|
100
127
|
// this finds elements with data binding attributes and applies those bindings
|
|
101
128
|
// must come after setting up the observer, or included templates
|
|
102
129
|
// won't trigger their own bindings
|
|
103
|
-
const bindings = this.options.container.querySelectorAll(
|
|
130
|
+
const bindings = this.options.container.querySelectorAll(
|
|
131
|
+
'['+this.options.attribute+'-field]'+
|
|
132
|
+
',['+this.options.attribute+'-list]'+
|
|
133
|
+
',['+this.options.attribute+'-map]'
|
|
134
|
+
)
|
|
104
135
|
if (bindings.length) {
|
|
105
136
|
applyBindings(bindings)
|
|
106
137
|
}
|
|
@@ -127,23 +158,25 @@ class SimplyBind {
|
|
|
127
158
|
}
|
|
128
159
|
let clone = template.content.cloneNode(true)
|
|
129
160
|
if (!clone.children?.length) {
|
|
130
|
-
|
|
161
|
+
return clone
|
|
131
162
|
}
|
|
132
163
|
if (clone.children.length>1) {
|
|
133
164
|
throw new Error('template must contain a single root node', { cause: template })
|
|
134
165
|
}
|
|
135
|
-
const bindings = clone.querySelectorAll('['+this.options.attribute+']')
|
|
136
166
|
const attribute = this.options.attribute
|
|
167
|
+
const attributes = [attribute+'-field',attribute+'-list',attribute+'-map']
|
|
168
|
+
const bindings = clone.querySelectorAll(`[${attribute}-field],[${attribute}-list],[${attribute}-map]`)
|
|
137
169
|
for (let binding of bindings) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
170
|
+
const attr = attributes.find(attr => binding.hasAttribute(attr))
|
|
171
|
+
const bind = binding.getAttribute(attr)
|
|
172
|
+
if (bind.substring(0, ':root.'.length)==':root.') {
|
|
173
|
+
binding.setAttribute(attr, bind.substring(':root.'.length))
|
|
174
|
+
} else if (bind==':value' && index!=null) {
|
|
175
|
+
binding.setAttribute(attr, path+'.'+index)
|
|
143
176
|
} else if (index!=null) {
|
|
144
|
-
binding.setAttribute(
|
|
177
|
+
binding.setAttribute(attr, path+'.'+index+'.'+bind)
|
|
145
178
|
} else {
|
|
146
|
-
binding.setAttribute(
|
|
179
|
+
binding.setAttribute(attr, parent+'.'+bind)
|
|
147
180
|
}
|
|
148
181
|
}
|
|
149
182
|
if (typeof index !== 'undefined') {
|
|
@@ -156,7 +189,16 @@ class SimplyBind {
|
|
|
156
189
|
}
|
|
157
190
|
|
|
158
191
|
getBindingPath(el) {
|
|
159
|
-
|
|
192
|
+
const attributes = [
|
|
193
|
+
this.options.attribute+'-field',
|
|
194
|
+
this.options.attribute+'-list',
|
|
195
|
+
this.options.attribute+'-map'
|
|
196
|
+
]
|
|
197
|
+
for (let attr of attributes) {
|
|
198
|
+
if (el.hasAttribute(attr)) {
|
|
199
|
+
return el.getAttribute(attr)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
160
202
|
}
|
|
161
203
|
|
|
162
204
|
/**
|
|
@@ -169,7 +211,7 @@ class SimplyBind {
|
|
|
169
211
|
let path = this.getBindingPath(t)
|
|
170
212
|
let currentItem
|
|
171
213
|
if (path) {
|
|
172
|
-
if (path.substr(0,6)=='
|
|
214
|
+
if (path.substr(0,6)==':root.') {
|
|
173
215
|
currentItem = getValueByPath(this.options.root, path)
|
|
174
216
|
} else {
|
|
175
217
|
currentItem = getValueByPath(value, path)
|
|
@@ -182,20 +224,21 @@ class SimplyBind {
|
|
|
182
224
|
const strItem = ''+currentItem
|
|
183
225
|
let matches = t.getAttribute(this.options.attribute+'-match')
|
|
184
226
|
if (matches) {
|
|
185
|
-
if (matches==='
|
|
227
|
+
if (matches===':empty' && !currentItem) {
|
|
186
228
|
return t
|
|
187
|
-
} else if (matches==='
|
|
229
|
+
} else if (matches===':notempty' && currentItem) {
|
|
188
230
|
return t
|
|
189
231
|
}
|
|
190
232
|
if (strItem.match(matches)) {
|
|
191
233
|
return t
|
|
192
234
|
}
|
|
193
235
|
}
|
|
194
|
-
if (!matches) {
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
236
|
+
if (!matches && currentItem!==null && currentItem!==undefined) {
|
|
237
|
+
//FIXME: this doesn't run templates in lists where list entry is null
|
|
238
|
+
//which messes up the count
|
|
239
|
+
//
|
|
240
|
+
// no data-bind-match is set, so return this template
|
|
241
|
+
return t
|
|
199
242
|
}
|
|
200
243
|
}
|
|
201
244
|
let template = Array.from(templates).find(templateMatches)
|
|
@@ -231,13 +274,13 @@ export function bind(options)
|
|
|
231
274
|
|
|
232
275
|
/**
|
|
233
276
|
* Returns true if a matches b, either by having the
|
|
234
|
-
* same string value, or matching string
|
|
277
|
+
* same string value, or matching string :empty against a falsy value
|
|
235
278
|
*/
|
|
236
279
|
export function matchValue(a,b) {
|
|
237
|
-
if (a=='
|
|
280
|
+
if (a==':empty' && !b) {
|
|
238
281
|
return true
|
|
239
282
|
}
|
|
240
|
-
if (b=='
|
|
283
|
+
if (b==':empty' && !a) {
|
|
241
284
|
return true
|
|
242
285
|
}
|
|
243
286
|
if (''+a == ''+b) {
|
|
@@ -259,11 +302,11 @@ export function getValueByPath(root, path)
|
|
|
259
302
|
let part, prevPart;
|
|
260
303
|
while (parts.length && curr) {
|
|
261
304
|
part = parts.shift()
|
|
262
|
-
if (part=='
|
|
305
|
+
if (part==':key') {
|
|
263
306
|
return prevPart
|
|
264
|
-
} else if (part=='
|
|
307
|
+
} else if (part==':value') {
|
|
265
308
|
return curr
|
|
266
|
-
} else if (part=='
|
|
309
|
+
} else if (part==':root') {
|
|
267
310
|
curr = root
|
|
268
311
|
} else {
|
|
269
312
|
part = decodeURIComponent(part)
|
|
@@ -278,7 +321,7 @@ export function getValueByPath(root, path)
|
|
|
278
321
|
* Default transformer for data binding
|
|
279
322
|
* Will be used unless overriden in the SimplyBind options parameter
|
|
280
323
|
*/
|
|
281
|
-
export function
|
|
324
|
+
export function defaultFieldTransformer(context) {
|
|
282
325
|
const el = context.element
|
|
283
326
|
const templates = context.templates
|
|
284
327
|
const templatesCount = templates.length
|
|
@@ -286,11 +329,7 @@ export function defaultTransformer(context) {
|
|
|
286
329
|
const value = context.value
|
|
287
330
|
const attribute = this.options.attribute
|
|
288
331
|
|
|
289
|
-
if (
|
|
290
|
-
transformArrayByTemplates.call(this, context)
|
|
291
|
-
} else if (typeof value == 'object' && templates?.length) {
|
|
292
|
-
transformObjectByTemplates.call(this, context)
|
|
293
|
-
} else if (templates?.length) {
|
|
332
|
+
if (templates?.length) {
|
|
294
333
|
transformLiteralByTemplates.call(this, context)
|
|
295
334
|
} else if (el.tagName=='INPUT') {
|
|
296
335
|
transformInput.call(this, context)
|
|
@@ -306,10 +345,49 @@ export function defaultTransformer(context) {
|
|
|
306
345
|
return context
|
|
307
346
|
}
|
|
308
347
|
|
|
348
|
+
export function defaultListTransformer(context) {
|
|
349
|
+
const el = context.element
|
|
350
|
+
const templates = context.templates
|
|
351
|
+
const templatesCount = templates.length
|
|
352
|
+
const path = context.path
|
|
353
|
+
const value = context.value
|
|
354
|
+
const attribute = this.options.attribute
|
|
355
|
+
|
|
356
|
+
if (!Array.isArray(value)) {
|
|
357
|
+
console.error('Value is not an array.', el, value)
|
|
358
|
+
} else if (!templates?.length) {
|
|
359
|
+
console.error('No templates found in', el)
|
|
360
|
+
} else {
|
|
361
|
+
transformArrayByTemplates.call(this, context)
|
|
362
|
+
}
|
|
363
|
+
return context
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function defaultMapTransformer(context) {
|
|
367
|
+
const el = context.element
|
|
368
|
+
const templates = context.templates
|
|
369
|
+
const templatesCount = templates.length
|
|
370
|
+
const path = context.path
|
|
371
|
+
const value = context.value
|
|
372
|
+
const attribute = this.options.attribute
|
|
373
|
+
|
|
374
|
+
if (typeof value != 'object') {
|
|
375
|
+
console.error('Value is not an object.', el, value)
|
|
376
|
+
} else if (!templates?.length) {
|
|
377
|
+
console.error('No templates found in', el)
|
|
378
|
+
} else {
|
|
379
|
+
transformObjectByTemplates.call(this, context)
|
|
380
|
+
}
|
|
381
|
+
return context
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
309
385
|
/**
|
|
310
386
|
* Renders an array value by applying templates for each entry
|
|
311
387
|
* Replaces or removes existing DOM children if needed
|
|
312
388
|
* Reuses (doesn't touch) DOM children if template doesn't change
|
|
389
|
+
* FIXME: this doesn't handle situations where there is no matching template
|
|
390
|
+
* this messes up self healing. check transformObjectByTemplates for a better implementation
|
|
313
391
|
*/
|
|
314
392
|
export function transformArrayByTemplates(context) {
|
|
315
393
|
const el = context.element
|
|
@@ -335,14 +413,14 @@ export function transformArrayByTemplates(context) {
|
|
|
335
413
|
// remove this
|
|
336
414
|
item.remove()
|
|
337
415
|
} else {
|
|
338
|
-
// check that all data-bind params start with current json path or
|
|
416
|
+
// check that all data-bind params start with current json path or ':root', otherwise replaceChild
|
|
339
417
|
let bindings = Array.from(item.querySelectorAll(`[${attribute}]`))
|
|
340
418
|
if (item.matches(`[${attribute}]`)) {
|
|
341
419
|
bindings.unshift(item)
|
|
342
420
|
}
|
|
343
421
|
let needsReplacement = bindings.find(b => {
|
|
344
422
|
let databind = b.getAttribute(attribute)
|
|
345
|
-
return (databind.substr(0,5)!=='
|
|
423
|
+
return (databind.substr(0,5)!==':root'
|
|
346
424
|
&& databind.substr(0, path.length)!==path)
|
|
347
425
|
})
|
|
348
426
|
if (!needsReplacement) {
|
|
@@ -385,7 +463,7 @@ export function transformArrayByTemplates(context) {
|
|
|
385
463
|
|
|
386
464
|
/**
|
|
387
465
|
* Renders an object value by applying templates for each entry (Object.entries)
|
|
388
|
-
* Replaces or removes existing DOM children if needed
|
|
466
|
+
* Replaces,moves or removes existing DOM children if needed
|
|
389
467
|
* Reuses (doesn't touch) DOM children if template doesn't change
|
|
390
468
|
*/
|
|
391
469
|
export function transformObjectByTemplates(context) {
|
|
@@ -397,61 +475,55 @@ export function transformObjectByTemplates(context) {
|
|
|
397
475
|
const attribute = this.options.attribute
|
|
398
476
|
context.list = value
|
|
399
477
|
|
|
400
|
-
let
|
|
401
|
-
let
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
478
|
+
let items = Array.from(el.querySelectorAll(':scope > ['+attribute+'-key]'))
|
|
479
|
+
for (let key in context.list) {
|
|
480
|
+
context.index = key
|
|
481
|
+
let item = items.shift()
|
|
482
|
+
if (!item) { // more properties than rendered items
|
|
483
|
+
let clone = this.applyTemplate(context)
|
|
484
|
+
if (clone.firstElementChild) {
|
|
485
|
+
el.appendChild(clone)
|
|
486
|
+
}
|
|
487
|
+
continue
|
|
407
488
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
} else {
|
|
417
|
-
let bindings = Array.from(item.querySelectorAll(`[${attribute}]`))
|
|
418
|
-
needsReplacement = bindings.find(b => {
|
|
419
|
-
const db = b.getAttribute(attribute)
|
|
420
|
-
return (db.substr(0,5)!=='#root' && db.substr(0, keypath.length)!==keypath)
|
|
421
|
-
})
|
|
422
|
-
if (!needsReplacement) {
|
|
423
|
-
if (item.$bindTemplate) {
|
|
424
|
-
let newTemplate = this.findTemplate(templates, value[key])
|
|
425
|
-
if (newTemplate != item.$bindTemplate){
|
|
426
|
-
needsReplacement = true
|
|
427
|
-
if (!newTemplate) {
|
|
428
|
-
skipped++
|
|
429
|
-
}
|
|
430
|
-
}
|
|
489
|
+
if (item.getAttribute[attribute+'-key']!=key) {
|
|
490
|
+
// next item doesn't match key
|
|
491
|
+
items.unshift(item) // put item back for next cycle
|
|
492
|
+
let outOfOrderItem = el.querySelector(':scope > ['+attribute+'-key="'+key+'"]') //FIXME: escape key
|
|
493
|
+
if (!outOfOrderItem) {
|
|
494
|
+
let clone = this.applyTemplate(context)
|
|
495
|
+
if (clone.firstElementChild) {
|
|
496
|
+
el.insertBefore(clone, item)
|
|
431
497
|
}
|
|
498
|
+
continue // new template doesn't need replacement, so continue
|
|
499
|
+
} else {
|
|
500
|
+
el.insertBefore(outOfOrderItem, item)
|
|
501
|
+
item = outOfOrderItem // check needsreplacement next
|
|
502
|
+
items = items.filter(i => i!=outOfOrderItem)
|
|
432
503
|
}
|
|
433
504
|
}
|
|
434
|
-
|
|
435
|
-
|
|
505
|
+
let newTemplate = this.findTemplate(templates, value[key])
|
|
506
|
+
if (newTemplate != item.$bindTemplate){
|
|
436
507
|
let clone = this.applyTemplate(context)
|
|
437
508
|
el.replaceChild(clone, item)
|
|
438
509
|
}
|
|
439
510
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
}
|
|
511
|
+
// clean up remaining items
|
|
512
|
+
while (items.length) {
|
|
513
|
+
item = items.shift()
|
|
514
|
+
item.remove()
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function getParentPath(el, attribute) {
|
|
519
|
+
const parentEl = el.parentElement?.closest(`[${attribute}-list],[${attribute}-map]`)
|
|
520
|
+
if (!parentEl) {
|
|
521
|
+
return ':root'
|
|
522
|
+
}
|
|
523
|
+
if (parentEl.hasAttribute(`${attribute}-list`)) {
|
|
524
|
+
return parentEl.getAttribute(`${attribute}-list`)
|
|
525
|
+
}
|
|
526
|
+
return parentEl.getAttribute(`${attribute}-map`)
|
|
455
527
|
}
|
|
456
528
|
|
|
457
529
|
/**
|
|
@@ -468,7 +540,8 @@ export function transformLiteralByTemplates(context) {
|
|
|
468
540
|
|
|
469
541
|
const rendered = el.querySelector(':scope > :not(template)')
|
|
470
542
|
const template = this.findTemplate(templates, value)
|
|
471
|
-
|
|
543
|
+
|
|
544
|
+
context.parent = getParentPath(el, attribute)
|
|
472
545
|
if (rendered) {
|
|
473
546
|
if (template) {
|
|
474
547
|
if (rendered?.$bindTemplate != template) {
|
|
@@ -567,6 +640,10 @@ export function transformElement(context) {
|
|
|
567
640
|
const value = context.value
|
|
568
641
|
|
|
569
642
|
if (!matchValue(el.innerHTML, value)) {
|
|
570
|
-
|
|
643
|
+
if (typeof value=='undefined' || value==null) {
|
|
644
|
+
el.innerHTML = ''
|
|
645
|
+
} else {
|
|
646
|
+
el.innerHTML = ''+value
|
|
647
|
+
}
|
|
571
648
|
}
|
|
572
649
|
}
|
package/src/command.mjs
CHANGED
|
@@ -21,7 +21,7 @@ class SimplyCommands {
|
|
|
21
21
|
return
|
|
22
22
|
}
|
|
23
23
|
const shouldContinue = this[command.name].call(options.app, command.source, command.value)
|
|
24
|
-
if (shouldContinue
|
|
24
|
+
if (shouldContinue!==true) {
|
|
25
25
|
evt.preventDefault()
|
|
26
26
|
evt.stopPropagation()
|
|
27
27
|
return false
|
package/src/everything.mjs
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import { activate } from './activate.mjs'
|
|
2
|
-
import
|
|
2
|
+
import { actions as action } from './action.mjs'
|
|
3
3
|
import { app } from './app.mjs'
|
|
4
|
-
import {
|
|
5
|
-
import * as command from './command.mjs'
|
|
4
|
+
import { commands as command } from './command.mjs'
|
|
6
5
|
import { include } from './include.mjs'
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import * as state from './state.mjs'
|
|
6
|
+
import { keys as key } from './key.mjs'
|
|
7
|
+
import { routes as route } from './route.mjs'
|
|
8
|
+
import { view } from './view.mjs'
|
|
11
9
|
|
|
12
10
|
const simply = {
|
|
13
11
|
activate,
|
|
14
12
|
action,
|
|
15
13
|
app,
|
|
16
|
-
bind,
|
|
17
14
|
command,
|
|
18
15
|
include,
|
|
19
16
|
key,
|
|
20
|
-
model,
|
|
21
17
|
route,
|
|
22
|
-
|
|
18
|
+
view
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
window.simply = simply
|
package/src/include.mjs
CHANGED
|
@@ -53,7 +53,7 @@ const waitForPreviousScripts = async () => {
|
|
|
53
53
|
// that triggers the Promise.resolve method
|
|
54
54
|
return new Promise(function(resolve) {
|
|
55
55
|
var next = globalThis.document.createElement('script')
|
|
56
|
-
next.src =
|
|
56
|
+
next.src = "https://cdn.jsdelivr.net/gh/simplyedit/simplyview/dist/simply.include.next.js"
|
|
57
57
|
next.async = false
|
|
58
58
|
globalThis.document.addEventListener('simply-include-next', () => {
|
|
59
59
|
head.removeChild(next)
|
|
@@ -122,18 +122,21 @@ export const include = {
|
|
|
122
122
|
// order in which they are defined
|
|
123
123
|
let scriptsFragment = globalThis.document.createDocumentFragment()
|
|
124
124
|
const scripts = fragment.querySelectorAll('script')
|
|
125
|
-
|
|
126
|
-
let
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
if (scripts.length) {
|
|
126
|
+
for (let script of scripts) {
|
|
127
|
+
let placeholder = globalThis.document.createComment(script.src || 'inline script')
|
|
128
|
+
script.parentNode.insertBefore(placeholder, script)
|
|
129
|
+
script.dataset.simplyLocation = scriptLocations.length
|
|
130
|
+
scriptLocations.push(placeholder)
|
|
131
|
+
scriptsFragment.appendChild(script)
|
|
132
|
+
}
|
|
133
|
+
globalThis.setTimeout(function() {
|
|
134
|
+
include.scripts(Array.from(scriptsFragment.children), link ? link.href : globalThis.location.href )
|
|
135
|
+
}, 10)
|
|
131
136
|
}
|
|
132
137
|
// add the remainder before the include link
|
|
133
138
|
link.parentNode.insertBefore(fragment, link ? link : null)
|
|
134
|
-
|
|
135
|
-
include.scripts(scriptsFragment.childNodes, link ? link.href : globalThis.location.href )
|
|
136
|
-
}, 10)
|
|
139
|
+
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
142
|
|
package/src/key.mjs
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
const KEY = Object.freeze({
|
|
2
|
+
Compose: 229,
|
|
3
|
+
Control: 17,
|
|
4
|
+
Meta: 224,
|
|
5
|
+
Alt: 18,
|
|
6
|
+
Shift: 16
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
class SimplyKey {
|
|
2
10
|
constructor(options = {}) {
|
|
3
11
|
if (!options.app) {
|
|
4
12
|
options.app = {}
|
|
@@ -9,38 +17,83 @@ class SimplyKeys {
|
|
|
9
17
|
Object.assign(this, options.keys)
|
|
10
18
|
|
|
11
19
|
const keyHandler = (e) => {
|
|
12
|
-
if (e.isComposing || e.keyCode ===
|
|
13
|
-
return
|
|
20
|
+
if (e.isComposing || e.keyCode === KEY.Compose) {
|
|
21
|
+
return
|
|
14
22
|
}
|
|
15
23
|
if (e.defaultPrevented) {
|
|
16
|
-
return
|
|
24
|
+
return
|
|
17
25
|
}
|
|
18
26
|
if (!e.target) {
|
|
19
|
-
return
|
|
27
|
+
return
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
let selectedKeyboard = 'default'
|
|
30
|
+
let selectedKeyboard = 'default'
|
|
23
31
|
if (e.target.closest('[data-simply-keyboard]')) {
|
|
24
|
-
selectedKeyboard = e.target.closest('[data-simply-keyboard]')
|
|
32
|
+
selectedKeyboard = e.target.closest('[data-simply-keyboard]')
|
|
33
|
+
.dataset.simplyKeyboard
|
|
34
|
+
}
|
|
35
|
+
let keyCombination = []
|
|
36
|
+
if (e.ctrlKey && e.keyCode!=KEY.Control) {
|
|
37
|
+
keyCombination.push('Control')
|
|
25
38
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
key+='Control+';
|
|
39
|
+
if (e.metaKey && e.keyCode!=KEY.Meta) {
|
|
40
|
+
keyCombination.push('Meta')
|
|
29
41
|
}
|
|
30
|
-
if (e.
|
|
31
|
-
|
|
42
|
+
if (e.altKey && e.keyCode!=KEY.Alt) {
|
|
43
|
+
keyCombination.push('Alt')
|
|
32
44
|
}
|
|
33
|
-
if (e.
|
|
34
|
-
|
|
45
|
+
if (e.shiftKey && e.keyCode!=KEY.Shift) {
|
|
46
|
+
keyCombination.push('Shift')
|
|
35
47
|
}
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
keyCombination.push(e.key.toLowerCase())
|
|
49
|
+
|
|
50
|
+
let keyboards = []
|
|
51
|
+
let keyboardElement = event.target.closest('[data-simply-keyboard]')
|
|
52
|
+
while (keyboardElement) {
|
|
53
|
+
keyboards.push(keyboardElement.dataset.simplyKeyboard)
|
|
54
|
+
keyboardElement = keyboardElement.parentNode.closest('[data-simply-keyboard]')
|
|
38
55
|
}
|
|
39
|
-
|
|
56
|
+
keyboards.push('')
|
|
57
|
+
|
|
58
|
+
let keyboard, subkeyboard
|
|
59
|
+
let separators = ['+','-']
|
|
60
|
+
|
|
61
|
+
for (i in keyboards) {
|
|
62
|
+
keyboard = keyboards[i]
|
|
63
|
+
if (keyboard == '') {
|
|
64
|
+
subkeyboard = 'default'
|
|
65
|
+
} else {
|
|
66
|
+
subkeyboard = keyboard
|
|
67
|
+
keyboard += '.'
|
|
68
|
+
}
|
|
69
|
+
for (let separator of separators) {
|
|
70
|
+
let keyString = keyCombination.join(separator)
|
|
71
|
+
|
|
72
|
+
if (this[subkeyboard] && (typeof this[subkeyboard][keyString]=='function')) {
|
|
73
|
+
let _continue = this[subkeyboard][keyString].call(this[subkeyboard], e)
|
|
74
|
+
if (!_continue) {
|
|
75
|
+
e.preventDefault()
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (typeof this[subkeyboard + keyString] == 'function') {
|
|
80
|
+
let _continue = this[subkeyboard + keyString].call(this, e)
|
|
81
|
+
if (!_continue) {
|
|
82
|
+
e.preventDefault()
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this[selectedKeyboard] && this[selectedKeyboard][keyString]) {
|
|
88
|
+
let targets = options.app.container.querySelectorAll('[data-simply-accesskey="'
|
|
89
|
+
+ keyboard + keyString + '"]')
|
|
90
|
+
if (targets.length) {
|
|
91
|
+
targets.forEach(t => t.click())
|
|
92
|
+
e.preventDefault()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
40
95
|
|
|
41
|
-
|
|
42
|
-
let keyboard = this[selectedKeyboard]
|
|
43
|
-
keyboard[key].call(options.app,e);
|
|
96
|
+
}
|
|
44
97
|
}
|
|
45
98
|
}
|
|
46
99
|
|
|
@@ -50,6 +103,6 @@ class SimplyKeys {
|
|
|
50
103
|
}
|
|
51
104
|
|
|
52
105
|
export function keys(options={}) {
|
|
53
|
-
return new
|
|
106
|
+
return new SimplyKey(options)
|
|
54
107
|
}
|
|
55
108
|
|
package/src/route.mjs
CHANGED
|
@@ -65,6 +65,8 @@ class SimplyRoute {
|
|
|
65
65
|
if (path && path[path.length-1]!='/') {
|
|
66
66
|
return this.match(path+'/', options)
|
|
67
67
|
}
|
|
68
|
+
console.log(path, this.routeInfo)
|
|
69
|
+
process.exit()
|
|
68
70
|
return false
|
|
69
71
|
}
|
|
70
72
|
|
|
@@ -199,8 +201,7 @@ function getRegexpFromRoute(route) {
|
|
|
199
201
|
return new RegExp('^'+route.replace(/:\w+/g, '([^/]+)').replace(/:\*/, '(.*)'));
|
|
200
202
|
}
|
|
201
203
|
|
|
202
|
-
function parseRoutes(routes) {
|
|
203
|
-
let routeInfo = []
|
|
204
|
+
function parseRoutes(routes, routeInfo) {
|
|
204
205
|
const paths = Object.keys(routes)
|
|
205
206
|
const matchParams = /:(\w+|\*)/g
|
|
206
207
|
for (let path of paths) {
|