@microsoft/fast-element 2.0.0-beta.1 → 2.0.0-beta.11
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/CHANGELOG.json +348 -0
- package/CHANGELOG.md +114 -1
- package/dist/dts/components/attributes.d.ts +10 -0
- package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +49 -25
- package/dist/dts/components/fast-definitions.d.ts +43 -9
- package/dist/dts/components/fast-element.d.ts +15 -21
- package/dist/dts/context.d.ts +157 -0
- package/dist/dts/di/di.d.ts +899 -0
- package/dist/dts/index.d.ts +2 -2
- package/dist/dts/interfaces.d.ts +45 -14
- package/dist/dts/metadata.d.ts +25 -0
- package/dist/dts/observation/arrays.d.ts +1 -1
- package/dist/dts/observation/observable.d.ts +101 -75
- package/dist/dts/pending-task.d.ts +20 -0
- package/dist/dts/platform.d.ts +7 -0
- package/dist/dts/polyfills.d.ts +1 -8
- package/dist/dts/state/exports.d.ts +3 -0
- package/dist/dts/state/reactive.d.ts +8 -0
- package/dist/dts/state/state.d.ts +141 -0
- package/dist/dts/state/visitor.d.ts +6 -0
- package/dist/dts/state/watch.d.ts +10 -0
- package/dist/dts/styles/css-directive.d.ts +2 -2
- package/dist/dts/styles/element-styles.d.ts +10 -17
- package/dist/dts/styles/host.d.ts +68 -0
- package/dist/dts/templating/binding-signal.d.ts +21 -0
- package/dist/dts/templating/binding-two-way.d.ts +39 -0
- package/dist/dts/templating/binding.d.ts +69 -294
- package/dist/dts/templating/children.d.ts +1 -1
- package/dist/dts/templating/compiler.d.ts +1 -2
- package/dist/dts/templating/html-directive.d.ts +93 -35
- package/dist/dts/templating/node-observation.d.ts +4 -5
- package/dist/dts/templating/ref.d.ts +5 -13
- package/dist/dts/templating/render.d.ts +272 -0
- package/dist/dts/templating/repeat.d.ts +20 -75
- package/dist/dts/templating/slotted.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +12 -61
- package/dist/dts/templating/view.d.ts +77 -12
- package/dist/dts/templating/when.d.ts +3 -3
- package/dist/dts/testing/exports.d.ts +3 -0
- package/dist/dts/testing/fakes.d.ts +4 -0
- package/dist/dts/testing/fixture.d.ts +84 -0
- package/dist/dts/testing/timeout.d.ts +7 -0
- package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +0 -0
- package/dist/dts/utilities.d.ts +0 -18
- package/dist/esm/components/attributes.js +13 -4
- package/dist/esm/components/{controller.js → element-controller.js} +188 -109
- package/dist/esm/components/fast-definitions.js +38 -28
- package/dist/esm/components/fast-element.js +31 -12
- package/dist/esm/context.js +163 -0
- package/dist/esm/debug.js +36 -4
- package/dist/esm/di/di.js +1435 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/interfaces.js +4 -0
- package/dist/esm/metadata.js +60 -0
- package/dist/esm/observation/arrays.js +304 -3
- package/dist/esm/observation/observable.js +81 -87
- package/dist/esm/pending-task.js +16 -0
- package/dist/esm/platform.js +26 -1
- package/dist/esm/polyfills.js +1 -55
- package/dist/esm/state/exports.js +3 -0
- package/dist/esm/state/reactive.js +34 -0
- package/dist/esm/state/state.js +148 -0
- package/dist/esm/state/visitor.js +28 -0
- package/dist/esm/state/watch.js +36 -0
- package/dist/esm/styles/css.js +4 -4
- package/dist/esm/styles/element-styles.js +14 -33
- package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
- package/dist/esm/templating/binding-signal.js +83 -0
- package/dist/esm/templating/binding-two-way.js +103 -0
- package/dist/esm/templating/binding.js +134 -414
- package/dist/esm/templating/compiler.js +30 -7
- package/dist/esm/templating/html-directive.js +100 -28
- package/dist/esm/templating/node-observation.js +9 -8
- package/dist/esm/templating/ref.js +4 -12
- package/dist/esm/templating/render.js +391 -0
- package/dist/esm/templating/repeat.js +96 -72
- package/dist/esm/templating/template.js +11 -29
- package/dist/esm/templating/view.js +107 -29
- package/dist/esm/templating/when.js +5 -4
- package/dist/esm/testing/exports.js +3 -0
- package/dist/esm/testing/fakes.js +76 -0
- package/dist/esm/testing/fixture.js +86 -0
- package/dist/esm/testing/timeout.js +24 -0
- package/dist/esm/utilities.js +0 -95
- package/dist/fast-element.api.json +9278 -10745
- package/dist/fast-element.d.ts +707 -813
- package/dist/fast-element.debug.js +1229 -944
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +1191 -938
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +716 -824
- package/docs/api-report.md +265 -319
- package/package.json +39 -14
- package/dist/dts/hooks.d.ts +0 -20
- package/dist/dts/observation/behavior.d.ts +0 -19
- package/dist/dts/observation/splice-strategies.d.ts +0 -13
- package/dist/esm/hooks.js +0 -32
- package/dist/esm/observation/splice-strategies.js +0 -400
package/dist/fast-element.js
CHANGED
|
@@ -28,61 +28,6 @@ if (!globalThis.trustedTypes) {
|
|
|
28
28
|
createPolicy: (n, r) => r,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
// ensure FAST global - duplicated in platform.ts
|
|
32
|
-
const propConfig$1 = {
|
|
33
|
-
configurable: false,
|
|
34
|
-
enumerable: false,
|
|
35
|
-
writable: false,
|
|
36
|
-
};
|
|
37
|
-
if (globalThis.FAST === void 0) {
|
|
38
|
-
Reflect.defineProperty(globalThis, "FAST", Object.assign({ value: Object.create(null) }, propConfig$1));
|
|
39
|
-
}
|
|
40
|
-
const FAST$1 = globalThis.FAST;
|
|
41
|
-
if (FAST$1.getById === void 0) {
|
|
42
|
-
const storage = Object.create(null);
|
|
43
|
-
Reflect.defineProperty(FAST$1, "getById", Object.assign({ value(id, initialize) {
|
|
44
|
-
let found = storage[id];
|
|
45
|
-
if (found === void 0) {
|
|
46
|
-
found = initialize ? (storage[id] = initialize()) : null;
|
|
47
|
-
}
|
|
48
|
-
return found;
|
|
49
|
-
} }, propConfig$1));
|
|
50
|
-
}
|
|
51
|
-
// duplicated from DOM
|
|
52
|
-
const supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
|
|
53
|
-
"replace" in CSSStyleSheet.prototype;
|
|
54
|
-
function usableStyleTarget(target) {
|
|
55
|
-
return target === document ? document.body : target;
|
|
56
|
-
}
|
|
57
|
-
let id$1 = 0;
|
|
58
|
-
const nextStyleId = () => `fast-${++id$1}`;
|
|
59
|
-
class StyleElementStrategy {
|
|
60
|
-
constructor(styles) {
|
|
61
|
-
this.styles = styles;
|
|
62
|
-
this.styleClass = nextStyleId();
|
|
63
|
-
}
|
|
64
|
-
addStylesTo(target) {
|
|
65
|
-
target = usableStyleTarget(target);
|
|
66
|
-
const styles = this.styles;
|
|
67
|
-
const styleClass = this.styleClass;
|
|
68
|
-
for (let i = 0; i < styles.length; i++) {
|
|
69
|
-
const element = document.createElement("style");
|
|
70
|
-
element.innerHTML = styles[i];
|
|
71
|
-
element.className = styleClass;
|
|
72
|
-
target.append(element);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
removeStylesFrom(target) {
|
|
76
|
-
const styles = target.querySelectorAll(`.${this.styleClass}`);
|
|
77
|
-
target = usableStyleTarget(target);
|
|
78
|
-
for (let i = 0, ii = styles.length; i < ii; ++i) {
|
|
79
|
-
target.removeChild(styles[i]);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (!supportsAdoptedStyleSheets) {
|
|
84
|
-
FAST$1.getById(/* KernelServiceId.styleSheetStrategy */ 5, () => StyleElementStrategy);
|
|
85
|
-
}
|
|
86
31
|
|
|
87
32
|
// ensure FAST global - duplicated in polyfills.ts and debug.ts
|
|
88
33
|
const propConfig = {
|
|
@@ -112,7 +57,7 @@ if (FAST.error === void 0) {
|
|
|
112
57
|
Object.assign(FAST, {
|
|
113
58
|
warn() { },
|
|
114
59
|
error(code) {
|
|
115
|
-
return new Error(`
|
|
60
|
+
return new Error(`Error ${code}`);
|
|
116
61
|
},
|
|
117
62
|
addMessages() { },
|
|
118
63
|
});
|
|
@@ -143,10 +88,34 @@ function createTypeRegistry() {
|
|
|
143
88
|
return typeToDefinition.get(key);
|
|
144
89
|
},
|
|
145
90
|
getForInstance(object) {
|
|
91
|
+
if (object === null || object === void 0) {
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
146
94
|
return typeToDefinition.get(object.constructor);
|
|
147
95
|
},
|
|
148
96
|
});
|
|
149
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Creates a function capable of locating metadata associated with a type.
|
|
100
|
+
* @returns A metadata locator function.
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
function createMetadataLocator() {
|
|
104
|
+
const metadataLookup = new WeakMap();
|
|
105
|
+
return function (target) {
|
|
106
|
+
let metadata = metadataLookup.get(target);
|
|
107
|
+
if (metadata === void 0) {
|
|
108
|
+
let currentTarget = Reflect.getPrototypeOf(target);
|
|
109
|
+
while (metadata === void 0 && currentTarget !== null) {
|
|
110
|
+
metadata = metadataLookup.get(currentTarget);
|
|
111
|
+
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
112
|
+
}
|
|
113
|
+
metadata = metadata === void 0 ? [] : metadata.slice(0);
|
|
114
|
+
metadataLookup.set(target, metadata);
|
|
115
|
+
}
|
|
116
|
+
return metadata;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
150
119
|
|
|
151
120
|
/**
|
|
152
121
|
* @internal
|
|
@@ -388,6 +357,21 @@ class PropertyChangeNotifier {
|
|
|
388
357
|
}
|
|
389
358
|
}
|
|
390
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Describes how the source's lifetime relates to its controller's lifetime.
|
|
362
|
+
* @public
|
|
363
|
+
*/
|
|
364
|
+
const SourceLifetime = Object.freeze({
|
|
365
|
+
/**
|
|
366
|
+
* The source to controller lifetime relationship is unknown.
|
|
367
|
+
*/
|
|
368
|
+
unknown: void 0,
|
|
369
|
+
/**
|
|
370
|
+
* The source and controller lifetimes are coupled to one another.
|
|
371
|
+
* They can/will be GC'd together.
|
|
372
|
+
*/
|
|
373
|
+
coupled: 1,
|
|
374
|
+
});
|
|
391
375
|
/**
|
|
392
376
|
* Common Observable APIs.
|
|
393
377
|
* @public
|
|
@@ -396,7 +380,6 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
396
380
|
const queueUpdate = Updates.enqueue;
|
|
397
381
|
const volatileRegex = /(:|&&|\|\||if)/;
|
|
398
382
|
const notifierLookup = new WeakMap();
|
|
399
|
-
const accessorLookup = new WeakMap();
|
|
400
383
|
let watcher = void 0;
|
|
401
384
|
let createArrayObserver = (array) => {
|
|
402
385
|
throw FAST.error(1101 /* Message.needsArrayObservation */);
|
|
@@ -411,19 +394,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
411
394
|
}
|
|
412
395
|
return found;
|
|
413
396
|
}
|
|
414
|
-
|
|
415
|
-
let accessors = accessorLookup.get(target);
|
|
416
|
-
if (accessors === void 0) {
|
|
417
|
-
let currentTarget = Reflect.getPrototypeOf(target);
|
|
418
|
-
while (accessors === void 0 && currentTarget !== null) {
|
|
419
|
-
accessors = accessorLookup.get(currentTarget);
|
|
420
|
-
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
421
|
-
}
|
|
422
|
-
accessors = accessors === void 0 ? [] : accessors.slice(0);
|
|
423
|
-
accessorLookup.set(target, accessors);
|
|
424
|
-
}
|
|
425
|
-
return accessors;
|
|
426
|
-
}
|
|
397
|
+
const getAccessors = createMetadataLocator();
|
|
427
398
|
class DefaultObservableAccessor {
|
|
428
399
|
constructor(name) {
|
|
429
400
|
this.name = name;
|
|
@@ -449,10 +420,10 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
449
420
|
}
|
|
450
421
|
}
|
|
451
422
|
}
|
|
452
|
-
class
|
|
453
|
-
constructor(
|
|
454
|
-
super(
|
|
455
|
-
this.
|
|
423
|
+
class ExpressionNotifierImplementation extends SubscriberSet {
|
|
424
|
+
constructor(expression, initialSubscriber, isVolatileBinding = false) {
|
|
425
|
+
super(expression, initialSubscriber);
|
|
426
|
+
this.expression = expression;
|
|
456
427
|
this.isVolatileBinding = isVolatileBinding;
|
|
457
428
|
this.needsRefresh = true;
|
|
458
429
|
this.needsQueue = true;
|
|
@@ -467,6 +438,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
467
438
|
setMode(isAsync) {
|
|
468
439
|
this.isAsync = this.needsQueue = isAsync;
|
|
469
440
|
}
|
|
441
|
+
bind(controller) {
|
|
442
|
+
this.controller = controller;
|
|
443
|
+
const value = this.observe(controller.source, controller.context);
|
|
444
|
+
if (!controller.isBound && this.requiresUnbind(controller)) {
|
|
445
|
+
controller.onUnbind(this);
|
|
446
|
+
}
|
|
447
|
+
return value;
|
|
448
|
+
}
|
|
449
|
+
requiresUnbind(controller) {
|
|
450
|
+
return (controller.sourceLifetime !== SourceLifetime.coupled ||
|
|
451
|
+
this.first !== this.last ||
|
|
452
|
+
this.first.propertySource !== controller.source);
|
|
453
|
+
}
|
|
454
|
+
unbind(controller) {
|
|
455
|
+
this.dispose();
|
|
456
|
+
}
|
|
470
457
|
observe(source, context) {
|
|
471
458
|
if (this.needsRefresh && this.last !== null) {
|
|
472
459
|
this.dispose();
|
|
@@ -474,10 +461,19 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
474
461
|
const previousWatcher = watcher;
|
|
475
462
|
watcher = this.needsRefresh ? this : void 0;
|
|
476
463
|
this.needsRefresh = this.isVolatileBinding;
|
|
477
|
-
|
|
478
|
-
|
|
464
|
+
let result;
|
|
465
|
+
try {
|
|
466
|
+
result = this.expression(source, context);
|
|
467
|
+
}
|
|
468
|
+
finally {
|
|
469
|
+
watcher = previousWatcher;
|
|
470
|
+
}
|
|
479
471
|
return result;
|
|
480
472
|
}
|
|
473
|
+
// backwards compat with v1 kernel
|
|
474
|
+
disconnect() {
|
|
475
|
+
this.dispose();
|
|
476
|
+
}
|
|
481
477
|
dispose() {
|
|
482
478
|
if (this.last !== null) {
|
|
483
479
|
let current = this.first;
|
|
@@ -604,22 +600,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
604
600
|
*/
|
|
605
601
|
getAccessors,
|
|
606
602
|
/**
|
|
607
|
-
* Creates a {@link
|
|
608
|
-
* provided {@link
|
|
609
|
-
* @param
|
|
603
|
+
* Creates a {@link ExpressionNotifier} that can watch the
|
|
604
|
+
* provided {@link Expression} for changes.
|
|
605
|
+
* @param expression - The binding to observe.
|
|
610
606
|
* @param initialSubscriber - An initial subscriber to changes in the binding value.
|
|
611
607
|
* @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
|
|
612
608
|
*/
|
|
613
|
-
binding(
|
|
614
|
-
return new
|
|
609
|
+
binding(expression, initialSubscriber, isVolatileBinding = this.isVolatileBinding(expression)) {
|
|
610
|
+
return new ExpressionNotifierImplementation(expression, initialSubscriber, isVolatileBinding);
|
|
615
611
|
},
|
|
616
612
|
/**
|
|
617
613
|
* Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
|
|
618
614
|
* on every evaluation of the value.
|
|
619
|
-
* @param
|
|
615
|
+
* @param expression - The binding to inspect.
|
|
620
616
|
*/
|
|
621
|
-
isVolatileBinding(
|
|
622
|
-
return volatileRegex.test(
|
|
617
|
+
isVolatileBinding(expression) {
|
|
618
|
+
return volatileRegex.test(expression.toString());
|
|
623
619
|
},
|
|
624
620
|
});
|
|
625
621
|
});
|
|
@@ -658,73 +654,40 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
|
|
|
658
654
|
},
|
|
659
655
|
};
|
|
660
656
|
});
|
|
661
|
-
class DefaultExecutionContext {
|
|
662
|
-
constructor(parentSource = null, parentContext = null) {
|
|
663
|
-
this.index = 0;
|
|
664
|
-
this.length = 0;
|
|
665
|
-
this.parent = parentSource;
|
|
666
|
-
this.parentContext = parentContext;
|
|
667
|
-
}
|
|
668
|
-
get event() {
|
|
669
|
-
return contextEvent.get();
|
|
670
|
-
}
|
|
671
|
-
get isEven() {
|
|
672
|
-
return this.index % 2 === 0;
|
|
673
|
-
}
|
|
674
|
-
get isOdd() {
|
|
675
|
-
return this.index % 2 !== 0;
|
|
676
|
-
}
|
|
677
|
-
get isFirst() {
|
|
678
|
-
return this.index === 0;
|
|
679
|
-
}
|
|
680
|
-
get isInMiddle() {
|
|
681
|
-
return !this.isFirst && !this.isLast;
|
|
682
|
-
}
|
|
683
|
-
get isLast() {
|
|
684
|
-
return this.index === this.length - 1;
|
|
685
|
-
}
|
|
686
|
-
eventDetail() {
|
|
687
|
-
return this.event.detail;
|
|
688
|
-
}
|
|
689
|
-
eventTarget() {
|
|
690
|
-
return this.event.target;
|
|
691
|
-
}
|
|
692
|
-
updatePosition(index, length) {
|
|
693
|
-
this.index = index;
|
|
694
|
-
this.length = length;
|
|
695
|
-
}
|
|
696
|
-
createChildContext(parentSource) {
|
|
697
|
-
return new DefaultExecutionContext(parentSource, this);
|
|
698
|
-
}
|
|
699
|
-
createItemContext(index, length) {
|
|
700
|
-
const childContext = Object.create(this);
|
|
701
|
-
childContext.index = index;
|
|
702
|
-
childContext.length = length;
|
|
703
|
-
return childContext;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
Observable.defineProperty(DefaultExecutionContext.prototype, "index");
|
|
707
|
-
Observable.defineProperty(DefaultExecutionContext.prototype, "length");
|
|
708
657
|
/**
|
|
709
|
-
*
|
|
658
|
+
* Provides additional contextual information available to behaviors and expressions.
|
|
710
659
|
* @public
|
|
711
660
|
*/
|
|
712
661
|
const ExecutionContext = Object.freeze({
|
|
713
|
-
default: new DefaultExecutionContext(),
|
|
714
662
|
/**
|
|
715
|
-
*
|
|
716
|
-
* @param event - The event to set.
|
|
717
|
-
* @internal
|
|
663
|
+
* A default execution context.
|
|
718
664
|
*/
|
|
719
|
-
|
|
720
|
-
|
|
665
|
+
default: {
|
|
666
|
+
index: 0,
|
|
667
|
+
length: 0,
|
|
668
|
+
get event() {
|
|
669
|
+
return ExecutionContext.getEvent();
|
|
670
|
+
},
|
|
671
|
+
eventDetail() {
|
|
672
|
+
return this.event.detail;
|
|
673
|
+
},
|
|
674
|
+
eventTarget() {
|
|
675
|
+
return this.event.target;
|
|
676
|
+
},
|
|
677
|
+
},
|
|
678
|
+
/**
|
|
679
|
+
* Gets the current event.
|
|
680
|
+
* @returns An event object.
|
|
681
|
+
*/
|
|
682
|
+
getEvent() {
|
|
683
|
+
return contextEvent.get();
|
|
721
684
|
},
|
|
722
685
|
/**
|
|
723
|
-
*
|
|
724
|
-
* @
|
|
686
|
+
* Sets the current event.
|
|
687
|
+
* @param event - An event object.
|
|
725
688
|
*/
|
|
726
|
-
|
|
727
|
-
|
|
689
|
+
setEvent(event) {
|
|
690
|
+
contextEvent.set(event);
|
|
728
691
|
},
|
|
729
692
|
});
|
|
730
693
|
|
|
@@ -793,10 +756,311 @@ const SpliceStrategySupport = Object.freeze({
|
|
|
793
756
|
const reset = new Splice(0, emptyArray, 0);
|
|
794
757
|
reset.reset = true;
|
|
795
758
|
const resetSplices = [reset];
|
|
759
|
+
// Note: This function is *based* on the computation of the Levenshtein
|
|
760
|
+
// "edit" distance. The one change is that "updates" are treated as two
|
|
761
|
+
// edits - not one. With Array splices, an update is really a delete
|
|
762
|
+
// followed by an add. By retaining this, we optimize for "keeping" the
|
|
763
|
+
// maximum array items in the original array. For example:
|
|
764
|
+
//
|
|
765
|
+
// 'xxxx123' to '123yyyy'
|
|
766
|
+
//
|
|
767
|
+
// With 1-edit updates, the shortest path would be just to update all seven
|
|
768
|
+
// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
|
|
769
|
+
// leaves the substring '123' intact.
|
|
770
|
+
function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
|
|
771
|
+
// "Deletion" columns
|
|
772
|
+
const rowCount = oldEnd - oldStart + 1;
|
|
773
|
+
const columnCount = currentEnd - currentStart + 1;
|
|
774
|
+
const distances = new Array(rowCount);
|
|
775
|
+
let north;
|
|
776
|
+
let west;
|
|
777
|
+
// "Addition" rows. Initialize null column.
|
|
778
|
+
for (let i = 0; i < rowCount; ++i) {
|
|
779
|
+
distances[i] = new Array(columnCount);
|
|
780
|
+
distances[i][0] = i;
|
|
781
|
+
}
|
|
782
|
+
// Initialize null row
|
|
783
|
+
for (let j = 0; j < columnCount; ++j) {
|
|
784
|
+
distances[0][j] = j;
|
|
785
|
+
}
|
|
786
|
+
for (let i = 1; i < rowCount; ++i) {
|
|
787
|
+
for (let j = 1; j < columnCount; ++j) {
|
|
788
|
+
if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
|
|
789
|
+
distances[i][j] = distances[i - 1][j - 1];
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
north = distances[i - 1][j] + 1;
|
|
793
|
+
west = distances[i][j - 1] + 1;
|
|
794
|
+
distances[i][j] = north < west ? north : west;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return distances;
|
|
799
|
+
}
|
|
800
|
+
// This starts at the final weight, and walks "backward" by finding
|
|
801
|
+
// the minimum previous weight recursively until the origin of the weight
|
|
802
|
+
// matrix.
|
|
803
|
+
function spliceOperationsFromEditDistances(distances) {
|
|
804
|
+
let i = distances.length - 1;
|
|
805
|
+
let j = distances[0].length - 1;
|
|
806
|
+
let current = distances[i][j];
|
|
807
|
+
const edits = [];
|
|
808
|
+
while (i > 0 || j > 0) {
|
|
809
|
+
if (i === 0) {
|
|
810
|
+
edits.push(2 /* Edit.add */);
|
|
811
|
+
j--;
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (j === 0) {
|
|
815
|
+
edits.push(3 /* Edit.delete */);
|
|
816
|
+
i--;
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
const northWest = distances[i - 1][j - 1];
|
|
820
|
+
const west = distances[i - 1][j];
|
|
821
|
+
const north = distances[i][j - 1];
|
|
822
|
+
let min;
|
|
823
|
+
if (west < north) {
|
|
824
|
+
min = west < northWest ? west : northWest;
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
min = north < northWest ? north : northWest;
|
|
828
|
+
}
|
|
829
|
+
if (min === northWest) {
|
|
830
|
+
if (northWest === current) {
|
|
831
|
+
edits.push(0 /* Edit.leave */);
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
edits.push(1 /* Edit.update */);
|
|
835
|
+
current = northWest;
|
|
836
|
+
}
|
|
837
|
+
i--;
|
|
838
|
+
j--;
|
|
839
|
+
}
|
|
840
|
+
else if (min === west) {
|
|
841
|
+
edits.push(3 /* Edit.delete */);
|
|
842
|
+
i--;
|
|
843
|
+
current = west;
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
edits.push(2 /* Edit.add */);
|
|
847
|
+
j--;
|
|
848
|
+
current = north;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return edits.reverse();
|
|
852
|
+
}
|
|
853
|
+
function sharedPrefix(current, old, searchLength) {
|
|
854
|
+
for (let i = 0; i < searchLength; ++i) {
|
|
855
|
+
if (current[i] !== old[i]) {
|
|
856
|
+
return i;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return searchLength;
|
|
860
|
+
}
|
|
861
|
+
function sharedSuffix(current, old, searchLength) {
|
|
862
|
+
let index1 = current.length;
|
|
863
|
+
let index2 = old.length;
|
|
864
|
+
let count = 0;
|
|
865
|
+
while (count < searchLength && current[--index1] === old[--index2]) {
|
|
866
|
+
count++;
|
|
867
|
+
}
|
|
868
|
+
return count;
|
|
869
|
+
}
|
|
870
|
+
function intersect(start1, end1, start2, end2) {
|
|
871
|
+
// Disjoint
|
|
872
|
+
if (end1 < start2 || end2 < start1) {
|
|
873
|
+
return -1;
|
|
874
|
+
}
|
|
875
|
+
// Adjacent
|
|
876
|
+
if (end1 === start2 || end2 === start1) {
|
|
877
|
+
return 0;
|
|
878
|
+
}
|
|
879
|
+
// Non-zero intersect, span1 first
|
|
880
|
+
if (start1 < start2) {
|
|
881
|
+
if (end1 < end2) {
|
|
882
|
+
return end1 - start2; // Overlap
|
|
883
|
+
}
|
|
884
|
+
return end2 - start2; // Contained
|
|
885
|
+
}
|
|
886
|
+
// Non-zero intersect, span2 first
|
|
887
|
+
if (end2 < end1) {
|
|
888
|
+
return end2 - start1; // Overlap
|
|
889
|
+
}
|
|
890
|
+
return end1 - start1; // Contained
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* @remarks
|
|
894
|
+
* Lacking individual splice mutation information, the minimal set of
|
|
895
|
+
* splices can be synthesized given the previous state and final state of an
|
|
896
|
+
* array. The basic approach is to calculate the edit distance matrix and
|
|
897
|
+
* choose the shortest path through it.
|
|
898
|
+
*
|
|
899
|
+
* Complexity: O(l * p)
|
|
900
|
+
* l: The length of the current array
|
|
901
|
+
* p: The length of the old array
|
|
902
|
+
*/
|
|
903
|
+
function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
|
|
904
|
+
let prefixCount = 0;
|
|
905
|
+
let suffixCount = 0;
|
|
906
|
+
const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
|
|
907
|
+
if (currentStart === 0 && oldStart === 0) {
|
|
908
|
+
prefixCount = sharedPrefix(current, old, minLength);
|
|
909
|
+
}
|
|
910
|
+
if (currentEnd === current.length && oldEnd === old.length) {
|
|
911
|
+
suffixCount = sharedSuffix(current, old, minLength - prefixCount);
|
|
912
|
+
}
|
|
913
|
+
currentStart += prefixCount;
|
|
914
|
+
oldStart += prefixCount;
|
|
915
|
+
currentEnd -= suffixCount;
|
|
916
|
+
oldEnd -= suffixCount;
|
|
917
|
+
if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
|
|
918
|
+
return emptyArray;
|
|
919
|
+
}
|
|
920
|
+
if (currentStart === currentEnd) {
|
|
921
|
+
const splice = new Splice(currentStart, [], 0);
|
|
922
|
+
while (oldStart < oldEnd) {
|
|
923
|
+
splice.removed.push(old[oldStart++]);
|
|
924
|
+
}
|
|
925
|
+
return [splice];
|
|
926
|
+
}
|
|
927
|
+
else if (oldStart === oldEnd) {
|
|
928
|
+
return [new Splice(currentStart, [], currentEnd - currentStart)];
|
|
929
|
+
}
|
|
930
|
+
const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
|
|
931
|
+
const splices = [];
|
|
932
|
+
let splice = void 0;
|
|
933
|
+
let index = currentStart;
|
|
934
|
+
let oldIndex = oldStart;
|
|
935
|
+
for (let i = 0; i < ops.length; ++i) {
|
|
936
|
+
switch (ops[i]) {
|
|
937
|
+
case 0 /* Edit.leave */:
|
|
938
|
+
if (splice !== void 0) {
|
|
939
|
+
splices.push(splice);
|
|
940
|
+
splice = void 0;
|
|
941
|
+
}
|
|
942
|
+
index++;
|
|
943
|
+
oldIndex++;
|
|
944
|
+
break;
|
|
945
|
+
case 1 /* Edit.update */:
|
|
946
|
+
if (splice === void 0) {
|
|
947
|
+
splice = new Splice(index, [], 0);
|
|
948
|
+
}
|
|
949
|
+
splice.addedCount++;
|
|
950
|
+
index++;
|
|
951
|
+
splice.removed.push(old[oldIndex]);
|
|
952
|
+
oldIndex++;
|
|
953
|
+
break;
|
|
954
|
+
case 2 /* Edit.add */:
|
|
955
|
+
if (splice === void 0) {
|
|
956
|
+
splice = new Splice(index, [], 0);
|
|
957
|
+
}
|
|
958
|
+
splice.addedCount++;
|
|
959
|
+
index++;
|
|
960
|
+
break;
|
|
961
|
+
case 3 /* Edit.delete */:
|
|
962
|
+
if (splice === void 0) {
|
|
963
|
+
splice = new Splice(index, [], 0);
|
|
964
|
+
}
|
|
965
|
+
splice.removed.push(old[oldIndex]);
|
|
966
|
+
oldIndex++;
|
|
967
|
+
break;
|
|
968
|
+
// no default
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
if (splice !== void 0) {
|
|
972
|
+
splices.push(splice);
|
|
973
|
+
}
|
|
974
|
+
return splices;
|
|
975
|
+
}
|
|
976
|
+
function merge(splice, splices) {
|
|
977
|
+
let inserted = false;
|
|
978
|
+
let insertionOffset = 0;
|
|
979
|
+
for (let i = 0; i < splices.length; i++) {
|
|
980
|
+
const current = splices[i];
|
|
981
|
+
current.index += insertionOffset;
|
|
982
|
+
if (inserted) {
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
|
|
986
|
+
if (intersectCount >= 0) {
|
|
987
|
+
// Merge the two splices
|
|
988
|
+
splices.splice(i, 1);
|
|
989
|
+
i--;
|
|
990
|
+
insertionOffset -= current.addedCount - current.removed.length;
|
|
991
|
+
splice.addedCount += current.addedCount - intersectCount;
|
|
992
|
+
const deleteCount = splice.removed.length + current.removed.length - intersectCount;
|
|
993
|
+
if (!splice.addedCount && !deleteCount) {
|
|
994
|
+
// merged splice is a noop. discard.
|
|
995
|
+
inserted = true;
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
let currentRemoved = current.removed;
|
|
999
|
+
if (splice.index < current.index) {
|
|
1000
|
+
// some prefix of splice.removed is prepended to current.removed.
|
|
1001
|
+
const prepend = splice.removed.slice(0, current.index - splice.index);
|
|
1002
|
+
prepend.push(...currentRemoved);
|
|
1003
|
+
currentRemoved = prepend;
|
|
1004
|
+
}
|
|
1005
|
+
if (splice.index + splice.removed.length >
|
|
1006
|
+
current.index + current.addedCount) {
|
|
1007
|
+
// some suffix of splice.removed is appended to current.removed.
|
|
1008
|
+
const append = splice.removed.slice(current.index + current.addedCount - splice.index);
|
|
1009
|
+
currentRemoved.push(...append);
|
|
1010
|
+
}
|
|
1011
|
+
splice.removed = currentRemoved;
|
|
1012
|
+
if (current.index < splice.index) {
|
|
1013
|
+
splice.index = current.index;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
else if (splice.index < current.index) {
|
|
1018
|
+
// Insert splice here.
|
|
1019
|
+
inserted = true;
|
|
1020
|
+
splices.splice(i, 0, splice);
|
|
1021
|
+
i++;
|
|
1022
|
+
const offset = splice.addedCount - splice.removed.length;
|
|
1023
|
+
current.index += offset;
|
|
1024
|
+
insertionOffset += offset;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (!inserted) {
|
|
1028
|
+
splices.push(splice);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
function project(array, changes) {
|
|
1032
|
+
let splices = [];
|
|
1033
|
+
const initialSplices = [];
|
|
1034
|
+
for (let i = 0, ii = changes.length; i < ii; i++) {
|
|
1035
|
+
merge(changes[i], initialSplices);
|
|
1036
|
+
}
|
|
1037
|
+
for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
|
|
1038
|
+
const splice = initialSplices[i];
|
|
1039
|
+
if (splice.addedCount === 1 && splice.removed.length === 1) {
|
|
1040
|
+
if (splice.removed[0] !== array[splice.index]) {
|
|
1041
|
+
splices.push(splice);
|
|
1042
|
+
}
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
|
|
1046
|
+
}
|
|
1047
|
+
return splices;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* A SpliceStrategy that attempts to merge all splices into the minimal set of
|
|
1051
|
+
* splices needed to represent the change from the old array to the new array.
|
|
1052
|
+
* @public
|
|
1053
|
+
*/
|
|
796
1054
|
let defaultSpliceStrategy = Object.freeze({
|
|
797
|
-
support: SpliceStrategySupport.
|
|
1055
|
+
support: SpliceStrategySupport.optimized,
|
|
798
1056
|
normalize(previous, current, changes) {
|
|
799
|
-
|
|
1057
|
+
if (previous === void 0) {
|
|
1058
|
+
if (changes === void 0) {
|
|
1059
|
+
return emptyArray;
|
|
1060
|
+
}
|
|
1061
|
+
return changes.length > 1 ? project(current, changes) : changes;
|
|
1062
|
+
}
|
|
1063
|
+
return resetSplices;
|
|
800
1064
|
},
|
|
801
1065
|
pop(array, observer, pop, args) {
|
|
802
1066
|
const notEmpty = array.length > 0;
|
|
@@ -981,7 +1245,7 @@ const ArrayObserver = Object.freeze({
|
|
|
981
1245
|
* @returns The length of the array.
|
|
982
1246
|
* @public
|
|
983
1247
|
*/
|
|
984
|
-
function
|
|
1248
|
+
function lengthOf(array) {
|
|
985
1249
|
if (!array) {
|
|
986
1250
|
return 0;
|
|
987
1251
|
}
|
|
@@ -994,7 +1258,6 @@ function length(array) {
|
|
|
994
1258
|
return array.length;
|
|
995
1259
|
}
|
|
996
1260
|
|
|
997
|
-
const styleSheetCache = new Map();
|
|
998
1261
|
let DefaultStyleStrategy;
|
|
999
1262
|
function reduceStyles(styles) {
|
|
1000
1263
|
return styles
|
|
@@ -1065,42 +1328,26 @@ class ElementStyles {
|
|
|
1065
1328
|
static setDefaultStrategy(Strategy) {
|
|
1066
1329
|
DefaultStyleStrategy = Strategy;
|
|
1067
1330
|
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Normalizes a set of composable style options.
|
|
1333
|
+
* @param styles - The style options to normalize.
|
|
1334
|
+
* @returns A singular ElementStyles instance or undefined.
|
|
1335
|
+
*/
|
|
1336
|
+
static normalize(styles) {
|
|
1337
|
+
return styles === void 0
|
|
1338
|
+
? void 0
|
|
1339
|
+
: Array.isArray(styles)
|
|
1340
|
+
? new ElementStyles(styles)
|
|
1341
|
+
: styles instanceof ElementStyles
|
|
1342
|
+
? styles
|
|
1343
|
+
: new ElementStyles([styles]);
|
|
1344
|
+
}
|
|
1068
1345
|
}
|
|
1069
1346
|
/**
|
|
1070
1347
|
* Indicates whether the DOM supports the adoptedStyleSheets feature.
|
|
1071
1348
|
*/
|
|
1072
1349
|
ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
|
|
1073
1350
|
"replace" in CSSStyleSheet.prototype;
|
|
1074
|
-
/**
|
|
1075
|
-
* https://wicg.github.io/construct-stylesheets/
|
|
1076
|
-
* https://developers.google.com/web/updates/2019/02/constructable-stylesheets
|
|
1077
|
-
*
|
|
1078
|
-
* @internal
|
|
1079
|
-
*/
|
|
1080
|
-
class AdoptedStyleSheetsStrategy {
|
|
1081
|
-
constructor(styles) {
|
|
1082
|
-
this.sheets = styles.map((x) => {
|
|
1083
|
-
if (x instanceof CSSStyleSheet) {
|
|
1084
|
-
return x;
|
|
1085
|
-
}
|
|
1086
|
-
let sheet = styleSheetCache.get(x);
|
|
1087
|
-
if (sheet === void 0) {
|
|
1088
|
-
sheet = new CSSStyleSheet();
|
|
1089
|
-
sheet.replaceSync(x);
|
|
1090
|
-
styleSheetCache.set(x, sheet);
|
|
1091
|
-
}
|
|
1092
|
-
return sheet;
|
|
1093
|
-
});
|
|
1094
|
-
}
|
|
1095
|
-
addStylesTo(target) {
|
|
1096
|
-
target.adoptedStyleSheets = [...target.adoptedStyleSheets, ...this.sheets];
|
|
1097
|
-
}
|
|
1098
|
-
removeStylesFrom(target) {
|
|
1099
|
-
const sheets = this.sheets;
|
|
1100
|
-
target.adoptedStyleSheets = target.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
ElementStyles.setDefaultStrategy(FAST.getById(5 /* KernelServiceId.styleSheetStrategy */, () => AdoptedStyleSheetsStrategy));
|
|
1104
1351
|
|
|
1105
1352
|
const registry$1 = createTypeRegistry();
|
|
1106
1353
|
/**
|
|
@@ -1209,11 +1456,11 @@ class CSSPartial {
|
|
|
1209
1456
|
}
|
|
1210
1457
|
return this.css;
|
|
1211
1458
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1459
|
+
addedCallback(controller) {
|
|
1460
|
+
controller.addStyles(this.styles);
|
|
1214
1461
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1462
|
+
removedCallback(controller) {
|
|
1463
|
+
controller.removeStyles(this.styles);
|
|
1217
1464
|
}
|
|
1218
1465
|
}
|
|
1219
1466
|
CSSDirective.define(CSSPartial);
|
|
@@ -1280,9 +1527,9 @@ const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
|
|
|
1280
1527
|
const interpolationStart = `${marker}{`;
|
|
1281
1528
|
const interpolationEnd = `}${marker}`;
|
|
1282
1529
|
const interpolationEndLength = interpolationEnd.length;
|
|
1283
|
-
let id = 0;
|
|
1530
|
+
let id$1 = 0;
|
|
1284
1531
|
/** @internal */
|
|
1285
|
-
const nextId = () => `${marker}-${++id}`;
|
|
1532
|
+
const nextId = () => `${marker}-${++id$1}`;
|
|
1286
1533
|
/**
|
|
1287
1534
|
* Common APIs related to markup generation.
|
|
1288
1535
|
* @public
|
|
@@ -1352,6 +1599,67 @@ const Parser = Object.freeze({
|
|
|
1352
1599
|
},
|
|
1353
1600
|
});
|
|
1354
1601
|
|
|
1602
|
+
/**
|
|
1603
|
+
* Bridges between ViewBehaviors and HostBehaviors, enabling a host to
|
|
1604
|
+
* control ViewBehaviors.
|
|
1605
|
+
* @public
|
|
1606
|
+
*/
|
|
1607
|
+
const ViewBehaviorOrchestrator = Object.freeze({
|
|
1608
|
+
/**
|
|
1609
|
+
* Creates a ViewBehaviorOrchestrator.
|
|
1610
|
+
* @param source - The source to to associate behaviors with.
|
|
1611
|
+
* @returns A ViewBehaviorOrchestrator.
|
|
1612
|
+
*/
|
|
1613
|
+
create(source) {
|
|
1614
|
+
const behaviors = [];
|
|
1615
|
+
const targets = {};
|
|
1616
|
+
let unbindables = null;
|
|
1617
|
+
let isConnected = false;
|
|
1618
|
+
return {
|
|
1619
|
+
source,
|
|
1620
|
+
context: ExecutionContext.default,
|
|
1621
|
+
targets,
|
|
1622
|
+
get isBound() {
|
|
1623
|
+
return isConnected;
|
|
1624
|
+
},
|
|
1625
|
+
addBehaviorFactory(factory, target) {
|
|
1626
|
+
const nodeId = factory.nodeId || (factory.nodeId = nextId());
|
|
1627
|
+
factory.id || (factory.id = nextId());
|
|
1628
|
+
this.addTarget(nodeId, target);
|
|
1629
|
+
this.addBehavior(factory.createBehavior());
|
|
1630
|
+
},
|
|
1631
|
+
addTarget(nodeId, target) {
|
|
1632
|
+
targets[nodeId] = target;
|
|
1633
|
+
},
|
|
1634
|
+
addBehavior(behavior) {
|
|
1635
|
+
behaviors.push(behavior);
|
|
1636
|
+
if (isConnected) {
|
|
1637
|
+
behavior.bind(this);
|
|
1638
|
+
}
|
|
1639
|
+
},
|
|
1640
|
+
onUnbind(unbindable) {
|
|
1641
|
+
if (unbindables === null) {
|
|
1642
|
+
unbindables = [];
|
|
1643
|
+
}
|
|
1644
|
+
unbindables.push(unbindable);
|
|
1645
|
+
},
|
|
1646
|
+
connectedCallback(controller) {
|
|
1647
|
+
if (!isConnected) {
|
|
1648
|
+
isConnected = true;
|
|
1649
|
+
behaviors.forEach(x => x.bind(this));
|
|
1650
|
+
}
|
|
1651
|
+
},
|
|
1652
|
+
disconnectedCallback(controller) {
|
|
1653
|
+
if (isConnected) {
|
|
1654
|
+
isConnected = false;
|
|
1655
|
+
if (unbindables !== null) {
|
|
1656
|
+
unbindables.forEach(x => x.unbind(this));
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
},
|
|
1660
|
+
};
|
|
1661
|
+
},
|
|
1662
|
+
});
|
|
1355
1663
|
const registry = createTypeRegistry();
|
|
1356
1664
|
/**
|
|
1357
1665
|
* Instructs the template engine to apply behavior to a node.
|
|
@@ -1391,6 +1699,22 @@ function htmlDirective(options) {
|
|
|
1391
1699
|
HTMLDirective.define(type, options);
|
|
1392
1700
|
};
|
|
1393
1701
|
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Captures a binding expression along with related information and capabilities.
|
|
1704
|
+
*
|
|
1705
|
+
* @public
|
|
1706
|
+
*/
|
|
1707
|
+
class Binding {
|
|
1708
|
+
/**
|
|
1709
|
+
* Creates a binding.
|
|
1710
|
+
* @param evaluate - Evaluates the binding.
|
|
1711
|
+
* @param isVolatile - Indicates whether the binding is volatile.
|
|
1712
|
+
*/
|
|
1713
|
+
constructor(evaluate, isVolatile = false) {
|
|
1714
|
+
this.evaluate = evaluate;
|
|
1715
|
+
this.isVolatile = isVolatile;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1394
1718
|
/**
|
|
1395
1719
|
* The type of HTML aspect to target.
|
|
1396
1720
|
* @public
|
|
@@ -1428,26 +1752,22 @@ const Aspect = Object.freeze({
|
|
|
1428
1752
|
*
|
|
1429
1753
|
* @param directive - The directive to assign the aspect to.
|
|
1430
1754
|
* @param value - The value to base the aspect determination on.
|
|
1755
|
+
* @remarks
|
|
1756
|
+
* If a falsy value is provided, then the content aspect will be assigned.
|
|
1431
1757
|
*/
|
|
1432
1758
|
assign(directive, value) {
|
|
1433
|
-
directive.sourceAspect = value;
|
|
1434
1759
|
if (!value) {
|
|
1760
|
+
directive.aspectType = Aspect.content;
|
|
1435
1761
|
return;
|
|
1436
1762
|
}
|
|
1763
|
+
directive.sourceAspect = value;
|
|
1437
1764
|
switch (value[0]) {
|
|
1438
1765
|
case ":":
|
|
1439
1766
|
directive.targetAspect = value.substring(1);
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
case "classList":
|
|
1445
|
-
directive.aspectType = Aspect.tokenList;
|
|
1446
|
-
break;
|
|
1447
|
-
default:
|
|
1448
|
-
directive.aspectType = Aspect.property;
|
|
1449
|
-
break;
|
|
1450
|
-
}
|
|
1767
|
+
directive.aspectType =
|
|
1768
|
+
directive.targetAspect === "classList"
|
|
1769
|
+
? Aspect.tokenList
|
|
1770
|
+
: Aspect.property;
|
|
1451
1771
|
break;
|
|
1452
1772
|
case "?":
|
|
1453
1773
|
directive.targetAspect = value.substring(1);
|
|
@@ -1458,14 +1778,8 @@ const Aspect = Object.freeze({
|
|
|
1458
1778
|
directive.aspectType = Aspect.event;
|
|
1459
1779
|
break;
|
|
1460
1780
|
default:
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
directive.aspectType = Aspect.property;
|
|
1464
|
-
}
|
|
1465
|
-
else {
|
|
1466
|
-
directive.targetAspect = value;
|
|
1467
|
-
directive.aspectType = Aspect.attribute;
|
|
1468
|
-
}
|
|
1781
|
+
directive.targetAspect = value;
|
|
1782
|
+
directive.aspectType = Aspect.attribute;
|
|
1469
1783
|
break;
|
|
1470
1784
|
}
|
|
1471
1785
|
},
|
|
@@ -1481,13 +1795,10 @@ class StatelessAttachedAttributeDirective {
|
|
|
1481
1795
|
*/
|
|
1482
1796
|
constructor(options) {
|
|
1483
1797
|
this.options = options;
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
*/
|
|
1489
|
-
createBehavior(targets) {
|
|
1490
|
-
return this;
|
|
1798
|
+
/**
|
|
1799
|
+
* The unique id of the factory.
|
|
1800
|
+
*/
|
|
1801
|
+
this.id = nextId();
|
|
1491
1802
|
}
|
|
1492
1803
|
/**
|
|
1493
1804
|
* Creates a placeholder string based on the directive's index within the template.
|
|
@@ -1498,6 +1809,13 @@ class StatelessAttachedAttributeDirective {
|
|
|
1498
1809
|
createHTML(add) {
|
|
1499
1810
|
return Markup.attribute(add(this));
|
|
1500
1811
|
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Creates a behavior.
|
|
1814
|
+
* @param targets - The targets available for behaviors to be attached to.
|
|
1815
|
+
*/
|
|
1816
|
+
createBehavior() {
|
|
1817
|
+
return this;
|
|
1818
|
+
}
|
|
1501
1819
|
}
|
|
1502
1820
|
|
|
1503
1821
|
const createInnerHTMLBinding = globalThis.TrustedHTML
|
|
@@ -1509,107 +1827,26 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
|
|
|
1509
1827
|
throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
|
|
1510
1828
|
}
|
|
1511
1829
|
: (binding) => binding;
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
*/
|
|
1516
|
-
const BindingMode = Object.freeze({
|
|
1517
|
-
/**
|
|
1518
|
-
* Creates a binding mode based on the supplied behavior types.
|
|
1519
|
-
* @param UpdateType - The base behavior type used to update aspects.
|
|
1520
|
-
* @param EventType - The base behavior type used to respond to events.
|
|
1521
|
-
* @returns A new binding mode.
|
|
1522
|
-
*/
|
|
1523
|
-
define(UpdateType, EventType = EventBinding) {
|
|
1524
|
-
return Object.freeze({
|
|
1525
|
-
[1]: d => new UpdateType(d, DOM.setAttribute),
|
|
1526
|
-
[2]: d => new UpdateType(d, DOM.setBooleanAttribute),
|
|
1527
|
-
[3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
|
|
1528
|
-
[4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
|
|
1529
|
-
[5]: d => new UpdateType(d, updateTokenListTarget),
|
|
1530
|
-
[6]: d => new EventType(d),
|
|
1531
|
-
});
|
|
1532
|
-
},
|
|
1533
|
-
});
|
|
1534
|
-
/**
|
|
1535
|
-
* Describes the configuration for a binding expression.
|
|
1536
|
-
* @public
|
|
1537
|
-
*/
|
|
1538
|
-
const BindingConfig = Object.freeze({
|
|
1539
|
-
/**
|
|
1540
|
-
* Creates a binding configuration based on the provided mode and options.
|
|
1541
|
-
* @param mode - The mode to use for the configuration.
|
|
1542
|
-
* @param defaultOptions - The default options to use for the configuration.
|
|
1543
|
-
* @returns A new binding configuration.
|
|
1544
|
-
*/
|
|
1545
|
-
define(mode, defaultOptions) {
|
|
1546
|
-
const config = (options) => {
|
|
1547
|
-
return {
|
|
1548
|
-
mode: config.mode,
|
|
1549
|
-
options: Object.assign({}, defaultOptions, options),
|
|
1550
|
-
};
|
|
1551
|
-
};
|
|
1552
|
-
config.options = defaultOptions;
|
|
1553
|
-
config.mode = mode;
|
|
1554
|
-
return config;
|
|
1555
|
-
},
|
|
1556
|
-
});
|
|
1557
|
-
/**
|
|
1558
|
-
* A base binding behavior for DOM updates.
|
|
1559
|
-
* @public
|
|
1560
|
-
*/
|
|
1561
|
-
class UpdateBinding {
|
|
1562
|
-
/**
|
|
1563
|
-
* Creates an instance of UpdateBinding.
|
|
1564
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1565
|
-
* @param updateTarget - The function used to update the target with the latest value.
|
|
1566
|
-
*/
|
|
1567
|
-
constructor(directive, updateTarget) {
|
|
1568
|
-
this.directive = directive;
|
|
1569
|
-
this.updateTarget = updateTarget;
|
|
1830
|
+
class OnChangeBinding extends Binding {
|
|
1831
|
+
createObserver(_, subscriber) {
|
|
1832
|
+
return Observable.binding(this.evaluate, subscriber, this.isVolatile);
|
|
1570
1833
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
* @param context - The execution context that the binding is operating within.
|
|
1575
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1576
|
-
*/
|
|
1577
|
-
bind(source, context, targets) { }
|
|
1578
|
-
/**
|
|
1579
|
-
* Unbinds this behavior from the source.
|
|
1580
|
-
* @param source - The source to unbind from.
|
|
1581
|
-
* @param context - The execution context that the binding is operating within.
|
|
1582
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1583
|
-
*/
|
|
1584
|
-
unbind(source, context, targets) { }
|
|
1585
|
-
/**
|
|
1586
|
-
* Creates a behavior.
|
|
1587
|
-
* @param targets - The targets available for behaviors to be attached to.
|
|
1588
|
-
*/
|
|
1589
|
-
createBehavior(targets) {
|
|
1834
|
+
}
|
|
1835
|
+
class OneTimeBinding extends Binding {
|
|
1836
|
+
createObserver() {
|
|
1590
1837
|
return this;
|
|
1591
1838
|
}
|
|
1839
|
+
bind(controller) {
|
|
1840
|
+
return this.evaluate(controller.source, controller.context);
|
|
1841
|
+
}
|
|
1592
1842
|
}
|
|
1593
|
-
function
|
|
1594
|
-
return class extends Type {
|
|
1595
|
-
unbind(source, context, targets) {
|
|
1596
|
-
super.unbind(source, context, targets);
|
|
1597
|
-
const target = targets[this.directive.nodeId];
|
|
1598
|
-
const view = target.$fastView;
|
|
1599
|
-
if (view !== void 0 && view.isComposed) {
|
|
1600
|
-
view.unbind();
|
|
1601
|
-
view.needsBindOnly = true;
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
};
|
|
1605
|
-
}
|
|
1606
|
-
function updateContentTarget(target, aspect, value, source, context) {
|
|
1843
|
+
function updateContent(target, aspect, value, controller) {
|
|
1607
1844
|
// If there's no actual value, then this equates to the
|
|
1608
1845
|
// empty string for the purposes of content bindings.
|
|
1609
1846
|
if (value === null || value === undefined) {
|
|
1610
1847
|
value = "";
|
|
1611
1848
|
}
|
|
1612
|
-
// If the value has a "create" method, then it's a
|
|
1849
|
+
// If the value has a "create" method, then it's a ContentTemplate.
|
|
1613
1850
|
if (value.create) {
|
|
1614
1851
|
target.textContent = "";
|
|
1615
1852
|
let view = target.$fastView;
|
|
@@ -1635,14 +1872,14 @@ function updateContentTarget(target, aspect, value, source, context) {
|
|
|
1635
1872
|
// and that there's actually no need to compose it.
|
|
1636
1873
|
if (!view.isComposed) {
|
|
1637
1874
|
view.isComposed = true;
|
|
1638
|
-
view.bind(source, context);
|
|
1875
|
+
view.bind(controller.source, controller.context);
|
|
1639
1876
|
view.insertBefore(target);
|
|
1640
1877
|
target.$fastView = view;
|
|
1641
1878
|
target.$fastTemplate = value;
|
|
1642
1879
|
}
|
|
1643
1880
|
else if (view.needsBindOnly) {
|
|
1644
1881
|
view.needsBindOnly = false;
|
|
1645
|
-
view.bind(source, context);
|
|
1882
|
+
view.bind(controller.source, controller.context);
|
|
1646
1883
|
}
|
|
1647
1884
|
}
|
|
1648
1885
|
else {
|
|
@@ -1662,10 +1899,9 @@ function updateContentTarget(target, aspect, value, source, context) {
|
|
|
1662
1899
|
target.textContent = value;
|
|
1663
1900
|
}
|
|
1664
1901
|
}
|
|
1665
|
-
function
|
|
1902
|
+
function updateTokenList(target, aspect, value) {
|
|
1666
1903
|
var _a;
|
|
1667
|
-
const
|
|
1668
|
-
const lookup = `${directive.id}-t`;
|
|
1904
|
+
const lookup = `${this.id}-t`;
|
|
1669
1905
|
const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
|
|
1670
1906
|
const versions = state.v;
|
|
1671
1907
|
let currentVersion = state.c;
|
|
@@ -1695,366 +1931,168 @@ function updateTokenListTarget(target, aspect, value) {
|
|
|
1695
1931
|
}
|
|
1696
1932
|
}
|
|
1697
1933
|
}
|
|
1934
|
+
const setProperty = (t, a, v) => (t[a] = v);
|
|
1935
|
+
const eventTarget = () => void 0;
|
|
1698
1936
|
/**
|
|
1699
|
-
* A
|
|
1937
|
+
* A directive that applies bindings.
|
|
1700
1938
|
* @public
|
|
1701
1939
|
*/
|
|
1702
|
-
class
|
|
1940
|
+
class HTMLBindingDirective {
|
|
1703
1941
|
/**
|
|
1704
|
-
*
|
|
1705
|
-
* @param
|
|
1706
|
-
* @param context - The execution context that the binding is operating within.
|
|
1707
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1942
|
+
* Creates an instance of HTMLBindingDirective.
|
|
1943
|
+
* @param dataBinding - The binding configuration to apply.
|
|
1708
1944
|
*/
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
this.updateTarget
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
this.
|
|
1945
|
+
constructor(dataBinding) {
|
|
1946
|
+
this.dataBinding = dataBinding;
|
|
1947
|
+
this.updateTarget = null;
|
|
1948
|
+
/**
|
|
1949
|
+
* The unique id of the factory.
|
|
1950
|
+
*/
|
|
1951
|
+
this.id = nextId();
|
|
1952
|
+
/**
|
|
1953
|
+
* The type of aspect to target.
|
|
1954
|
+
*/
|
|
1955
|
+
this.aspectType = Aspect.content;
|
|
1956
|
+
/** @internal */
|
|
1957
|
+
this.bind = this.bindDefault;
|
|
1958
|
+
this.data = `${this.id}-d`;
|
|
1723
1959
|
}
|
|
1724
1960
|
/**
|
|
1725
|
-
*
|
|
1726
|
-
* @param
|
|
1727
|
-
* @param context - The execution context that the binding is operating within.
|
|
1728
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1961
|
+
* Creates HTML to be used within a template.
|
|
1962
|
+
* @param add - Can be used to add behavior factories to a template.
|
|
1729
1963
|
*/
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
const target = targets[directive.nodeId];
|
|
1733
|
-
const signal = this.getSignal(source, context);
|
|
1734
|
-
const handler = (target[this.handlerProperty] = () => {
|
|
1735
|
-
this.updateTarget(target, directive.targetAspect, directive.binding(source, context), source, context);
|
|
1736
|
-
});
|
|
1737
|
-
handler();
|
|
1738
|
-
const found = signals[signal];
|
|
1739
|
-
if (found) {
|
|
1740
|
-
Array.isArray(found)
|
|
1741
|
-
? found.push(handler)
|
|
1742
|
-
: (signals[signal] = [found, handler]);
|
|
1743
|
-
}
|
|
1744
|
-
else {
|
|
1745
|
-
signals[signal] = handler;
|
|
1746
|
-
}
|
|
1964
|
+
createHTML(add) {
|
|
1965
|
+
return Markup.interpolation(add(this));
|
|
1747
1966
|
}
|
|
1748
1967
|
/**
|
|
1749
|
-
*
|
|
1750
|
-
* @param source - The source to unbind from.
|
|
1751
|
-
* @param context - The execution context that the binding is operating within.
|
|
1752
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1968
|
+
* Creates a behavior.
|
|
1753
1969
|
*/
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1970
|
+
createBehavior() {
|
|
1971
|
+
if (this.updateTarget === null) {
|
|
1972
|
+
if (this.targetAspect === "innerHTML") {
|
|
1973
|
+
this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
|
|
1974
|
+
}
|
|
1975
|
+
switch (this.aspectType) {
|
|
1976
|
+
case 1:
|
|
1977
|
+
this.updateTarget = DOM.setAttribute;
|
|
1978
|
+
break;
|
|
1979
|
+
case 2:
|
|
1980
|
+
this.updateTarget = DOM.setBooleanAttribute;
|
|
1981
|
+
break;
|
|
1982
|
+
case 3:
|
|
1983
|
+
this.updateTarget = setProperty;
|
|
1984
|
+
break;
|
|
1985
|
+
case 4:
|
|
1986
|
+
this.bind = this.bindContent;
|
|
1987
|
+
this.updateTarget = updateContent;
|
|
1988
|
+
break;
|
|
1989
|
+
case 5:
|
|
1990
|
+
this.updateTarget = updateTokenList;
|
|
1991
|
+
break;
|
|
1992
|
+
case 6:
|
|
1993
|
+
this.bind = this.bindEvent;
|
|
1994
|
+
this.updateTarget = eventTarget;
|
|
1995
|
+
break;
|
|
1996
|
+
default:
|
|
1997
|
+
throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
|
|
1764
1998
|
}
|
|
1765
1999
|
}
|
|
1766
|
-
|
|
1767
|
-
signals[signal] = void 0;
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
getSignal(source, context) {
|
|
1771
|
-
const options = this.directive.options;
|
|
1772
|
-
return isString(options) ? options : options(source, context);
|
|
1773
|
-
}
|
|
1774
|
-
/**
|
|
1775
|
-
* Sends the specified signal to signaled bindings.
|
|
1776
|
-
* @param signal - The signal to send.
|
|
1777
|
-
* @public
|
|
1778
|
-
*/
|
|
1779
|
-
static send(signal) {
|
|
1780
|
-
const found = signals[signal];
|
|
1781
|
-
if (found) {
|
|
1782
|
-
Array.isArray(found) ? found.forEach(x => x()) : found();
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
/**
|
|
1787
|
-
* A binding behavior for bindings that change.
|
|
1788
|
-
* @public
|
|
1789
|
-
*/
|
|
1790
|
-
class ChangeBinding extends UpdateBinding {
|
|
1791
|
-
/**
|
|
1792
|
-
* Creates an instance of ChangeBinding.
|
|
1793
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1794
|
-
* @param updateTarget - The function used to update the target with the latest value.
|
|
1795
|
-
*/
|
|
1796
|
-
constructor(directive, updateTarget) {
|
|
1797
|
-
super(directive, updateTarget);
|
|
1798
|
-
this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
|
|
1799
|
-
this.observerProperty = `${directive.id}-o`;
|
|
2000
|
+
return this;
|
|
1800
2001
|
}
|
|
1801
|
-
/**
|
|
1802
|
-
|
|
1803
|
-
* @param target - The target node.
|
|
1804
|
-
* @returns A BindingObserver.
|
|
1805
|
-
*/
|
|
1806
|
-
getObserver(target) {
|
|
2002
|
+
/** @internal */
|
|
2003
|
+
bindDefault(controller) {
|
|
1807
2004
|
var _a;
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
/**
|
|
1811
|
-
* Bind this behavior to the source.
|
|
1812
|
-
* @param source - The source to bind to.
|
|
1813
|
-
* @param context - The execution context that the binding is operating within.
|
|
1814
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1815
|
-
*/
|
|
1816
|
-
bind(source, context, targets) {
|
|
1817
|
-
const directive = this.directive;
|
|
1818
|
-
const target = targets[directive.nodeId];
|
|
1819
|
-
const observer = this.getObserver(target);
|
|
2005
|
+
const target = controller.targets[this.nodeId];
|
|
2006
|
+
const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
|
|
1820
2007
|
observer.target = target;
|
|
1821
|
-
observer.
|
|
1822
|
-
observer.
|
|
1823
|
-
this.updateTarget
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
* Unbinds this behavior from the source.
|
|
1827
|
-
* @param source - The source to unbind from.
|
|
1828
|
-
* @param context - The execution context that the binding is operating within.
|
|
1829
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1830
|
-
*/
|
|
1831
|
-
unbind(source, context, targets) {
|
|
1832
|
-
const target = targets[this.directive.nodeId];
|
|
1833
|
-
const observer = this.getObserver(target);
|
|
1834
|
-
observer.dispose();
|
|
1835
|
-
observer.target = null;
|
|
1836
|
-
observer.source = null;
|
|
1837
|
-
observer.context = null;
|
|
2008
|
+
observer.controller = controller;
|
|
2009
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
2010
|
+
if (this.updateTarget === updateContent) {
|
|
2011
|
+
controller.onUnbind(this);
|
|
2012
|
+
}
|
|
1838
2013
|
}
|
|
1839
2014
|
/** @internal */
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
const context = observer.context;
|
|
1844
|
-
this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
|
|
2015
|
+
bindContent(controller) {
|
|
2016
|
+
this.bindDefault(controller);
|
|
2017
|
+
controller.onUnbind(this);
|
|
1845
2018
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
class EventBinding {
|
|
1852
|
-
/**
|
|
1853
|
-
* Creates an instance of EventBinding.
|
|
1854
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1855
|
-
*/
|
|
1856
|
-
constructor(directive) {
|
|
1857
|
-
this.directive = directive;
|
|
1858
|
-
this.sourceProperty = `${directive.id}-s`;
|
|
1859
|
-
this.contextProperty = `${directive.id}-c`;
|
|
1860
|
-
}
|
|
1861
|
-
/**
|
|
1862
|
-
* Bind this behavior to the source.
|
|
1863
|
-
* @param source - The source to bind to.
|
|
1864
|
-
* @param context - The execution context that the binding is operating within.
|
|
1865
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1866
|
-
*/
|
|
1867
|
-
bind(source, context, targets) {
|
|
1868
|
-
const directive = this.directive;
|
|
1869
|
-
const target = targets[directive.nodeId];
|
|
1870
|
-
target[this.sourceProperty] = source;
|
|
1871
|
-
target[this.contextProperty] = context;
|
|
1872
|
-
target.addEventListener(directive.targetAspect, this, directive.options);
|
|
1873
|
-
}
|
|
1874
|
-
/**
|
|
1875
|
-
* Unbinds this behavior from the source.
|
|
1876
|
-
* @param source - The source to unbind from.
|
|
1877
|
-
* @param context - The execution context that the binding is operating within.
|
|
1878
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1879
|
-
*/
|
|
1880
|
-
unbind(source, context, targets) {
|
|
1881
|
-
const directive = this.directive;
|
|
1882
|
-
const target = targets[directive.nodeId];
|
|
1883
|
-
target[this.sourceProperty] = target[this.contextProperty] = null;
|
|
1884
|
-
target.removeEventListener(directive.targetAspect, this, directive.options);
|
|
2019
|
+
/** @internal */
|
|
2020
|
+
bindEvent(controller) {
|
|
2021
|
+
const target = controller.targets[this.nodeId];
|
|
2022
|
+
target[this.data] = controller;
|
|
2023
|
+
target.addEventListener(this.targetAspect, this, this.dataBinding.options);
|
|
1885
2024
|
}
|
|
1886
|
-
/**
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
2025
|
+
/** @internal */
|
|
2026
|
+
unbind(controller) {
|
|
2027
|
+
const target = controller.targets[this.nodeId];
|
|
2028
|
+
const view = target.$fastView;
|
|
2029
|
+
if (view !== void 0 && view.isComposed) {
|
|
2030
|
+
view.unbind();
|
|
2031
|
+
view.needsBindOnly = true;
|
|
2032
|
+
}
|
|
1892
2033
|
}
|
|
1893
|
-
/**
|
|
1894
|
-
* @internal
|
|
1895
|
-
*/
|
|
2034
|
+
/** @internal */
|
|
1896
2035
|
handleEvent(event) {
|
|
1897
2036
|
const target = event.currentTarget;
|
|
1898
2037
|
ExecutionContext.setEvent(event);
|
|
1899
|
-
const
|
|
2038
|
+
const controller = target[this.data];
|
|
2039
|
+
const result = this.dataBinding.evaluate(controller.source, controller.context);
|
|
1900
2040
|
ExecutionContext.setEvent(null);
|
|
1901
2041
|
if (result !== true) {
|
|
1902
2042
|
event.preventDefault();
|
|
1903
2043
|
}
|
|
1904
2044
|
}
|
|
1905
|
-
}
|
|
1906
|
-
let twoWaySettings = {
|
|
1907
|
-
determineChangeEvent() {
|
|
1908
|
-
return "change";
|
|
1909
|
-
},
|
|
1910
|
-
};
|
|
1911
|
-
/**
|
|
1912
|
-
* A binding behavior for bindings that update in two directions.
|
|
1913
|
-
* @public
|
|
1914
|
-
*/
|
|
1915
|
-
class TwoWayBinding extends ChangeBinding {
|
|
1916
|
-
/**
|
|
1917
|
-
* Bind this behavior to the source.
|
|
1918
|
-
* @param source - The source to bind to.
|
|
1919
|
-
* @param context - The execution context that the binding is operating within.
|
|
1920
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1921
|
-
*/
|
|
1922
|
-
bind(source, context, targets) {
|
|
1923
|
-
var _a;
|
|
1924
|
-
super.bind(source, context, targets);
|
|
1925
|
-
const directive = this.directive;
|
|
1926
|
-
const target = targets[directive.nodeId];
|
|
1927
|
-
if (!this.changeEvent) {
|
|
1928
|
-
this.changeEvent =
|
|
1929
|
-
(_a = directive.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(directive, target);
|
|
1930
|
-
}
|
|
1931
|
-
target.addEventListener(this.changeEvent, this);
|
|
1932
|
-
}
|
|
1933
|
-
/**
|
|
1934
|
-
* Unbinds this behavior from the source.
|
|
1935
|
-
* @param source - The source to unbind from.
|
|
1936
|
-
* @param context - The execution context that the binding is operating within.
|
|
1937
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1938
|
-
*/
|
|
1939
|
-
unbind(source, context, targets) {
|
|
1940
|
-
super.unbind(source, context, targets);
|
|
1941
|
-
targets[this.directive.nodeId].removeEventListener(this.changeEvent, this);
|
|
1942
|
-
}
|
|
1943
2045
|
/** @internal */
|
|
1944
|
-
|
|
1945
|
-
const
|
|
1946
|
-
const
|
|
1947
|
-
|
|
1948
|
-
switch (directive.aspectType) {
|
|
1949
|
-
case 1:
|
|
1950
|
-
value = target.getAttribute(directive.targetAspect);
|
|
1951
|
-
break;
|
|
1952
|
-
case 2:
|
|
1953
|
-
value = target.hasAttribute(directive.targetAspect);
|
|
1954
|
-
break;
|
|
1955
|
-
case 4:
|
|
1956
|
-
value = target.innerText;
|
|
1957
|
-
break;
|
|
1958
|
-
default:
|
|
1959
|
-
value = target[directive.targetAspect];
|
|
1960
|
-
break;
|
|
1961
|
-
}
|
|
1962
|
-
const observer = this.getObserver(target);
|
|
1963
|
-
const last = observer.last; // using internal API!!!
|
|
1964
|
-
last.propertySource[last.propertyName] = directive.options.fromView(value);
|
|
1965
|
-
}
|
|
1966
|
-
/**
|
|
1967
|
-
* Configures two-way binding.
|
|
1968
|
-
* @param settings - The settings to use for the two-way binding system.
|
|
1969
|
-
*/
|
|
1970
|
-
static configure(settings) {
|
|
1971
|
-
twoWaySettings = settings;
|
|
2046
|
+
handleChange(binding, observer) {
|
|
2047
|
+
const target = observer.target;
|
|
2048
|
+
const controller = observer.controller;
|
|
2049
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
1972
2050
|
}
|
|
1973
2051
|
}
|
|
2052
|
+
HTMLDirective.define(HTMLBindingDirective, { aspected: true });
|
|
1974
2053
|
/**
|
|
1975
|
-
*
|
|
1976
|
-
* @
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
/**
|
|
1980
|
-
* The default twoWay binding configuration.
|
|
1981
|
-
* @public
|
|
1982
|
-
*/
|
|
1983
|
-
const twoWay = BindingConfig.define(BindingMode.define(TwoWayBinding), {
|
|
1984
|
-
fromView: v => v,
|
|
1985
|
-
});
|
|
1986
|
-
/**
|
|
1987
|
-
* The default onTime binding configuration.
|
|
2054
|
+
* Creates an standard binding.
|
|
2055
|
+
* @param binding - The binding to refresh when changed.
|
|
2056
|
+
* @param isVolatile - Indicates whether the binding is volatile or not.
|
|
2057
|
+
* @returns A binding configuration.
|
|
1988
2058
|
* @public
|
|
1989
2059
|
*/
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
}
|
|
1993
|
-
const signalMode = BindingMode.define(SignalBinding);
|
|
2060
|
+
function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
|
|
2061
|
+
return new OnChangeBinding(binding, isVolatile);
|
|
2062
|
+
}
|
|
1994
2063
|
/**
|
|
1995
|
-
* Creates a
|
|
1996
|
-
* @param
|
|
2064
|
+
* Creates a one time binding
|
|
2065
|
+
* @param binding - The binding to refresh when signaled.
|
|
1997
2066
|
* @returns A binding configuration.
|
|
1998
2067
|
* @public
|
|
1999
2068
|
*/
|
|
2000
|
-
|
|
2001
|
-
return
|
|
2002
|
-
}
|
|
2069
|
+
function oneTime(binding) {
|
|
2070
|
+
return new OneTimeBinding(binding);
|
|
2071
|
+
}
|
|
2003
2072
|
/**
|
|
2004
|
-
*
|
|
2073
|
+
* Creates an event listener binding.
|
|
2074
|
+
* @param binding - The binding to invoke when the event is raised.
|
|
2075
|
+
* @param options - Event listener options.
|
|
2076
|
+
* @returns A binding configuration.
|
|
2005
2077
|
* @public
|
|
2006
2078
|
*/
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
* @param mode - The binding mode to use when applying the binding.
|
|
2012
|
-
* @param options - The options to configure the binding with.
|
|
2013
|
-
*/
|
|
2014
|
-
constructor(binding, mode, options) {
|
|
2015
|
-
this.binding = binding;
|
|
2016
|
-
this.mode = mode;
|
|
2017
|
-
this.options = options;
|
|
2018
|
-
this.factory = null;
|
|
2019
|
-
/**
|
|
2020
|
-
* The type of aspect to target.
|
|
2021
|
-
*/
|
|
2022
|
-
this.aspectType = Aspect.content;
|
|
2023
|
-
}
|
|
2024
|
-
/**
|
|
2025
|
-
* Creates HTML to be used within a template.
|
|
2026
|
-
* @param add - Can be used to add behavior factories to a template.
|
|
2027
|
-
*/
|
|
2028
|
-
createHTML(add) {
|
|
2029
|
-
return Markup.interpolation(add(this));
|
|
2030
|
-
}
|
|
2031
|
-
/**
|
|
2032
|
-
* Creates a behavior.
|
|
2033
|
-
* @param targets - The targets available for behaviors to be attached to.
|
|
2034
|
-
*/
|
|
2035
|
-
createBehavior(targets) {
|
|
2036
|
-
if (this.factory == null) {
|
|
2037
|
-
if (this.targetAspect === "innerHTML") {
|
|
2038
|
-
this.binding = createInnerHTMLBinding(this.binding);
|
|
2039
|
-
}
|
|
2040
|
-
this.factory = this.mode[this.aspectType](this);
|
|
2041
|
-
}
|
|
2042
|
-
return this.factory.createBehavior(targets);
|
|
2043
|
-
}
|
|
2079
|
+
function listener(binding, options) {
|
|
2080
|
+
const config = new OnChangeBinding(binding, false);
|
|
2081
|
+
config.options = options;
|
|
2082
|
+
return config;
|
|
2044
2083
|
}
|
|
2045
|
-
HTMLDirective.define(HTMLBindingDirective, { aspected: true });
|
|
2046
2084
|
/**
|
|
2047
|
-
*
|
|
2048
|
-
* @param
|
|
2049
|
-
* @
|
|
2050
|
-
* @returns A binding directive.
|
|
2085
|
+
* Normalizes the input value into a binding.
|
|
2086
|
+
* @param value - The value to create the default binding for.
|
|
2087
|
+
* @returns A binding configuration for the provided value.
|
|
2051
2088
|
* @public
|
|
2052
2089
|
*/
|
|
2053
|
-
function
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2090
|
+
function normalizeBinding(value) {
|
|
2091
|
+
return isFunction(value)
|
|
2092
|
+
? bind(value)
|
|
2093
|
+
: value instanceof Binding
|
|
2094
|
+
? value
|
|
2095
|
+
: oneTime(() => value);
|
|
2058
2096
|
}
|
|
2059
2097
|
|
|
2060
2098
|
function removeNodeSequence(firstNode, lastNode) {
|
|
@@ -2078,21 +2116,91 @@ class HTMLView {
|
|
|
2078
2116
|
* @param fragment - The html fragment that contains the nodes for this view.
|
|
2079
2117
|
* @param behaviors - The behaviors to be applied to this view.
|
|
2080
2118
|
*/
|
|
2081
|
-
constructor(fragment, factories, targets) {
|
|
2082
|
-
this.fragment = fragment;
|
|
2083
|
-
this.factories = factories;
|
|
2084
|
-
this.targets = targets;
|
|
2085
|
-
this.behaviors = null;
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
this.
|
|
2095
|
-
|
|
2119
|
+
constructor(fragment, factories, targets) {
|
|
2120
|
+
this.fragment = fragment;
|
|
2121
|
+
this.factories = factories;
|
|
2122
|
+
this.targets = targets;
|
|
2123
|
+
this.behaviors = null;
|
|
2124
|
+
this.unbindables = [];
|
|
2125
|
+
/**
|
|
2126
|
+
* The data that the view is bound to.
|
|
2127
|
+
*/
|
|
2128
|
+
this.source = null;
|
|
2129
|
+
/**
|
|
2130
|
+
* Indicates whether the controller is bound.
|
|
2131
|
+
*/
|
|
2132
|
+
this.isBound = false;
|
|
2133
|
+
/**
|
|
2134
|
+
* Indicates how the source's lifetime relates to the controller's lifetime.
|
|
2135
|
+
*/
|
|
2136
|
+
this.sourceLifetime = SourceLifetime.unknown;
|
|
2137
|
+
/**
|
|
2138
|
+
* The execution context the view is running within.
|
|
2139
|
+
*/
|
|
2140
|
+
this.context = this;
|
|
2141
|
+
/**
|
|
2142
|
+
* The index of the current item within a repeat context.
|
|
2143
|
+
*/
|
|
2144
|
+
this.index = 0;
|
|
2145
|
+
/**
|
|
2146
|
+
* The length of the current collection within a repeat context.
|
|
2147
|
+
*/
|
|
2148
|
+
this.length = 0;
|
|
2149
|
+
this.firstChild = fragment.firstChild;
|
|
2150
|
+
this.lastChild = fragment.lastChild;
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* The current event within an event handler.
|
|
2154
|
+
*/
|
|
2155
|
+
get event() {
|
|
2156
|
+
return ExecutionContext.getEvent();
|
|
2157
|
+
}
|
|
2158
|
+
/**
|
|
2159
|
+
* Indicates whether the current item within a repeat context
|
|
2160
|
+
* has an even index.
|
|
2161
|
+
*/
|
|
2162
|
+
get isEven() {
|
|
2163
|
+
return this.index % 2 === 0;
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* Indicates whether the current item within a repeat context
|
|
2167
|
+
* has an odd index.
|
|
2168
|
+
*/
|
|
2169
|
+
get isOdd() {
|
|
2170
|
+
return this.index % 2 !== 0;
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Indicates whether the current item within a repeat context
|
|
2174
|
+
* is the first item in the collection.
|
|
2175
|
+
*/
|
|
2176
|
+
get isFirst() {
|
|
2177
|
+
return this.index === 0;
|
|
2178
|
+
}
|
|
2179
|
+
/**
|
|
2180
|
+
* Indicates whether the current item within a repeat context
|
|
2181
|
+
* is somewhere in the middle of the collection.
|
|
2182
|
+
*/
|
|
2183
|
+
get isInMiddle() {
|
|
2184
|
+
return !this.isFirst && !this.isLast;
|
|
2185
|
+
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Indicates whether the current item within a repeat context
|
|
2188
|
+
* is the last item in the collection.
|
|
2189
|
+
*/
|
|
2190
|
+
get isLast() {
|
|
2191
|
+
return this.index === this.length - 1;
|
|
2192
|
+
}
|
|
2193
|
+
/**
|
|
2194
|
+
* Returns the typed event detail of a custom event.
|
|
2195
|
+
*/
|
|
2196
|
+
eventDetail() {
|
|
2197
|
+
return this.event.detail;
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Returns the typed event target of the event.
|
|
2201
|
+
*/
|
|
2202
|
+
eventTarget() {
|
|
2203
|
+
return this.event.target;
|
|
2096
2204
|
}
|
|
2097
2205
|
/**
|
|
2098
2206
|
* Appends the view's DOM nodes to the referenced node.
|
|
@@ -2110,8 +2218,10 @@ class HTMLView {
|
|
|
2110
2218
|
node.parentNode.insertBefore(this.fragment, node);
|
|
2111
2219
|
}
|
|
2112
2220
|
else {
|
|
2113
|
-
const parentNode = node.parentNode;
|
|
2114
2221
|
const end = this.lastChild;
|
|
2222
|
+
if (node.previousSibling === end)
|
|
2223
|
+
return;
|
|
2224
|
+
const parentNode = node.parentNode;
|
|
2115
2225
|
let current = this.firstChild;
|
|
2116
2226
|
let next;
|
|
2117
2227
|
while (current !== end) {
|
|
@@ -2146,58 +2256,61 @@ class HTMLView {
|
|
|
2146
2256
|
removeNodeSequence(this.firstChild, this.lastChild);
|
|
2147
2257
|
this.unbind();
|
|
2148
2258
|
}
|
|
2259
|
+
onUnbind(behavior) {
|
|
2260
|
+
this.unbindables.push(behavior);
|
|
2261
|
+
}
|
|
2149
2262
|
/**
|
|
2150
2263
|
* Binds a view's behaviors to its binding source.
|
|
2151
2264
|
* @param source - The binding source for the view's binding behaviors.
|
|
2152
2265
|
* @param context - The execution context to run the behaviors within.
|
|
2153
2266
|
*/
|
|
2154
|
-
bind(source, context) {
|
|
2155
|
-
|
|
2156
|
-
const oldSource = this.source;
|
|
2157
|
-
if (oldSource === source) {
|
|
2267
|
+
bind(source, context = this) {
|
|
2268
|
+
if (this.source === source) {
|
|
2158
2269
|
return;
|
|
2159
2270
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2165
|
-
const current = behaviors[i];
|
|
2166
|
-
current.unbind(oldSource, context, targets);
|
|
2167
|
-
current.bind(source, context, targets);
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
else if (behaviors === null) {
|
|
2271
|
+
let behaviors = this.behaviors;
|
|
2272
|
+
if (behaviors === null) {
|
|
2273
|
+
this.source = source;
|
|
2274
|
+
this.context = context;
|
|
2171
2275
|
this.behaviors = behaviors = new Array(this.factories.length);
|
|
2172
2276
|
const factories = this.factories;
|
|
2173
2277
|
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
2174
|
-
const behavior = factories[i].createBehavior(
|
|
2175
|
-
behavior.bind(
|
|
2278
|
+
const behavior = factories[i].createBehavior();
|
|
2279
|
+
behavior.bind(this);
|
|
2176
2280
|
behaviors[i] = behavior;
|
|
2177
2281
|
}
|
|
2178
2282
|
}
|
|
2179
2283
|
else {
|
|
2284
|
+
if (this.source !== null) {
|
|
2285
|
+
this.evaluateUnbindables();
|
|
2286
|
+
}
|
|
2287
|
+
this.isBound = false;
|
|
2288
|
+
this.source = source;
|
|
2289
|
+
this.context = context;
|
|
2180
2290
|
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2181
|
-
behaviors[i].bind(
|
|
2291
|
+
behaviors[i].bind(this);
|
|
2182
2292
|
}
|
|
2183
2293
|
}
|
|
2294
|
+
this.isBound = true;
|
|
2184
2295
|
}
|
|
2185
2296
|
/**
|
|
2186
2297
|
* Unbinds a view's behaviors from its binding source.
|
|
2187
2298
|
*/
|
|
2188
2299
|
unbind() {
|
|
2189
|
-
|
|
2190
|
-
if (oldSource === null) {
|
|
2300
|
+
if (!this.isBound || this.source === null) {
|
|
2191
2301
|
return;
|
|
2192
2302
|
}
|
|
2193
|
-
|
|
2194
|
-
const context = this.context;
|
|
2195
|
-
const behaviors = this.behaviors;
|
|
2196
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2197
|
-
behaviors[i].unbind(oldSource, context, targets);
|
|
2198
|
-
}
|
|
2303
|
+
this.evaluateUnbindables();
|
|
2199
2304
|
this.source = null;
|
|
2200
|
-
this.context =
|
|
2305
|
+
this.context = this;
|
|
2306
|
+
this.isBound = false;
|
|
2307
|
+
}
|
|
2308
|
+
evaluateUnbindables() {
|
|
2309
|
+
const unbindables = this.unbindables;
|
|
2310
|
+
for (let i = 0, ii = unbindables.length; i < ii; ++i) {
|
|
2311
|
+
unbindables[i].unbind(this);
|
|
2312
|
+
}
|
|
2313
|
+
unbindables.length = 0;
|
|
2201
2314
|
}
|
|
2202
2315
|
/**
|
|
2203
2316
|
* Efficiently disposes of a contiguous range of synthetic view instances.
|
|
@@ -2213,6 +2326,8 @@ class HTMLView {
|
|
|
2213
2326
|
}
|
|
2214
2327
|
}
|
|
2215
2328
|
}
|
|
2329
|
+
Observable.defineProperty(HTMLView.prototype, "index");
|
|
2330
|
+
Observable.defineProperty(HTMLView.prototype, "length");
|
|
2216
2331
|
|
|
2217
2332
|
const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
|
|
2218
2333
|
const descriptorCache = {};
|
|
@@ -2221,6 +2336,22 @@ const next = {
|
|
|
2221
2336
|
index: 0,
|
|
2222
2337
|
node: null,
|
|
2223
2338
|
};
|
|
2339
|
+
function tryWarn(name) {
|
|
2340
|
+
if (!name.startsWith("fast-")) {
|
|
2341
|
+
FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
const warningHost = new Proxy(document.createElement("div"), {
|
|
2345
|
+
get(target, property) {
|
|
2346
|
+
tryWarn(property);
|
|
2347
|
+
const value = Reflect.get(target, property);
|
|
2348
|
+
return isFunction(value) ? value.bind(target) : value;
|
|
2349
|
+
},
|
|
2350
|
+
set(target, property, value) {
|
|
2351
|
+
tryWarn(property);
|
|
2352
|
+
return Reflect.set(target, property, value);
|
|
2353
|
+
},
|
|
2354
|
+
});
|
|
2224
2355
|
class CompilationContext {
|
|
2225
2356
|
constructor(fragment, directives) {
|
|
2226
2357
|
this.fragment = fragment;
|
|
@@ -2271,7 +2402,7 @@ class CompilationContext {
|
|
|
2271
2402
|
const fragment = this.fragment.cloneNode(true);
|
|
2272
2403
|
const targets = Object.create(this.proto);
|
|
2273
2404
|
targets.r = fragment;
|
|
2274
|
-
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget :
|
|
2405
|
+
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
|
|
2275
2406
|
for (const id of this.nodeIds) {
|
|
2276
2407
|
targets[id]; // trigger locator
|
|
2277
2408
|
}
|
|
@@ -2288,7 +2419,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
|
|
|
2288
2419
|
let result = null;
|
|
2289
2420
|
if (parseResult === null) {
|
|
2290
2421
|
if (includeBasicValues) {
|
|
2291
|
-
result =
|
|
2422
|
+
result = new HTMLBindingDirective(oneTime(() => attrValue));
|
|
2292
2423
|
Aspect.assign(result, attr.name);
|
|
2293
2424
|
}
|
|
2294
2425
|
}
|
|
@@ -2325,6 +2456,7 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
|
|
|
2325
2456
|
}
|
|
2326
2457
|
else {
|
|
2327
2458
|
currentNode.textContent = " ";
|
|
2459
|
+
Aspect.assign(currentPart);
|
|
2328
2460
|
context.addFactory(currentPart, parentId, nodeId, nodeIndex);
|
|
2329
2461
|
}
|
|
2330
2462
|
lastNode = currentNode;
|
|
@@ -2457,22 +2589,28 @@ const Compiler = {
|
|
|
2457
2589
|
return parts[0];
|
|
2458
2590
|
}
|
|
2459
2591
|
let sourceAspect;
|
|
2592
|
+
let binding;
|
|
2593
|
+
let isVolatile = false;
|
|
2460
2594
|
const partCount = parts.length;
|
|
2461
2595
|
const finalParts = parts.map((x) => {
|
|
2462
2596
|
if (isString(x)) {
|
|
2463
2597
|
return () => x;
|
|
2464
2598
|
}
|
|
2465
2599
|
sourceAspect = x.sourceAspect || sourceAspect;
|
|
2466
|
-
|
|
2600
|
+
binding = x.dataBinding || binding;
|
|
2601
|
+
isVolatile = isVolatile || x.dataBinding.isVolatile;
|
|
2602
|
+
return x.dataBinding.evaluate;
|
|
2467
2603
|
});
|
|
2468
|
-
const
|
|
2604
|
+
const expression = (scope, context) => {
|
|
2469
2605
|
let output = "";
|
|
2470
2606
|
for (let i = 0; i < partCount; ++i) {
|
|
2471
2607
|
output += finalParts[i](scope, context);
|
|
2472
2608
|
}
|
|
2473
2609
|
return output;
|
|
2474
2610
|
};
|
|
2475
|
-
|
|
2611
|
+
binding.evaluate = expression;
|
|
2612
|
+
binding.isVolatile = isVolatile;
|
|
2613
|
+
const directive = new HTMLBindingDirective(binding);
|
|
2476
2614
|
Aspect.assign(directive, sourceAspect);
|
|
2477
2615
|
return directive;
|
|
2478
2616
|
},
|
|
@@ -2510,9 +2648,9 @@ class ViewTemplate {
|
|
|
2510
2648
|
* @param hostBindingTarget - An HTML element to target the host bindings at if different from the
|
|
2511
2649
|
* host that the template is being attached to.
|
|
2512
2650
|
*/
|
|
2513
|
-
render(source, host, hostBindingTarget
|
|
2514
|
-
const view = this.create(hostBindingTarget
|
|
2515
|
-
view.bind(source
|
|
2651
|
+
render(source, host, hostBindingTarget) {
|
|
2652
|
+
const view = this.create(hostBindingTarget);
|
|
2653
|
+
view.bind(source);
|
|
2516
2654
|
view.appendTo(host);
|
|
2517
2655
|
return view;
|
|
2518
2656
|
}
|
|
@@ -2552,12 +2690,12 @@ function html(strings, ...values) {
|
|
|
2552
2690
|
let definition;
|
|
2553
2691
|
html += currentString;
|
|
2554
2692
|
if (isFunction(currentValue)) {
|
|
2555
|
-
html += createAspectedHTML(bind(currentValue), currentString, add);
|
|
2693
|
+
html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
|
|
2556
2694
|
}
|
|
2557
2695
|
else if (isString(currentValue)) {
|
|
2558
2696
|
const match = lastAttributeNameRegex.exec(currentString);
|
|
2559
2697
|
if (match !== null) {
|
|
2560
|
-
const directive =
|
|
2698
|
+
const directive = new HTMLBindingDirective(oneTime(() => currentValue));
|
|
2561
2699
|
Aspect.assign(directive, match[2]);
|
|
2562
2700
|
html += directive.createHTML(add);
|
|
2563
2701
|
}
|
|
@@ -2565,8 +2703,11 @@ function html(strings, ...values) {
|
|
|
2565
2703
|
html += currentValue;
|
|
2566
2704
|
}
|
|
2567
2705
|
}
|
|
2706
|
+
else if (currentValue instanceof Binding) {
|
|
2707
|
+
html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
|
|
2708
|
+
}
|
|
2568
2709
|
else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
|
|
2569
|
-
html += createAspectedHTML(
|
|
2710
|
+
html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
|
|
2570
2711
|
}
|
|
2571
2712
|
else {
|
|
2572
2713
|
if (definition.aspected) {
|
|
@@ -2579,26 +2720,6 @@ function html(strings, ...values) {
|
|
|
2579
2720
|
}
|
|
2580
2721
|
return new ViewTemplate(html + strings[strings.length - 1], factories);
|
|
2581
2722
|
}
|
|
2582
|
-
/**
|
|
2583
|
-
* Transforms a template literal string into a ChildViewTemplate.
|
|
2584
|
-
* @param strings - The string fragments that are interpolated with the values.
|
|
2585
|
-
* @param values - The values that are interpolated with the string fragments.
|
|
2586
|
-
* @remarks
|
|
2587
|
-
* The html helper supports interpolation of strings, numbers, binding expressions,
|
|
2588
|
-
* other template instances, and Directive instances.
|
|
2589
|
-
* @public
|
|
2590
|
-
*/
|
|
2591
|
-
const child = html;
|
|
2592
|
-
/**
|
|
2593
|
-
* Transforms a template literal string into an ItemViewTemplate.
|
|
2594
|
-
* @param strings - The string fragments that are interpolated with the values.
|
|
2595
|
-
* @param values - The values that are interpolated with the string fragments.
|
|
2596
|
-
* @remarks
|
|
2597
|
-
* The html helper supports interpolation of strings, numbers, binding expressions,
|
|
2598
|
-
* other template instances, and Directive instances.
|
|
2599
|
-
* @public
|
|
2600
|
-
*/
|
|
2601
|
-
const item = html;
|
|
2602
2723
|
|
|
2603
2724
|
/**
|
|
2604
2725
|
* The runtime behavior for template references.
|
|
@@ -2606,20 +2727,12 @@ const item = html;
|
|
|
2606
2727
|
*/
|
|
2607
2728
|
class RefDirective extends StatelessAttachedAttributeDirective {
|
|
2608
2729
|
/**
|
|
2609
|
-
* Bind this behavior
|
|
2610
|
-
* @param
|
|
2611
|
-
* @param context - The execution context that the binding is operating within.
|
|
2612
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
2730
|
+
* Bind this behavior.
|
|
2731
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2613
2732
|
*/
|
|
2614
|
-
bind(
|
|
2615
|
-
source[this.options] = targets[this.nodeId];
|
|
2733
|
+
bind(controller) {
|
|
2734
|
+
controller.source[this.options] = controller.targets[this.nodeId];
|
|
2616
2735
|
}
|
|
2617
|
-
/**
|
|
2618
|
-
* Unbinds this behavior from the source.
|
|
2619
|
-
* @param source - The source to unbind from.
|
|
2620
|
-
*/
|
|
2621
|
-
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
|
|
2622
|
-
unbind() { }
|
|
2623
2736
|
}
|
|
2624
2737
|
HTMLDirective.define(RefDirective);
|
|
2625
2738
|
/**
|
|
@@ -2631,27 +2744,34 @@ const ref = (propertyName) => new RefDirective(propertyName);
|
|
|
2631
2744
|
|
|
2632
2745
|
/**
|
|
2633
2746
|
* A directive that enables basic conditional rendering in a template.
|
|
2634
|
-
* @param
|
|
2747
|
+
* @param condition - The condition to test for rendering.
|
|
2635
2748
|
* @param templateOrTemplateBinding - The template or a binding that gets
|
|
2636
2749
|
* the template to render when the condition is true.
|
|
2637
2750
|
* @public
|
|
2638
2751
|
*/
|
|
2639
|
-
function when(
|
|
2640
|
-
const
|
|
2752
|
+
function when(condition, templateOrTemplateBinding) {
|
|
2753
|
+
const dataBinding = isFunction(condition) ? condition : () => condition;
|
|
2754
|
+
const templateBinding = isFunction(templateOrTemplateBinding)
|
|
2641
2755
|
? templateOrTemplateBinding
|
|
2642
2756
|
: () => templateOrTemplateBinding;
|
|
2643
|
-
return (source, context) =>
|
|
2757
|
+
return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
|
|
2644
2758
|
}
|
|
2645
2759
|
|
|
2646
2760
|
const defaultRepeatOptions = Object.freeze({
|
|
2647
2761
|
positioning: false,
|
|
2648
2762
|
recycle: true,
|
|
2649
2763
|
});
|
|
2650
|
-
function bindWithoutPositioning(view, items, index,
|
|
2651
|
-
view.
|
|
2764
|
+
function bindWithoutPositioning(view, items, index, controller) {
|
|
2765
|
+
view.context.parent = controller.source;
|
|
2766
|
+
view.context.parentContext = controller.context;
|
|
2767
|
+
view.bind(items[index]);
|
|
2652
2768
|
}
|
|
2653
|
-
function bindWithPositioning(view, items, index,
|
|
2654
|
-
view.
|
|
2769
|
+
function bindWithPositioning(view, items, index, controller) {
|
|
2770
|
+
view.context.parent = controller.source;
|
|
2771
|
+
view.context.parentContext = controller.context;
|
|
2772
|
+
view.context.length = items.length;
|
|
2773
|
+
view.context.index = index;
|
|
2774
|
+
view.bind(items[index]);
|
|
2655
2775
|
}
|
|
2656
2776
|
/**
|
|
2657
2777
|
* A behavior that renders a template for each item in an array.
|
|
@@ -2661,57 +2781,45 @@ class RepeatBehavior {
|
|
|
2661
2781
|
/**
|
|
2662
2782
|
* Creates an instance of RepeatBehavior.
|
|
2663
2783
|
* @param location - The location in the DOM to render the repeat.
|
|
2664
|
-
* @param
|
|
2784
|
+
* @param dataBinding - The array to render.
|
|
2665
2785
|
* @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
|
|
2666
2786
|
* @param templateBinding - The template to render for each item.
|
|
2667
2787
|
* @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
|
|
2668
2788
|
* @param options - Options used to turn on special repeat features.
|
|
2669
2789
|
*/
|
|
2670
|
-
constructor(
|
|
2671
|
-
this.
|
|
2672
|
-
this.itemsBinding = itemsBinding;
|
|
2673
|
-
this.templateBinding = templateBinding;
|
|
2674
|
-
this.options = options;
|
|
2675
|
-
this.source = null;
|
|
2790
|
+
constructor(directive) {
|
|
2791
|
+
this.directive = directive;
|
|
2676
2792
|
this.views = [];
|
|
2677
2793
|
this.items = null;
|
|
2678
2794
|
this.itemsObserver = null;
|
|
2679
|
-
this.context = void 0;
|
|
2680
|
-
this.childContext = void 0;
|
|
2681
2795
|
this.bindView = bindWithoutPositioning;
|
|
2682
|
-
this.itemsBindingObserver =
|
|
2683
|
-
this.templateBindingObserver =
|
|
2684
|
-
if (options.positioning) {
|
|
2796
|
+
this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
|
|
2797
|
+
this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
|
|
2798
|
+
if (directive.options.positioning) {
|
|
2685
2799
|
this.bindView = bindWithPositioning;
|
|
2686
2800
|
}
|
|
2687
2801
|
}
|
|
2688
2802
|
/**
|
|
2689
|
-
* Bind this behavior
|
|
2690
|
-
* @param
|
|
2691
|
-
* @param context - The execution context that the binding is operating within.
|
|
2803
|
+
* Bind this behavior.
|
|
2804
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2692
2805
|
*/
|
|
2693
|
-
bind(
|
|
2694
|
-
this.
|
|
2695
|
-
this.
|
|
2696
|
-
this.
|
|
2697
|
-
this.
|
|
2698
|
-
this.template = this.templateBindingObserver.observe(source, this.context);
|
|
2806
|
+
bind(controller) {
|
|
2807
|
+
this.location = controller.targets[this.directive.nodeId];
|
|
2808
|
+
this.controller = controller;
|
|
2809
|
+
this.items = this.itemsBindingObserver.bind(controller);
|
|
2810
|
+
this.template = this.templateBindingObserver.bind(controller);
|
|
2699
2811
|
this.observeItems(true);
|
|
2700
2812
|
this.refreshAllViews();
|
|
2813
|
+
controller.onUnbind(this);
|
|
2701
2814
|
}
|
|
2702
2815
|
/**
|
|
2703
|
-
* Unbinds this behavior
|
|
2704
|
-
* @param source - The source to unbind from.
|
|
2816
|
+
* Unbinds this behavior.
|
|
2705
2817
|
*/
|
|
2706
2818
|
unbind() {
|
|
2707
|
-
this.source = null;
|
|
2708
|
-
this.items = null;
|
|
2709
2819
|
if (this.itemsObserver !== null) {
|
|
2710
2820
|
this.itemsObserver.unsubscribe(this);
|
|
2711
2821
|
}
|
|
2712
2822
|
this.unbindAllViews();
|
|
2713
|
-
this.itemsBindingObserver.dispose();
|
|
2714
|
-
this.templateBindingObserver.dispose();
|
|
2715
2823
|
}
|
|
2716
2824
|
/**
|
|
2717
2825
|
* Handles changes in the array, its items, and the repeat template.
|
|
@@ -2719,15 +2827,18 @@ class RepeatBehavior {
|
|
|
2719
2827
|
* @param args - The details about what was changed.
|
|
2720
2828
|
*/
|
|
2721
2829
|
handleChange(source, args) {
|
|
2722
|
-
if (
|
|
2723
|
-
this.items = this.itemsBindingObserver.
|
|
2830
|
+
if (args === this.itemsBindingObserver) {
|
|
2831
|
+
this.items = this.itemsBindingObserver.bind(this.controller);
|
|
2724
2832
|
this.observeItems();
|
|
2725
2833
|
this.refreshAllViews();
|
|
2726
2834
|
}
|
|
2727
|
-
else if (
|
|
2728
|
-
this.template = this.templateBindingObserver.
|
|
2835
|
+
else if (args === this.templateBindingObserver) {
|
|
2836
|
+
this.template = this.templateBindingObserver.bind(this.controller);
|
|
2729
2837
|
this.refreshAllViews(true);
|
|
2730
2838
|
}
|
|
2839
|
+
else if (!args[0]) {
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2731
2842
|
else if (args[0].reset) {
|
|
2732
2843
|
this.refreshAllViews();
|
|
2733
2844
|
}
|
|
@@ -2752,39 +2863,57 @@ class RepeatBehavior {
|
|
|
2752
2863
|
}
|
|
2753
2864
|
updateViews(splices) {
|
|
2754
2865
|
const views = this.views;
|
|
2755
|
-
const childContext = this.childContext;
|
|
2756
|
-
const totalRemoved = [];
|
|
2757
2866
|
const bindView = this.bindView;
|
|
2758
|
-
let removeDelta = 0;
|
|
2759
|
-
for (let i = 0, ii = splices.length; i < ii; ++i) {
|
|
2760
|
-
const splice = splices[i];
|
|
2761
|
-
const removed = splice.removed;
|
|
2762
|
-
totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
|
|
2763
|
-
removeDelta -= splice.addedCount;
|
|
2764
|
-
}
|
|
2765
2867
|
const items = this.items;
|
|
2766
2868
|
const template = this.template;
|
|
2869
|
+
const controller = this.controller;
|
|
2870
|
+
const recycle = this.directive.options.recycle;
|
|
2871
|
+
const leftoverViews = [];
|
|
2872
|
+
let leftoverIndex = 0;
|
|
2873
|
+
let availableViews = 0;
|
|
2767
2874
|
for (let i = 0, ii = splices.length; i < ii; ++i) {
|
|
2768
2875
|
const splice = splices[i];
|
|
2876
|
+
const removed = splice.removed;
|
|
2877
|
+
let removeIndex = 0;
|
|
2769
2878
|
let addIndex = splice.index;
|
|
2770
2879
|
const end = addIndex + splice.addedCount;
|
|
2880
|
+
const removedViews = views.splice(splice.index, removed.length);
|
|
2881
|
+
const totalAvailableViews = (availableViews =
|
|
2882
|
+
leftoverViews.length + removedViews.length);
|
|
2771
2883
|
for (; addIndex < end; ++addIndex) {
|
|
2772
2884
|
const neighbor = views[addIndex];
|
|
2773
2885
|
const location = neighbor ? neighbor.firstChild : this.location;
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2886
|
+
let view;
|
|
2887
|
+
if (recycle && availableViews > 0) {
|
|
2888
|
+
if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
|
|
2889
|
+
view = removedViews[removeIndex];
|
|
2890
|
+
removeIndex++;
|
|
2891
|
+
}
|
|
2892
|
+
else {
|
|
2893
|
+
view = leftoverViews[leftoverIndex];
|
|
2894
|
+
leftoverIndex++;
|
|
2895
|
+
}
|
|
2896
|
+
availableViews--;
|
|
2897
|
+
}
|
|
2898
|
+
else {
|
|
2899
|
+
view = template.create();
|
|
2900
|
+
}
|
|
2777
2901
|
views.splice(addIndex, 0, view);
|
|
2778
|
-
bindView(view, items, addIndex,
|
|
2902
|
+
bindView(view, items, addIndex, controller);
|
|
2779
2903
|
view.insertBefore(location);
|
|
2780
2904
|
}
|
|
2905
|
+
if (removedViews[removeIndex]) {
|
|
2906
|
+
leftoverViews.push(...removedViews.slice(removeIndex));
|
|
2907
|
+
}
|
|
2781
2908
|
}
|
|
2782
|
-
for (let i =
|
|
2783
|
-
|
|
2909
|
+
for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
|
|
2910
|
+
leftoverViews[i].dispose();
|
|
2784
2911
|
}
|
|
2785
|
-
if (this.options.positioning) {
|
|
2912
|
+
if (this.directive.options.positioning) {
|
|
2786
2913
|
for (let i = 0, ii = views.length; i < ii; ++i) {
|
|
2787
|
-
views[i].context
|
|
2914
|
+
const context = views[i].context;
|
|
2915
|
+
context.length = i;
|
|
2916
|
+
context.index = ii;
|
|
2788
2917
|
}
|
|
2789
2918
|
}
|
|
2790
2919
|
}
|
|
@@ -2793,11 +2922,11 @@ class RepeatBehavior {
|
|
|
2793
2922
|
const template = this.template;
|
|
2794
2923
|
const location = this.location;
|
|
2795
2924
|
const bindView = this.bindView;
|
|
2796
|
-
const
|
|
2925
|
+
const controller = this.controller;
|
|
2797
2926
|
let itemsLength = items.length;
|
|
2798
2927
|
let views = this.views;
|
|
2799
2928
|
let viewsLength = views.length;
|
|
2800
|
-
if (itemsLength === 0 || templateChanged) {
|
|
2929
|
+
if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
|
|
2801
2930
|
// all views need to be removed
|
|
2802
2931
|
HTMLView.disposeContiguousBatch(views);
|
|
2803
2932
|
viewsLength = 0;
|
|
@@ -2807,7 +2936,7 @@ class RepeatBehavior {
|
|
|
2807
2936
|
this.views = views = new Array(itemsLength);
|
|
2808
2937
|
for (let i = 0; i < itemsLength; ++i) {
|
|
2809
2938
|
const view = template.create();
|
|
2810
|
-
bindView(view, items, i,
|
|
2939
|
+
bindView(view, items, i, controller);
|
|
2811
2940
|
views[i] = view;
|
|
2812
2941
|
view.insertBefore(location);
|
|
2813
2942
|
}
|
|
@@ -2818,11 +2947,11 @@ class RepeatBehavior {
|
|
|
2818
2947
|
for (; i < itemsLength; ++i) {
|
|
2819
2948
|
if (i < viewsLength) {
|
|
2820
2949
|
const view = views[i];
|
|
2821
|
-
bindView(view, items, i,
|
|
2950
|
+
bindView(view, items, i, controller);
|
|
2822
2951
|
}
|
|
2823
2952
|
else {
|
|
2824
2953
|
const view = template.create();
|
|
2825
|
-
bindView(view, items, i,
|
|
2954
|
+
bindView(view, items, i, controller);
|
|
2826
2955
|
views.push(view);
|
|
2827
2956
|
view.insertBefore(location);
|
|
2828
2957
|
}
|
|
@@ -2847,17 +2976,19 @@ class RepeatBehavior {
|
|
|
2847
2976
|
class RepeatDirective {
|
|
2848
2977
|
/**
|
|
2849
2978
|
* Creates an instance of RepeatDirective.
|
|
2850
|
-
* @param
|
|
2979
|
+
* @param dataBinding - The binding that provides the array to render.
|
|
2851
2980
|
* @param templateBinding - The template binding used to obtain a template to render for each item in the array.
|
|
2852
2981
|
* @param options - Options used to turn on special repeat features.
|
|
2853
2982
|
*/
|
|
2854
|
-
constructor(
|
|
2855
|
-
this.
|
|
2983
|
+
constructor(dataBinding, templateBinding, options) {
|
|
2984
|
+
this.dataBinding = dataBinding;
|
|
2856
2985
|
this.templateBinding = templateBinding;
|
|
2857
2986
|
this.options = options;
|
|
2987
|
+
/**
|
|
2988
|
+
* The unique id of the factory.
|
|
2989
|
+
*/
|
|
2990
|
+
this.id = nextId();
|
|
2858
2991
|
ArrayObserver.enable();
|
|
2859
|
-
this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
|
|
2860
|
-
this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
|
|
2861
2992
|
}
|
|
2862
2993
|
/**
|
|
2863
2994
|
* Creates a placeholder string based on the directive's index within the template.
|
|
@@ -2870,16 +3001,23 @@ class RepeatDirective {
|
|
|
2870
3001
|
* Creates a behavior for the provided target node.
|
|
2871
3002
|
* @param target - The node instance to create the behavior for.
|
|
2872
3003
|
*/
|
|
2873
|
-
createBehavior(
|
|
2874
|
-
return new RepeatBehavior(
|
|
3004
|
+
createBehavior() {
|
|
3005
|
+
return new RepeatBehavior(this);
|
|
2875
3006
|
}
|
|
2876
3007
|
}
|
|
2877
3008
|
HTMLDirective.define(RepeatDirective);
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
3009
|
+
/**
|
|
3010
|
+
* A directive that enables list rendering.
|
|
3011
|
+
* @param items - The array to render.
|
|
3012
|
+
* @param template - The template or a template binding used obtain a template
|
|
3013
|
+
* to render for each item in the array.
|
|
3014
|
+
* @param options - Options used to turn on special repeat features.
|
|
3015
|
+
* @public
|
|
3016
|
+
*/
|
|
3017
|
+
function repeat(items, template, options = defaultRepeatOptions) {
|
|
3018
|
+
const dataBinding = normalizeBinding(items);
|
|
3019
|
+
const templateBinding = normalizeBinding(template);
|
|
3020
|
+
return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
|
|
2883
3021
|
}
|
|
2884
3022
|
|
|
2885
3023
|
const selectElements = (value) => value.nodeType === 1;
|
|
@@ -2908,11 +3046,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
|
2908
3046
|
* @param context - The execution context that the binding is operating within.
|
|
2909
3047
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
2910
3048
|
*/
|
|
2911
|
-
bind(
|
|
2912
|
-
const target = targets[this.nodeId];
|
|
2913
|
-
target[this.sourceProperty] = source;
|
|
2914
|
-
this.updateTarget(source, this.computeNodes(target));
|
|
3049
|
+
bind(controller) {
|
|
3050
|
+
const target = controller.targets[this.nodeId];
|
|
3051
|
+
target[this.sourceProperty] = controller.source;
|
|
3052
|
+
this.updateTarget(controller.source, this.computeNodes(target));
|
|
2915
3053
|
this.observe(target);
|
|
3054
|
+
controller.onUnbind(this);
|
|
2916
3055
|
}
|
|
2917
3056
|
/**
|
|
2918
3057
|
* Unbinds this behavior from the source.
|
|
@@ -2920,9 +3059,9 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
|
2920
3059
|
* @param context - The execution context that the binding is operating within.
|
|
2921
3060
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
2922
3061
|
*/
|
|
2923
|
-
unbind(
|
|
2924
|
-
const target = targets[this.nodeId];
|
|
2925
|
-
this.updateTarget(source, emptyArray);
|
|
3062
|
+
unbind(controller) {
|
|
3063
|
+
const target = controller.targets[this.nodeId];
|
|
3064
|
+
this.updateTarget(controller.source, emptyArray);
|
|
2926
3065
|
this.disconnect(target);
|
|
2927
3066
|
target[this.sourceProperty] = null;
|
|
2928
3067
|
}
|
|
@@ -3071,6 +3210,16 @@ function children(propertyOrOptions) {
|
|
|
3071
3210
|
|
|
3072
3211
|
const booleanMode = "boolean";
|
|
3073
3212
|
const reflectMode = "reflect";
|
|
3213
|
+
/**
|
|
3214
|
+
* Metadata used to configure a custom attribute's behavior.
|
|
3215
|
+
* @public
|
|
3216
|
+
*/
|
|
3217
|
+
const AttributeConfiguration = Object.freeze({
|
|
3218
|
+
/**
|
|
3219
|
+
* Locates all attribute configurations associated with a type.
|
|
3220
|
+
*/
|
|
3221
|
+
locate: createMetadataLocator(),
|
|
3222
|
+
});
|
|
3074
3223
|
/**
|
|
3075
3224
|
* A {@link ValueConverter} that converts to and from `boolean` values.
|
|
3076
3225
|
* @remarks
|
|
@@ -3208,7 +3357,7 @@ class AttributeDefinition {
|
|
|
3208
3357
|
*/
|
|
3209
3358
|
static collect(Owner, ...attributeLists) {
|
|
3210
3359
|
const attributes = [];
|
|
3211
|
-
attributeLists.push(Owner
|
|
3360
|
+
attributeLists.push(AttributeConfiguration.locate(Owner));
|
|
3212
3361
|
for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
|
|
3213
3362
|
const list = attributeLists[i];
|
|
3214
3363
|
if (list === void 0) {
|
|
@@ -3238,9 +3387,7 @@ function attr(configOrTarget, prop) {
|
|
|
3238
3387
|
// - @attr({...opts})
|
|
3239
3388
|
config.property = $prop;
|
|
3240
3389
|
}
|
|
3241
|
-
|
|
3242
|
-
($target.constructor.attributes = []);
|
|
3243
|
-
attributes.push(config);
|
|
3390
|
+
AttributeConfiguration.locate($target.constructor).push(config);
|
|
3244
3391
|
}
|
|
3245
3392
|
if (arguments.length > 1) {
|
|
3246
3393
|
// Non invocation:
|
|
@@ -3258,25 +3405,24 @@ function attr(configOrTarget, prop) {
|
|
|
3258
3405
|
|
|
3259
3406
|
const defaultShadowOptions = { mode: "open" };
|
|
3260
3407
|
const defaultElementOptions = {};
|
|
3408
|
+
const fastElementBaseTypes = new Set();
|
|
3261
3409
|
const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */, () => createTypeRegistry());
|
|
3262
3410
|
/**
|
|
3263
3411
|
* Defines metadata for a FASTElement.
|
|
3264
3412
|
* @public
|
|
3265
3413
|
*/
|
|
3266
3414
|
class FASTElementDefinition {
|
|
3267
|
-
/**
|
|
3268
|
-
* Creates an instance of FASTElementDefinition.
|
|
3269
|
-
* @param type - The type this definition is being created for.
|
|
3270
|
-
* @param nameOrConfig - The name of the element to define or a config object
|
|
3271
|
-
* that describes the element to define.
|
|
3272
|
-
*/
|
|
3273
3415
|
constructor(type, nameOrConfig = type.definition) {
|
|
3416
|
+
var _a;
|
|
3417
|
+
this.platformDefined = false;
|
|
3274
3418
|
if (isString(nameOrConfig)) {
|
|
3275
3419
|
nameOrConfig = { name: nameOrConfig };
|
|
3276
3420
|
}
|
|
3277
3421
|
this.type = type;
|
|
3278
3422
|
this.name = nameOrConfig.name;
|
|
3279
3423
|
this.template = nameOrConfig.template;
|
|
3424
|
+
this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
|
|
3425
|
+
const proto = type.prototype;
|
|
3280
3426
|
const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
|
|
3281
3427
|
const observedAttributes = new Array(attributes.length);
|
|
3282
3428
|
const propertyLookup = {};
|
|
@@ -3286,9 +3432,13 @@ class FASTElementDefinition {
|
|
|
3286
3432
|
observedAttributes[i] = current.attribute;
|
|
3287
3433
|
propertyLookup[current.name] = current;
|
|
3288
3434
|
attributeLookup[current.attribute] = current;
|
|
3435
|
+
Observable.defineProperty(proto, current);
|
|
3289
3436
|
}
|
|
3437
|
+
Reflect.defineProperty(type, "observedAttributes", {
|
|
3438
|
+
value: observedAttributes,
|
|
3439
|
+
enumerable: true,
|
|
3440
|
+
});
|
|
3290
3441
|
this.attributes = attributes;
|
|
3291
|
-
this.observedAttributes = observedAttributes;
|
|
3292
3442
|
this.propertyLookup = propertyLookup;
|
|
3293
3443
|
this.attributeLookup = attributeLookup;
|
|
3294
3444
|
this.shadowOptions =
|
|
@@ -3301,43 +3451,50 @@ class FASTElementDefinition {
|
|
|
3301
3451
|
nameOrConfig.elementOptions === void 0
|
|
3302
3452
|
? defaultElementOptions
|
|
3303
3453
|
: Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
|
|
3304
|
-
this.styles =
|
|
3305
|
-
|
|
3306
|
-
? void 0
|
|
3307
|
-
: Array.isArray(nameOrConfig.styles)
|
|
3308
|
-
? new ElementStyles(nameOrConfig.styles)
|
|
3309
|
-
: nameOrConfig.styles instanceof ElementStyles
|
|
3310
|
-
? nameOrConfig.styles
|
|
3311
|
-
: new ElementStyles([nameOrConfig.styles]);
|
|
3454
|
+
this.styles = ElementStyles.normalize(nameOrConfig.styles);
|
|
3455
|
+
fastElementRegistry.register(this);
|
|
3312
3456
|
}
|
|
3313
3457
|
/**
|
|
3314
3458
|
* Indicates if this element has been defined in at least one registry.
|
|
3315
3459
|
*/
|
|
3316
3460
|
get isDefined() {
|
|
3317
|
-
return
|
|
3461
|
+
return this.platformDefined;
|
|
3318
3462
|
}
|
|
3319
3463
|
/**
|
|
3320
3464
|
* Defines a custom element based on this definition.
|
|
3321
3465
|
* @param registry - The element registry to define the element in.
|
|
3466
|
+
* @remarks
|
|
3467
|
+
* This operation is idempotent per registry.
|
|
3322
3468
|
*/
|
|
3323
|
-
define(registry =
|
|
3469
|
+
define(registry = this.registry) {
|
|
3324
3470
|
const type = this.type;
|
|
3325
|
-
if (fastElementRegistry.register(this)) {
|
|
3326
|
-
const attributes = this.attributes;
|
|
3327
|
-
const proto = type.prototype;
|
|
3328
|
-
for (let i = 0, ii = attributes.length; i < ii; ++i) {
|
|
3329
|
-
Observable.defineProperty(proto, attributes[i]);
|
|
3330
|
-
}
|
|
3331
|
-
Reflect.defineProperty(type, "observedAttributes", {
|
|
3332
|
-
value: this.observedAttributes,
|
|
3333
|
-
enumerable: true,
|
|
3334
|
-
});
|
|
3335
|
-
}
|
|
3336
3471
|
if (!registry.get(this.name)) {
|
|
3472
|
+
this.platformDefined = true;
|
|
3337
3473
|
registry.define(this.name, type, this.elementOptions);
|
|
3338
3474
|
}
|
|
3339
3475
|
return this;
|
|
3340
3476
|
}
|
|
3477
|
+
/**
|
|
3478
|
+
* Creates an instance of FASTElementDefinition.
|
|
3479
|
+
* @param type - The type this definition is being created for.
|
|
3480
|
+
* @param nameOrDef - The name of the element to define or a config object
|
|
3481
|
+
* that describes the element to define.
|
|
3482
|
+
*/
|
|
3483
|
+
static compose(type, nameOrDef) {
|
|
3484
|
+
if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
|
|
3485
|
+
return new FASTElementDefinition(class extends type {
|
|
3486
|
+
}, nameOrDef);
|
|
3487
|
+
}
|
|
3488
|
+
return new FASTElementDefinition(type, nameOrDef);
|
|
3489
|
+
}
|
|
3490
|
+
/**
|
|
3491
|
+
* Registers a FASTElement base type.
|
|
3492
|
+
* @param type - The type to register as a base type.
|
|
3493
|
+
* @internal
|
|
3494
|
+
*/
|
|
3495
|
+
static registerBaseType(type) {
|
|
3496
|
+
fastElementBaseTypes.add(type);
|
|
3497
|
+
}
|
|
3341
3498
|
}
|
|
3342
3499
|
/**
|
|
3343
3500
|
* Gets the element definition associated with the specified type.
|
|
@@ -3350,22 +3507,22 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
|
|
|
3350
3507
|
*/
|
|
3351
3508
|
FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
|
|
3352
3509
|
|
|
3353
|
-
const shadowRoots = new WeakMap();
|
|
3354
3510
|
const defaultEventOptions = {
|
|
3355
3511
|
bubbles: true,
|
|
3356
3512
|
composed: true,
|
|
3357
3513
|
cancelable: true,
|
|
3358
3514
|
};
|
|
3515
|
+
const isConnectedPropertyName = "isConnected";
|
|
3516
|
+
const shadowRoots = new WeakMap();
|
|
3359
3517
|
function getShadowRoot(element) {
|
|
3360
3518
|
var _a, _b;
|
|
3361
3519
|
return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
|
|
3362
3520
|
}
|
|
3363
|
-
const isConnectedPropertyName = "isConnected";
|
|
3364
3521
|
/**
|
|
3365
3522
|
* Controls the lifecycle and rendering of a `FASTElement`.
|
|
3366
3523
|
* @public
|
|
3367
3524
|
*/
|
|
3368
|
-
class
|
|
3525
|
+
class ElementController extends PropertyChangeNotifier {
|
|
3369
3526
|
/**
|
|
3370
3527
|
* Creates a Controller to control the specified element.
|
|
3371
3528
|
* @param element - The element to be controlled by this controller.
|
|
@@ -3376,12 +3533,12 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3376
3533
|
constructor(element, definition) {
|
|
3377
3534
|
super(element);
|
|
3378
3535
|
this.boundObservables = null;
|
|
3379
|
-
this.behaviors = null;
|
|
3380
3536
|
this.needsInitialization = true;
|
|
3381
3537
|
this.hasExistingShadowRoot = false;
|
|
3382
3538
|
this._template = null;
|
|
3383
|
-
this._styles = null;
|
|
3384
3539
|
this._isConnected = false;
|
|
3540
|
+
this.behaviors = null;
|
|
3541
|
+
this._mainStyles = null;
|
|
3385
3542
|
/**
|
|
3386
3543
|
* This allows Observable.getNotifier(...) to return the Controller
|
|
3387
3544
|
* when the notifier for the Controller itself is being requested. The
|
|
@@ -3397,7 +3554,7 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3397
3554
|
* If `null` then the element is managing its own rendering.
|
|
3398
3555
|
*/
|
|
3399
3556
|
this.view = null;
|
|
3400
|
-
this.
|
|
3557
|
+
this.source = element;
|
|
3401
3558
|
this.definition = definition;
|
|
3402
3559
|
const shadowOptions = definition.shadowOptions;
|
|
3403
3560
|
if (shadowOptions !== void 0) {
|
|
@@ -3451,9 +3608,9 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3451
3608
|
// 1. Template overrides take top precedence.
|
|
3452
3609
|
if (this._template === null) {
|
|
3453
3610
|
const definition = this.definition;
|
|
3454
|
-
if (this.
|
|
3611
|
+
if (this.source.resolveTemplate) {
|
|
3455
3612
|
// 2. Allow for element instance overrides next.
|
|
3456
|
-
this._template = this.
|
|
3613
|
+
this._template = this.source.resolveTemplate();
|
|
3457
3614
|
}
|
|
3458
3615
|
else if (definition.template) {
|
|
3459
3616
|
// 3. Default to the static definition.
|
|
@@ -3472,56 +3629,102 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3472
3629
|
}
|
|
3473
3630
|
}
|
|
3474
3631
|
/**
|
|
3475
|
-
*
|
|
3476
|
-
*
|
|
3477
|
-
* This value can only be accurately read after connect but can be set at any time.
|
|
3632
|
+
* The main set of styles used for the component, independent
|
|
3633
|
+
* of any dynamically added styles.
|
|
3478
3634
|
*/
|
|
3479
|
-
get
|
|
3635
|
+
get mainStyles() {
|
|
3480
3636
|
var _a;
|
|
3481
3637
|
// 1. Styles overrides take top precedence.
|
|
3482
|
-
if (this.
|
|
3638
|
+
if (this._mainStyles === null) {
|
|
3483
3639
|
const definition = this.definition;
|
|
3484
|
-
if (this.
|
|
3640
|
+
if (this.source.resolveStyles) {
|
|
3485
3641
|
// 2. Allow for element instance overrides next.
|
|
3486
|
-
this.
|
|
3642
|
+
this._mainStyles = this.source.resolveStyles();
|
|
3487
3643
|
}
|
|
3488
3644
|
else if (definition.styles) {
|
|
3489
3645
|
// 3. Default to the static definition.
|
|
3490
|
-
this.
|
|
3646
|
+
this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
|
|
3491
3647
|
}
|
|
3492
3648
|
}
|
|
3493
|
-
return this.
|
|
3649
|
+
return this._mainStyles;
|
|
3494
3650
|
}
|
|
3495
|
-
set
|
|
3496
|
-
if (this.
|
|
3651
|
+
set mainStyles(value) {
|
|
3652
|
+
if (this._mainStyles === value) {
|
|
3497
3653
|
return;
|
|
3498
3654
|
}
|
|
3499
|
-
if (this.
|
|
3500
|
-
this.removeStyles(this.
|
|
3655
|
+
if (this._mainStyles !== null) {
|
|
3656
|
+
this.removeStyles(this._mainStyles);
|
|
3501
3657
|
}
|
|
3502
|
-
this.
|
|
3658
|
+
this._mainStyles = value;
|
|
3503
3659
|
if (!this.needsInitialization) {
|
|
3504
3660
|
this.addStyles(value);
|
|
3505
3661
|
}
|
|
3506
3662
|
}
|
|
3663
|
+
/**
|
|
3664
|
+
* Adds the behavior to the component.
|
|
3665
|
+
* @param behavior - The behavior to add.
|
|
3666
|
+
*/
|
|
3667
|
+
addBehavior(behavior) {
|
|
3668
|
+
var _a, _b;
|
|
3669
|
+
const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
|
|
3670
|
+
const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
|
|
3671
|
+
if (count === 0) {
|
|
3672
|
+
targetBehaviors.set(behavior, 1);
|
|
3673
|
+
behavior.addedCallback && behavior.addedCallback(this);
|
|
3674
|
+
if (behavior.connectedCallback && this.isConnected) {
|
|
3675
|
+
behavior.connectedCallback(this);
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
else {
|
|
3679
|
+
targetBehaviors.set(behavior, count + 1);
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
/**
|
|
3683
|
+
* Removes the behavior from the component.
|
|
3684
|
+
* @param behavior - The behavior to remove.
|
|
3685
|
+
* @param force - Forces removal even if this behavior was added more than once.
|
|
3686
|
+
*/
|
|
3687
|
+
removeBehavior(behavior, force = false) {
|
|
3688
|
+
const targetBehaviors = this.behaviors;
|
|
3689
|
+
if (targetBehaviors === null) {
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
const count = targetBehaviors.get(behavior);
|
|
3693
|
+
if (count === void 0) {
|
|
3694
|
+
return;
|
|
3695
|
+
}
|
|
3696
|
+
if (count === 1 || force) {
|
|
3697
|
+
targetBehaviors.delete(behavior);
|
|
3698
|
+
if (behavior.disconnectedCallback && this.isConnected) {
|
|
3699
|
+
behavior.disconnectedCallback(this);
|
|
3700
|
+
}
|
|
3701
|
+
behavior.removedCallback && behavior.removedCallback(this);
|
|
3702
|
+
}
|
|
3703
|
+
else {
|
|
3704
|
+
targetBehaviors.set(behavior, count - 1);
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3507
3707
|
/**
|
|
3508
3708
|
* Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
|
|
3509
3709
|
* @param styles - The styles to add.
|
|
3510
3710
|
*/
|
|
3511
3711
|
addStyles(styles) {
|
|
3712
|
+
var _a;
|
|
3512
3713
|
if (!styles) {
|
|
3513
3714
|
return;
|
|
3514
3715
|
}
|
|
3515
|
-
const
|
|
3516
|
-
this.element.getRootNode();
|
|
3716
|
+
const source = this.source;
|
|
3517
3717
|
if (styles instanceof HTMLElement) {
|
|
3718
|
+
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : this.source;
|
|
3518
3719
|
target.append(styles);
|
|
3519
3720
|
}
|
|
3520
|
-
else if (!styles.isAttachedTo(
|
|
3721
|
+
else if (!styles.isAttachedTo(source)) {
|
|
3521
3722
|
const sourceBehaviors = styles.behaviors;
|
|
3522
|
-
styles.addStylesTo(
|
|
3723
|
+
styles.addStylesTo(source);
|
|
3523
3724
|
if (sourceBehaviors !== null) {
|
|
3524
|
-
|
|
3725
|
+
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
3726
|
+
this.addBehavior(sourceBehaviors[i]);
|
|
3727
|
+
}
|
|
3525
3728
|
}
|
|
3526
3729
|
}
|
|
3527
3730
|
}
|
|
@@ -3530,97 +3733,42 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3530
3733
|
* @param styles - the styles to remove.
|
|
3531
3734
|
*/
|
|
3532
3735
|
removeStyles(styles) {
|
|
3736
|
+
var _a;
|
|
3533
3737
|
if (!styles) {
|
|
3534
3738
|
return;
|
|
3535
3739
|
}
|
|
3536
|
-
const
|
|
3537
|
-
this.element.getRootNode();
|
|
3740
|
+
const source = this.source;
|
|
3538
3741
|
if (styles instanceof HTMLElement) {
|
|
3742
|
+
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source;
|
|
3539
3743
|
target.removeChild(styles);
|
|
3540
3744
|
}
|
|
3541
|
-
else if (styles.isAttachedTo(
|
|
3745
|
+
else if (styles.isAttachedTo(source)) {
|
|
3542
3746
|
const sourceBehaviors = styles.behaviors;
|
|
3543
|
-
styles.removeStylesFrom(
|
|
3747
|
+
styles.removeStylesFrom(source);
|
|
3544
3748
|
if (sourceBehaviors !== null) {
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
}
|
|
3549
|
-
/**
|
|
3550
|
-
* Adds behaviors to this element.
|
|
3551
|
-
* @param behaviors - The behaviors to add.
|
|
3552
|
-
*/
|
|
3553
|
-
addBehaviors(behaviors) {
|
|
3554
|
-
var _a;
|
|
3555
|
-
const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
|
|
3556
|
-
const length = behaviors.length;
|
|
3557
|
-
const behaviorsToBind = [];
|
|
3558
|
-
for (let i = 0; i < length; ++i) {
|
|
3559
|
-
const behavior = behaviors[i];
|
|
3560
|
-
if (targetBehaviors.has(behavior)) {
|
|
3561
|
-
targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
|
|
3562
|
-
}
|
|
3563
|
-
else {
|
|
3564
|
-
targetBehaviors.set(behavior, 1);
|
|
3565
|
-
behaviorsToBind.push(behavior);
|
|
3566
|
-
}
|
|
3567
|
-
}
|
|
3568
|
-
if (this._isConnected) {
|
|
3569
|
-
const element = this.element;
|
|
3570
|
-
const context = ExecutionContext.default;
|
|
3571
|
-
for (let i = 0; i < behaviorsToBind.length; ++i) {
|
|
3572
|
-
behaviorsToBind[i].bind(element, context);
|
|
3573
|
-
}
|
|
3574
|
-
}
|
|
3575
|
-
}
|
|
3576
|
-
/**
|
|
3577
|
-
* Removes behaviors from this element.
|
|
3578
|
-
* @param behaviors - The behaviors to remove.
|
|
3579
|
-
* @param force - Forces unbinding of behaviors.
|
|
3580
|
-
*/
|
|
3581
|
-
removeBehaviors(behaviors, force = false) {
|
|
3582
|
-
const targetBehaviors = this.behaviors;
|
|
3583
|
-
if (targetBehaviors === null) {
|
|
3584
|
-
return;
|
|
3585
|
-
}
|
|
3586
|
-
const length = behaviors.length;
|
|
3587
|
-
const behaviorsToUnbind = [];
|
|
3588
|
-
for (let i = 0; i < length; ++i) {
|
|
3589
|
-
const behavior = behaviors[i];
|
|
3590
|
-
if (targetBehaviors.has(behavior)) {
|
|
3591
|
-
const count = targetBehaviors.get(behavior) - 1;
|
|
3592
|
-
count === 0 || force
|
|
3593
|
-
? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
|
|
3594
|
-
: targetBehaviors.set(behavior, count);
|
|
3595
|
-
}
|
|
3596
|
-
}
|
|
3597
|
-
if (this._isConnected) {
|
|
3598
|
-
const element = this.element;
|
|
3599
|
-
const context = ExecutionContext.default;
|
|
3600
|
-
for (let i = 0; i < behaviorsToUnbind.length; ++i) {
|
|
3601
|
-
behaviorsToUnbind[i].unbind(element, context);
|
|
3749
|
+
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
3750
|
+
this.addBehavior(sourceBehaviors[i]);
|
|
3751
|
+
}
|
|
3602
3752
|
}
|
|
3603
3753
|
}
|
|
3604
3754
|
}
|
|
3605
3755
|
/**
|
|
3606
3756
|
* Runs connected lifecycle behavior on the associated element.
|
|
3607
3757
|
*/
|
|
3608
|
-
|
|
3758
|
+
connect() {
|
|
3609
3759
|
if (this._isConnected) {
|
|
3610
3760
|
return;
|
|
3611
3761
|
}
|
|
3612
|
-
const element = this.element;
|
|
3613
|
-
const context = ExecutionContext.default;
|
|
3614
3762
|
if (this.needsInitialization) {
|
|
3615
3763
|
this.finishInitialization();
|
|
3616
3764
|
}
|
|
3617
3765
|
else if (this.view !== null) {
|
|
3618
|
-
this.view.bind(
|
|
3766
|
+
this.view.bind(this.source);
|
|
3619
3767
|
}
|
|
3620
3768
|
const behaviors = this.behaviors;
|
|
3621
3769
|
if (behaviors !== null) {
|
|
3622
|
-
for (const
|
|
3623
|
-
|
|
3770
|
+
for (const key of behaviors.keys()) {
|
|
3771
|
+
key.connectedCallback && key.connectedCallback(this);
|
|
3624
3772
|
}
|
|
3625
3773
|
}
|
|
3626
3774
|
this.setIsConnected(true);
|
|
@@ -3628,21 +3776,18 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3628
3776
|
/**
|
|
3629
3777
|
* Runs disconnected lifecycle behavior on the associated element.
|
|
3630
3778
|
*/
|
|
3631
|
-
|
|
3779
|
+
disconnect() {
|
|
3632
3780
|
if (!this._isConnected) {
|
|
3633
3781
|
return;
|
|
3634
3782
|
}
|
|
3635
3783
|
this.setIsConnected(false);
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
view.unbind();
|
|
3784
|
+
if (this.view !== null) {
|
|
3785
|
+
this.view.unbind();
|
|
3639
3786
|
}
|
|
3640
3787
|
const behaviors = this.behaviors;
|
|
3641
3788
|
if (behaviors !== null) {
|
|
3642
|
-
const
|
|
3643
|
-
|
|
3644
|
-
for (const behavior of behaviors.keys()) {
|
|
3645
|
-
behavior.unbind(element, context);
|
|
3789
|
+
for (const key of behaviors.keys()) {
|
|
3790
|
+
key.disconnectedCallback && key.disconnectedCallback(this);
|
|
3646
3791
|
}
|
|
3647
3792
|
}
|
|
3648
3793
|
}
|
|
@@ -3655,7 +3800,7 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3655
3800
|
onAttributeChangedCallback(name, oldValue, newValue) {
|
|
3656
3801
|
const attrDef = this.definition.attributeLookup[name];
|
|
3657
3802
|
if (attrDef !== void 0) {
|
|
3658
|
-
attrDef.onAttributeChangedCallback(this.
|
|
3803
|
+
attrDef.onAttributeChangedCallback(this.source, newValue);
|
|
3659
3804
|
}
|
|
3660
3805
|
}
|
|
3661
3806
|
/**
|
|
@@ -3668,12 +3813,12 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3668
3813
|
*/
|
|
3669
3814
|
emit(type, detail, options) {
|
|
3670
3815
|
if (this._isConnected) {
|
|
3671
|
-
return this.
|
|
3816
|
+
return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
|
|
3672
3817
|
}
|
|
3673
3818
|
return false;
|
|
3674
3819
|
}
|
|
3675
3820
|
finishInitialization() {
|
|
3676
|
-
const element = this.
|
|
3821
|
+
const element = this.source;
|
|
3677
3822
|
const boundObservables = this.boundObservables;
|
|
3678
3823
|
// If we have any observables that were bound, re-apply their values.
|
|
3679
3824
|
if (boundObservables !== null) {
|
|
@@ -3685,15 +3830,15 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3685
3830
|
this.boundObservables = null;
|
|
3686
3831
|
}
|
|
3687
3832
|
this.renderTemplate(this.template);
|
|
3688
|
-
this.addStyles(this.
|
|
3833
|
+
this.addStyles(this.mainStyles);
|
|
3689
3834
|
this.needsInitialization = false;
|
|
3690
3835
|
}
|
|
3691
3836
|
renderTemplate(template) {
|
|
3692
3837
|
var _a;
|
|
3693
|
-
const element = this.element;
|
|
3694
3838
|
// When getting the host to render to, we start by looking
|
|
3695
3839
|
// up the shadow root. If there isn't one, then that means
|
|
3696
3840
|
// we're doing a Light DOM render to the element's direct children.
|
|
3841
|
+
const element = this.source;
|
|
3697
3842
|
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
3698
3843
|
if (this.view !== null) {
|
|
3699
3844
|
// If there's already a view, we need to unbind and remove through dispose.
|
|
@@ -3710,6 +3855,8 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3710
3855
|
if (template) {
|
|
3711
3856
|
// If a new template was provided, render it.
|
|
3712
3857
|
this.view = template.render(element, host, element);
|
|
3858
|
+
this.view.sourceLifetime =
|
|
3859
|
+
SourceLifetime.coupled;
|
|
3713
3860
|
}
|
|
3714
3861
|
}
|
|
3715
3862
|
/**
|
|
@@ -3729,31 +3876,136 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3729
3876
|
if (definition === void 0) {
|
|
3730
3877
|
throw FAST.error(1401 /* Message.missingElementDefinition */);
|
|
3731
3878
|
}
|
|
3732
|
-
return (element.$fastController = new
|
|
3879
|
+
return (element.$fastController = new ElementController(element, definition));
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
/**
|
|
3883
|
+
* Converts a styleTarget into the operative target. When the provided target is an Element
|
|
3884
|
+
* that is a FASTElement, the function will return the ShadowRoot for that element. Otherwise,
|
|
3885
|
+
* it will return the root node for the element.
|
|
3886
|
+
* @param target
|
|
3887
|
+
* @returns
|
|
3888
|
+
*/
|
|
3889
|
+
function normalizeStyleTarget(target) {
|
|
3890
|
+
var _a;
|
|
3891
|
+
if ("adoptedStyleSheets" in target) {
|
|
3892
|
+
return target;
|
|
3893
|
+
}
|
|
3894
|
+
else {
|
|
3895
|
+
return ((_a = getShadowRoot(target)) !== null && _a !== void 0 ? _a : target.getRootNode());
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
// Default StyleStrategy implementations are defined in this module because they
|
|
3899
|
+
// require access to element shadowRoots, and we don't want to leak shadowRoot
|
|
3900
|
+
// objects out of this module.
|
|
3901
|
+
/**
|
|
3902
|
+
* https://wicg.github.io/construct-stylesheets/
|
|
3903
|
+
* https://developers.google.com/web/updates/2019/02/constructable-stylesheets
|
|
3904
|
+
*
|
|
3905
|
+
* @internal
|
|
3906
|
+
*/
|
|
3907
|
+
class AdoptedStyleSheetsStrategy {
|
|
3908
|
+
constructor(styles) {
|
|
3909
|
+
const styleSheetCache = AdoptedStyleSheetsStrategy.styleSheetCache;
|
|
3910
|
+
this.sheets = styles.map((x) => {
|
|
3911
|
+
if (x instanceof CSSStyleSheet) {
|
|
3912
|
+
return x;
|
|
3913
|
+
}
|
|
3914
|
+
let sheet = styleSheetCache.get(x);
|
|
3915
|
+
if (sheet === void 0) {
|
|
3916
|
+
sheet = new CSSStyleSheet();
|
|
3917
|
+
sheet.replaceSync(x);
|
|
3918
|
+
styleSheetCache.set(x, sheet);
|
|
3919
|
+
}
|
|
3920
|
+
return sheet;
|
|
3921
|
+
});
|
|
3922
|
+
}
|
|
3923
|
+
addStylesTo(target) {
|
|
3924
|
+
const t = normalizeStyleTarget(target);
|
|
3925
|
+
t.adoptedStyleSheets = [...t.adoptedStyleSheets, ...this.sheets];
|
|
3926
|
+
}
|
|
3927
|
+
removeStylesFrom(target) {
|
|
3928
|
+
const t = normalizeStyleTarget(target);
|
|
3929
|
+
const sheets = this.sheets;
|
|
3930
|
+
t.adoptedStyleSheets = t.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
AdoptedStyleSheetsStrategy.styleSheetCache = new Map();
|
|
3934
|
+
let id = 0;
|
|
3935
|
+
const nextStyleId = () => `fast-${++id}`;
|
|
3936
|
+
function usableStyleTarget(target) {
|
|
3937
|
+
return target === document ? document.body : target;
|
|
3938
|
+
}
|
|
3939
|
+
/**
|
|
3940
|
+
* @internal
|
|
3941
|
+
*/
|
|
3942
|
+
class StyleElementStrategy {
|
|
3943
|
+
constructor(styles) {
|
|
3944
|
+
this.styles = styles;
|
|
3945
|
+
this.styleClass = nextStyleId();
|
|
3946
|
+
}
|
|
3947
|
+
addStylesTo(target) {
|
|
3948
|
+
target = usableStyleTarget(normalizeStyleTarget(target));
|
|
3949
|
+
const styles = this.styles;
|
|
3950
|
+
const styleClass = this.styleClass;
|
|
3951
|
+
for (let i = 0; i < styles.length; i++) {
|
|
3952
|
+
const element = document.createElement("style");
|
|
3953
|
+
element.innerHTML = styles[i];
|
|
3954
|
+
element.className = styleClass;
|
|
3955
|
+
target.append(element);
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
removeStylesFrom(target) {
|
|
3959
|
+
target = usableStyleTarget(normalizeStyleTarget(target));
|
|
3960
|
+
const styles = target.querySelectorAll(`.${this.styleClass}`);
|
|
3961
|
+
styles[0].parentNode;
|
|
3962
|
+
for (let i = 0, ii = styles.length; i < ii; ++i) {
|
|
3963
|
+
target.removeChild(styles[i]);
|
|
3964
|
+
}
|
|
3733
3965
|
}
|
|
3734
3966
|
}
|
|
3967
|
+
ElementStyles.setDefaultStrategy(ElementStyles.supportsAdoptedStyleSheets
|
|
3968
|
+
? AdoptedStyleSheetsStrategy
|
|
3969
|
+
: StyleElementStrategy);
|
|
3735
3970
|
|
|
3736
3971
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
3737
3972
|
function createFASTElement(BaseType) {
|
|
3738
|
-
|
|
3973
|
+
const type = class extends BaseType {
|
|
3739
3974
|
constructor() {
|
|
3740
3975
|
/* eslint-disable-next-line */
|
|
3741
3976
|
super();
|
|
3742
|
-
|
|
3977
|
+
ElementController.forCustomElement(this);
|
|
3743
3978
|
}
|
|
3744
3979
|
$emit(type, detail, options) {
|
|
3745
3980
|
return this.$fastController.emit(type, detail, options);
|
|
3746
3981
|
}
|
|
3747
3982
|
connectedCallback() {
|
|
3748
|
-
this.$fastController.
|
|
3983
|
+
this.$fastController.connect();
|
|
3749
3984
|
}
|
|
3750
3985
|
disconnectedCallback() {
|
|
3751
|
-
this.$fastController.
|
|
3986
|
+
this.$fastController.disconnect();
|
|
3752
3987
|
}
|
|
3753
3988
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
3754
3989
|
this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
|
|
3755
3990
|
}
|
|
3756
3991
|
};
|
|
3992
|
+
FASTElementDefinition.registerBaseType(type);
|
|
3993
|
+
return type;
|
|
3994
|
+
}
|
|
3995
|
+
function compose(type, nameOrDef) {
|
|
3996
|
+
if (isFunction(type)) {
|
|
3997
|
+
return FASTElementDefinition.compose(type, nameOrDef);
|
|
3998
|
+
}
|
|
3999
|
+
return FASTElementDefinition.compose(this, type);
|
|
4000
|
+
}
|
|
4001
|
+
function define(type, nameOrDef) {
|
|
4002
|
+
if (isFunction(type)) {
|
|
4003
|
+
return FASTElementDefinition.compose(type, nameOrDef).define().type;
|
|
4004
|
+
}
|
|
4005
|
+
return FASTElementDefinition.compose(this, type).define().type;
|
|
4006
|
+
}
|
|
4007
|
+
function from(BaseType) {
|
|
4008
|
+
return createFASTElement(BaseType);
|
|
3757
4009
|
}
|
|
3758
4010
|
/**
|
|
3759
4011
|
* A minimal base class for FASTElements that also provides
|
|
@@ -3766,18 +4018,19 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
|
|
|
3766
4018
|
* provided base type.
|
|
3767
4019
|
* @param BaseType - The base element type to inherit from.
|
|
3768
4020
|
*/
|
|
3769
|
-
from
|
|
3770
|
-
return createFASTElement(BaseType);
|
|
3771
|
-
},
|
|
4021
|
+
from,
|
|
3772
4022
|
/**
|
|
3773
4023
|
* Defines a platform custom element based on the provided type and definition.
|
|
3774
4024
|
* @param type - The custom element type to define.
|
|
3775
4025
|
* @param nameOrDef - The name of the element to define or a definition object
|
|
3776
4026
|
* that describes the element to define.
|
|
3777
4027
|
*/
|
|
3778
|
-
define
|
|
3779
|
-
|
|
3780
|
-
|
|
4028
|
+
define,
|
|
4029
|
+
/**
|
|
4030
|
+
* Defines metadata for a FASTElement which can be used to later define the element.
|
|
4031
|
+
* @public
|
|
4032
|
+
*/
|
|
4033
|
+
compose,
|
|
3781
4034
|
});
|
|
3782
4035
|
/**
|
|
3783
4036
|
* Decorator: Defines a platform custom element based on `FASTElement`.
|
|
@@ -3788,8 +4041,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
|
|
|
3788
4041
|
function customElement(nameOrDef) {
|
|
3789
4042
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
3790
4043
|
return function (type) {
|
|
3791
|
-
|
|
4044
|
+
define(type, nameOrDef);
|
|
3792
4045
|
};
|
|
3793
4046
|
}
|
|
3794
4047
|
|
|
3795
|
-
export {
|
|
4048
|
+
export { ArrayObserver, Aspect, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewBehaviorOrchestrator, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };
|