@microsoft/fast-element 2.0.0-beta.1 → 2.0.0-beta.10
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 +333 -0
- package/CHANGELOG.md +106 -1
- package/dist/dts/components/attributes.d.ts +10 -0
- package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +24 -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 +44 -12
- 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 +6 -0
- 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 +9 -3
- 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} +95 -105
- 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 +25 -1
- 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 -0
- 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 +9034 -10524
- package/dist/fast-element.d.ts +707 -811
- package/dist/fast-element.debug.js +1133 -850
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +1097 -846
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +724 -818
- package/docs/api-report.md +264 -305
- package/package.json +39 -10
- 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
|
@@ -97,19 +97,51 @@ const debugMessages = {
|
|
|
97
97
|
[1101 /* needsArrayObservation */]: "Must call enableArrayObservation before observing arrays.",
|
|
98
98
|
[1201 /* onlySetHTMLPolicyOnce */]: "The HTML policy can only be set once.",
|
|
99
99
|
[1202 /* bindingInnerHTMLRequiresTrustedTypes */]: "To bind innerHTML, you must use a TrustedTypesPolicy.",
|
|
100
|
+
[1203 /* twoWayBindingRequiresObservables */]: "View=>Model update skipped. To use twoWay binding, the target property must be observable.",
|
|
101
|
+
[1204 /* hostBindingWithoutHost */]: "No host element is present. Cannot bind host with ${name}.",
|
|
102
|
+
[1205 /* unsupportedBindingBehavior */]: "The requested binding behavior is not supported by the binding engine.",
|
|
100
103
|
[1401 /* missingElementDefinition */]: "Missing FASTElement definition.",
|
|
104
|
+
[1501 /* noRegistrationForContext */]: "No registration for Context/Interface '${name}'.",
|
|
105
|
+
[1502 /* noFactoryForResolver */]: "Dependency injection resolver for '${key}' returned a null factory.",
|
|
106
|
+
[1503 /* invalidResolverStrategy */]: "Invalid dependency injection resolver strategy specified '${strategy}'.",
|
|
107
|
+
[1504 /* cannotAutoregisterDependency */]: "Unable to autoregister dependency.",
|
|
108
|
+
[1505 /* cannotResolveKey */]: "Unable to resolve dependency injection key '${key}'.",
|
|
109
|
+
[1506 /* cannotConstructNativeFunction */]: "'${name}' is a native function and therefore cannot be safely constructed by DI. If this is intentional, please use a callback or cachedCallback resolver.",
|
|
110
|
+
[1507 /* cannotJITRegisterNonConstructor */]: "Attempted to jitRegister something that is not a constructor '${value}'. Did you forget to register this dependency?",
|
|
111
|
+
[1508 /* cannotJITRegisterIntrinsic */]: "Attempted to jitRegister an intrinsic type '${value}'. Did you forget to add @inject(Key)?",
|
|
112
|
+
[1509 /* cannotJITRegisterInterface */]: "Attempted to jitRegister an interface '${value}'.",
|
|
113
|
+
[1510 /* invalidResolver */]: "A valid resolver was not returned from the register method.",
|
|
114
|
+
[1511 /* invalidKey */]: "Key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?",
|
|
115
|
+
[1512 /* noDefaultResolver */]: "'${key}' not registered. Did you forget to add @singleton()?",
|
|
116
|
+
[1513 /* cyclicDependency */]: "Cyclic dependency found '${name}'.",
|
|
117
|
+
[1514 /* connectUpdateRequiresController */]: "Injected properties that are updated on changes to DOM connectivity require the target object to be an instance of FASTElement.",
|
|
101
118
|
};
|
|
119
|
+
const allPlaceholders = /(\$\{\w+?})/g;
|
|
120
|
+
const placeholder = /\$\{(\w+?)}/g;
|
|
121
|
+
const noValues = Object.freeze({});
|
|
122
|
+
function formatMessage(message, values) {
|
|
123
|
+
return message
|
|
124
|
+
.split(allPlaceholders)
|
|
125
|
+
.map(v => {
|
|
126
|
+
var _a;
|
|
127
|
+
const replaced = v.replace(placeholder, "$1");
|
|
128
|
+
return String((_a = values[replaced]) !== null && _a !== void 0 ? _a : v);
|
|
129
|
+
})
|
|
130
|
+
.join("");
|
|
131
|
+
}
|
|
102
132
|
Object.assign(FAST$1, {
|
|
103
133
|
addMessages(messages) {
|
|
104
134
|
Object.assign(debugMessages, messages);
|
|
105
135
|
},
|
|
106
|
-
warn(code,
|
|
136
|
+
warn(code, values = noValues) {
|
|
107
137
|
var _a;
|
|
108
|
-
|
|
138
|
+
const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning";
|
|
139
|
+
console.warn(formatMessage(message, values));
|
|
109
140
|
},
|
|
110
|
-
error(code,
|
|
141
|
+
error(code, values = noValues) {
|
|
111
142
|
var _a;
|
|
112
|
-
|
|
143
|
+
const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error";
|
|
144
|
+
return new Error(formatMessage(message, values));
|
|
113
145
|
},
|
|
114
146
|
});
|
|
115
147
|
|
|
@@ -141,7 +173,7 @@ if (FAST.error === void 0) {
|
|
|
141
173
|
Object.assign(FAST, {
|
|
142
174
|
warn() { },
|
|
143
175
|
error(code) {
|
|
144
|
-
return new Error(`
|
|
176
|
+
return new Error(`Error ${code}`);
|
|
145
177
|
},
|
|
146
178
|
addMessages() { },
|
|
147
179
|
});
|
|
@@ -172,10 +204,34 @@ function createTypeRegistry() {
|
|
|
172
204
|
return typeToDefinition.get(key);
|
|
173
205
|
},
|
|
174
206
|
getForInstance(object) {
|
|
207
|
+
if (object === null || object === void 0) {
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
175
210
|
return typeToDefinition.get(object.constructor);
|
|
176
211
|
},
|
|
177
212
|
});
|
|
178
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Creates a function capable of locating metadata associated with a type.
|
|
216
|
+
* @returns A metadata locator function.
|
|
217
|
+
* @internal
|
|
218
|
+
*/
|
|
219
|
+
function createMetadataLocator() {
|
|
220
|
+
const metadataLookup = new WeakMap();
|
|
221
|
+
return function (target) {
|
|
222
|
+
let metadata = metadataLookup.get(target);
|
|
223
|
+
if (metadata === void 0) {
|
|
224
|
+
let currentTarget = Reflect.getPrototypeOf(target);
|
|
225
|
+
while (metadata === void 0 && currentTarget !== null) {
|
|
226
|
+
metadata = metadataLookup.get(currentTarget);
|
|
227
|
+
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
228
|
+
}
|
|
229
|
+
metadata = metadata === void 0 ? [] : metadata.slice(0);
|
|
230
|
+
metadataLookup.set(target, metadata);
|
|
231
|
+
}
|
|
232
|
+
return metadata;
|
|
233
|
+
};
|
|
234
|
+
}
|
|
179
235
|
|
|
180
236
|
/**
|
|
181
237
|
* @internal
|
|
@@ -417,6 +473,21 @@ class PropertyChangeNotifier {
|
|
|
417
473
|
}
|
|
418
474
|
}
|
|
419
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Describes how the source's lifetime relates to its controller's lifetime.
|
|
478
|
+
* @public
|
|
479
|
+
*/
|
|
480
|
+
const SourceLifetime = Object.freeze({
|
|
481
|
+
/**
|
|
482
|
+
* The source to controller lifetime relationship is unknown.
|
|
483
|
+
*/
|
|
484
|
+
unknown: void 0,
|
|
485
|
+
/**
|
|
486
|
+
* The source and controller lifetimes are coupled to one another.
|
|
487
|
+
* They can/will be GC'd together.
|
|
488
|
+
*/
|
|
489
|
+
coupled: 1,
|
|
490
|
+
});
|
|
420
491
|
/**
|
|
421
492
|
* Common Observable APIs.
|
|
422
493
|
* @public
|
|
@@ -425,7 +496,6 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
425
496
|
const queueUpdate = Updates.enqueue;
|
|
426
497
|
const volatileRegex = /(:|&&|\|\||if)/;
|
|
427
498
|
const notifierLookup = new WeakMap();
|
|
428
|
-
const accessorLookup = new WeakMap();
|
|
429
499
|
let watcher = void 0;
|
|
430
500
|
let createArrayObserver = (array) => {
|
|
431
501
|
throw FAST.error(1101 /* Message.needsArrayObservation */);
|
|
@@ -440,19 +510,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
440
510
|
}
|
|
441
511
|
return found;
|
|
442
512
|
}
|
|
443
|
-
|
|
444
|
-
let accessors = accessorLookup.get(target);
|
|
445
|
-
if (accessors === void 0) {
|
|
446
|
-
let currentTarget = Reflect.getPrototypeOf(target);
|
|
447
|
-
while (accessors === void 0 && currentTarget !== null) {
|
|
448
|
-
accessors = accessorLookup.get(currentTarget);
|
|
449
|
-
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
450
|
-
}
|
|
451
|
-
accessors = accessors === void 0 ? [] : accessors.slice(0);
|
|
452
|
-
accessorLookup.set(target, accessors);
|
|
453
|
-
}
|
|
454
|
-
return accessors;
|
|
455
|
-
}
|
|
513
|
+
const getAccessors = createMetadataLocator();
|
|
456
514
|
class DefaultObservableAccessor {
|
|
457
515
|
constructor(name) {
|
|
458
516
|
this.name = name;
|
|
@@ -478,10 +536,10 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
478
536
|
}
|
|
479
537
|
}
|
|
480
538
|
}
|
|
481
|
-
class
|
|
482
|
-
constructor(
|
|
483
|
-
super(
|
|
484
|
-
this.
|
|
539
|
+
class ExpressionNotifierImplementation extends SubscriberSet {
|
|
540
|
+
constructor(expression, initialSubscriber, isVolatileBinding = false) {
|
|
541
|
+
super(expression, initialSubscriber);
|
|
542
|
+
this.expression = expression;
|
|
485
543
|
this.isVolatileBinding = isVolatileBinding;
|
|
486
544
|
this.needsRefresh = true;
|
|
487
545
|
this.needsQueue = true;
|
|
@@ -496,6 +554,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
496
554
|
setMode(isAsync) {
|
|
497
555
|
this.isAsync = this.needsQueue = isAsync;
|
|
498
556
|
}
|
|
557
|
+
bind(controller) {
|
|
558
|
+
this.controller = controller;
|
|
559
|
+
const value = this.observe(controller.source, controller.context);
|
|
560
|
+
if (!controller.isBound && this.requiresUnbind(controller)) {
|
|
561
|
+
controller.onUnbind(this);
|
|
562
|
+
}
|
|
563
|
+
return value;
|
|
564
|
+
}
|
|
565
|
+
requiresUnbind(controller) {
|
|
566
|
+
return (controller.sourceLifetime !== SourceLifetime.coupled ||
|
|
567
|
+
this.first !== this.last ||
|
|
568
|
+
this.first.propertySource !== controller.source);
|
|
569
|
+
}
|
|
570
|
+
unbind(controller) {
|
|
571
|
+
this.dispose();
|
|
572
|
+
}
|
|
499
573
|
observe(source, context) {
|
|
500
574
|
if (this.needsRefresh && this.last !== null) {
|
|
501
575
|
this.dispose();
|
|
@@ -503,10 +577,19 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
503
577
|
const previousWatcher = watcher;
|
|
504
578
|
watcher = this.needsRefresh ? this : void 0;
|
|
505
579
|
this.needsRefresh = this.isVolatileBinding;
|
|
506
|
-
|
|
507
|
-
|
|
580
|
+
let result;
|
|
581
|
+
try {
|
|
582
|
+
result = this.expression(source, context);
|
|
583
|
+
}
|
|
584
|
+
finally {
|
|
585
|
+
watcher = previousWatcher;
|
|
586
|
+
}
|
|
508
587
|
return result;
|
|
509
588
|
}
|
|
589
|
+
// backwards compat with v1 kernel
|
|
590
|
+
disconnect() {
|
|
591
|
+
this.dispose();
|
|
592
|
+
}
|
|
510
593
|
dispose() {
|
|
511
594
|
if (this.last !== null) {
|
|
512
595
|
let current = this.first;
|
|
@@ -633,22 +716,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
633
716
|
*/
|
|
634
717
|
getAccessors,
|
|
635
718
|
/**
|
|
636
|
-
* Creates a {@link
|
|
637
|
-
* provided {@link
|
|
638
|
-
* @param
|
|
719
|
+
* Creates a {@link ExpressionNotifier} that can watch the
|
|
720
|
+
* provided {@link Expression} for changes.
|
|
721
|
+
* @param expression - The binding to observe.
|
|
639
722
|
* @param initialSubscriber - An initial subscriber to changes in the binding value.
|
|
640
723
|
* @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
|
|
641
724
|
*/
|
|
642
|
-
binding(
|
|
643
|
-
return new
|
|
725
|
+
binding(expression, initialSubscriber, isVolatileBinding = this.isVolatileBinding(expression)) {
|
|
726
|
+
return new ExpressionNotifierImplementation(expression, initialSubscriber, isVolatileBinding);
|
|
644
727
|
},
|
|
645
728
|
/**
|
|
646
729
|
* Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
|
|
647
730
|
* on every evaluation of the value.
|
|
648
|
-
* @param
|
|
731
|
+
* @param expression - The binding to inspect.
|
|
649
732
|
*/
|
|
650
|
-
isVolatileBinding(
|
|
651
|
-
return volatileRegex.test(
|
|
733
|
+
isVolatileBinding(expression) {
|
|
734
|
+
return volatileRegex.test(expression.toString());
|
|
652
735
|
},
|
|
653
736
|
});
|
|
654
737
|
});
|
|
@@ -687,73 +770,40 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
|
|
|
687
770
|
},
|
|
688
771
|
};
|
|
689
772
|
});
|
|
690
|
-
class DefaultExecutionContext {
|
|
691
|
-
constructor(parentSource = null, parentContext = null) {
|
|
692
|
-
this.index = 0;
|
|
693
|
-
this.length = 0;
|
|
694
|
-
this.parent = parentSource;
|
|
695
|
-
this.parentContext = parentContext;
|
|
696
|
-
}
|
|
697
|
-
get event() {
|
|
698
|
-
return contextEvent.get();
|
|
699
|
-
}
|
|
700
|
-
get isEven() {
|
|
701
|
-
return this.index % 2 === 0;
|
|
702
|
-
}
|
|
703
|
-
get isOdd() {
|
|
704
|
-
return this.index % 2 !== 0;
|
|
705
|
-
}
|
|
706
|
-
get isFirst() {
|
|
707
|
-
return this.index === 0;
|
|
708
|
-
}
|
|
709
|
-
get isInMiddle() {
|
|
710
|
-
return !this.isFirst && !this.isLast;
|
|
711
|
-
}
|
|
712
|
-
get isLast() {
|
|
713
|
-
return this.index === this.length - 1;
|
|
714
|
-
}
|
|
715
|
-
eventDetail() {
|
|
716
|
-
return this.event.detail;
|
|
717
|
-
}
|
|
718
|
-
eventTarget() {
|
|
719
|
-
return this.event.target;
|
|
720
|
-
}
|
|
721
|
-
updatePosition(index, length) {
|
|
722
|
-
this.index = index;
|
|
723
|
-
this.length = length;
|
|
724
|
-
}
|
|
725
|
-
createChildContext(parentSource) {
|
|
726
|
-
return new DefaultExecutionContext(parentSource, this);
|
|
727
|
-
}
|
|
728
|
-
createItemContext(index, length) {
|
|
729
|
-
const childContext = Object.create(this);
|
|
730
|
-
childContext.index = index;
|
|
731
|
-
childContext.length = length;
|
|
732
|
-
return childContext;
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
Observable.defineProperty(DefaultExecutionContext.prototype, "index");
|
|
736
|
-
Observable.defineProperty(DefaultExecutionContext.prototype, "length");
|
|
737
773
|
/**
|
|
738
|
-
*
|
|
774
|
+
* Provides additional contextual information available to behaviors and expressions.
|
|
739
775
|
* @public
|
|
740
776
|
*/
|
|
741
777
|
const ExecutionContext = Object.freeze({
|
|
742
|
-
default: new DefaultExecutionContext(),
|
|
743
778
|
/**
|
|
744
|
-
*
|
|
745
|
-
* @param event - The event to set.
|
|
746
|
-
* @internal
|
|
779
|
+
* A default execution context.
|
|
747
780
|
*/
|
|
748
|
-
|
|
749
|
-
|
|
781
|
+
default: {
|
|
782
|
+
index: 0,
|
|
783
|
+
length: 0,
|
|
784
|
+
get event() {
|
|
785
|
+
return ExecutionContext.getEvent();
|
|
786
|
+
},
|
|
787
|
+
eventDetail() {
|
|
788
|
+
return this.event.detail;
|
|
789
|
+
},
|
|
790
|
+
eventTarget() {
|
|
791
|
+
return this.event.target;
|
|
792
|
+
},
|
|
750
793
|
},
|
|
751
794
|
/**
|
|
752
|
-
*
|
|
753
|
-
* @returns
|
|
795
|
+
* Gets the current event.
|
|
796
|
+
* @returns An event object.
|
|
754
797
|
*/
|
|
755
|
-
|
|
756
|
-
return
|
|
798
|
+
getEvent() {
|
|
799
|
+
return contextEvent.get();
|
|
800
|
+
},
|
|
801
|
+
/**
|
|
802
|
+
* Sets the current event.
|
|
803
|
+
* @param event - An event object.
|
|
804
|
+
*/
|
|
805
|
+
setEvent(event) {
|
|
806
|
+
contextEvent.set(event);
|
|
757
807
|
},
|
|
758
808
|
});
|
|
759
809
|
|
|
@@ -822,10 +872,311 @@ const SpliceStrategySupport = Object.freeze({
|
|
|
822
872
|
const reset = new Splice(0, emptyArray, 0);
|
|
823
873
|
reset.reset = true;
|
|
824
874
|
const resetSplices = [reset];
|
|
875
|
+
// Note: This function is *based* on the computation of the Levenshtein
|
|
876
|
+
// "edit" distance. The one change is that "updates" are treated as two
|
|
877
|
+
// edits - not one. With Array splices, an update is really a delete
|
|
878
|
+
// followed by an add. By retaining this, we optimize for "keeping" the
|
|
879
|
+
// maximum array items in the original array. For example:
|
|
880
|
+
//
|
|
881
|
+
// 'xxxx123' to '123yyyy'
|
|
882
|
+
//
|
|
883
|
+
// With 1-edit updates, the shortest path would be just to update all seven
|
|
884
|
+
// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
|
|
885
|
+
// leaves the substring '123' intact.
|
|
886
|
+
function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
|
|
887
|
+
// "Deletion" columns
|
|
888
|
+
const rowCount = oldEnd - oldStart + 1;
|
|
889
|
+
const columnCount = currentEnd - currentStart + 1;
|
|
890
|
+
const distances = new Array(rowCount);
|
|
891
|
+
let north;
|
|
892
|
+
let west;
|
|
893
|
+
// "Addition" rows. Initialize null column.
|
|
894
|
+
for (let i = 0; i < rowCount; ++i) {
|
|
895
|
+
distances[i] = new Array(columnCount);
|
|
896
|
+
distances[i][0] = i;
|
|
897
|
+
}
|
|
898
|
+
// Initialize null row
|
|
899
|
+
for (let j = 0; j < columnCount; ++j) {
|
|
900
|
+
distances[0][j] = j;
|
|
901
|
+
}
|
|
902
|
+
for (let i = 1; i < rowCount; ++i) {
|
|
903
|
+
for (let j = 1; j < columnCount; ++j) {
|
|
904
|
+
if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
|
|
905
|
+
distances[i][j] = distances[i - 1][j - 1];
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
north = distances[i - 1][j] + 1;
|
|
909
|
+
west = distances[i][j - 1] + 1;
|
|
910
|
+
distances[i][j] = north < west ? north : west;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return distances;
|
|
915
|
+
}
|
|
916
|
+
// This starts at the final weight, and walks "backward" by finding
|
|
917
|
+
// the minimum previous weight recursively until the origin of the weight
|
|
918
|
+
// matrix.
|
|
919
|
+
function spliceOperationsFromEditDistances(distances) {
|
|
920
|
+
let i = distances.length - 1;
|
|
921
|
+
let j = distances[0].length - 1;
|
|
922
|
+
let current = distances[i][j];
|
|
923
|
+
const edits = [];
|
|
924
|
+
while (i > 0 || j > 0) {
|
|
925
|
+
if (i === 0) {
|
|
926
|
+
edits.push(2 /* Edit.add */);
|
|
927
|
+
j--;
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
if (j === 0) {
|
|
931
|
+
edits.push(3 /* Edit.delete */);
|
|
932
|
+
i--;
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
const northWest = distances[i - 1][j - 1];
|
|
936
|
+
const west = distances[i - 1][j];
|
|
937
|
+
const north = distances[i][j - 1];
|
|
938
|
+
let min;
|
|
939
|
+
if (west < north) {
|
|
940
|
+
min = west < northWest ? west : northWest;
|
|
941
|
+
}
|
|
942
|
+
else {
|
|
943
|
+
min = north < northWest ? north : northWest;
|
|
944
|
+
}
|
|
945
|
+
if (min === northWest) {
|
|
946
|
+
if (northWest === current) {
|
|
947
|
+
edits.push(0 /* Edit.leave */);
|
|
948
|
+
}
|
|
949
|
+
else {
|
|
950
|
+
edits.push(1 /* Edit.update */);
|
|
951
|
+
current = northWest;
|
|
952
|
+
}
|
|
953
|
+
i--;
|
|
954
|
+
j--;
|
|
955
|
+
}
|
|
956
|
+
else if (min === west) {
|
|
957
|
+
edits.push(3 /* Edit.delete */);
|
|
958
|
+
i--;
|
|
959
|
+
current = west;
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
edits.push(2 /* Edit.add */);
|
|
963
|
+
j--;
|
|
964
|
+
current = north;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return edits.reverse();
|
|
968
|
+
}
|
|
969
|
+
function sharedPrefix(current, old, searchLength) {
|
|
970
|
+
for (let i = 0; i < searchLength; ++i) {
|
|
971
|
+
if (current[i] !== old[i]) {
|
|
972
|
+
return i;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
return searchLength;
|
|
976
|
+
}
|
|
977
|
+
function sharedSuffix(current, old, searchLength) {
|
|
978
|
+
let index1 = current.length;
|
|
979
|
+
let index2 = old.length;
|
|
980
|
+
let count = 0;
|
|
981
|
+
while (count < searchLength && current[--index1] === old[--index2]) {
|
|
982
|
+
count++;
|
|
983
|
+
}
|
|
984
|
+
return count;
|
|
985
|
+
}
|
|
986
|
+
function intersect(start1, end1, start2, end2) {
|
|
987
|
+
// Disjoint
|
|
988
|
+
if (end1 < start2 || end2 < start1) {
|
|
989
|
+
return -1;
|
|
990
|
+
}
|
|
991
|
+
// Adjacent
|
|
992
|
+
if (end1 === start2 || end2 === start1) {
|
|
993
|
+
return 0;
|
|
994
|
+
}
|
|
995
|
+
// Non-zero intersect, span1 first
|
|
996
|
+
if (start1 < start2) {
|
|
997
|
+
if (end1 < end2) {
|
|
998
|
+
return end1 - start2; // Overlap
|
|
999
|
+
}
|
|
1000
|
+
return end2 - start2; // Contained
|
|
1001
|
+
}
|
|
1002
|
+
// Non-zero intersect, span2 first
|
|
1003
|
+
if (end2 < end1) {
|
|
1004
|
+
return end2 - start1; // Overlap
|
|
1005
|
+
}
|
|
1006
|
+
return end1 - start1; // Contained
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* @remarks
|
|
1010
|
+
* Lacking individual splice mutation information, the minimal set of
|
|
1011
|
+
* splices can be synthesized given the previous state and final state of an
|
|
1012
|
+
* array. The basic approach is to calculate the edit distance matrix and
|
|
1013
|
+
* choose the shortest path through it.
|
|
1014
|
+
*
|
|
1015
|
+
* Complexity: O(l * p)
|
|
1016
|
+
* l: The length of the current array
|
|
1017
|
+
* p: The length of the old array
|
|
1018
|
+
*/
|
|
1019
|
+
function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
|
|
1020
|
+
let prefixCount = 0;
|
|
1021
|
+
let suffixCount = 0;
|
|
1022
|
+
const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
|
|
1023
|
+
if (currentStart === 0 && oldStart === 0) {
|
|
1024
|
+
prefixCount = sharedPrefix(current, old, minLength);
|
|
1025
|
+
}
|
|
1026
|
+
if (currentEnd === current.length && oldEnd === old.length) {
|
|
1027
|
+
suffixCount = sharedSuffix(current, old, minLength - prefixCount);
|
|
1028
|
+
}
|
|
1029
|
+
currentStart += prefixCount;
|
|
1030
|
+
oldStart += prefixCount;
|
|
1031
|
+
currentEnd -= suffixCount;
|
|
1032
|
+
oldEnd -= suffixCount;
|
|
1033
|
+
if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
|
|
1034
|
+
return emptyArray;
|
|
1035
|
+
}
|
|
1036
|
+
if (currentStart === currentEnd) {
|
|
1037
|
+
const splice = new Splice(currentStart, [], 0);
|
|
1038
|
+
while (oldStart < oldEnd) {
|
|
1039
|
+
splice.removed.push(old[oldStart++]);
|
|
1040
|
+
}
|
|
1041
|
+
return [splice];
|
|
1042
|
+
}
|
|
1043
|
+
else if (oldStart === oldEnd) {
|
|
1044
|
+
return [new Splice(currentStart, [], currentEnd - currentStart)];
|
|
1045
|
+
}
|
|
1046
|
+
const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
|
|
1047
|
+
const splices = [];
|
|
1048
|
+
let splice = void 0;
|
|
1049
|
+
let index = currentStart;
|
|
1050
|
+
let oldIndex = oldStart;
|
|
1051
|
+
for (let i = 0; i < ops.length; ++i) {
|
|
1052
|
+
switch (ops[i]) {
|
|
1053
|
+
case 0 /* Edit.leave */:
|
|
1054
|
+
if (splice !== void 0) {
|
|
1055
|
+
splices.push(splice);
|
|
1056
|
+
splice = void 0;
|
|
1057
|
+
}
|
|
1058
|
+
index++;
|
|
1059
|
+
oldIndex++;
|
|
1060
|
+
break;
|
|
1061
|
+
case 1 /* Edit.update */:
|
|
1062
|
+
if (splice === void 0) {
|
|
1063
|
+
splice = new Splice(index, [], 0);
|
|
1064
|
+
}
|
|
1065
|
+
splice.addedCount++;
|
|
1066
|
+
index++;
|
|
1067
|
+
splice.removed.push(old[oldIndex]);
|
|
1068
|
+
oldIndex++;
|
|
1069
|
+
break;
|
|
1070
|
+
case 2 /* Edit.add */:
|
|
1071
|
+
if (splice === void 0) {
|
|
1072
|
+
splice = new Splice(index, [], 0);
|
|
1073
|
+
}
|
|
1074
|
+
splice.addedCount++;
|
|
1075
|
+
index++;
|
|
1076
|
+
break;
|
|
1077
|
+
case 3 /* Edit.delete */:
|
|
1078
|
+
if (splice === void 0) {
|
|
1079
|
+
splice = new Splice(index, [], 0);
|
|
1080
|
+
}
|
|
1081
|
+
splice.removed.push(old[oldIndex]);
|
|
1082
|
+
oldIndex++;
|
|
1083
|
+
break;
|
|
1084
|
+
// no default
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
if (splice !== void 0) {
|
|
1088
|
+
splices.push(splice);
|
|
1089
|
+
}
|
|
1090
|
+
return splices;
|
|
1091
|
+
}
|
|
1092
|
+
function merge(splice, splices) {
|
|
1093
|
+
let inserted = false;
|
|
1094
|
+
let insertionOffset = 0;
|
|
1095
|
+
for (let i = 0; i < splices.length; i++) {
|
|
1096
|
+
const current = splices[i];
|
|
1097
|
+
current.index += insertionOffset;
|
|
1098
|
+
if (inserted) {
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
|
|
1102
|
+
if (intersectCount >= 0) {
|
|
1103
|
+
// Merge the two splices
|
|
1104
|
+
splices.splice(i, 1);
|
|
1105
|
+
i--;
|
|
1106
|
+
insertionOffset -= current.addedCount - current.removed.length;
|
|
1107
|
+
splice.addedCount += current.addedCount - intersectCount;
|
|
1108
|
+
const deleteCount = splice.removed.length + current.removed.length - intersectCount;
|
|
1109
|
+
if (!splice.addedCount && !deleteCount) {
|
|
1110
|
+
// merged splice is a noop. discard.
|
|
1111
|
+
inserted = true;
|
|
1112
|
+
}
|
|
1113
|
+
else {
|
|
1114
|
+
let currentRemoved = current.removed;
|
|
1115
|
+
if (splice.index < current.index) {
|
|
1116
|
+
// some prefix of splice.removed is prepended to current.removed.
|
|
1117
|
+
const prepend = splice.removed.slice(0, current.index - splice.index);
|
|
1118
|
+
prepend.push(...currentRemoved);
|
|
1119
|
+
currentRemoved = prepend;
|
|
1120
|
+
}
|
|
1121
|
+
if (splice.index + splice.removed.length >
|
|
1122
|
+
current.index + current.addedCount) {
|
|
1123
|
+
// some suffix of splice.removed is appended to current.removed.
|
|
1124
|
+
const append = splice.removed.slice(current.index + current.addedCount - splice.index);
|
|
1125
|
+
currentRemoved.push(...append);
|
|
1126
|
+
}
|
|
1127
|
+
splice.removed = currentRemoved;
|
|
1128
|
+
if (current.index < splice.index) {
|
|
1129
|
+
splice.index = current.index;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
else if (splice.index < current.index) {
|
|
1134
|
+
// Insert splice here.
|
|
1135
|
+
inserted = true;
|
|
1136
|
+
splices.splice(i, 0, splice);
|
|
1137
|
+
i++;
|
|
1138
|
+
const offset = splice.addedCount - splice.removed.length;
|
|
1139
|
+
current.index += offset;
|
|
1140
|
+
insertionOffset += offset;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
if (!inserted) {
|
|
1144
|
+
splices.push(splice);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
function project(array, changes) {
|
|
1148
|
+
let splices = [];
|
|
1149
|
+
const initialSplices = [];
|
|
1150
|
+
for (let i = 0, ii = changes.length; i < ii; i++) {
|
|
1151
|
+
merge(changes[i], initialSplices);
|
|
1152
|
+
}
|
|
1153
|
+
for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
|
|
1154
|
+
const splice = initialSplices[i];
|
|
1155
|
+
if (splice.addedCount === 1 && splice.removed.length === 1) {
|
|
1156
|
+
if (splice.removed[0] !== array[splice.index]) {
|
|
1157
|
+
splices.push(splice);
|
|
1158
|
+
}
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
|
|
1162
|
+
}
|
|
1163
|
+
return splices;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* A SpliceStrategy that attempts to merge all splices into the minimal set of
|
|
1167
|
+
* splices needed to represent the change from the old array to the new array.
|
|
1168
|
+
* @public
|
|
1169
|
+
*/
|
|
825
1170
|
let defaultSpliceStrategy = Object.freeze({
|
|
826
|
-
support: SpliceStrategySupport.
|
|
1171
|
+
support: SpliceStrategySupport.optimized,
|
|
827
1172
|
normalize(previous, current, changes) {
|
|
828
|
-
|
|
1173
|
+
if (previous === void 0) {
|
|
1174
|
+
if (changes === void 0) {
|
|
1175
|
+
return emptyArray;
|
|
1176
|
+
}
|
|
1177
|
+
return changes.length > 1 ? project(current, changes) : changes;
|
|
1178
|
+
}
|
|
1179
|
+
return resetSplices;
|
|
829
1180
|
},
|
|
830
1181
|
pop(array, observer, pop, args) {
|
|
831
1182
|
const notEmpty = array.length > 0;
|
|
@@ -1010,7 +1361,7 @@ const ArrayObserver = Object.freeze({
|
|
|
1010
1361
|
* @returns The length of the array.
|
|
1011
1362
|
* @public
|
|
1012
1363
|
*/
|
|
1013
|
-
function
|
|
1364
|
+
function lengthOf(array) {
|
|
1014
1365
|
if (!array) {
|
|
1015
1366
|
return 0;
|
|
1016
1367
|
}
|
|
@@ -1094,6 +1445,20 @@ class ElementStyles {
|
|
|
1094
1445
|
static setDefaultStrategy(Strategy) {
|
|
1095
1446
|
DefaultStyleStrategy = Strategy;
|
|
1096
1447
|
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Normalizes a set of composable style options.
|
|
1450
|
+
* @param styles - The style options to normalize.
|
|
1451
|
+
* @returns A singular ElementStyles instance or undefined.
|
|
1452
|
+
*/
|
|
1453
|
+
static normalize(styles) {
|
|
1454
|
+
return styles === void 0
|
|
1455
|
+
? void 0
|
|
1456
|
+
: Array.isArray(styles)
|
|
1457
|
+
? new ElementStyles(styles)
|
|
1458
|
+
: styles instanceof ElementStyles
|
|
1459
|
+
? styles
|
|
1460
|
+
: new ElementStyles([styles]);
|
|
1461
|
+
}
|
|
1097
1462
|
}
|
|
1098
1463
|
/**
|
|
1099
1464
|
* Indicates whether the DOM supports the adoptedStyleSheets feature.
|
|
@@ -1238,11 +1603,11 @@ class CSSPartial {
|
|
|
1238
1603
|
}
|
|
1239
1604
|
return this.css;
|
|
1240
1605
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1606
|
+
addedCallback(controller) {
|
|
1607
|
+
controller.addStyles(this.styles);
|
|
1243
1608
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1609
|
+
removedCallback(controller) {
|
|
1610
|
+
controller.removeStyles(this.styles);
|
|
1246
1611
|
}
|
|
1247
1612
|
}
|
|
1248
1613
|
CSSDirective.define(CSSPartial);
|
|
@@ -1381,6 +1746,67 @@ const Parser = Object.freeze({
|
|
|
1381
1746
|
},
|
|
1382
1747
|
});
|
|
1383
1748
|
|
|
1749
|
+
/**
|
|
1750
|
+
* Bridges between ViewBehaviors and HostBehaviors, enabling a host to
|
|
1751
|
+
* control ViewBehaviors.
|
|
1752
|
+
* @public
|
|
1753
|
+
*/
|
|
1754
|
+
const ViewBehaviorOrchestrator = Object.freeze({
|
|
1755
|
+
/**
|
|
1756
|
+
* Creates a ViewBehaviorOrchestrator.
|
|
1757
|
+
* @param source - The source to to associate behaviors with.
|
|
1758
|
+
* @returns A ViewBehaviorOrchestrator.
|
|
1759
|
+
*/
|
|
1760
|
+
create(source) {
|
|
1761
|
+
const behaviors = [];
|
|
1762
|
+
const targets = {};
|
|
1763
|
+
let unbindables = null;
|
|
1764
|
+
let isConnected = false;
|
|
1765
|
+
return {
|
|
1766
|
+
source,
|
|
1767
|
+
context: ExecutionContext.default,
|
|
1768
|
+
targets,
|
|
1769
|
+
get isBound() {
|
|
1770
|
+
return isConnected;
|
|
1771
|
+
},
|
|
1772
|
+
addBehaviorFactory(factory, target) {
|
|
1773
|
+
const nodeId = factory.nodeId || (factory.nodeId = nextId());
|
|
1774
|
+
factory.id || (factory.id = nextId());
|
|
1775
|
+
this.addTarget(nodeId, target);
|
|
1776
|
+
this.addBehavior(factory.createBehavior());
|
|
1777
|
+
},
|
|
1778
|
+
addTarget(nodeId, target) {
|
|
1779
|
+
targets[nodeId] = target;
|
|
1780
|
+
},
|
|
1781
|
+
addBehavior(behavior) {
|
|
1782
|
+
behaviors.push(behavior);
|
|
1783
|
+
if (isConnected) {
|
|
1784
|
+
behavior.bind(this);
|
|
1785
|
+
}
|
|
1786
|
+
},
|
|
1787
|
+
onUnbind(unbindable) {
|
|
1788
|
+
if (unbindables === null) {
|
|
1789
|
+
unbindables = [];
|
|
1790
|
+
}
|
|
1791
|
+
unbindables.push(unbindable);
|
|
1792
|
+
},
|
|
1793
|
+
connectedCallback(controller) {
|
|
1794
|
+
if (!isConnected) {
|
|
1795
|
+
isConnected = true;
|
|
1796
|
+
behaviors.forEach(x => x.bind(this));
|
|
1797
|
+
}
|
|
1798
|
+
},
|
|
1799
|
+
disconnectedCallback(controller) {
|
|
1800
|
+
if (isConnected) {
|
|
1801
|
+
isConnected = false;
|
|
1802
|
+
if (unbindables !== null) {
|
|
1803
|
+
unbindables.forEach(x => x.unbind(this));
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
},
|
|
1807
|
+
};
|
|
1808
|
+
},
|
|
1809
|
+
});
|
|
1384
1810
|
const registry = createTypeRegistry();
|
|
1385
1811
|
/**
|
|
1386
1812
|
* Instructs the template engine to apply behavior to a node.
|
|
@@ -1420,6 +1846,22 @@ function htmlDirective(options) {
|
|
|
1420
1846
|
HTMLDirective.define(type, options);
|
|
1421
1847
|
};
|
|
1422
1848
|
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Captures a binding expression along with related information and capabilities.
|
|
1851
|
+
*
|
|
1852
|
+
* @public
|
|
1853
|
+
*/
|
|
1854
|
+
class Binding {
|
|
1855
|
+
/**
|
|
1856
|
+
* Creates a binding.
|
|
1857
|
+
* @param evaluate - Evaluates the binding.
|
|
1858
|
+
* @param isVolatile - Indicates whether the binding is volatile.
|
|
1859
|
+
*/
|
|
1860
|
+
constructor(evaluate, isVolatile = false) {
|
|
1861
|
+
this.evaluate = evaluate;
|
|
1862
|
+
this.isVolatile = isVolatile;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1423
1865
|
/**
|
|
1424
1866
|
* The type of HTML aspect to target.
|
|
1425
1867
|
* @public
|
|
@@ -1457,26 +1899,22 @@ const Aspect = Object.freeze({
|
|
|
1457
1899
|
*
|
|
1458
1900
|
* @param directive - The directive to assign the aspect to.
|
|
1459
1901
|
* @param value - The value to base the aspect determination on.
|
|
1902
|
+
* @remarks
|
|
1903
|
+
* If a falsy value is provided, then the content aspect will be assigned.
|
|
1460
1904
|
*/
|
|
1461
1905
|
assign(directive, value) {
|
|
1462
|
-
directive.sourceAspect = value;
|
|
1463
1906
|
if (!value) {
|
|
1907
|
+
directive.aspectType = Aspect.content;
|
|
1464
1908
|
return;
|
|
1465
1909
|
}
|
|
1910
|
+
directive.sourceAspect = value;
|
|
1466
1911
|
switch (value[0]) {
|
|
1467
1912
|
case ":":
|
|
1468
1913
|
directive.targetAspect = value.substring(1);
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
case "classList":
|
|
1474
|
-
directive.aspectType = Aspect.tokenList;
|
|
1475
|
-
break;
|
|
1476
|
-
default:
|
|
1477
|
-
directive.aspectType = Aspect.property;
|
|
1478
|
-
break;
|
|
1479
|
-
}
|
|
1914
|
+
directive.aspectType =
|
|
1915
|
+
directive.targetAspect === "classList"
|
|
1916
|
+
? Aspect.tokenList
|
|
1917
|
+
: Aspect.property;
|
|
1480
1918
|
break;
|
|
1481
1919
|
case "?":
|
|
1482
1920
|
directive.targetAspect = value.substring(1);
|
|
@@ -1487,14 +1925,8 @@ const Aspect = Object.freeze({
|
|
|
1487
1925
|
directive.aspectType = Aspect.event;
|
|
1488
1926
|
break;
|
|
1489
1927
|
default:
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
directive.aspectType = Aspect.property;
|
|
1493
|
-
}
|
|
1494
|
-
else {
|
|
1495
|
-
directive.targetAspect = value;
|
|
1496
|
-
directive.aspectType = Aspect.attribute;
|
|
1497
|
-
}
|
|
1928
|
+
directive.targetAspect = value;
|
|
1929
|
+
directive.aspectType = Aspect.attribute;
|
|
1498
1930
|
break;
|
|
1499
1931
|
}
|
|
1500
1932
|
},
|
|
@@ -1510,13 +1942,10 @@ class StatelessAttachedAttributeDirective {
|
|
|
1510
1942
|
*/
|
|
1511
1943
|
constructor(options) {
|
|
1512
1944
|
this.options = options;
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
*/
|
|
1518
|
-
createBehavior(targets) {
|
|
1519
|
-
return this;
|
|
1945
|
+
/**
|
|
1946
|
+
* The unique id of the factory.
|
|
1947
|
+
*/
|
|
1948
|
+
this.id = nextId();
|
|
1520
1949
|
}
|
|
1521
1950
|
/**
|
|
1522
1951
|
* Creates a placeholder string based on the directive's index within the template.
|
|
@@ -1527,6 +1956,13 @@ class StatelessAttachedAttributeDirective {
|
|
|
1527
1956
|
createHTML(add) {
|
|
1528
1957
|
return Markup.attribute(add(this));
|
|
1529
1958
|
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Creates a behavior.
|
|
1961
|
+
* @param targets - The targets available for behaviors to be attached to.
|
|
1962
|
+
*/
|
|
1963
|
+
createBehavior() {
|
|
1964
|
+
return this;
|
|
1965
|
+
}
|
|
1530
1966
|
}
|
|
1531
1967
|
|
|
1532
1968
|
const createInnerHTMLBinding = globalThis.TrustedHTML
|
|
@@ -1538,107 +1974,26 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
|
|
|
1538
1974
|
throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
|
|
1539
1975
|
}
|
|
1540
1976
|
: (binding) => binding;
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
*/
|
|
1545
|
-
const BindingMode = Object.freeze({
|
|
1546
|
-
/**
|
|
1547
|
-
* Creates a binding mode based on the supplied behavior types.
|
|
1548
|
-
* @param UpdateType - The base behavior type used to update aspects.
|
|
1549
|
-
* @param EventType - The base behavior type used to respond to events.
|
|
1550
|
-
* @returns A new binding mode.
|
|
1551
|
-
*/
|
|
1552
|
-
define(UpdateType, EventType = EventBinding) {
|
|
1553
|
-
return Object.freeze({
|
|
1554
|
-
[1]: d => new UpdateType(d, DOM.setAttribute),
|
|
1555
|
-
[2]: d => new UpdateType(d, DOM.setBooleanAttribute),
|
|
1556
|
-
[3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
|
|
1557
|
-
[4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
|
|
1558
|
-
[5]: d => new UpdateType(d, updateTokenListTarget),
|
|
1559
|
-
[6]: d => new EventType(d),
|
|
1560
|
-
});
|
|
1561
|
-
},
|
|
1562
|
-
});
|
|
1563
|
-
/**
|
|
1564
|
-
* Describes the configuration for a binding expression.
|
|
1565
|
-
* @public
|
|
1566
|
-
*/
|
|
1567
|
-
const BindingConfig = Object.freeze({
|
|
1568
|
-
/**
|
|
1569
|
-
* Creates a binding configuration based on the provided mode and options.
|
|
1570
|
-
* @param mode - The mode to use for the configuration.
|
|
1571
|
-
* @param defaultOptions - The default options to use for the configuration.
|
|
1572
|
-
* @returns A new binding configuration.
|
|
1573
|
-
*/
|
|
1574
|
-
define(mode, defaultOptions) {
|
|
1575
|
-
const config = (options) => {
|
|
1576
|
-
return {
|
|
1577
|
-
mode: config.mode,
|
|
1578
|
-
options: Object.assign({}, defaultOptions, options),
|
|
1579
|
-
};
|
|
1580
|
-
};
|
|
1581
|
-
config.options = defaultOptions;
|
|
1582
|
-
config.mode = mode;
|
|
1583
|
-
return config;
|
|
1584
|
-
},
|
|
1585
|
-
});
|
|
1586
|
-
/**
|
|
1587
|
-
* A base binding behavior for DOM updates.
|
|
1588
|
-
* @public
|
|
1589
|
-
*/
|
|
1590
|
-
class UpdateBinding {
|
|
1591
|
-
/**
|
|
1592
|
-
* Creates an instance of UpdateBinding.
|
|
1593
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1594
|
-
* @param updateTarget - The function used to update the target with the latest value.
|
|
1595
|
-
*/
|
|
1596
|
-
constructor(directive, updateTarget) {
|
|
1597
|
-
this.directive = directive;
|
|
1598
|
-
this.updateTarget = updateTarget;
|
|
1977
|
+
class OnChangeBinding extends Binding {
|
|
1978
|
+
createObserver(_, subscriber) {
|
|
1979
|
+
return Observable.binding(this.evaluate, subscriber, this.isVolatile);
|
|
1599
1980
|
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
* @param context - The execution context that the binding is operating within.
|
|
1604
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1605
|
-
*/
|
|
1606
|
-
bind(source, context, targets) { }
|
|
1607
|
-
/**
|
|
1608
|
-
* Unbinds this behavior from the source.
|
|
1609
|
-
* @param source - The source to unbind from.
|
|
1610
|
-
* @param context - The execution context that the binding is operating within.
|
|
1611
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1612
|
-
*/
|
|
1613
|
-
unbind(source, context, targets) { }
|
|
1614
|
-
/**
|
|
1615
|
-
* Creates a behavior.
|
|
1616
|
-
* @param targets - The targets available for behaviors to be attached to.
|
|
1617
|
-
*/
|
|
1618
|
-
createBehavior(targets) {
|
|
1981
|
+
}
|
|
1982
|
+
class OneTimeBinding extends Binding {
|
|
1983
|
+
createObserver() {
|
|
1619
1984
|
return this;
|
|
1620
1985
|
}
|
|
1986
|
+
bind(controller) {
|
|
1987
|
+
return this.evaluate(controller.source, controller.context);
|
|
1988
|
+
}
|
|
1621
1989
|
}
|
|
1622
|
-
function
|
|
1623
|
-
return class extends Type {
|
|
1624
|
-
unbind(source, context, targets) {
|
|
1625
|
-
super.unbind(source, context, targets);
|
|
1626
|
-
const target = targets[this.directive.nodeId];
|
|
1627
|
-
const view = target.$fastView;
|
|
1628
|
-
if (view !== void 0 && view.isComposed) {
|
|
1629
|
-
view.unbind();
|
|
1630
|
-
view.needsBindOnly = true;
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
};
|
|
1634
|
-
}
|
|
1635
|
-
function updateContentTarget(target, aspect, value, source, context) {
|
|
1990
|
+
function updateContent(target, aspect, value, controller) {
|
|
1636
1991
|
// If there's no actual value, then this equates to the
|
|
1637
1992
|
// empty string for the purposes of content bindings.
|
|
1638
1993
|
if (value === null || value === undefined) {
|
|
1639
1994
|
value = "";
|
|
1640
1995
|
}
|
|
1641
|
-
// If the value has a "create" method, then it's a
|
|
1996
|
+
// If the value has a "create" method, then it's a ContentTemplate.
|
|
1642
1997
|
if (value.create) {
|
|
1643
1998
|
target.textContent = "";
|
|
1644
1999
|
let view = target.$fastView;
|
|
@@ -1664,14 +2019,14 @@ function updateContentTarget(target, aspect, value, source, context) {
|
|
|
1664
2019
|
// and that there's actually no need to compose it.
|
|
1665
2020
|
if (!view.isComposed) {
|
|
1666
2021
|
view.isComposed = true;
|
|
1667
|
-
view.bind(source, context);
|
|
2022
|
+
view.bind(controller.source, controller.context);
|
|
1668
2023
|
view.insertBefore(target);
|
|
1669
2024
|
target.$fastView = view;
|
|
1670
2025
|
target.$fastTemplate = value;
|
|
1671
2026
|
}
|
|
1672
2027
|
else if (view.needsBindOnly) {
|
|
1673
2028
|
view.needsBindOnly = false;
|
|
1674
|
-
view.bind(source, context);
|
|
2029
|
+
view.bind(controller.source, controller.context);
|
|
1675
2030
|
}
|
|
1676
2031
|
}
|
|
1677
2032
|
else {
|
|
@@ -1691,10 +2046,9 @@ function updateContentTarget(target, aspect, value, source, context) {
|
|
|
1691
2046
|
target.textContent = value;
|
|
1692
2047
|
}
|
|
1693
2048
|
}
|
|
1694
|
-
function
|
|
2049
|
+
function updateTokenList(target, aspect, value) {
|
|
1695
2050
|
var _a;
|
|
1696
|
-
const
|
|
1697
|
-
const lookup = `${directive.id}-t`;
|
|
2051
|
+
const lookup = `${this.id}-t`;
|
|
1698
2052
|
const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
|
|
1699
2053
|
const versions = state.v;
|
|
1700
2054
|
let currentVersion = state.c;
|
|
@@ -1724,311 +2078,8 @@ function updateTokenListTarget(target, aspect, value) {
|
|
|
1724
2078
|
}
|
|
1725
2079
|
}
|
|
1726
2080
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
* @public
|
|
1730
|
-
*/
|
|
1731
|
-
class OneTimeBinding extends UpdateBinding {
|
|
1732
|
-
/**
|
|
1733
|
-
* Bind this behavior to the source.
|
|
1734
|
-
* @param source - The source to bind to.
|
|
1735
|
-
* @param context - The execution context that the binding is operating within.
|
|
1736
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1737
|
-
*/
|
|
1738
|
-
bind(source, context, targets) {
|
|
1739
|
-
const directive = this.directive;
|
|
1740
|
-
this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
const signals = Object.create(null);
|
|
1744
|
-
/**
|
|
1745
|
-
* A binding behavior for signal bindings.
|
|
1746
|
-
* @public
|
|
1747
|
-
*/
|
|
1748
|
-
class SignalBinding extends UpdateBinding {
|
|
1749
|
-
constructor() {
|
|
1750
|
-
super(...arguments);
|
|
1751
|
-
this.handlerProperty = `${this.directive.id}-h`;
|
|
1752
|
-
}
|
|
1753
|
-
/**
|
|
1754
|
-
* Bind this behavior to the source.
|
|
1755
|
-
* @param source - The source to bind to.
|
|
1756
|
-
* @param context - The execution context that the binding is operating within.
|
|
1757
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1758
|
-
*/
|
|
1759
|
-
bind(source, context, targets) {
|
|
1760
|
-
const directive = this.directive;
|
|
1761
|
-
const target = targets[directive.nodeId];
|
|
1762
|
-
const signal = this.getSignal(source, context);
|
|
1763
|
-
const handler = (target[this.handlerProperty] = () => {
|
|
1764
|
-
this.updateTarget(target, directive.targetAspect, directive.binding(source, context), source, context);
|
|
1765
|
-
});
|
|
1766
|
-
handler();
|
|
1767
|
-
const found = signals[signal];
|
|
1768
|
-
if (found) {
|
|
1769
|
-
Array.isArray(found)
|
|
1770
|
-
? found.push(handler)
|
|
1771
|
-
: (signals[signal] = [found, handler]);
|
|
1772
|
-
}
|
|
1773
|
-
else {
|
|
1774
|
-
signals[signal] = handler;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
/**
|
|
1778
|
-
* Unbinds this behavior from the source.
|
|
1779
|
-
* @param source - The source to unbind from.
|
|
1780
|
-
* @param context - The execution context that the binding is operating within.
|
|
1781
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1782
|
-
*/
|
|
1783
|
-
unbind(source, context, targets) {
|
|
1784
|
-
const signal = this.getSignal(source, context);
|
|
1785
|
-
const found = signals[signal];
|
|
1786
|
-
if (found && Array.isArray(found)) {
|
|
1787
|
-
const directive = this.directive;
|
|
1788
|
-
const target = targets[directive.nodeId];
|
|
1789
|
-
const handler = target[this.handlerProperty];
|
|
1790
|
-
const index = found.indexOf(handler);
|
|
1791
|
-
if (index !== -1) {
|
|
1792
|
-
found.splice(index, 1);
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
else {
|
|
1796
|
-
signals[signal] = void 0;
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
getSignal(source, context) {
|
|
1800
|
-
const options = this.directive.options;
|
|
1801
|
-
return isString(options) ? options : options(source, context);
|
|
1802
|
-
}
|
|
1803
|
-
/**
|
|
1804
|
-
* Sends the specified signal to signaled bindings.
|
|
1805
|
-
* @param signal - The signal to send.
|
|
1806
|
-
* @public
|
|
1807
|
-
*/
|
|
1808
|
-
static send(signal) {
|
|
1809
|
-
const found = signals[signal];
|
|
1810
|
-
if (found) {
|
|
1811
|
-
Array.isArray(found) ? found.forEach(x => x()) : found();
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
/**
|
|
1816
|
-
* A binding behavior for bindings that change.
|
|
1817
|
-
* @public
|
|
1818
|
-
*/
|
|
1819
|
-
class ChangeBinding extends UpdateBinding {
|
|
1820
|
-
/**
|
|
1821
|
-
* Creates an instance of ChangeBinding.
|
|
1822
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1823
|
-
* @param updateTarget - The function used to update the target with the latest value.
|
|
1824
|
-
*/
|
|
1825
|
-
constructor(directive, updateTarget) {
|
|
1826
|
-
super(directive, updateTarget);
|
|
1827
|
-
this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
|
|
1828
|
-
this.observerProperty = `${directive.id}-o`;
|
|
1829
|
-
}
|
|
1830
|
-
/**
|
|
1831
|
-
* Returns the binding observer used to update the node.
|
|
1832
|
-
* @param target - The target node.
|
|
1833
|
-
* @returns A BindingObserver.
|
|
1834
|
-
*/
|
|
1835
|
-
getObserver(target) {
|
|
1836
|
-
var _a;
|
|
1837
|
-
return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
|
|
1838
|
-
}
|
|
1839
|
-
/**
|
|
1840
|
-
* Bind this behavior to the source.
|
|
1841
|
-
* @param source - The source to bind to.
|
|
1842
|
-
* @param context - The execution context that the binding is operating within.
|
|
1843
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1844
|
-
*/
|
|
1845
|
-
bind(source, context, targets) {
|
|
1846
|
-
const directive = this.directive;
|
|
1847
|
-
const target = targets[directive.nodeId];
|
|
1848
|
-
const observer = this.getObserver(target);
|
|
1849
|
-
observer.target = target;
|
|
1850
|
-
observer.source = source;
|
|
1851
|
-
observer.context = context;
|
|
1852
|
-
this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
|
|
1853
|
-
}
|
|
1854
|
-
/**
|
|
1855
|
-
* Unbinds this behavior from the source.
|
|
1856
|
-
* @param source - The source to unbind from.
|
|
1857
|
-
* @param context - The execution context that the binding is operating within.
|
|
1858
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1859
|
-
*/
|
|
1860
|
-
unbind(source, context, targets) {
|
|
1861
|
-
const target = targets[this.directive.nodeId];
|
|
1862
|
-
const observer = this.getObserver(target);
|
|
1863
|
-
observer.dispose();
|
|
1864
|
-
observer.target = null;
|
|
1865
|
-
observer.source = null;
|
|
1866
|
-
observer.context = null;
|
|
1867
|
-
}
|
|
1868
|
-
/** @internal */
|
|
1869
|
-
handleChange(binding, observer) {
|
|
1870
|
-
const target = observer.target;
|
|
1871
|
-
const source = observer.source;
|
|
1872
|
-
const context = observer.context;
|
|
1873
|
-
this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
/**
|
|
1877
|
-
* A binding behavior for handling events.
|
|
1878
|
-
* @public
|
|
1879
|
-
*/
|
|
1880
|
-
class EventBinding {
|
|
1881
|
-
/**
|
|
1882
|
-
* Creates an instance of EventBinding.
|
|
1883
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1884
|
-
*/
|
|
1885
|
-
constructor(directive) {
|
|
1886
|
-
this.directive = directive;
|
|
1887
|
-
this.sourceProperty = `${directive.id}-s`;
|
|
1888
|
-
this.contextProperty = `${directive.id}-c`;
|
|
1889
|
-
}
|
|
1890
|
-
/**
|
|
1891
|
-
* Bind this behavior to the source.
|
|
1892
|
-
* @param source - The source to bind to.
|
|
1893
|
-
* @param context - The execution context that the binding is operating within.
|
|
1894
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1895
|
-
*/
|
|
1896
|
-
bind(source, context, targets) {
|
|
1897
|
-
const directive = this.directive;
|
|
1898
|
-
const target = targets[directive.nodeId];
|
|
1899
|
-
target[this.sourceProperty] = source;
|
|
1900
|
-
target[this.contextProperty] = context;
|
|
1901
|
-
target.addEventListener(directive.targetAspect, this, directive.options);
|
|
1902
|
-
}
|
|
1903
|
-
/**
|
|
1904
|
-
* Unbinds this behavior from the source.
|
|
1905
|
-
* @param source - The source to unbind from.
|
|
1906
|
-
* @param context - The execution context that the binding is operating within.
|
|
1907
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1908
|
-
*/
|
|
1909
|
-
unbind(source, context, targets) {
|
|
1910
|
-
const directive = this.directive;
|
|
1911
|
-
const target = targets[directive.nodeId];
|
|
1912
|
-
target[this.sourceProperty] = target[this.contextProperty] = null;
|
|
1913
|
-
target.removeEventListener(directive.targetAspect, this, directive.options);
|
|
1914
|
-
}
|
|
1915
|
-
/**
|
|
1916
|
-
* Creates a behavior.
|
|
1917
|
-
* @param targets - The targets available for behaviors to be attached to.
|
|
1918
|
-
*/
|
|
1919
|
-
createBehavior(targets) {
|
|
1920
|
-
return this;
|
|
1921
|
-
}
|
|
1922
|
-
/**
|
|
1923
|
-
* @internal
|
|
1924
|
-
*/
|
|
1925
|
-
handleEvent(event) {
|
|
1926
|
-
const target = event.currentTarget;
|
|
1927
|
-
ExecutionContext.setEvent(event);
|
|
1928
|
-
const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
|
|
1929
|
-
ExecutionContext.setEvent(null);
|
|
1930
|
-
if (result !== true) {
|
|
1931
|
-
event.preventDefault();
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
let twoWaySettings = {
|
|
1936
|
-
determineChangeEvent() {
|
|
1937
|
-
return "change";
|
|
1938
|
-
},
|
|
1939
|
-
};
|
|
1940
|
-
/**
|
|
1941
|
-
* A binding behavior for bindings that update in two directions.
|
|
1942
|
-
* @public
|
|
1943
|
-
*/
|
|
1944
|
-
class TwoWayBinding extends ChangeBinding {
|
|
1945
|
-
/**
|
|
1946
|
-
* Bind this behavior to the source.
|
|
1947
|
-
* @param source - The source to bind to.
|
|
1948
|
-
* @param context - The execution context that the binding is operating within.
|
|
1949
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1950
|
-
*/
|
|
1951
|
-
bind(source, context, targets) {
|
|
1952
|
-
var _a;
|
|
1953
|
-
super.bind(source, context, targets);
|
|
1954
|
-
const directive = this.directive;
|
|
1955
|
-
const target = targets[directive.nodeId];
|
|
1956
|
-
if (!this.changeEvent) {
|
|
1957
|
-
this.changeEvent =
|
|
1958
|
-
(_a = directive.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(directive, target);
|
|
1959
|
-
}
|
|
1960
|
-
target.addEventListener(this.changeEvent, this);
|
|
1961
|
-
}
|
|
1962
|
-
/**
|
|
1963
|
-
* Unbinds this behavior from the source.
|
|
1964
|
-
* @param source - The source to unbind from.
|
|
1965
|
-
* @param context - The execution context that the binding is operating within.
|
|
1966
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1967
|
-
*/
|
|
1968
|
-
unbind(source, context, targets) {
|
|
1969
|
-
super.unbind(source, context, targets);
|
|
1970
|
-
targets[this.directive.nodeId].removeEventListener(this.changeEvent, this);
|
|
1971
|
-
}
|
|
1972
|
-
/** @internal */
|
|
1973
|
-
handleEvent(event) {
|
|
1974
|
-
const directive = this.directive;
|
|
1975
|
-
const target = event.currentTarget;
|
|
1976
|
-
let value;
|
|
1977
|
-
switch (directive.aspectType) {
|
|
1978
|
-
case 1:
|
|
1979
|
-
value = target.getAttribute(directive.targetAspect);
|
|
1980
|
-
break;
|
|
1981
|
-
case 2:
|
|
1982
|
-
value = target.hasAttribute(directive.targetAspect);
|
|
1983
|
-
break;
|
|
1984
|
-
case 4:
|
|
1985
|
-
value = target.innerText;
|
|
1986
|
-
break;
|
|
1987
|
-
default:
|
|
1988
|
-
value = target[directive.targetAspect];
|
|
1989
|
-
break;
|
|
1990
|
-
}
|
|
1991
|
-
const observer = this.getObserver(target);
|
|
1992
|
-
const last = observer.last; // using internal API!!!
|
|
1993
|
-
last.propertySource[last.propertyName] = directive.options.fromView(value);
|
|
1994
|
-
}
|
|
1995
|
-
/**
|
|
1996
|
-
* Configures two-way binding.
|
|
1997
|
-
* @param settings - The settings to use for the two-way binding system.
|
|
1998
|
-
*/
|
|
1999
|
-
static configure(settings) {
|
|
2000
|
-
twoWaySettings = settings;
|
|
2001
|
-
}
|
|
2002
|
-
}
|
|
2003
|
-
/**
|
|
2004
|
-
* The default onChange binding configuration.
|
|
2005
|
-
* @public
|
|
2006
|
-
*/
|
|
2007
|
-
const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
|
|
2008
|
-
/**
|
|
2009
|
-
* The default twoWay binding configuration.
|
|
2010
|
-
* @public
|
|
2011
|
-
*/
|
|
2012
|
-
const twoWay = BindingConfig.define(BindingMode.define(TwoWayBinding), {
|
|
2013
|
-
fromView: v => v,
|
|
2014
|
-
});
|
|
2015
|
-
/**
|
|
2016
|
-
* The default onTime binding configuration.
|
|
2017
|
-
* @public
|
|
2018
|
-
*/
|
|
2019
|
-
const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
|
|
2020
|
-
once: true,
|
|
2021
|
-
});
|
|
2022
|
-
const signalMode = BindingMode.define(SignalBinding);
|
|
2023
|
-
/**
|
|
2024
|
-
* Creates a signal binding configuration with the supplied options.
|
|
2025
|
-
* @param options - The signal name or a binding to use to retrieve the signal name.
|
|
2026
|
-
* @returns A binding configuration.
|
|
2027
|
-
* @public
|
|
2028
|
-
*/
|
|
2029
|
-
const signal = (options) => {
|
|
2030
|
-
return { mode: signalMode, options };
|
|
2031
|
-
};
|
|
2081
|
+
const setProperty = (t, a, v) => (t[a] = v);
|
|
2082
|
+
const eventTarget = () => void 0;
|
|
2032
2083
|
/**
|
|
2033
2084
|
* A directive that applies bindings.
|
|
2034
2085
|
* @public
|
|
@@ -2036,19 +2087,22 @@ const signal = (options) => {
|
|
|
2036
2087
|
class HTMLBindingDirective {
|
|
2037
2088
|
/**
|
|
2038
2089
|
* Creates an instance of HTMLBindingDirective.
|
|
2039
|
-
* @param
|
|
2040
|
-
* @param mode - The binding mode to use when applying the binding.
|
|
2041
|
-
* @param options - The options to configure the binding with.
|
|
2090
|
+
* @param dataBinding - The binding configuration to apply.
|
|
2042
2091
|
*/
|
|
2043
|
-
constructor(
|
|
2044
|
-
this.
|
|
2045
|
-
this.
|
|
2046
|
-
|
|
2047
|
-
|
|
2092
|
+
constructor(dataBinding) {
|
|
2093
|
+
this.dataBinding = dataBinding;
|
|
2094
|
+
this.updateTarget = null;
|
|
2095
|
+
/**
|
|
2096
|
+
* The unique id of the factory.
|
|
2097
|
+
*/
|
|
2098
|
+
this.id = nextId();
|
|
2048
2099
|
/**
|
|
2049
2100
|
* The type of aspect to target.
|
|
2050
2101
|
*/
|
|
2051
2102
|
this.aspectType = Aspect.content;
|
|
2103
|
+
/** @internal */
|
|
2104
|
+
this.bind = this.bindDefault;
|
|
2105
|
+
this.data = `${this.id}-d`;
|
|
2052
2106
|
}
|
|
2053
2107
|
/**
|
|
2054
2108
|
* Creates HTML to be used within a template.
|
|
@@ -2059,31 +2113,133 @@ class HTMLBindingDirective {
|
|
|
2059
2113
|
}
|
|
2060
2114
|
/**
|
|
2061
2115
|
* Creates a behavior.
|
|
2062
|
-
* @param targets - The targets available for behaviors to be attached to.
|
|
2063
2116
|
*/
|
|
2064
|
-
createBehavior(
|
|
2065
|
-
if (this.
|
|
2117
|
+
createBehavior() {
|
|
2118
|
+
if (this.updateTarget === null) {
|
|
2066
2119
|
if (this.targetAspect === "innerHTML") {
|
|
2067
|
-
this.
|
|
2120
|
+
this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
|
|
2068
2121
|
}
|
|
2069
|
-
|
|
2122
|
+
switch (this.aspectType) {
|
|
2123
|
+
case 1:
|
|
2124
|
+
this.updateTarget = DOM.setAttribute;
|
|
2125
|
+
break;
|
|
2126
|
+
case 2:
|
|
2127
|
+
this.updateTarget = DOM.setBooleanAttribute;
|
|
2128
|
+
break;
|
|
2129
|
+
case 3:
|
|
2130
|
+
this.updateTarget = setProperty;
|
|
2131
|
+
break;
|
|
2132
|
+
case 4:
|
|
2133
|
+
this.bind = this.bindContent;
|
|
2134
|
+
this.updateTarget = updateContent;
|
|
2135
|
+
break;
|
|
2136
|
+
case 5:
|
|
2137
|
+
this.updateTarget = updateTokenList;
|
|
2138
|
+
break;
|
|
2139
|
+
case 6:
|
|
2140
|
+
this.bind = this.bindEvent;
|
|
2141
|
+
this.updateTarget = eventTarget;
|
|
2142
|
+
break;
|
|
2143
|
+
default:
|
|
2144
|
+
throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
return this;
|
|
2148
|
+
}
|
|
2149
|
+
/** @internal */
|
|
2150
|
+
bindDefault(controller) {
|
|
2151
|
+
var _a;
|
|
2152
|
+
const target = controller.targets[this.nodeId];
|
|
2153
|
+
const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
|
|
2154
|
+
observer.target = target;
|
|
2155
|
+
observer.controller = controller;
|
|
2156
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
2157
|
+
if (this.updateTarget === updateContent) {
|
|
2158
|
+
controller.onUnbind(this);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
/** @internal */
|
|
2162
|
+
bindContent(controller) {
|
|
2163
|
+
this.bindDefault(controller);
|
|
2164
|
+
controller.onUnbind(this);
|
|
2165
|
+
}
|
|
2166
|
+
/** @internal */
|
|
2167
|
+
bindEvent(controller) {
|
|
2168
|
+
const target = controller.targets[this.nodeId];
|
|
2169
|
+
target[this.data] = controller;
|
|
2170
|
+
target.addEventListener(this.targetAspect, this, this.dataBinding.options);
|
|
2171
|
+
}
|
|
2172
|
+
/** @internal */
|
|
2173
|
+
unbind(controller) {
|
|
2174
|
+
const target = controller.targets[this.nodeId];
|
|
2175
|
+
const view = target.$fastView;
|
|
2176
|
+
if (view !== void 0 && view.isComposed) {
|
|
2177
|
+
view.unbind();
|
|
2178
|
+
view.needsBindOnly = true;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
/** @internal */
|
|
2182
|
+
handleEvent(event) {
|
|
2183
|
+
const target = event.currentTarget;
|
|
2184
|
+
ExecutionContext.setEvent(event);
|
|
2185
|
+
const controller = target[this.data];
|
|
2186
|
+
const result = this.dataBinding.evaluate(controller.source, controller.context);
|
|
2187
|
+
ExecutionContext.setEvent(null);
|
|
2188
|
+
if (result !== true) {
|
|
2189
|
+
event.preventDefault();
|
|
2070
2190
|
}
|
|
2071
|
-
|
|
2191
|
+
}
|
|
2192
|
+
/** @internal */
|
|
2193
|
+
handleChange(binding, observer) {
|
|
2194
|
+
const target = observer.target;
|
|
2195
|
+
const controller = observer.controller;
|
|
2196
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
2072
2197
|
}
|
|
2073
2198
|
}
|
|
2074
2199
|
HTMLDirective.define(HTMLBindingDirective, { aspected: true });
|
|
2075
2200
|
/**
|
|
2076
|
-
* Creates
|
|
2077
|
-
* @param binding - The binding
|
|
2078
|
-
* @param
|
|
2079
|
-
* @returns A binding
|
|
2201
|
+
* Creates an standard binding.
|
|
2202
|
+
* @param binding - The binding to refresh when changed.
|
|
2203
|
+
* @param isVolatile - Indicates whether the binding is volatile or not.
|
|
2204
|
+
* @returns A binding configuration.
|
|
2205
|
+
* @public
|
|
2206
|
+
*/
|
|
2207
|
+
function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
|
|
2208
|
+
return new OnChangeBinding(binding, isVolatile);
|
|
2209
|
+
}
|
|
2210
|
+
/**
|
|
2211
|
+
* Creates a one time binding
|
|
2212
|
+
* @param binding - The binding to refresh when signaled.
|
|
2213
|
+
* @returns A binding configuration.
|
|
2080
2214
|
* @public
|
|
2081
2215
|
*/
|
|
2082
|
-
function
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2216
|
+
function oneTime(binding) {
|
|
2217
|
+
return new OneTimeBinding(binding);
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Creates an event listener binding.
|
|
2221
|
+
* @param binding - The binding to invoke when the event is raised.
|
|
2222
|
+
* @param options - Event listener options.
|
|
2223
|
+
* @returns A binding configuration.
|
|
2224
|
+
* @public
|
|
2225
|
+
*/
|
|
2226
|
+
function listener(binding, options) {
|
|
2227
|
+
const config = new OnChangeBinding(binding, false);
|
|
2228
|
+
config.options = options;
|
|
2229
|
+
return config;
|
|
2230
|
+
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Normalizes the input value into a binding.
|
|
2233
|
+
* @param value - The value to create the default binding for.
|
|
2234
|
+
* @returns A binding configuration for the provided value.
|
|
2235
|
+
* @public
|
|
2236
|
+
*/
|
|
2237
|
+
function normalizeBinding(value) {
|
|
2238
|
+
return isFunction(value)
|
|
2239
|
+
? bind(value)
|
|
2240
|
+
: value instanceof Binding
|
|
2241
|
+
? value
|
|
2242
|
+
: oneTime(() => value);
|
|
2087
2243
|
}
|
|
2088
2244
|
|
|
2089
2245
|
function removeNodeSequence(firstNode, lastNode) {
|
|
@@ -2112,17 +2268,87 @@ class HTMLView {
|
|
|
2112
2268
|
this.factories = factories;
|
|
2113
2269
|
this.targets = targets;
|
|
2114
2270
|
this.behaviors = null;
|
|
2271
|
+
this.unbindables = [];
|
|
2115
2272
|
/**
|
|
2116
2273
|
* The data that the view is bound to.
|
|
2117
2274
|
*/
|
|
2118
2275
|
this.source = null;
|
|
2276
|
+
/**
|
|
2277
|
+
* Indicates whether the controller is bound.
|
|
2278
|
+
*/
|
|
2279
|
+
this.isBound = false;
|
|
2280
|
+
/**
|
|
2281
|
+
* Indicates how the source's lifetime relates to the controller's lifetime.
|
|
2282
|
+
*/
|
|
2283
|
+
this.sourceLifetime = SourceLifetime.unknown;
|
|
2119
2284
|
/**
|
|
2120
2285
|
* The execution context the view is running within.
|
|
2121
2286
|
*/
|
|
2122
|
-
this.context =
|
|
2287
|
+
this.context = this;
|
|
2288
|
+
/**
|
|
2289
|
+
* The index of the current item within a repeat context.
|
|
2290
|
+
*/
|
|
2291
|
+
this.index = 0;
|
|
2292
|
+
/**
|
|
2293
|
+
* The length of the current collection within a repeat context.
|
|
2294
|
+
*/
|
|
2295
|
+
this.length = 0;
|
|
2123
2296
|
this.firstChild = fragment.firstChild;
|
|
2124
2297
|
this.lastChild = fragment.lastChild;
|
|
2125
2298
|
}
|
|
2299
|
+
/**
|
|
2300
|
+
* The current event within an event handler.
|
|
2301
|
+
*/
|
|
2302
|
+
get event() {
|
|
2303
|
+
return ExecutionContext.getEvent();
|
|
2304
|
+
}
|
|
2305
|
+
/**
|
|
2306
|
+
* Indicates whether the current item within a repeat context
|
|
2307
|
+
* has an even index.
|
|
2308
|
+
*/
|
|
2309
|
+
get isEven() {
|
|
2310
|
+
return this.index % 2 === 0;
|
|
2311
|
+
}
|
|
2312
|
+
/**
|
|
2313
|
+
* Indicates whether the current item within a repeat context
|
|
2314
|
+
* has an odd index.
|
|
2315
|
+
*/
|
|
2316
|
+
get isOdd() {
|
|
2317
|
+
return this.index % 2 !== 0;
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Indicates whether the current item within a repeat context
|
|
2321
|
+
* is the first item in the collection.
|
|
2322
|
+
*/
|
|
2323
|
+
get isFirst() {
|
|
2324
|
+
return this.index === 0;
|
|
2325
|
+
}
|
|
2326
|
+
/**
|
|
2327
|
+
* Indicates whether the current item within a repeat context
|
|
2328
|
+
* is somewhere in the middle of the collection.
|
|
2329
|
+
*/
|
|
2330
|
+
get isInMiddle() {
|
|
2331
|
+
return !this.isFirst && !this.isLast;
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Indicates whether the current item within a repeat context
|
|
2335
|
+
* is the last item in the collection.
|
|
2336
|
+
*/
|
|
2337
|
+
get isLast() {
|
|
2338
|
+
return this.index === this.length - 1;
|
|
2339
|
+
}
|
|
2340
|
+
/**
|
|
2341
|
+
* Returns the typed event detail of a custom event.
|
|
2342
|
+
*/
|
|
2343
|
+
eventDetail() {
|
|
2344
|
+
return this.event.detail;
|
|
2345
|
+
}
|
|
2346
|
+
/**
|
|
2347
|
+
* Returns the typed event target of the event.
|
|
2348
|
+
*/
|
|
2349
|
+
eventTarget() {
|
|
2350
|
+
return this.event.target;
|
|
2351
|
+
}
|
|
2126
2352
|
/**
|
|
2127
2353
|
* Appends the view's DOM nodes to the referenced node.
|
|
2128
2354
|
* @param node - The parent node to append the view's DOM nodes to.
|
|
@@ -2139,8 +2365,10 @@ class HTMLView {
|
|
|
2139
2365
|
node.parentNode.insertBefore(this.fragment, node);
|
|
2140
2366
|
}
|
|
2141
2367
|
else {
|
|
2142
|
-
const parentNode = node.parentNode;
|
|
2143
2368
|
const end = this.lastChild;
|
|
2369
|
+
if (node.previousSibling === end)
|
|
2370
|
+
return;
|
|
2371
|
+
const parentNode = node.parentNode;
|
|
2144
2372
|
let current = this.firstChild;
|
|
2145
2373
|
let next;
|
|
2146
2374
|
while (current !== end) {
|
|
@@ -2175,58 +2403,61 @@ class HTMLView {
|
|
|
2175
2403
|
removeNodeSequence(this.firstChild, this.lastChild);
|
|
2176
2404
|
this.unbind();
|
|
2177
2405
|
}
|
|
2406
|
+
onUnbind(behavior) {
|
|
2407
|
+
this.unbindables.push(behavior);
|
|
2408
|
+
}
|
|
2178
2409
|
/**
|
|
2179
2410
|
* Binds a view's behaviors to its binding source.
|
|
2180
2411
|
* @param source - The binding source for the view's binding behaviors.
|
|
2181
2412
|
* @param context - The execution context to run the behaviors within.
|
|
2182
2413
|
*/
|
|
2183
|
-
bind(source, context) {
|
|
2184
|
-
|
|
2185
|
-
const oldSource = this.source;
|
|
2186
|
-
if (oldSource === source) {
|
|
2414
|
+
bind(source, context = this) {
|
|
2415
|
+
if (this.source === source) {
|
|
2187
2416
|
return;
|
|
2188
2417
|
}
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2194
|
-
const current = behaviors[i];
|
|
2195
|
-
current.unbind(oldSource, context, targets);
|
|
2196
|
-
current.bind(source, context, targets);
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
else if (behaviors === null) {
|
|
2418
|
+
let behaviors = this.behaviors;
|
|
2419
|
+
if (behaviors === null) {
|
|
2420
|
+
this.source = source;
|
|
2421
|
+
this.context = context;
|
|
2200
2422
|
this.behaviors = behaviors = new Array(this.factories.length);
|
|
2201
2423
|
const factories = this.factories;
|
|
2202
2424
|
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
2203
|
-
const behavior = factories[i].createBehavior(
|
|
2204
|
-
behavior.bind(
|
|
2425
|
+
const behavior = factories[i].createBehavior();
|
|
2426
|
+
behavior.bind(this);
|
|
2205
2427
|
behaviors[i] = behavior;
|
|
2206
2428
|
}
|
|
2207
2429
|
}
|
|
2208
2430
|
else {
|
|
2431
|
+
if (this.source !== null) {
|
|
2432
|
+
this.evaluateUnbindables();
|
|
2433
|
+
}
|
|
2434
|
+
this.isBound = false;
|
|
2435
|
+
this.source = source;
|
|
2436
|
+
this.context = context;
|
|
2209
2437
|
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2210
|
-
behaviors[i].bind(
|
|
2438
|
+
behaviors[i].bind(this);
|
|
2211
2439
|
}
|
|
2212
2440
|
}
|
|
2441
|
+
this.isBound = true;
|
|
2213
2442
|
}
|
|
2214
2443
|
/**
|
|
2215
2444
|
* Unbinds a view's behaviors from its binding source.
|
|
2216
2445
|
*/
|
|
2217
2446
|
unbind() {
|
|
2218
|
-
|
|
2219
|
-
if (oldSource === null) {
|
|
2447
|
+
if (!this.isBound || this.source === null) {
|
|
2220
2448
|
return;
|
|
2221
2449
|
}
|
|
2222
|
-
|
|
2223
|
-
const context = this.context;
|
|
2224
|
-
const behaviors = this.behaviors;
|
|
2225
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2226
|
-
behaviors[i].unbind(oldSource, context, targets);
|
|
2227
|
-
}
|
|
2450
|
+
this.evaluateUnbindables();
|
|
2228
2451
|
this.source = null;
|
|
2229
|
-
this.context =
|
|
2452
|
+
this.context = this;
|
|
2453
|
+
this.isBound = false;
|
|
2454
|
+
}
|
|
2455
|
+
evaluateUnbindables() {
|
|
2456
|
+
const unbindables = this.unbindables;
|
|
2457
|
+
for (let i = 0, ii = unbindables.length; i < ii; ++i) {
|
|
2458
|
+
unbindables[i].unbind(this);
|
|
2459
|
+
}
|
|
2460
|
+
unbindables.length = 0;
|
|
2230
2461
|
}
|
|
2231
2462
|
/**
|
|
2232
2463
|
* Efficiently disposes of a contiguous range of synthetic view instances.
|
|
@@ -2242,6 +2473,8 @@ class HTMLView {
|
|
|
2242
2473
|
}
|
|
2243
2474
|
}
|
|
2244
2475
|
}
|
|
2476
|
+
Observable.defineProperty(HTMLView.prototype, "index");
|
|
2477
|
+
Observable.defineProperty(HTMLView.prototype, "length");
|
|
2245
2478
|
|
|
2246
2479
|
const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
|
|
2247
2480
|
const descriptorCache = {};
|
|
@@ -2250,6 +2483,22 @@ const next = {
|
|
|
2250
2483
|
index: 0,
|
|
2251
2484
|
node: null,
|
|
2252
2485
|
};
|
|
2486
|
+
function tryWarn(name) {
|
|
2487
|
+
if (!name.startsWith("fast-")) {
|
|
2488
|
+
FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
const warningHost = new Proxy(document.createElement("div"), {
|
|
2492
|
+
get(target, property) {
|
|
2493
|
+
tryWarn(property);
|
|
2494
|
+
const value = Reflect.get(target, property);
|
|
2495
|
+
return isFunction(value) ? value.bind(target) : value;
|
|
2496
|
+
},
|
|
2497
|
+
set(target, property, value) {
|
|
2498
|
+
tryWarn(property);
|
|
2499
|
+
return Reflect.set(target, property, value);
|
|
2500
|
+
},
|
|
2501
|
+
});
|
|
2253
2502
|
class CompilationContext {
|
|
2254
2503
|
constructor(fragment, directives) {
|
|
2255
2504
|
this.fragment = fragment;
|
|
@@ -2300,7 +2549,7 @@ class CompilationContext {
|
|
|
2300
2549
|
const fragment = this.fragment.cloneNode(true);
|
|
2301
2550
|
const targets = Object.create(this.proto);
|
|
2302
2551
|
targets.r = fragment;
|
|
2303
|
-
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget :
|
|
2552
|
+
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
|
|
2304
2553
|
for (const id of this.nodeIds) {
|
|
2305
2554
|
targets[id]; // trigger locator
|
|
2306
2555
|
}
|
|
@@ -2317,7 +2566,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
|
|
|
2317
2566
|
let result = null;
|
|
2318
2567
|
if (parseResult === null) {
|
|
2319
2568
|
if (includeBasicValues) {
|
|
2320
|
-
result =
|
|
2569
|
+
result = new HTMLBindingDirective(oneTime(() => attrValue));
|
|
2321
2570
|
Aspect.assign(result, attr.name);
|
|
2322
2571
|
}
|
|
2323
2572
|
}
|
|
@@ -2354,6 +2603,7 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
|
|
|
2354
2603
|
}
|
|
2355
2604
|
else {
|
|
2356
2605
|
currentNode.textContent = " ";
|
|
2606
|
+
Aspect.assign(currentPart);
|
|
2357
2607
|
context.addFactory(currentPart, parentId, nodeId, nodeIndex);
|
|
2358
2608
|
}
|
|
2359
2609
|
lastNode = currentNode;
|
|
@@ -2486,22 +2736,28 @@ const Compiler = {
|
|
|
2486
2736
|
return parts[0];
|
|
2487
2737
|
}
|
|
2488
2738
|
let sourceAspect;
|
|
2739
|
+
let binding;
|
|
2740
|
+
let isVolatile = false;
|
|
2489
2741
|
const partCount = parts.length;
|
|
2490
2742
|
const finalParts = parts.map((x) => {
|
|
2491
2743
|
if (isString(x)) {
|
|
2492
2744
|
return () => x;
|
|
2493
2745
|
}
|
|
2494
2746
|
sourceAspect = x.sourceAspect || sourceAspect;
|
|
2495
|
-
|
|
2747
|
+
binding = x.dataBinding || binding;
|
|
2748
|
+
isVolatile = isVolatile || x.dataBinding.isVolatile;
|
|
2749
|
+
return x.dataBinding.evaluate;
|
|
2496
2750
|
});
|
|
2497
|
-
const
|
|
2751
|
+
const expression = (scope, context) => {
|
|
2498
2752
|
let output = "";
|
|
2499
2753
|
for (let i = 0; i < partCount; ++i) {
|
|
2500
2754
|
output += finalParts[i](scope, context);
|
|
2501
2755
|
}
|
|
2502
2756
|
return output;
|
|
2503
2757
|
};
|
|
2504
|
-
|
|
2758
|
+
binding.evaluate = expression;
|
|
2759
|
+
binding.isVolatile = isVolatile;
|
|
2760
|
+
const directive = new HTMLBindingDirective(binding);
|
|
2505
2761
|
Aspect.assign(directive, sourceAspect);
|
|
2506
2762
|
return directive;
|
|
2507
2763
|
},
|
|
@@ -2539,9 +2795,9 @@ class ViewTemplate {
|
|
|
2539
2795
|
* @param hostBindingTarget - An HTML element to target the host bindings at if different from the
|
|
2540
2796
|
* host that the template is being attached to.
|
|
2541
2797
|
*/
|
|
2542
|
-
render(source, host, hostBindingTarget
|
|
2543
|
-
const view = this.create(hostBindingTarget
|
|
2544
|
-
view.bind(source
|
|
2798
|
+
render(source, host, hostBindingTarget) {
|
|
2799
|
+
const view = this.create(hostBindingTarget);
|
|
2800
|
+
view.bind(source);
|
|
2545
2801
|
view.appendTo(host);
|
|
2546
2802
|
return view;
|
|
2547
2803
|
}
|
|
@@ -2581,12 +2837,12 @@ function html(strings, ...values) {
|
|
|
2581
2837
|
let definition;
|
|
2582
2838
|
html += currentString;
|
|
2583
2839
|
if (isFunction(currentValue)) {
|
|
2584
|
-
html += createAspectedHTML(bind(currentValue), currentString, add);
|
|
2840
|
+
html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
|
|
2585
2841
|
}
|
|
2586
2842
|
else if (isString(currentValue)) {
|
|
2587
2843
|
const match = lastAttributeNameRegex.exec(currentString);
|
|
2588
2844
|
if (match !== null) {
|
|
2589
|
-
const directive =
|
|
2845
|
+
const directive = new HTMLBindingDirective(oneTime(() => currentValue));
|
|
2590
2846
|
Aspect.assign(directive, match[2]);
|
|
2591
2847
|
html += directive.createHTML(add);
|
|
2592
2848
|
}
|
|
@@ -2594,8 +2850,11 @@ function html(strings, ...values) {
|
|
|
2594
2850
|
html += currentValue;
|
|
2595
2851
|
}
|
|
2596
2852
|
}
|
|
2853
|
+
else if (currentValue instanceof Binding) {
|
|
2854
|
+
html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
|
|
2855
|
+
}
|
|
2597
2856
|
else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
|
|
2598
|
-
html += createAspectedHTML(
|
|
2857
|
+
html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
|
|
2599
2858
|
}
|
|
2600
2859
|
else {
|
|
2601
2860
|
if (definition.aspected) {
|
|
@@ -2608,26 +2867,6 @@ function html(strings, ...values) {
|
|
|
2608
2867
|
}
|
|
2609
2868
|
return new ViewTemplate(html + strings[strings.length - 1], factories);
|
|
2610
2869
|
}
|
|
2611
|
-
/**
|
|
2612
|
-
* Transforms a template literal string into a ChildViewTemplate.
|
|
2613
|
-
* @param strings - The string fragments that are interpolated with the values.
|
|
2614
|
-
* @param values - The values that are interpolated with the string fragments.
|
|
2615
|
-
* @remarks
|
|
2616
|
-
* The html helper supports interpolation of strings, numbers, binding expressions,
|
|
2617
|
-
* other template instances, and Directive instances.
|
|
2618
|
-
* @public
|
|
2619
|
-
*/
|
|
2620
|
-
const child = html;
|
|
2621
|
-
/**
|
|
2622
|
-
* Transforms a template literal string into an ItemViewTemplate.
|
|
2623
|
-
* @param strings - The string fragments that are interpolated with the values.
|
|
2624
|
-
* @param values - The values that are interpolated with the string fragments.
|
|
2625
|
-
* @remarks
|
|
2626
|
-
* The html helper supports interpolation of strings, numbers, binding expressions,
|
|
2627
|
-
* other template instances, and Directive instances.
|
|
2628
|
-
* @public
|
|
2629
|
-
*/
|
|
2630
|
-
const item = html;
|
|
2631
2870
|
|
|
2632
2871
|
/**
|
|
2633
2872
|
* The runtime behavior for template references.
|
|
@@ -2635,20 +2874,12 @@ const item = html;
|
|
|
2635
2874
|
*/
|
|
2636
2875
|
class RefDirective extends StatelessAttachedAttributeDirective {
|
|
2637
2876
|
/**
|
|
2638
|
-
* Bind this behavior
|
|
2639
|
-
* @param
|
|
2640
|
-
* @param context - The execution context that the binding is operating within.
|
|
2641
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
2877
|
+
* Bind this behavior.
|
|
2878
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2642
2879
|
*/
|
|
2643
|
-
bind(
|
|
2644
|
-
source[this.options] = targets[this.nodeId];
|
|
2880
|
+
bind(controller) {
|
|
2881
|
+
controller.source[this.options] = controller.targets[this.nodeId];
|
|
2645
2882
|
}
|
|
2646
|
-
/**
|
|
2647
|
-
* Unbinds this behavior from the source.
|
|
2648
|
-
* @param source - The source to unbind from.
|
|
2649
|
-
*/
|
|
2650
|
-
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
|
|
2651
|
-
unbind() { }
|
|
2652
2883
|
}
|
|
2653
2884
|
HTMLDirective.define(RefDirective);
|
|
2654
2885
|
/**
|
|
@@ -2660,27 +2891,34 @@ const ref = (propertyName) => new RefDirective(propertyName);
|
|
|
2660
2891
|
|
|
2661
2892
|
/**
|
|
2662
2893
|
* A directive that enables basic conditional rendering in a template.
|
|
2663
|
-
* @param
|
|
2894
|
+
* @param condition - The condition to test for rendering.
|
|
2664
2895
|
* @param templateOrTemplateBinding - The template or a binding that gets
|
|
2665
2896
|
* the template to render when the condition is true.
|
|
2666
2897
|
* @public
|
|
2667
2898
|
*/
|
|
2668
|
-
function when(
|
|
2669
|
-
const
|
|
2899
|
+
function when(condition, templateOrTemplateBinding) {
|
|
2900
|
+
const dataBinding = isFunction(condition) ? condition : () => condition;
|
|
2901
|
+
const templateBinding = isFunction(templateOrTemplateBinding)
|
|
2670
2902
|
? templateOrTemplateBinding
|
|
2671
2903
|
: () => templateOrTemplateBinding;
|
|
2672
|
-
return (source, context) =>
|
|
2904
|
+
return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
|
|
2673
2905
|
}
|
|
2674
2906
|
|
|
2675
2907
|
const defaultRepeatOptions = Object.freeze({
|
|
2676
2908
|
positioning: false,
|
|
2677
2909
|
recycle: true,
|
|
2678
2910
|
});
|
|
2679
|
-
function bindWithoutPositioning(view, items, index,
|
|
2680
|
-
view.
|
|
2911
|
+
function bindWithoutPositioning(view, items, index, controller) {
|
|
2912
|
+
view.context.parent = controller.source;
|
|
2913
|
+
view.context.parentContext = controller.context;
|
|
2914
|
+
view.bind(items[index]);
|
|
2681
2915
|
}
|
|
2682
|
-
function bindWithPositioning(view, items, index,
|
|
2683
|
-
view.
|
|
2916
|
+
function bindWithPositioning(view, items, index, controller) {
|
|
2917
|
+
view.context.parent = controller.source;
|
|
2918
|
+
view.context.parentContext = controller.context;
|
|
2919
|
+
view.context.length = items.length;
|
|
2920
|
+
view.context.index = index;
|
|
2921
|
+
view.bind(items[index]);
|
|
2684
2922
|
}
|
|
2685
2923
|
/**
|
|
2686
2924
|
* A behavior that renders a template for each item in an array.
|
|
@@ -2690,57 +2928,45 @@ class RepeatBehavior {
|
|
|
2690
2928
|
/**
|
|
2691
2929
|
* Creates an instance of RepeatBehavior.
|
|
2692
2930
|
* @param location - The location in the DOM to render the repeat.
|
|
2693
|
-
* @param
|
|
2931
|
+
* @param dataBinding - The array to render.
|
|
2694
2932
|
* @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
|
|
2695
2933
|
* @param templateBinding - The template to render for each item.
|
|
2696
2934
|
* @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
|
|
2697
2935
|
* @param options - Options used to turn on special repeat features.
|
|
2698
2936
|
*/
|
|
2699
|
-
constructor(
|
|
2700
|
-
this.
|
|
2701
|
-
this.itemsBinding = itemsBinding;
|
|
2702
|
-
this.templateBinding = templateBinding;
|
|
2703
|
-
this.options = options;
|
|
2704
|
-
this.source = null;
|
|
2937
|
+
constructor(directive) {
|
|
2938
|
+
this.directive = directive;
|
|
2705
2939
|
this.views = [];
|
|
2706
2940
|
this.items = null;
|
|
2707
2941
|
this.itemsObserver = null;
|
|
2708
|
-
this.context = void 0;
|
|
2709
|
-
this.childContext = void 0;
|
|
2710
2942
|
this.bindView = bindWithoutPositioning;
|
|
2711
|
-
this.itemsBindingObserver =
|
|
2712
|
-
this.templateBindingObserver =
|
|
2713
|
-
if (options.positioning) {
|
|
2943
|
+
this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
|
|
2944
|
+
this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
|
|
2945
|
+
if (directive.options.positioning) {
|
|
2714
2946
|
this.bindView = bindWithPositioning;
|
|
2715
2947
|
}
|
|
2716
2948
|
}
|
|
2717
2949
|
/**
|
|
2718
|
-
* Bind this behavior
|
|
2719
|
-
* @param
|
|
2720
|
-
* @param context - The execution context that the binding is operating within.
|
|
2950
|
+
* Bind this behavior.
|
|
2951
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2721
2952
|
*/
|
|
2722
|
-
bind(
|
|
2723
|
-
this.
|
|
2724
|
-
this.
|
|
2725
|
-
this.
|
|
2726
|
-
this.
|
|
2727
|
-
this.template = this.templateBindingObserver.observe(source, this.context);
|
|
2953
|
+
bind(controller) {
|
|
2954
|
+
this.location = controller.targets[this.directive.nodeId];
|
|
2955
|
+
this.controller = controller;
|
|
2956
|
+
this.items = this.itemsBindingObserver.bind(controller);
|
|
2957
|
+
this.template = this.templateBindingObserver.bind(controller);
|
|
2728
2958
|
this.observeItems(true);
|
|
2729
2959
|
this.refreshAllViews();
|
|
2960
|
+
controller.onUnbind(this);
|
|
2730
2961
|
}
|
|
2731
2962
|
/**
|
|
2732
|
-
* Unbinds this behavior
|
|
2733
|
-
* @param source - The source to unbind from.
|
|
2963
|
+
* Unbinds this behavior.
|
|
2734
2964
|
*/
|
|
2735
2965
|
unbind() {
|
|
2736
|
-
this.source = null;
|
|
2737
|
-
this.items = null;
|
|
2738
2966
|
if (this.itemsObserver !== null) {
|
|
2739
2967
|
this.itemsObserver.unsubscribe(this);
|
|
2740
2968
|
}
|
|
2741
2969
|
this.unbindAllViews();
|
|
2742
|
-
this.itemsBindingObserver.dispose();
|
|
2743
|
-
this.templateBindingObserver.dispose();
|
|
2744
2970
|
}
|
|
2745
2971
|
/**
|
|
2746
2972
|
* Handles changes in the array, its items, and the repeat template.
|
|
@@ -2748,15 +2974,18 @@ class RepeatBehavior {
|
|
|
2748
2974
|
* @param args - The details about what was changed.
|
|
2749
2975
|
*/
|
|
2750
2976
|
handleChange(source, args) {
|
|
2751
|
-
if (
|
|
2752
|
-
this.items = this.itemsBindingObserver.
|
|
2977
|
+
if (args === this.itemsBindingObserver) {
|
|
2978
|
+
this.items = this.itemsBindingObserver.bind(this.controller);
|
|
2753
2979
|
this.observeItems();
|
|
2754
2980
|
this.refreshAllViews();
|
|
2755
2981
|
}
|
|
2756
|
-
else if (
|
|
2757
|
-
this.template = this.templateBindingObserver.
|
|
2982
|
+
else if (args === this.templateBindingObserver) {
|
|
2983
|
+
this.template = this.templateBindingObserver.bind(this.controller);
|
|
2758
2984
|
this.refreshAllViews(true);
|
|
2759
2985
|
}
|
|
2986
|
+
else if (!args[0]) {
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2760
2989
|
else if (args[0].reset) {
|
|
2761
2990
|
this.refreshAllViews();
|
|
2762
2991
|
}
|
|
@@ -2781,39 +3010,57 @@ class RepeatBehavior {
|
|
|
2781
3010
|
}
|
|
2782
3011
|
updateViews(splices) {
|
|
2783
3012
|
const views = this.views;
|
|
2784
|
-
const childContext = this.childContext;
|
|
2785
|
-
const totalRemoved = [];
|
|
2786
3013
|
const bindView = this.bindView;
|
|
2787
|
-
let removeDelta = 0;
|
|
2788
|
-
for (let i = 0, ii = splices.length; i < ii; ++i) {
|
|
2789
|
-
const splice = splices[i];
|
|
2790
|
-
const removed = splice.removed;
|
|
2791
|
-
totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
|
|
2792
|
-
removeDelta -= splice.addedCount;
|
|
2793
|
-
}
|
|
2794
3014
|
const items = this.items;
|
|
2795
3015
|
const template = this.template;
|
|
3016
|
+
const controller = this.controller;
|
|
3017
|
+
const recycle = this.directive.options.recycle;
|
|
3018
|
+
const leftoverViews = [];
|
|
3019
|
+
let leftoverIndex = 0;
|
|
3020
|
+
let availableViews = 0;
|
|
2796
3021
|
for (let i = 0, ii = splices.length; i < ii; ++i) {
|
|
2797
3022
|
const splice = splices[i];
|
|
3023
|
+
const removed = splice.removed;
|
|
3024
|
+
let removeIndex = 0;
|
|
2798
3025
|
let addIndex = splice.index;
|
|
2799
3026
|
const end = addIndex + splice.addedCount;
|
|
3027
|
+
const removedViews = views.splice(splice.index, removed.length);
|
|
3028
|
+
const totalAvailableViews = (availableViews =
|
|
3029
|
+
leftoverViews.length + removedViews.length);
|
|
2800
3030
|
for (; addIndex < end; ++addIndex) {
|
|
2801
3031
|
const neighbor = views[addIndex];
|
|
2802
3032
|
const location = neighbor ? neighbor.firstChild : this.location;
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
3033
|
+
let view;
|
|
3034
|
+
if (recycle && availableViews > 0) {
|
|
3035
|
+
if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
|
|
3036
|
+
view = removedViews[removeIndex];
|
|
3037
|
+
removeIndex++;
|
|
3038
|
+
}
|
|
3039
|
+
else {
|
|
3040
|
+
view = leftoverViews[leftoverIndex];
|
|
3041
|
+
leftoverIndex++;
|
|
3042
|
+
}
|
|
3043
|
+
availableViews--;
|
|
3044
|
+
}
|
|
3045
|
+
else {
|
|
3046
|
+
view = template.create();
|
|
3047
|
+
}
|
|
2806
3048
|
views.splice(addIndex, 0, view);
|
|
2807
|
-
bindView(view, items, addIndex,
|
|
3049
|
+
bindView(view, items, addIndex, controller);
|
|
2808
3050
|
view.insertBefore(location);
|
|
2809
3051
|
}
|
|
3052
|
+
if (removedViews[removeIndex]) {
|
|
3053
|
+
leftoverViews.push(...removedViews.slice(removeIndex));
|
|
3054
|
+
}
|
|
2810
3055
|
}
|
|
2811
|
-
for (let i =
|
|
2812
|
-
|
|
3056
|
+
for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
|
|
3057
|
+
leftoverViews[i].dispose();
|
|
2813
3058
|
}
|
|
2814
|
-
if (this.options.positioning) {
|
|
3059
|
+
if (this.directive.options.positioning) {
|
|
2815
3060
|
for (let i = 0, ii = views.length; i < ii; ++i) {
|
|
2816
|
-
views[i].context
|
|
3061
|
+
const context = views[i].context;
|
|
3062
|
+
context.length = i;
|
|
3063
|
+
context.index = ii;
|
|
2817
3064
|
}
|
|
2818
3065
|
}
|
|
2819
3066
|
}
|
|
@@ -2822,11 +3069,11 @@ class RepeatBehavior {
|
|
|
2822
3069
|
const template = this.template;
|
|
2823
3070
|
const location = this.location;
|
|
2824
3071
|
const bindView = this.bindView;
|
|
2825
|
-
const
|
|
3072
|
+
const controller = this.controller;
|
|
2826
3073
|
let itemsLength = items.length;
|
|
2827
3074
|
let views = this.views;
|
|
2828
3075
|
let viewsLength = views.length;
|
|
2829
|
-
if (itemsLength === 0 || templateChanged) {
|
|
3076
|
+
if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
|
|
2830
3077
|
// all views need to be removed
|
|
2831
3078
|
HTMLView.disposeContiguousBatch(views);
|
|
2832
3079
|
viewsLength = 0;
|
|
@@ -2836,7 +3083,7 @@ class RepeatBehavior {
|
|
|
2836
3083
|
this.views = views = new Array(itemsLength);
|
|
2837
3084
|
for (let i = 0; i < itemsLength; ++i) {
|
|
2838
3085
|
const view = template.create();
|
|
2839
|
-
bindView(view, items, i,
|
|
3086
|
+
bindView(view, items, i, controller);
|
|
2840
3087
|
views[i] = view;
|
|
2841
3088
|
view.insertBefore(location);
|
|
2842
3089
|
}
|
|
@@ -2847,11 +3094,11 @@ class RepeatBehavior {
|
|
|
2847
3094
|
for (; i < itemsLength; ++i) {
|
|
2848
3095
|
if (i < viewsLength) {
|
|
2849
3096
|
const view = views[i];
|
|
2850
|
-
bindView(view, items, i,
|
|
3097
|
+
bindView(view, items, i, controller);
|
|
2851
3098
|
}
|
|
2852
3099
|
else {
|
|
2853
3100
|
const view = template.create();
|
|
2854
|
-
bindView(view, items, i,
|
|
3101
|
+
bindView(view, items, i, controller);
|
|
2855
3102
|
views.push(view);
|
|
2856
3103
|
view.insertBefore(location);
|
|
2857
3104
|
}
|
|
@@ -2876,17 +3123,19 @@ class RepeatBehavior {
|
|
|
2876
3123
|
class RepeatDirective {
|
|
2877
3124
|
/**
|
|
2878
3125
|
* Creates an instance of RepeatDirective.
|
|
2879
|
-
* @param
|
|
3126
|
+
* @param dataBinding - The binding that provides the array to render.
|
|
2880
3127
|
* @param templateBinding - The template binding used to obtain a template to render for each item in the array.
|
|
2881
3128
|
* @param options - Options used to turn on special repeat features.
|
|
2882
3129
|
*/
|
|
2883
|
-
constructor(
|
|
2884
|
-
this.
|
|
3130
|
+
constructor(dataBinding, templateBinding, options) {
|
|
3131
|
+
this.dataBinding = dataBinding;
|
|
2885
3132
|
this.templateBinding = templateBinding;
|
|
2886
3133
|
this.options = options;
|
|
3134
|
+
/**
|
|
3135
|
+
* The unique id of the factory.
|
|
3136
|
+
*/
|
|
3137
|
+
this.id = nextId();
|
|
2887
3138
|
ArrayObserver.enable();
|
|
2888
|
-
this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
|
|
2889
|
-
this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
|
|
2890
3139
|
}
|
|
2891
3140
|
/**
|
|
2892
3141
|
* Creates a placeholder string based on the directive's index within the template.
|
|
@@ -2899,16 +3148,23 @@ class RepeatDirective {
|
|
|
2899
3148
|
* Creates a behavior for the provided target node.
|
|
2900
3149
|
* @param target - The node instance to create the behavior for.
|
|
2901
3150
|
*/
|
|
2902
|
-
createBehavior(
|
|
2903
|
-
return new RepeatBehavior(
|
|
3151
|
+
createBehavior() {
|
|
3152
|
+
return new RepeatBehavior(this);
|
|
2904
3153
|
}
|
|
2905
3154
|
}
|
|
2906
3155
|
HTMLDirective.define(RepeatDirective);
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
3156
|
+
/**
|
|
3157
|
+
* A directive that enables list rendering.
|
|
3158
|
+
* @param items - The array to render.
|
|
3159
|
+
* @param template - The template or a template binding used obtain a template
|
|
3160
|
+
* to render for each item in the array.
|
|
3161
|
+
* @param options - Options used to turn on special repeat features.
|
|
3162
|
+
* @public
|
|
3163
|
+
*/
|
|
3164
|
+
function repeat(items, template, options = defaultRepeatOptions) {
|
|
3165
|
+
const dataBinding = normalizeBinding(items);
|
|
3166
|
+
const templateBinding = normalizeBinding(template);
|
|
3167
|
+
return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
|
|
2912
3168
|
}
|
|
2913
3169
|
|
|
2914
3170
|
const selectElements = (value) => value.nodeType === 1;
|
|
@@ -2937,11 +3193,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
|
2937
3193
|
* @param context - The execution context that the binding is operating within.
|
|
2938
3194
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
2939
3195
|
*/
|
|
2940
|
-
bind(
|
|
2941
|
-
const target = targets[this.nodeId];
|
|
2942
|
-
target[this.sourceProperty] = source;
|
|
2943
|
-
this.updateTarget(source, this.computeNodes(target));
|
|
3196
|
+
bind(controller) {
|
|
3197
|
+
const target = controller.targets[this.nodeId];
|
|
3198
|
+
target[this.sourceProperty] = controller.source;
|
|
3199
|
+
this.updateTarget(controller.source, this.computeNodes(target));
|
|
2944
3200
|
this.observe(target);
|
|
3201
|
+
controller.onUnbind(this);
|
|
2945
3202
|
}
|
|
2946
3203
|
/**
|
|
2947
3204
|
* Unbinds this behavior from the source.
|
|
@@ -2949,9 +3206,9 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
|
2949
3206
|
* @param context - The execution context that the binding is operating within.
|
|
2950
3207
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
2951
3208
|
*/
|
|
2952
|
-
unbind(
|
|
2953
|
-
const target = targets[this.nodeId];
|
|
2954
|
-
this.updateTarget(source, emptyArray);
|
|
3209
|
+
unbind(controller) {
|
|
3210
|
+
const target = controller.targets[this.nodeId];
|
|
3211
|
+
this.updateTarget(controller.source, emptyArray);
|
|
2955
3212
|
this.disconnect(target);
|
|
2956
3213
|
target[this.sourceProperty] = null;
|
|
2957
3214
|
}
|
|
@@ -3100,6 +3357,16 @@ function children(propertyOrOptions) {
|
|
|
3100
3357
|
|
|
3101
3358
|
const booleanMode = "boolean";
|
|
3102
3359
|
const reflectMode = "reflect";
|
|
3360
|
+
/**
|
|
3361
|
+
* Metadata used to configure a custom attribute's behavior.
|
|
3362
|
+
* @public
|
|
3363
|
+
*/
|
|
3364
|
+
const AttributeConfiguration = Object.freeze({
|
|
3365
|
+
/**
|
|
3366
|
+
* Locates all attribute configurations associated with a type.
|
|
3367
|
+
*/
|
|
3368
|
+
locate: createMetadataLocator(),
|
|
3369
|
+
});
|
|
3103
3370
|
/**
|
|
3104
3371
|
* A {@link ValueConverter} that converts to and from `boolean` values.
|
|
3105
3372
|
* @remarks
|
|
@@ -3237,7 +3504,7 @@ class AttributeDefinition {
|
|
|
3237
3504
|
*/
|
|
3238
3505
|
static collect(Owner, ...attributeLists) {
|
|
3239
3506
|
const attributes = [];
|
|
3240
|
-
attributeLists.push(Owner
|
|
3507
|
+
attributeLists.push(AttributeConfiguration.locate(Owner));
|
|
3241
3508
|
for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
|
|
3242
3509
|
const list = attributeLists[i];
|
|
3243
3510
|
if (list === void 0) {
|
|
@@ -3267,9 +3534,7 @@ function attr(configOrTarget, prop) {
|
|
|
3267
3534
|
// - @attr({...opts})
|
|
3268
3535
|
config.property = $prop;
|
|
3269
3536
|
}
|
|
3270
|
-
|
|
3271
|
-
($target.constructor.attributes = []);
|
|
3272
|
-
attributes.push(config);
|
|
3537
|
+
AttributeConfiguration.locate($target.constructor).push(config);
|
|
3273
3538
|
}
|
|
3274
3539
|
if (arguments.length > 1) {
|
|
3275
3540
|
// Non invocation:
|
|
@@ -3287,25 +3552,24 @@ function attr(configOrTarget, prop) {
|
|
|
3287
3552
|
|
|
3288
3553
|
const defaultShadowOptions = { mode: "open" };
|
|
3289
3554
|
const defaultElementOptions = {};
|
|
3555
|
+
const fastElementBaseTypes = new Set();
|
|
3290
3556
|
const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */, () => createTypeRegistry());
|
|
3291
3557
|
/**
|
|
3292
3558
|
* Defines metadata for a FASTElement.
|
|
3293
3559
|
* @public
|
|
3294
3560
|
*/
|
|
3295
3561
|
class FASTElementDefinition {
|
|
3296
|
-
/**
|
|
3297
|
-
* Creates an instance of FASTElementDefinition.
|
|
3298
|
-
* @param type - The type this definition is being created for.
|
|
3299
|
-
* @param nameOrConfig - The name of the element to define or a config object
|
|
3300
|
-
* that describes the element to define.
|
|
3301
|
-
*/
|
|
3302
3562
|
constructor(type, nameOrConfig = type.definition) {
|
|
3563
|
+
var _a;
|
|
3564
|
+
this.platformDefined = false;
|
|
3303
3565
|
if (isString(nameOrConfig)) {
|
|
3304
3566
|
nameOrConfig = { name: nameOrConfig };
|
|
3305
3567
|
}
|
|
3306
3568
|
this.type = type;
|
|
3307
3569
|
this.name = nameOrConfig.name;
|
|
3308
3570
|
this.template = nameOrConfig.template;
|
|
3571
|
+
this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
|
|
3572
|
+
const proto = type.prototype;
|
|
3309
3573
|
const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
|
|
3310
3574
|
const observedAttributes = new Array(attributes.length);
|
|
3311
3575
|
const propertyLookup = {};
|
|
@@ -3315,9 +3579,13 @@ class FASTElementDefinition {
|
|
|
3315
3579
|
observedAttributes[i] = current.attribute;
|
|
3316
3580
|
propertyLookup[current.name] = current;
|
|
3317
3581
|
attributeLookup[current.attribute] = current;
|
|
3582
|
+
Observable.defineProperty(proto, current);
|
|
3318
3583
|
}
|
|
3584
|
+
Reflect.defineProperty(type, "observedAttributes", {
|
|
3585
|
+
value: observedAttributes,
|
|
3586
|
+
enumerable: true,
|
|
3587
|
+
});
|
|
3319
3588
|
this.attributes = attributes;
|
|
3320
|
-
this.observedAttributes = observedAttributes;
|
|
3321
3589
|
this.propertyLookup = propertyLookup;
|
|
3322
3590
|
this.attributeLookup = attributeLookup;
|
|
3323
3591
|
this.shadowOptions =
|
|
@@ -3330,43 +3598,50 @@ class FASTElementDefinition {
|
|
|
3330
3598
|
nameOrConfig.elementOptions === void 0
|
|
3331
3599
|
? defaultElementOptions
|
|
3332
3600
|
: Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
|
|
3333
|
-
this.styles =
|
|
3334
|
-
|
|
3335
|
-
? void 0
|
|
3336
|
-
: Array.isArray(nameOrConfig.styles)
|
|
3337
|
-
? new ElementStyles(nameOrConfig.styles)
|
|
3338
|
-
: nameOrConfig.styles instanceof ElementStyles
|
|
3339
|
-
? nameOrConfig.styles
|
|
3340
|
-
: new ElementStyles([nameOrConfig.styles]);
|
|
3601
|
+
this.styles = ElementStyles.normalize(nameOrConfig.styles);
|
|
3602
|
+
fastElementRegistry.register(this);
|
|
3341
3603
|
}
|
|
3342
3604
|
/**
|
|
3343
3605
|
* Indicates if this element has been defined in at least one registry.
|
|
3344
3606
|
*/
|
|
3345
3607
|
get isDefined() {
|
|
3346
|
-
return
|
|
3608
|
+
return this.platformDefined;
|
|
3347
3609
|
}
|
|
3348
3610
|
/**
|
|
3349
3611
|
* Defines a custom element based on this definition.
|
|
3350
3612
|
* @param registry - The element registry to define the element in.
|
|
3613
|
+
* @remarks
|
|
3614
|
+
* This operation is idempotent per registry.
|
|
3351
3615
|
*/
|
|
3352
|
-
define(registry =
|
|
3616
|
+
define(registry = this.registry) {
|
|
3353
3617
|
const type = this.type;
|
|
3354
|
-
if (fastElementRegistry.register(this)) {
|
|
3355
|
-
const attributes = this.attributes;
|
|
3356
|
-
const proto = type.prototype;
|
|
3357
|
-
for (let i = 0, ii = attributes.length; i < ii; ++i) {
|
|
3358
|
-
Observable.defineProperty(proto, attributes[i]);
|
|
3359
|
-
}
|
|
3360
|
-
Reflect.defineProperty(type, "observedAttributes", {
|
|
3361
|
-
value: this.observedAttributes,
|
|
3362
|
-
enumerable: true,
|
|
3363
|
-
});
|
|
3364
|
-
}
|
|
3365
3618
|
if (!registry.get(this.name)) {
|
|
3619
|
+
this.platformDefined = true;
|
|
3366
3620
|
registry.define(this.name, type, this.elementOptions);
|
|
3367
3621
|
}
|
|
3368
3622
|
return this;
|
|
3369
3623
|
}
|
|
3624
|
+
/**
|
|
3625
|
+
* Creates an instance of FASTElementDefinition.
|
|
3626
|
+
* @param type - The type this definition is being created for.
|
|
3627
|
+
* @param nameOrDef - The name of the element to define or a config object
|
|
3628
|
+
* that describes the element to define.
|
|
3629
|
+
*/
|
|
3630
|
+
static compose(type, nameOrDef) {
|
|
3631
|
+
if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
|
|
3632
|
+
return new FASTElementDefinition(class extends type {
|
|
3633
|
+
}, nameOrDef);
|
|
3634
|
+
}
|
|
3635
|
+
return new FASTElementDefinition(type, nameOrDef);
|
|
3636
|
+
}
|
|
3637
|
+
/**
|
|
3638
|
+
* Registers a FASTElement base type.
|
|
3639
|
+
* @param type - The type to register as a base type.
|
|
3640
|
+
* @internal
|
|
3641
|
+
*/
|
|
3642
|
+
static registerBaseType(type) {
|
|
3643
|
+
fastElementBaseTypes.add(type);
|
|
3644
|
+
}
|
|
3370
3645
|
}
|
|
3371
3646
|
/**
|
|
3372
3647
|
* Gets the element definition associated with the specified type.
|
|
@@ -3379,22 +3654,22 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
|
|
|
3379
3654
|
*/
|
|
3380
3655
|
FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
|
|
3381
3656
|
|
|
3382
|
-
const shadowRoots = new WeakMap();
|
|
3383
3657
|
const defaultEventOptions = {
|
|
3384
3658
|
bubbles: true,
|
|
3385
3659
|
composed: true,
|
|
3386
3660
|
cancelable: true,
|
|
3387
3661
|
};
|
|
3662
|
+
const isConnectedPropertyName = "isConnected";
|
|
3663
|
+
const shadowRoots = new WeakMap();
|
|
3388
3664
|
function getShadowRoot(element) {
|
|
3389
3665
|
var _a, _b;
|
|
3390
3666
|
return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
|
|
3391
3667
|
}
|
|
3392
|
-
const isConnectedPropertyName = "isConnected";
|
|
3393
3668
|
/**
|
|
3394
3669
|
* Controls the lifecycle and rendering of a `FASTElement`.
|
|
3395
3670
|
* @public
|
|
3396
3671
|
*/
|
|
3397
|
-
class
|
|
3672
|
+
class ElementController extends PropertyChangeNotifier {
|
|
3398
3673
|
/**
|
|
3399
3674
|
* Creates a Controller to control the specified element.
|
|
3400
3675
|
* @param element - The element to be controlled by this controller.
|
|
@@ -3405,12 +3680,12 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3405
3680
|
constructor(element, definition) {
|
|
3406
3681
|
super(element);
|
|
3407
3682
|
this.boundObservables = null;
|
|
3408
|
-
this.behaviors = null;
|
|
3409
3683
|
this.needsInitialization = true;
|
|
3410
3684
|
this.hasExistingShadowRoot = false;
|
|
3411
3685
|
this._template = null;
|
|
3412
|
-
this._styles = null;
|
|
3413
3686
|
this._isConnected = false;
|
|
3687
|
+
this.behaviors = null;
|
|
3688
|
+
this._mainStyles = null;
|
|
3414
3689
|
/**
|
|
3415
3690
|
* This allows Observable.getNotifier(...) to return the Controller
|
|
3416
3691
|
* when the notifier for the Controller itself is being requested. The
|
|
@@ -3426,7 +3701,7 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3426
3701
|
* If `null` then the element is managing its own rendering.
|
|
3427
3702
|
*/
|
|
3428
3703
|
this.view = null;
|
|
3429
|
-
this.
|
|
3704
|
+
this.source = element;
|
|
3430
3705
|
this.definition = definition;
|
|
3431
3706
|
const shadowOptions = definition.shadowOptions;
|
|
3432
3707
|
if (shadowOptions !== void 0) {
|
|
@@ -3480,9 +3755,9 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3480
3755
|
// 1. Template overrides take top precedence.
|
|
3481
3756
|
if (this._template === null) {
|
|
3482
3757
|
const definition = this.definition;
|
|
3483
|
-
if (this.
|
|
3758
|
+
if (this.source.resolveTemplate) {
|
|
3484
3759
|
// 2. Allow for element instance overrides next.
|
|
3485
|
-
this._template = this.
|
|
3760
|
+
this._template = this.source.resolveTemplate();
|
|
3486
3761
|
}
|
|
3487
3762
|
else if (definition.template) {
|
|
3488
3763
|
// 3. Default to the static definition.
|
|
@@ -3501,48 +3776,92 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3501
3776
|
}
|
|
3502
3777
|
}
|
|
3503
3778
|
/**
|
|
3504
|
-
*
|
|
3505
|
-
*
|
|
3506
|
-
* This value can only be accurately read after connect but can be set at any time.
|
|
3779
|
+
* The main set of styles used for the component, independent
|
|
3780
|
+
* of any dynamically added styles.
|
|
3507
3781
|
*/
|
|
3508
|
-
get
|
|
3782
|
+
get mainStyles() {
|
|
3509
3783
|
var _a;
|
|
3510
3784
|
// 1. Styles overrides take top precedence.
|
|
3511
|
-
if (this.
|
|
3785
|
+
if (this._mainStyles === null) {
|
|
3512
3786
|
const definition = this.definition;
|
|
3513
|
-
if (this.
|
|
3787
|
+
if (this.source.resolveStyles) {
|
|
3514
3788
|
// 2. Allow for element instance overrides next.
|
|
3515
|
-
this.
|
|
3789
|
+
this._mainStyles = this.source.resolveStyles();
|
|
3516
3790
|
}
|
|
3517
3791
|
else if (definition.styles) {
|
|
3518
3792
|
// 3. Default to the static definition.
|
|
3519
|
-
this.
|
|
3793
|
+
this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
|
|
3520
3794
|
}
|
|
3521
3795
|
}
|
|
3522
|
-
return this.
|
|
3796
|
+
return this._mainStyles;
|
|
3523
3797
|
}
|
|
3524
|
-
set
|
|
3525
|
-
if (this.
|
|
3798
|
+
set mainStyles(value) {
|
|
3799
|
+
if (this._mainStyles === value) {
|
|
3526
3800
|
return;
|
|
3527
3801
|
}
|
|
3528
|
-
if (this.
|
|
3529
|
-
this.removeStyles(this.
|
|
3802
|
+
if (this._mainStyles !== null) {
|
|
3803
|
+
this.removeStyles(this._mainStyles);
|
|
3530
3804
|
}
|
|
3531
|
-
this.
|
|
3805
|
+
this._mainStyles = value;
|
|
3532
3806
|
if (!this.needsInitialization) {
|
|
3533
3807
|
this.addStyles(value);
|
|
3534
3808
|
}
|
|
3535
3809
|
}
|
|
3810
|
+
/**
|
|
3811
|
+
* Adds the behavior to the component.
|
|
3812
|
+
* @param behavior - The behavior to add.
|
|
3813
|
+
*/
|
|
3814
|
+
addBehavior(behavior) {
|
|
3815
|
+
var _a, _b;
|
|
3816
|
+
const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
|
|
3817
|
+
const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
|
|
3818
|
+
if (count === 0) {
|
|
3819
|
+
targetBehaviors.set(behavior, 1);
|
|
3820
|
+
behavior.addedCallback && behavior.addedCallback(this);
|
|
3821
|
+
if (behavior.connectedCallback && this.isConnected) {
|
|
3822
|
+
behavior.connectedCallback(this);
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
else {
|
|
3826
|
+
targetBehaviors.set(behavior, count + 1);
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
/**
|
|
3830
|
+
* Removes the behavior from the component.
|
|
3831
|
+
* @param behavior - The behavior to remove.
|
|
3832
|
+
* @param force - Forces removal even if this behavior was added more than once.
|
|
3833
|
+
*/
|
|
3834
|
+
removeBehavior(behavior, force = false) {
|
|
3835
|
+
const targetBehaviors = this.behaviors;
|
|
3836
|
+
if (targetBehaviors === null) {
|
|
3837
|
+
return;
|
|
3838
|
+
}
|
|
3839
|
+
const count = targetBehaviors.get(behavior);
|
|
3840
|
+
if (count === void 0) {
|
|
3841
|
+
return;
|
|
3842
|
+
}
|
|
3843
|
+
if (count === 1 || force) {
|
|
3844
|
+
targetBehaviors.delete(behavior);
|
|
3845
|
+
if (behavior.disconnectedCallback && this.isConnected) {
|
|
3846
|
+
behavior.disconnectedCallback(this);
|
|
3847
|
+
}
|
|
3848
|
+
behavior.removedCallback && behavior.removedCallback(this);
|
|
3849
|
+
}
|
|
3850
|
+
else {
|
|
3851
|
+
targetBehaviors.set(behavior, count - 1);
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3536
3854
|
/**
|
|
3537
3855
|
* Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
|
|
3538
3856
|
* @param styles - The styles to add.
|
|
3539
3857
|
*/
|
|
3540
3858
|
addStyles(styles) {
|
|
3859
|
+
var _a;
|
|
3541
3860
|
if (!styles) {
|
|
3542
3861
|
return;
|
|
3543
3862
|
}
|
|
3544
|
-
const
|
|
3545
|
-
|
|
3863
|
+
const source = this.source;
|
|
3864
|
+
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
|
|
3546
3865
|
if (styles instanceof HTMLElement) {
|
|
3547
3866
|
target.append(styles);
|
|
3548
3867
|
}
|
|
@@ -3550,7 +3869,9 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3550
3869
|
const sourceBehaviors = styles.behaviors;
|
|
3551
3870
|
styles.addStylesTo(target);
|
|
3552
3871
|
if (sourceBehaviors !== null) {
|
|
3553
|
-
|
|
3872
|
+
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
3873
|
+
this.addBehavior(sourceBehaviors[i]);
|
|
3874
|
+
}
|
|
3554
3875
|
}
|
|
3555
3876
|
}
|
|
3556
3877
|
}
|
|
@@ -3559,11 +3880,12 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3559
3880
|
* @param styles - the styles to remove.
|
|
3560
3881
|
*/
|
|
3561
3882
|
removeStyles(styles) {
|
|
3883
|
+
var _a;
|
|
3562
3884
|
if (!styles) {
|
|
3563
3885
|
return;
|
|
3564
3886
|
}
|
|
3565
|
-
const
|
|
3566
|
-
|
|
3887
|
+
const source = this.source;
|
|
3888
|
+
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
|
|
3567
3889
|
if (styles instanceof HTMLElement) {
|
|
3568
3890
|
target.removeChild(styles);
|
|
3569
3891
|
}
|
|
@@ -3571,85 +3893,29 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3571
3893
|
const sourceBehaviors = styles.behaviors;
|
|
3572
3894
|
styles.removeStylesFrom(target);
|
|
3573
3895
|
if (sourceBehaviors !== null) {
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
}
|
|
3578
|
-
/**
|
|
3579
|
-
* Adds behaviors to this element.
|
|
3580
|
-
* @param behaviors - The behaviors to add.
|
|
3581
|
-
*/
|
|
3582
|
-
addBehaviors(behaviors) {
|
|
3583
|
-
var _a;
|
|
3584
|
-
const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
|
|
3585
|
-
const length = behaviors.length;
|
|
3586
|
-
const behaviorsToBind = [];
|
|
3587
|
-
for (let i = 0; i < length; ++i) {
|
|
3588
|
-
const behavior = behaviors[i];
|
|
3589
|
-
if (targetBehaviors.has(behavior)) {
|
|
3590
|
-
targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
|
|
3591
|
-
}
|
|
3592
|
-
else {
|
|
3593
|
-
targetBehaviors.set(behavior, 1);
|
|
3594
|
-
behaviorsToBind.push(behavior);
|
|
3595
|
-
}
|
|
3596
|
-
}
|
|
3597
|
-
if (this._isConnected) {
|
|
3598
|
-
const element = this.element;
|
|
3599
|
-
const context = ExecutionContext.default;
|
|
3600
|
-
for (let i = 0; i < behaviorsToBind.length; ++i) {
|
|
3601
|
-
behaviorsToBind[i].bind(element, context);
|
|
3602
|
-
}
|
|
3603
|
-
}
|
|
3604
|
-
}
|
|
3605
|
-
/**
|
|
3606
|
-
* Removes behaviors from this element.
|
|
3607
|
-
* @param behaviors - The behaviors to remove.
|
|
3608
|
-
* @param force - Forces unbinding of behaviors.
|
|
3609
|
-
*/
|
|
3610
|
-
removeBehaviors(behaviors, force = false) {
|
|
3611
|
-
const targetBehaviors = this.behaviors;
|
|
3612
|
-
if (targetBehaviors === null) {
|
|
3613
|
-
return;
|
|
3614
|
-
}
|
|
3615
|
-
const length = behaviors.length;
|
|
3616
|
-
const behaviorsToUnbind = [];
|
|
3617
|
-
for (let i = 0; i < length; ++i) {
|
|
3618
|
-
const behavior = behaviors[i];
|
|
3619
|
-
if (targetBehaviors.has(behavior)) {
|
|
3620
|
-
const count = targetBehaviors.get(behavior) - 1;
|
|
3621
|
-
count === 0 || force
|
|
3622
|
-
? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
|
|
3623
|
-
: targetBehaviors.set(behavior, count);
|
|
3624
|
-
}
|
|
3625
|
-
}
|
|
3626
|
-
if (this._isConnected) {
|
|
3627
|
-
const element = this.element;
|
|
3628
|
-
const context = ExecutionContext.default;
|
|
3629
|
-
for (let i = 0; i < behaviorsToUnbind.length; ++i) {
|
|
3630
|
-
behaviorsToUnbind[i].unbind(element, context);
|
|
3896
|
+
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
3897
|
+
this.addBehavior(sourceBehaviors[i]);
|
|
3898
|
+
}
|
|
3631
3899
|
}
|
|
3632
3900
|
}
|
|
3633
3901
|
}
|
|
3634
3902
|
/**
|
|
3635
3903
|
* Runs connected lifecycle behavior on the associated element.
|
|
3636
3904
|
*/
|
|
3637
|
-
|
|
3905
|
+
connect() {
|
|
3638
3906
|
if (this._isConnected) {
|
|
3639
3907
|
return;
|
|
3640
3908
|
}
|
|
3641
|
-
const element = this.element;
|
|
3642
|
-
const context = ExecutionContext.default;
|
|
3643
3909
|
if (this.needsInitialization) {
|
|
3644
3910
|
this.finishInitialization();
|
|
3645
3911
|
}
|
|
3646
3912
|
else if (this.view !== null) {
|
|
3647
|
-
this.view.bind(
|
|
3913
|
+
this.view.bind(this.source);
|
|
3648
3914
|
}
|
|
3649
3915
|
const behaviors = this.behaviors;
|
|
3650
3916
|
if (behaviors !== null) {
|
|
3651
|
-
for (const
|
|
3652
|
-
|
|
3917
|
+
for (const key of behaviors.keys()) {
|
|
3918
|
+
key.connectedCallback && key.connectedCallback(this);
|
|
3653
3919
|
}
|
|
3654
3920
|
}
|
|
3655
3921
|
this.setIsConnected(true);
|
|
@@ -3657,21 +3923,18 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3657
3923
|
/**
|
|
3658
3924
|
* Runs disconnected lifecycle behavior on the associated element.
|
|
3659
3925
|
*/
|
|
3660
|
-
|
|
3926
|
+
disconnect() {
|
|
3661
3927
|
if (!this._isConnected) {
|
|
3662
3928
|
return;
|
|
3663
3929
|
}
|
|
3664
3930
|
this.setIsConnected(false);
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
view.unbind();
|
|
3931
|
+
if (this.view !== null) {
|
|
3932
|
+
this.view.unbind();
|
|
3668
3933
|
}
|
|
3669
3934
|
const behaviors = this.behaviors;
|
|
3670
3935
|
if (behaviors !== null) {
|
|
3671
|
-
const
|
|
3672
|
-
|
|
3673
|
-
for (const behavior of behaviors.keys()) {
|
|
3674
|
-
behavior.unbind(element, context);
|
|
3936
|
+
for (const key of behaviors.keys()) {
|
|
3937
|
+
key.disconnectedCallback && key.disconnectedCallback(this);
|
|
3675
3938
|
}
|
|
3676
3939
|
}
|
|
3677
3940
|
}
|
|
@@ -3684,7 +3947,7 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3684
3947
|
onAttributeChangedCallback(name, oldValue, newValue) {
|
|
3685
3948
|
const attrDef = this.definition.attributeLookup[name];
|
|
3686
3949
|
if (attrDef !== void 0) {
|
|
3687
|
-
attrDef.onAttributeChangedCallback(this.
|
|
3950
|
+
attrDef.onAttributeChangedCallback(this.source, newValue);
|
|
3688
3951
|
}
|
|
3689
3952
|
}
|
|
3690
3953
|
/**
|
|
@@ -3697,12 +3960,12 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3697
3960
|
*/
|
|
3698
3961
|
emit(type, detail, options) {
|
|
3699
3962
|
if (this._isConnected) {
|
|
3700
|
-
return this.
|
|
3963
|
+
return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
|
|
3701
3964
|
}
|
|
3702
3965
|
return false;
|
|
3703
3966
|
}
|
|
3704
3967
|
finishInitialization() {
|
|
3705
|
-
const element = this.
|
|
3968
|
+
const element = this.source;
|
|
3706
3969
|
const boundObservables = this.boundObservables;
|
|
3707
3970
|
// If we have any observables that were bound, re-apply their values.
|
|
3708
3971
|
if (boundObservables !== null) {
|
|
@@ -3714,15 +3977,15 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3714
3977
|
this.boundObservables = null;
|
|
3715
3978
|
}
|
|
3716
3979
|
this.renderTemplate(this.template);
|
|
3717
|
-
this.addStyles(this.
|
|
3980
|
+
this.addStyles(this.mainStyles);
|
|
3718
3981
|
this.needsInitialization = false;
|
|
3719
3982
|
}
|
|
3720
3983
|
renderTemplate(template) {
|
|
3721
3984
|
var _a;
|
|
3722
|
-
const element = this.element;
|
|
3723
3985
|
// When getting the host to render to, we start by looking
|
|
3724
3986
|
// up the shadow root. If there isn't one, then that means
|
|
3725
3987
|
// we're doing a Light DOM render to the element's direct children.
|
|
3988
|
+
const element = this.source;
|
|
3726
3989
|
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
3727
3990
|
if (this.view !== null) {
|
|
3728
3991
|
// If there's already a view, we need to unbind and remove through dispose.
|
|
@@ -3739,6 +4002,8 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3739
4002
|
if (template) {
|
|
3740
4003
|
// If a new template was provided, render it.
|
|
3741
4004
|
this.view = template.render(element, host, element);
|
|
4005
|
+
this.view.sourceLifetime =
|
|
4006
|
+
SourceLifetime.coupled;
|
|
3742
4007
|
}
|
|
3743
4008
|
}
|
|
3744
4009
|
/**
|
|
@@ -3758,31 +4023,48 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3758
4023
|
if (definition === void 0) {
|
|
3759
4024
|
throw FAST.error(1401 /* Message.missingElementDefinition */);
|
|
3760
4025
|
}
|
|
3761
|
-
return (element.$fastController = new
|
|
4026
|
+
return (element.$fastController = new ElementController(element, definition));
|
|
3762
4027
|
}
|
|
3763
4028
|
}
|
|
3764
4029
|
|
|
3765
4030
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
3766
4031
|
function createFASTElement(BaseType) {
|
|
3767
|
-
|
|
4032
|
+
const type = class extends BaseType {
|
|
3768
4033
|
constructor() {
|
|
3769
4034
|
/* eslint-disable-next-line */
|
|
3770
4035
|
super();
|
|
3771
|
-
|
|
4036
|
+
ElementController.forCustomElement(this);
|
|
3772
4037
|
}
|
|
3773
4038
|
$emit(type, detail, options) {
|
|
3774
4039
|
return this.$fastController.emit(type, detail, options);
|
|
3775
4040
|
}
|
|
3776
4041
|
connectedCallback() {
|
|
3777
|
-
this.$fastController.
|
|
4042
|
+
this.$fastController.connect();
|
|
3778
4043
|
}
|
|
3779
4044
|
disconnectedCallback() {
|
|
3780
|
-
this.$fastController.
|
|
4045
|
+
this.$fastController.disconnect();
|
|
3781
4046
|
}
|
|
3782
4047
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
3783
4048
|
this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
|
|
3784
4049
|
}
|
|
3785
4050
|
};
|
|
4051
|
+
FASTElementDefinition.registerBaseType(type);
|
|
4052
|
+
return type;
|
|
4053
|
+
}
|
|
4054
|
+
function compose(type, nameOrDef) {
|
|
4055
|
+
if (isFunction(type)) {
|
|
4056
|
+
return FASTElementDefinition.compose(type, nameOrDef);
|
|
4057
|
+
}
|
|
4058
|
+
return FASTElementDefinition.compose(this, type);
|
|
4059
|
+
}
|
|
4060
|
+
function define(type, nameOrDef) {
|
|
4061
|
+
if (isFunction(type)) {
|
|
4062
|
+
return FASTElementDefinition.compose(type, nameOrDef).define().type;
|
|
4063
|
+
}
|
|
4064
|
+
return FASTElementDefinition.compose(this, type).define().type;
|
|
4065
|
+
}
|
|
4066
|
+
function from(BaseType) {
|
|
4067
|
+
return createFASTElement(BaseType);
|
|
3786
4068
|
}
|
|
3787
4069
|
/**
|
|
3788
4070
|
* A minimal base class for FASTElements that also provides
|
|
@@ -3795,18 +4077,19 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
|
|
|
3795
4077
|
* provided base type.
|
|
3796
4078
|
* @param BaseType - The base element type to inherit from.
|
|
3797
4079
|
*/
|
|
3798
|
-
from
|
|
3799
|
-
return createFASTElement(BaseType);
|
|
3800
|
-
},
|
|
4080
|
+
from,
|
|
3801
4081
|
/**
|
|
3802
4082
|
* Defines a platform custom element based on the provided type and definition.
|
|
3803
4083
|
* @param type - The custom element type to define.
|
|
3804
4084
|
* @param nameOrDef - The name of the element to define or a definition object
|
|
3805
4085
|
* that describes the element to define.
|
|
3806
4086
|
*/
|
|
3807
|
-
define
|
|
3808
|
-
|
|
3809
|
-
|
|
4087
|
+
define,
|
|
4088
|
+
/**
|
|
4089
|
+
* Defines metadata for a FASTElement which can be used to later define the element.
|
|
4090
|
+
* @public
|
|
4091
|
+
*/
|
|
4092
|
+
compose,
|
|
3810
4093
|
});
|
|
3811
4094
|
/**
|
|
3812
4095
|
* Decorator: Defines a platform custom element based on `FASTElement`.
|
|
@@ -3817,8 +4100,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
|
|
|
3817
4100
|
function customElement(nameOrDef) {
|
|
3818
4101
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
3819
4102
|
return function (type) {
|
|
3820
|
-
|
|
4103
|
+
define(type, nameOrDef);
|
|
3821
4104
|
};
|
|
3822
4105
|
}
|
|
3823
4106
|
|
|
3824
|
-
export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect,
|
|
4107
|
+
export { AdoptedStyleSheetsStrategy, 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 };
|