@fluentui/web-components 3.0.0-beta.57 → 3.0.0-beta.59
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.md +20 -2
- package/dist/dts/theme/set-theme.d.ts +5 -2
- package/dist/dts/theme/set-theme.local.bench.d.ts +3 -0
- package/dist/dts/theme/set-theme.shadow.bench.d.ts +3 -0
- package/dist/esm/theme/{set-theme.bench.js → set-theme.global.bench.js} +3 -1
- package/dist/esm/theme/set-theme.global.bench.js.map +1 -0
- package/dist/esm/theme/set-theme.js +159 -35
- package/dist/esm/theme/set-theme.js.map +1 -1
- package/dist/esm/theme/set-theme.local.bench.js +27 -0
- package/dist/esm/theme/set-theme.local.bench.js.map +1 -0
- package/dist/esm/theme/set-theme.shadow.bench.js +29 -0
- package/dist/esm/theme/set-theme.shadow.bench.js.map +1 -0
- package/dist/web-components.d.ts +5 -2
- package/dist/web-components.js +1024 -321
- package/dist/web-components.min.js +270 -270
- package/package.json +3 -3
- package/dist/esm/theme/set-theme.bench.js.map +0 -1
- /package/dist/dts/theme/{set-theme.bench.d.ts → set-theme.global.bench.d.ts} +0 -0
package/dist/web-components.js
CHANGED
|
@@ -1174,6 +1174,109 @@ css.partial = (strings, ...values) => {
|
|
|
1174
1174
|
return new CSSPartial(styles, behaviors);
|
|
1175
1175
|
};
|
|
1176
1176
|
|
|
1177
|
+
const bindingStartMarker = /fe-b\$\$start\$\$(\d+)\$\$(.+)\$\$fe-b/;
|
|
1178
|
+
const bindingEndMarker = /fe-b\$\$end\$\$(\d+)\$\$(.+)\$\$fe-b/;
|
|
1179
|
+
const repeatViewStartMarker = /fe-repeat\$\$start\$\$(\d+)\$\$fe-repeat/;
|
|
1180
|
+
const repeatViewEndMarker = /fe-repeat\$\$end\$\$(\d+)\$\$fe-repeat/;
|
|
1181
|
+
const elementBoundaryStartMarker = /^(?:.{0,1000})fe-eb\$\$start\$\$(.+?)\$\$fe-eb/;
|
|
1182
|
+
const elementBoundaryEndMarker = /fe-eb\$\$end\$\$(.{0,1000})\$\$fe-eb(?:.{0,1000})$/;
|
|
1183
|
+
function isComment$1(node) {
|
|
1184
|
+
return node && node.nodeType === Node.COMMENT_NODE;
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Markup utilities to aid in template hydration.
|
|
1188
|
+
* @internal
|
|
1189
|
+
*/
|
|
1190
|
+
const HydrationMarkup = Object.freeze({
|
|
1191
|
+
attributeMarkerName: "data-fe-b",
|
|
1192
|
+
attributeBindingSeparator: " ",
|
|
1193
|
+
contentBindingStartMarker(index, uniqueId) {
|
|
1194
|
+
return `fe-b$$start$$${index}$$${uniqueId}$$fe-b`;
|
|
1195
|
+
},
|
|
1196
|
+
contentBindingEndMarker(index, uniqueId) {
|
|
1197
|
+
return `fe-b$$end$$${index}$$${uniqueId}$$fe-b`;
|
|
1198
|
+
},
|
|
1199
|
+
repeatStartMarker(index) {
|
|
1200
|
+
return `fe-repeat$$start$$${index}$$fe-repeat`;
|
|
1201
|
+
},
|
|
1202
|
+
repeatEndMarker(index) {
|
|
1203
|
+
return `fe-repeat$$end$$${index}$$fe-repeat`;
|
|
1204
|
+
},
|
|
1205
|
+
isContentBindingStartMarker(content) {
|
|
1206
|
+
return bindingStartMarker.test(content);
|
|
1207
|
+
},
|
|
1208
|
+
isContentBindingEndMarker(content) {
|
|
1209
|
+
return bindingEndMarker.test(content);
|
|
1210
|
+
},
|
|
1211
|
+
isRepeatViewStartMarker(content) {
|
|
1212
|
+
return repeatViewStartMarker.test(content);
|
|
1213
|
+
},
|
|
1214
|
+
isRepeatViewEndMarker(content) {
|
|
1215
|
+
return repeatViewEndMarker.test(content);
|
|
1216
|
+
},
|
|
1217
|
+
isElementBoundaryStartMarker(node) {
|
|
1218
|
+
return isComment$1(node) && elementBoundaryStartMarker.test(node.data.trim());
|
|
1219
|
+
},
|
|
1220
|
+
isElementBoundaryEndMarker(node) {
|
|
1221
|
+
return isComment$1(node) && elementBoundaryEndMarker.test(node.data);
|
|
1222
|
+
},
|
|
1223
|
+
/**
|
|
1224
|
+
* Returns the indexes of the ViewBehaviorFactories affecting
|
|
1225
|
+
* attributes for the element, or null if no factories were found.
|
|
1226
|
+
*/
|
|
1227
|
+
parseAttributeBinding(node) {
|
|
1228
|
+
const attr = node.getAttribute(this.attributeMarkerName);
|
|
1229
|
+
return attr === null ? attr : attr.split(this.attributeBindingSeparator).map(i => parseInt(i));
|
|
1230
|
+
},
|
|
1231
|
+
/**
|
|
1232
|
+
* Parses the ViewBehaviorFactory index from string data. Returns
|
|
1233
|
+
* the binding index or null if the index cannot be retrieved.
|
|
1234
|
+
*/
|
|
1235
|
+
parseContentBindingStartMarker(content) {
|
|
1236
|
+
return parseIndexAndIdMarker(bindingStartMarker, content);
|
|
1237
|
+
},
|
|
1238
|
+
parseContentBindingEndMarker(content) {
|
|
1239
|
+
return parseIndexAndIdMarker(bindingEndMarker, content);
|
|
1240
|
+
},
|
|
1241
|
+
/**
|
|
1242
|
+
* Parses the index of a repeat directive from a content string.
|
|
1243
|
+
*/
|
|
1244
|
+
parseRepeatStartMarker(content) {
|
|
1245
|
+
return parseIntMarker(repeatViewStartMarker, content);
|
|
1246
|
+
},
|
|
1247
|
+
parseRepeatEndMarker(content) {
|
|
1248
|
+
return parseIntMarker(repeatViewEndMarker, content);
|
|
1249
|
+
},
|
|
1250
|
+
/**
|
|
1251
|
+
* Parses element Id from element boundary markers
|
|
1252
|
+
*/
|
|
1253
|
+
parseElementBoundaryStartMarker(content) {
|
|
1254
|
+
return parseStringMarker(elementBoundaryStartMarker, content.trim());
|
|
1255
|
+
},
|
|
1256
|
+
parseElementBoundaryEndMarker(content) {
|
|
1257
|
+
return parseStringMarker(elementBoundaryEndMarker, content);
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
function parseIntMarker(regex, content) {
|
|
1261
|
+
const match = regex.exec(content);
|
|
1262
|
+
return match === null ? match : parseInt(match[1]);
|
|
1263
|
+
}
|
|
1264
|
+
function parseStringMarker(regex, content) {
|
|
1265
|
+
const match = regex.exec(content);
|
|
1266
|
+
return match === null ? match : match[1];
|
|
1267
|
+
}
|
|
1268
|
+
function parseIndexAndIdMarker(regex, content) {
|
|
1269
|
+
const match = regex.exec(content);
|
|
1270
|
+
return match === null ? match : [parseInt(match[1]), match[2]];
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* @internal
|
|
1274
|
+
*/
|
|
1275
|
+
const Hydratable = Symbol.for("fe-hydration");
|
|
1276
|
+
function isHydratable(value) {
|
|
1277
|
+
return value[Hydratable] === Hydratable;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1177
1280
|
const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
|
|
1178
1281
|
const interpolationStart = `${marker}{`;
|
|
1179
1282
|
const interpolationEnd = `}${marker}`;
|
|
@@ -1340,250 +1443,209 @@ class StatelessAttachedAttributeDirective {
|
|
|
1340
1443
|
}
|
|
1341
1444
|
makeSerializationNoop(StatelessAttachedAttributeDirective);
|
|
1342
1445
|
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1446
|
+
class HydrationTargetElementError extends Error {
|
|
1447
|
+
constructor(
|
|
1448
|
+
/**
|
|
1449
|
+
* The error message
|
|
1450
|
+
*/
|
|
1451
|
+
message,
|
|
1452
|
+
/**
|
|
1453
|
+
* The Compiled View Behavior Factories that belong to the view.
|
|
1454
|
+
*/
|
|
1455
|
+
factories,
|
|
1456
|
+
/**
|
|
1457
|
+
* The node to target factory.
|
|
1458
|
+
*/
|
|
1459
|
+
node) {
|
|
1460
|
+
super(message);
|
|
1461
|
+
this.factories = factories;
|
|
1462
|
+
this.node = node;
|
|
1348
1463
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1464
|
+
}
|
|
1465
|
+
function isComment(node) {
|
|
1466
|
+
return node.nodeType === Node.COMMENT_NODE;
|
|
1467
|
+
}
|
|
1468
|
+
function isText(node) {
|
|
1469
|
+
return node.nodeType === Node.TEXT_NODE;
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Returns a range object inclusive of all nodes including and between the
|
|
1473
|
+
* provided first and last node.
|
|
1474
|
+
* @param first - The first node
|
|
1475
|
+
* @param last - This last node
|
|
1476
|
+
* @returns
|
|
1477
|
+
*/
|
|
1478
|
+
function createRangeForNodes(first, last) {
|
|
1479
|
+
const range = document.createRange();
|
|
1480
|
+
range.setStart(first, 0);
|
|
1481
|
+
// The lastIndex should be inclusive of the end of the lastChild. Obtain offset based
|
|
1482
|
+
// on usageNotes: https://developer.mozilla.org/en-US/docs/Web/API/Range/setEnd#usage_notes
|
|
1483
|
+
range.setEnd(last, isComment(last) || isText(last) ? last.data.length : last.childNodes.length);
|
|
1484
|
+
return range;
|
|
1485
|
+
}
|
|
1486
|
+
function isShadowRoot(node) {
|
|
1487
|
+
return node instanceof DocumentFragment && "mode" in node;
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Maps {@link CompiledViewBehaviorFactory} ids to the corresponding node targets for the view.
|
|
1491
|
+
* @param firstNode - The first node of the view.
|
|
1492
|
+
* @param lastNode - The last node of the view.
|
|
1493
|
+
* @param factories - The Compiled View Behavior Factories that belong to the view.
|
|
1494
|
+
* @returns - A {@link ViewBehaviorTargets } object for the factories in the view.
|
|
1495
|
+
*/
|
|
1496
|
+
function buildViewBindingTargets(firstNode, lastNode, factories) {
|
|
1497
|
+
const range = createRangeForNodes(firstNode, lastNode);
|
|
1498
|
+
const treeRoot = range.commonAncestorContainer;
|
|
1499
|
+
const walker = document.createTreeWalker(treeRoot, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_COMMENT + NodeFilter.SHOW_TEXT, {
|
|
1500
|
+
acceptNode(node) {
|
|
1501
|
+
return range.comparePoint(node, 0) === 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
1381
1502
|
}
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1503
|
+
});
|
|
1504
|
+
const targets = {};
|
|
1505
|
+
const boundaries = {};
|
|
1506
|
+
let node = walker.currentNode = firstNode;
|
|
1507
|
+
while (node !== null) {
|
|
1508
|
+
switch (node.nodeType) {
|
|
1509
|
+
case Node.ELEMENT_NODE:
|
|
1510
|
+
{
|
|
1511
|
+
targetElement(node, factories, targets);
|
|
1512
|
+
break;
|
|
1513
|
+
}
|
|
1514
|
+
case Node.COMMENT_NODE:
|
|
1515
|
+
{
|
|
1516
|
+
targetComment(node, walker, factories, targets, boundaries);
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1394
1519
|
}
|
|
1395
|
-
|
|
1520
|
+
node = walker.nextNode();
|
|
1396
1521
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : target[lookup] = {
|
|
1402
|
-
v: 0,
|
|
1403
|
-
cv: Object.create(null)
|
|
1522
|
+
range.detach();
|
|
1523
|
+
return {
|
|
1524
|
+
targets,
|
|
1525
|
+
boundaries
|
|
1404
1526
|
};
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
if (
|
|
1410
|
-
const
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
if (currentName === "") {
|
|
1414
|
-
continue;
|
|
1527
|
+
}
|
|
1528
|
+
function targetElement(node, factories, targets) {
|
|
1529
|
+
// Check for attributes and map any factories.
|
|
1530
|
+
const attrFactoryIds = HydrationMarkup.parseAttributeBinding(node);
|
|
1531
|
+
if (attrFactoryIds !== null) {
|
|
1532
|
+
for (const id of attrFactoryIds) {
|
|
1533
|
+
if (!factories[id]) {
|
|
1534
|
+
throw new HydrationTargetElementError(`HydrationView was unable to successfully target factory on ${node.nodeName} inside ${node.getRootNode().host.nodeName}. This likely indicates a template mismatch between SSR rendering and hydration.`, factories, node);
|
|
1415
1535
|
}
|
|
1416
|
-
|
|
1417
|
-
tokenList.add(currentName);
|
|
1536
|
+
targetFactory(factories[id], node, targets);
|
|
1418
1537
|
}
|
|
1538
|
+
node.removeAttribute(HydrationMarkup.attributeMarkerName);
|
|
1419
1539
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
if (
|
|
1540
|
+
}
|
|
1541
|
+
function targetComment(node, walker, factories, targets, boundaries) {
|
|
1542
|
+
if (HydrationMarkup.isElementBoundaryStartMarker(node)) {
|
|
1543
|
+
skipToElementBoundaryEndMarker(node, walker);
|
|
1423
1544
|
return;
|
|
1424
1545
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
tokenList.remove(name);
|
|
1546
|
+
if (HydrationMarkup.isContentBindingStartMarker(node.data)) {
|
|
1547
|
+
const parsed = HydrationMarkup.parseContentBindingStartMarker(node.data);
|
|
1548
|
+
if (parsed === null) {
|
|
1549
|
+
return;
|
|
1430
1550
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
const
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
*/
|
|
1445
|
-
class HTMLBindingDirective {
|
|
1446
|
-
/**
|
|
1447
|
-
* Creates an instance of HTMLBindingDirective.
|
|
1448
|
-
* @param dataBinding - The binding configuration to apply.
|
|
1449
|
-
*/
|
|
1450
|
-
constructor(dataBinding) {
|
|
1451
|
-
this.dataBinding = dataBinding;
|
|
1452
|
-
this.updateTarget = null;
|
|
1453
|
-
/**
|
|
1454
|
-
* The type of aspect to target.
|
|
1455
|
-
*/
|
|
1456
|
-
this.aspectType = DOMAspect.content;
|
|
1457
|
-
}
|
|
1458
|
-
/**
|
|
1459
|
-
* Creates HTML to be used within a template.
|
|
1460
|
-
* @param add - Can be used to add behavior factories to a template.
|
|
1461
|
-
*/
|
|
1462
|
-
createHTML(add) {
|
|
1463
|
-
return Markup.interpolation(add(this));
|
|
1464
|
-
}
|
|
1465
|
-
/**
|
|
1466
|
-
* Creates a behavior.
|
|
1467
|
-
*/
|
|
1468
|
-
createBehavior() {
|
|
1469
|
-
var _a;
|
|
1470
|
-
if (this.updateTarget === null) {
|
|
1471
|
-
const sink = sinkLookup[this.aspectType];
|
|
1472
|
-
const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
|
|
1473
|
-
if (!sink) {
|
|
1474
|
-
throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
|
|
1551
|
+
const [index, id] = parsed;
|
|
1552
|
+
const factory = factories[index];
|
|
1553
|
+
const nodes = [];
|
|
1554
|
+
let current = walker.nextSibling();
|
|
1555
|
+
node.data = "";
|
|
1556
|
+
const first = current;
|
|
1557
|
+
// Search for the binding end marker that closes the binding.
|
|
1558
|
+
while (current !== null) {
|
|
1559
|
+
if (isComment(current)) {
|
|
1560
|
+
const parsed = HydrationMarkup.parseContentBindingEndMarker(current.data);
|
|
1561
|
+
if (parsed && parsed[1] === id) {
|
|
1562
|
+
break;
|
|
1563
|
+
}
|
|
1475
1564
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1565
|
+
nodes.push(current);
|
|
1566
|
+
current = walker.nextSibling();
|
|
1478
1567
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
bind(controller) {
|
|
1483
|
-
var _a;
|
|
1484
|
-
const target = controller.targets[this.targetNodeId];
|
|
1485
|
-
switch (this.aspectType) {
|
|
1486
|
-
case DOMAspect.event:
|
|
1487
|
-
target[this.data] = controller;
|
|
1488
|
-
target.addEventListener(this.targetAspect, this, this.dataBinding.options);
|
|
1489
|
-
break;
|
|
1490
|
-
case DOMAspect.content:
|
|
1491
|
-
controller.onUnbind(this);
|
|
1492
|
-
// intentional fall through
|
|
1493
|
-
default:
|
|
1494
|
-
const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : target[this.data] = this.dataBinding.createObserver(this, this);
|
|
1495
|
-
observer.target = target;
|
|
1496
|
-
observer.controller = controller;
|
|
1497
|
-
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
1498
|
-
break;
|
|
1568
|
+
if (current === null) {
|
|
1569
|
+
const root = node.getRootNode();
|
|
1570
|
+
throw new Error(`Error hydrating Comment node inside "${isShadowRoot(root) ? root.host.nodeName : root.nodeName}".`);
|
|
1499
1571
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1572
|
+
current.data = "";
|
|
1573
|
+
if (nodes.length === 1 && isText(nodes[0])) {
|
|
1574
|
+
targetFactory(factory, nodes[0], targets);
|
|
1575
|
+
} else {
|
|
1576
|
+
// If current === first, it means there is no content in
|
|
1577
|
+
// the view. This happens when a `when` directive evaluates false,
|
|
1578
|
+
// or whenever a content binding returns null or undefined.
|
|
1579
|
+
// In that case, there will never be any content
|
|
1580
|
+
// to hydrate and Binding can simply create a HTMLView
|
|
1581
|
+
// whenever it needs to.
|
|
1582
|
+
if (current !== first && current.previousSibling !== null) {
|
|
1583
|
+
boundaries[factory.targetNodeId] = {
|
|
1584
|
+
first,
|
|
1585
|
+
last: current.previousSibling
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
// Binding evaluates to null / undefined or a template.
|
|
1589
|
+
// If binding revaluates to string, it will replace content in target
|
|
1590
|
+
// So we always insert a text node to ensure that
|
|
1591
|
+
// text content binding will be written to this text node instead of comment
|
|
1592
|
+
const dummyTextNode = current.parentNode.insertBefore(document.createTextNode(""), current);
|
|
1593
|
+
targetFactory(factory, dummyTextNode, targets);
|
|
1508
1594
|
}
|
|
1509
1595
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* Moves TreeWalker to element boundary end marker
|
|
1599
|
+
* @param node - element boundary start marker node
|
|
1600
|
+
* @param walker - tree walker
|
|
1601
|
+
*/
|
|
1602
|
+
function skipToElementBoundaryEndMarker(node, walker) {
|
|
1603
|
+
const id = HydrationMarkup.parseElementBoundaryStartMarker(node.data);
|
|
1604
|
+
let current = walker.nextSibling();
|
|
1605
|
+
while (current !== null) {
|
|
1606
|
+
if (isComment(current)) {
|
|
1607
|
+
const parsed = HydrationMarkup.parseElementBoundaryEndMarker(current.data);
|
|
1608
|
+
if (parsed && parsed === id) {
|
|
1609
|
+
break;
|
|
1519
1610
|
}
|
|
1520
1611
|
}
|
|
1612
|
+
current = walker.nextSibling();
|
|
1521
1613
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1614
|
+
}
|
|
1615
|
+
function targetFactory(factory, node, targets) {
|
|
1616
|
+
if (factory.targetNodeId === undefined) {
|
|
1617
|
+
// Dev error, this shouldn't ever be thrown
|
|
1618
|
+
throw new Error("Factory could not be target to the node");
|
|
1527
1619
|
}
|
|
1620
|
+
targets[factory.targetNodeId] = node;
|
|
1528
1621
|
}
|
|
1529
|
-
HTMLDirective.define(HTMLBindingDirective, {
|
|
1530
|
-
aspected: true
|
|
1531
|
-
});
|
|
1532
1622
|
|
|
1623
|
+
var _a;
|
|
1533
1624
|
function removeNodeSequence(firstNode, lastNode) {
|
|
1534
1625
|
const parent = firstNode.parentNode;
|
|
1535
1626
|
let current = firstNode;
|
|
1536
1627
|
let next;
|
|
1537
1628
|
while (current !== lastNode) {
|
|
1538
1629
|
next = current.nextSibling;
|
|
1630
|
+
if (!next) {
|
|
1631
|
+
throw new Error(`Unmatched first/last child inside "${lastNode.getRootNode().host.nodeName}".`);
|
|
1632
|
+
}
|
|
1539
1633
|
parent.removeChild(current);
|
|
1540
1634
|
current = next;
|
|
1541
1635
|
}
|
|
1542
1636
|
parent.removeChild(lastNode);
|
|
1543
1637
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
this.fragment = fragment;
|
|
1556
|
-
this.factories = factories;
|
|
1557
|
-
this.targets = targets;
|
|
1558
|
-
this.behaviors = null;
|
|
1559
|
-
this.unbindables = [];
|
|
1560
|
-
/**
|
|
1561
|
-
* The data that the view is bound to.
|
|
1562
|
-
*/
|
|
1563
|
-
this.source = null;
|
|
1564
|
-
/**
|
|
1565
|
-
* Indicates whether the controller is bound.
|
|
1566
|
-
*/
|
|
1567
|
-
this.isBound = false;
|
|
1568
|
-
/**
|
|
1569
|
-
* Indicates how the source's lifetime relates to the controller's lifetime.
|
|
1570
|
-
*/
|
|
1571
|
-
this.sourceLifetime = SourceLifetime.unknown;
|
|
1572
|
-
/**
|
|
1573
|
-
* The execution context the view is running within.
|
|
1574
|
-
*/
|
|
1575
|
-
this.context = this;
|
|
1576
|
-
/**
|
|
1577
|
-
* The index of the current item within a repeat context.
|
|
1578
|
-
*/
|
|
1579
|
-
this.index = 0;
|
|
1580
|
-
/**
|
|
1581
|
-
* The length of the current collection within a repeat context.
|
|
1582
|
-
*/
|
|
1583
|
-
this.length = 0;
|
|
1584
|
-
this.firstChild = fragment.firstChild;
|
|
1585
|
-
this.lastChild = fragment.lastChild;
|
|
1586
|
-
}
|
|
1638
|
+
class DefaultExecutionContext {
|
|
1639
|
+
constructor() {
|
|
1640
|
+
/**
|
|
1641
|
+
* The index of the current item within a repeat context.
|
|
1642
|
+
*/
|
|
1643
|
+
this.index = 0;
|
|
1644
|
+
/**
|
|
1645
|
+
* The length of the current collection within a repeat context.
|
|
1646
|
+
*/
|
|
1647
|
+
this.length = 0;
|
|
1648
|
+
}
|
|
1587
1649
|
/**
|
|
1588
1650
|
* The current event within an event handler.
|
|
1589
1651
|
*/
|
|
@@ -1637,6 +1699,43 @@ class HTMLView {
|
|
|
1637
1699
|
eventTarget() {
|
|
1638
1700
|
return this.event.target;
|
|
1639
1701
|
}
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* The standard View implementation, which also implements ElementView and SyntheticView.
|
|
1705
|
+
* @public
|
|
1706
|
+
*/
|
|
1707
|
+
class HTMLView extends DefaultExecutionContext {
|
|
1708
|
+
/**
|
|
1709
|
+
* Constructs an instance of HTMLView.
|
|
1710
|
+
* @param fragment - The html fragment that contains the nodes for this view.
|
|
1711
|
+
* @param behaviors - The behaviors to be applied to this view.
|
|
1712
|
+
*/
|
|
1713
|
+
constructor(fragment, factories, targets) {
|
|
1714
|
+
super();
|
|
1715
|
+
this.fragment = fragment;
|
|
1716
|
+
this.factories = factories;
|
|
1717
|
+
this.targets = targets;
|
|
1718
|
+
this.behaviors = null;
|
|
1719
|
+
this.unbindables = [];
|
|
1720
|
+
/**
|
|
1721
|
+
* The data that the view is bound to.
|
|
1722
|
+
*/
|
|
1723
|
+
this.source = null;
|
|
1724
|
+
/**
|
|
1725
|
+
* Indicates whether the controller is bound.
|
|
1726
|
+
*/
|
|
1727
|
+
this.isBound = false;
|
|
1728
|
+
/**
|
|
1729
|
+
* Indicates how the source's lifetime relates to the controller's lifetime.
|
|
1730
|
+
*/
|
|
1731
|
+
this.sourceLifetime = SourceLifetime.unknown;
|
|
1732
|
+
/**
|
|
1733
|
+
* The execution context the view is running within.
|
|
1734
|
+
*/
|
|
1735
|
+
this.context = this;
|
|
1736
|
+
this.firstChild = fragment.firstChild;
|
|
1737
|
+
this.lastChild = fragment.lastChild;
|
|
1738
|
+
}
|
|
1640
1739
|
/**
|
|
1641
1740
|
* Appends the view's DOM nodes to the referenced node.
|
|
1642
1741
|
* @param node - The parent node to append the view's DOM nodes to.
|
|
@@ -1666,101 +1765,510 @@ class HTMLView {
|
|
|
1666
1765
|
}
|
|
1667
1766
|
}
|
|
1668
1767
|
/**
|
|
1669
|
-
* Removes the view's DOM nodes.
|
|
1670
|
-
* The nodes are not disposed and the view can later be re-inserted.
|
|
1768
|
+
* Removes the view's DOM nodes.
|
|
1769
|
+
* The nodes are not disposed and the view can later be re-inserted.
|
|
1770
|
+
*/
|
|
1771
|
+
remove() {
|
|
1772
|
+
const fragment = this.fragment;
|
|
1773
|
+
const end = this.lastChild;
|
|
1774
|
+
let current = this.firstChild;
|
|
1775
|
+
let next;
|
|
1776
|
+
while (current !== end) {
|
|
1777
|
+
next = current.nextSibling;
|
|
1778
|
+
fragment.appendChild(current);
|
|
1779
|
+
current = next;
|
|
1780
|
+
}
|
|
1781
|
+
fragment.appendChild(end);
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Removes the view and unbinds its behaviors, disposing of DOM nodes afterward.
|
|
1785
|
+
* Once a view has been disposed, it cannot be inserted or bound again.
|
|
1786
|
+
*/
|
|
1787
|
+
dispose() {
|
|
1788
|
+
removeNodeSequence(this.firstChild, this.lastChild);
|
|
1789
|
+
this.unbind();
|
|
1790
|
+
}
|
|
1791
|
+
onUnbind(behavior) {
|
|
1792
|
+
this.unbindables.push(behavior);
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Binds a view's behaviors to its binding source.
|
|
1796
|
+
* @param source - The binding source for the view's binding behaviors.
|
|
1797
|
+
* @param context - The execution context to run the behaviors within.
|
|
1798
|
+
*/
|
|
1799
|
+
bind(source, context = this) {
|
|
1800
|
+
if (this.source === source) {
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
let behaviors = this.behaviors;
|
|
1804
|
+
if (behaviors === null) {
|
|
1805
|
+
this.source = source;
|
|
1806
|
+
this.context = context;
|
|
1807
|
+
this.behaviors = behaviors = new Array(this.factories.length);
|
|
1808
|
+
const factories = this.factories;
|
|
1809
|
+
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
1810
|
+
const behavior = factories[i].createBehavior();
|
|
1811
|
+
behavior.bind(this);
|
|
1812
|
+
behaviors[i] = behavior;
|
|
1813
|
+
}
|
|
1814
|
+
} else {
|
|
1815
|
+
if (this.source !== null) {
|
|
1816
|
+
this.evaluateUnbindables();
|
|
1817
|
+
}
|
|
1818
|
+
this.isBound = false;
|
|
1819
|
+
this.source = source;
|
|
1820
|
+
this.context = context;
|
|
1821
|
+
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
1822
|
+
behaviors[i].bind(this);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
this.isBound = true;
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Unbinds a view's behaviors from its binding source.
|
|
1829
|
+
*/
|
|
1830
|
+
unbind() {
|
|
1831
|
+
if (!this.isBound || this.source === null) {
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
this.evaluateUnbindables();
|
|
1835
|
+
this.source = null;
|
|
1836
|
+
this.context = this;
|
|
1837
|
+
this.isBound = false;
|
|
1838
|
+
}
|
|
1839
|
+
evaluateUnbindables() {
|
|
1840
|
+
const unbindables = this.unbindables;
|
|
1841
|
+
for (let i = 0, ii = unbindables.length; i < ii; ++i) {
|
|
1842
|
+
unbindables[i].unbind(this);
|
|
1843
|
+
}
|
|
1844
|
+
unbindables.length = 0;
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Efficiently disposes of a contiguous range of synthetic view instances.
|
|
1848
|
+
* @param views - A contiguous range of views to be disposed.
|
|
1849
|
+
*/
|
|
1850
|
+
static disposeContiguousBatch(views) {
|
|
1851
|
+
if (views.length === 0) {
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
removeNodeSequence(views[0].firstChild, views[views.length - 1].lastChild);
|
|
1855
|
+
for (let i = 0, ii = views.length; i < ii; ++i) {
|
|
1856
|
+
views[i].unbind();
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
makeSerializationNoop(HTMLView);
|
|
1861
|
+
Observable.defineProperty(HTMLView.prototype, "index");
|
|
1862
|
+
Observable.defineProperty(HTMLView.prototype, "length");
|
|
1863
|
+
const HydrationStage = {
|
|
1864
|
+
unhydrated: "unhydrated",
|
|
1865
|
+
hydrating: "hydrating",
|
|
1866
|
+
hydrated: "hydrated"
|
|
1867
|
+
};
|
|
1868
|
+
/** @public */
|
|
1869
|
+
class HydrationBindingError extends Error {
|
|
1870
|
+
constructor(
|
|
1871
|
+
/**
|
|
1872
|
+
* The error message
|
|
1873
|
+
*/
|
|
1874
|
+
message,
|
|
1875
|
+
/**
|
|
1876
|
+
* The factory that was unable to be bound
|
|
1877
|
+
*/
|
|
1878
|
+
factory,
|
|
1879
|
+
/**
|
|
1880
|
+
* A DocumentFragment containing a clone of the
|
|
1881
|
+
* view's Nodes.
|
|
1882
|
+
*/
|
|
1883
|
+
fragment,
|
|
1884
|
+
/**
|
|
1885
|
+
* String representation of the HTML in the template that
|
|
1886
|
+
* threw the binding error.
|
|
1887
|
+
*/
|
|
1888
|
+
templateString) {
|
|
1889
|
+
super(message);
|
|
1890
|
+
this.factory = factory;
|
|
1891
|
+
this.fragment = fragment;
|
|
1892
|
+
this.templateString = templateString;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
class HydrationView extends DefaultExecutionContext {
|
|
1896
|
+
constructor(firstChild, lastChild, sourceTemplate, hostBindingTarget) {
|
|
1897
|
+
super();
|
|
1898
|
+
this.firstChild = firstChild;
|
|
1899
|
+
this.lastChild = lastChild;
|
|
1900
|
+
this.sourceTemplate = sourceTemplate;
|
|
1901
|
+
this.hostBindingTarget = hostBindingTarget;
|
|
1902
|
+
this[_a] = Hydratable;
|
|
1903
|
+
this.context = this;
|
|
1904
|
+
this.source = null;
|
|
1905
|
+
this.isBound = false;
|
|
1906
|
+
this.sourceLifetime = SourceLifetime.unknown;
|
|
1907
|
+
this.unbindables = [];
|
|
1908
|
+
this.fragment = null;
|
|
1909
|
+
this.behaviors = null;
|
|
1910
|
+
this._hydrationStage = HydrationStage.unhydrated;
|
|
1911
|
+
this._bindingViewBoundaries = {};
|
|
1912
|
+
this._targets = {};
|
|
1913
|
+
this.factories = sourceTemplate.compile().factories;
|
|
1914
|
+
}
|
|
1915
|
+
get hydrationStage() {
|
|
1916
|
+
return this._hydrationStage;
|
|
1917
|
+
}
|
|
1918
|
+
get targets() {
|
|
1919
|
+
return this._targets;
|
|
1920
|
+
}
|
|
1921
|
+
get bindingViewBoundaries() {
|
|
1922
|
+
return this._bindingViewBoundaries;
|
|
1923
|
+
}
|
|
1924
|
+
/**
|
|
1925
|
+
* no-op. Hydrated views are don't need to be moved from a documentFragment
|
|
1926
|
+
* to the target node.
|
|
1927
|
+
*/
|
|
1928
|
+
insertBefore(node) {
|
|
1929
|
+
// No-op in cases where this is called before the view is removed,
|
|
1930
|
+
// because the nodes will already be in the document and just need hydrating.
|
|
1931
|
+
if (this.fragment === null) {
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
if (this.fragment.hasChildNodes()) {
|
|
1935
|
+
node.parentNode.insertBefore(this.fragment, node);
|
|
1936
|
+
} else {
|
|
1937
|
+
const end = this.lastChild;
|
|
1938
|
+
if (node.previousSibling === end) return;
|
|
1939
|
+
const parentNode = node.parentNode;
|
|
1940
|
+
let current = this.firstChild;
|
|
1941
|
+
let next;
|
|
1942
|
+
while (current !== end) {
|
|
1943
|
+
next = current.nextSibling;
|
|
1944
|
+
parentNode.insertBefore(current, node);
|
|
1945
|
+
current = next;
|
|
1946
|
+
}
|
|
1947
|
+
parentNode.insertBefore(end, node);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Appends the view to a node. In cases where this is called before the
|
|
1952
|
+
* view has been removed, the method will no-op.
|
|
1953
|
+
* @param node - the node to append the view to.
|
|
1954
|
+
*/
|
|
1955
|
+
appendTo(node) {
|
|
1956
|
+
if (this.fragment !== null) {
|
|
1957
|
+
node.appendChild(this.fragment);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
remove() {
|
|
1961
|
+
const fragment = this.fragment || (this.fragment = document.createDocumentFragment());
|
|
1962
|
+
const end = this.lastChild;
|
|
1963
|
+
let current = this.firstChild;
|
|
1964
|
+
let next;
|
|
1965
|
+
while (current !== end) {
|
|
1966
|
+
next = current.nextSibling;
|
|
1967
|
+
if (!next) {
|
|
1968
|
+
throw new Error(`Unmatched first/last child inside "${end.getRootNode().host.nodeName}".`);
|
|
1969
|
+
}
|
|
1970
|
+
fragment.appendChild(current);
|
|
1971
|
+
current = next;
|
|
1972
|
+
}
|
|
1973
|
+
fragment.appendChild(end);
|
|
1974
|
+
}
|
|
1975
|
+
bind(source, context = this) {
|
|
1976
|
+
var _b, _c;
|
|
1977
|
+
if (this.hydrationStage !== HydrationStage.hydrated) {
|
|
1978
|
+
this._hydrationStage = HydrationStage.hydrating;
|
|
1979
|
+
}
|
|
1980
|
+
if (this.source === source) {
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
let behaviors = this.behaviors;
|
|
1984
|
+
if (behaviors === null) {
|
|
1985
|
+
this.source = source;
|
|
1986
|
+
this.context = context;
|
|
1987
|
+
try {
|
|
1988
|
+
const {
|
|
1989
|
+
targets,
|
|
1990
|
+
boundaries
|
|
1991
|
+
} = buildViewBindingTargets(this.firstChild, this.lastChild, this.factories);
|
|
1992
|
+
this._targets = targets;
|
|
1993
|
+
this._bindingViewBoundaries = boundaries;
|
|
1994
|
+
} catch (error) {
|
|
1995
|
+
if (error instanceof HydrationTargetElementError) {
|
|
1996
|
+
let templateString = this.sourceTemplate.html;
|
|
1997
|
+
if (typeof templateString !== "string") {
|
|
1998
|
+
templateString = templateString.innerHTML;
|
|
1999
|
+
}
|
|
2000
|
+
error.templateString = templateString;
|
|
2001
|
+
}
|
|
2002
|
+
throw error;
|
|
2003
|
+
}
|
|
2004
|
+
this.behaviors = behaviors = new Array(this.factories.length);
|
|
2005
|
+
const factories = this.factories;
|
|
2006
|
+
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
2007
|
+
const factory = factories[i];
|
|
2008
|
+
if (factory.targetNodeId === "h" && this.hostBindingTarget) {
|
|
2009
|
+
targetFactory(factory, this.hostBindingTarget, this._targets);
|
|
2010
|
+
}
|
|
2011
|
+
// If the binding has been targeted or it is a host binding and the view has a hostBindingTarget
|
|
2012
|
+
if (factory.targetNodeId in this.targets) {
|
|
2013
|
+
const behavior = factory.createBehavior();
|
|
2014
|
+
behavior.bind(this);
|
|
2015
|
+
behaviors[i] = behavior;
|
|
2016
|
+
} else {
|
|
2017
|
+
let templateString = this.sourceTemplate.html;
|
|
2018
|
+
if (typeof templateString !== "string") {
|
|
2019
|
+
templateString = templateString.innerHTML;
|
|
2020
|
+
}
|
|
2021
|
+
throw new HydrationBindingError(`HydrationView was unable to successfully target bindings inside "${(_c = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode()).host) === null || _c === void 0 ? void 0 : _c.nodeName}".`, factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
} else {
|
|
2025
|
+
if (this.source !== null) {
|
|
2026
|
+
this.evaluateUnbindables();
|
|
2027
|
+
}
|
|
2028
|
+
this.isBound = false;
|
|
2029
|
+
this.source = source;
|
|
2030
|
+
this.context = context;
|
|
2031
|
+
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2032
|
+
behaviors[i].bind(this);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
this.isBound = true;
|
|
2036
|
+
this._hydrationStage = HydrationStage.hydrated;
|
|
2037
|
+
}
|
|
2038
|
+
unbind() {
|
|
2039
|
+
if (!this.isBound || this.source === null) {
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
this.evaluateUnbindables();
|
|
2043
|
+
this.source = null;
|
|
2044
|
+
this.context = this;
|
|
2045
|
+
this.isBound = false;
|
|
2046
|
+
}
|
|
2047
|
+
/**
|
|
2048
|
+
* Removes the view and unbinds its behaviors, disposing of DOM nodes afterward.
|
|
2049
|
+
* Once a view has been disposed, it cannot be inserted or bound again.
|
|
2050
|
+
*/
|
|
2051
|
+
dispose() {
|
|
2052
|
+
removeNodeSequence(this.firstChild, this.lastChild);
|
|
2053
|
+
this.unbind();
|
|
2054
|
+
}
|
|
2055
|
+
onUnbind(behavior) {
|
|
2056
|
+
this.unbindables.push(behavior);
|
|
2057
|
+
}
|
|
2058
|
+
evaluateUnbindables() {
|
|
2059
|
+
const unbindables = this.unbindables;
|
|
2060
|
+
for (let i = 0, ii = unbindables.length; i < ii; ++i) {
|
|
2061
|
+
unbindables[i].unbind(this);
|
|
2062
|
+
}
|
|
2063
|
+
unbindables.length = 0;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
_a = Hydratable;
|
|
2067
|
+
makeSerializationNoop(HydrationView);
|
|
2068
|
+
|
|
2069
|
+
function isContentTemplate(value) {
|
|
2070
|
+
return value.create !== undefined;
|
|
2071
|
+
}
|
|
2072
|
+
function updateContent(target, aspect, value, controller) {
|
|
2073
|
+
// If there's no actual value, then this equates to the
|
|
2074
|
+
// empty string for the purposes of content bindings.
|
|
2075
|
+
if (value === null || value === undefined) {
|
|
2076
|
+
value = "";
|
|
2077
|
+
}
|
|
2078
|
+
// If the value has a "create" method, then it's a ContentTemplate.
|
|
2079
|
+
if (isContentTemplate(value)) {
|
|
2080
|
+
target.textContent = "";
|
|
2081
|
+
let view = target.$fastView;
|
|
2082
|
+
// If there's no previous view that we might be able to
|
|
2083
|
+
// reuse then create a new view from the template.
|
|
2084
|
+
if (view === void 0) {
|
|
2085
|
+
if (isHydratable(controller) && isHydratable(value) && controller.bindingViewBoundaries[this.targetNodeId] !== undefined && controller.hydrationStage !== HydrationStage.hydrated) {
|
|
2086
|
+
const viewNodes = controller.bindingViewBoundaries[this.targetNodeId];
|
|
2087
|
+
view = value.hydrate(viewNodes.first, viewNodes.last);
|
|
2088
|
+
} else {
|
|
2089
|
+
view = value.create();
|
|
2090
|
+
}
|
|
2091
|
+
} else {
|
|
2092
|
+
// If there is a previous view, but it wasn't created
|
|
2093
|
+
// from the same template as the new value, then we
|
|
2094
|
+
// need to remove the old view if it's still in the DOM
|
|
2095
|
+
// and create a new view from the template.
|
|
2096
|
+
if (target.$fastTemplate !== value) {
|
|
2097
|
+
if (view.isComposed) {
|
|
2098
|
+
view.remove();
|
|
2099
|
+
view.unbind();
|
|
2100
|
+
}
|
|
2101
|
+
view = value.create();
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
// It's possible that the value is the same as the previous template
|
|
2105
|
+
// and that there's actually no need to compose it.
|
|
2106
|
+
if (!view.isComposed) {
|
|
2107
|
+
view.isComposed = true;
|
|
2108
|
+
view.bind(controller.source, controller.context);
|
|
2109
|
+
view.insertBefore(target);
|
|
2110
|
+
target.$fastView = view;
|
|
2111
|
+
target.$fastTemplate = value;
|
|
2112
|
+
} else if (view.needsBindOnly) {
|
|
2113
|
+
view.needsBindOnly = false;
|
|
2114
|
+
view.bind(controller.source, controller.context);
|
|
2115
|
+
}
|
|
2116
|
+
} else {
|
|
2117
|
+
const view = target.$fastView;
|
|
2118
|
+
// If there is a view and it's currently composed into
|
|
2119
|
+
// the DOM, then we need to remove it.
|
|
2120
|
+
if (view !== void 0 && view.isComposed) {
|
|
2121
|
+
view.isComposed = false;
|
|
2122
|
+
view.remove();
|
|
2123
|
+
if (view.needsBindOnly) {
|
|
2124
|
+
view.needsBindOnly = false;
|
|
2125
|
+
} else {
|
|
2126
|
+
view.unbind();
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
target.textContent = value;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
function updateTokenList(target, aspect, value) {
|
|
2133
|
+
var _a;
|
|
2134
|
+
const lookup = `${this.id}-t`;
|
|
2135
|
+
const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : target[lookup] = {
|
|
2136
|
+
v: 0,
|
|
2137
|
+
cv: Object.create(null)
|
|
2138
|
+
};
|
|
2139
|
+
const classVersions = state.cv;
|
|
2140
|
+
let version = state.v;
|
|
2141
|
+
const tokenList = target[aspect];
|
|
2142
|
+
// Add the classes, tracking the version at which they were added.
|
|
2143
|
+
if (value !== null && value !== undefined && value.length) {
|
|
2144
|
+
const names = value.split(/\s+/);
|
|
2145
|
+
for (let i = 0, ii = names.length; i < ii; ++i) {
|
|
2146
|
+
const currentName = names[i];
|
|
2147
|
+
if (currentName === "") {
|
|
2148
|
+
continue;
|
|
2149
|
+
}
|
|
2150
|
+
classVersions[currentName] = version;
|
|
2151
|
+
tokenList.add(currentName);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
state.v = version + 1;
|
|
2155
|
+
// If this is the first call to add classes, there's no need to remove old ones.
|
|
2156
|
+
if (version === 0) {
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
// Remove classes from the previous version.
|
|
2160
|
+
version -= 1;
|
|
2161
|
+
for (const name in classVersions) {
|
|
2162
|
+
if (classVersions[name] === version) {
|
|
2163
|
+
tokenList.remove(name);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
const sinkLookup = {
|
|
2168
|
+
[DOMAspect.attribute]: DOM.setAttribute,
|
|
2169
|
+
[DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
|
|
2170
|
+
[DOMAspect.property]: (t, a, v) => t[a] = v,
|
|
2171
|
+
[DOMAspect.content]: updateContent,
|
|
2172
|
+
[DOMAspect.tokenList]: updateTokenList,
|
|
2173
|
+
[DOMAspect.event]: () => void 0
|
|
2174
|
+
};
|
|
2175
|
+
/**
|
|
2176
|
+
* A directive that applies bindings.
|
|
2177
|
+
* @public
|
|
2178
|
+
*/
|
|
2179
|
+
class HTMLBindingDirective {
|
|
2180
|
+
/**
|
|
2181
|
+
* Creates an instance of HTMLBindingDirective.
|
|
2182
|
+
* @param dataBinding - The binding configuration to apply.
|
|
1671
2183
|
*/
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
fragment.appendChild(current);
|
|
1680
|
-
current = next;
|
|
1681
|
-
}
|
|
1682
|
-
fragment.appendChild(end);
|
|
2184
|
+
constructor(dataBinding) {
|
|
2185
|
+
this.dataBinding = dataBinding;
|
|
2186
|
+
this.updateTarget = null;
|
|
2187
|
+
/**
|
|
2188
|
+
* The type of aspect to target.
|
|
2189
|
+
*/
|
|
2190
|
+
this.aspectType = DOMAspect.content;
|
|
1683
2191
|
}
|
|
1684
2192
|
/**
|
|
1685
|
-
*
|
|
1686
|
-
*
|
|
2193
|
+
* Creates HTML to be used within a template.
|
|
2194
|
+
* @param add - Can be used to add behavior factories to a template.
|
|
1687
2195
|
*/
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
this.unbind();
|
|
1691
|
-
}
|
|
1692
|
-
onUnbind(behavior) {
|
|
1693
|
-
this.unbindables.push(behavior);
|
|
2196
|
+
createHTML(add) {
|
|
2197
|
+
return Markup.interpolation(add(this));
|
|
1694
2198
|
}
|
|
1695
2199
|
/**
|
|
1696
|
-
*
|
|
1697
|
-
* @param source - The binding source for the view's binding behaviors.
|
|
1698
|
-
* @param context - The execution context to run the behaviors within.
|
|
2200
|
+
* Creates a behavior.
|
|
1699
2201
|
*/
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
this.context = context;
|
|
1708
|
-
this.behaviors = behaviors = new Array(this.factories.length);
|
|
1709
|
-
const factories = this.factories;
|
|
1710
|
-
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
1711
|
-
const behavior = factories[i].createBehavior();
|
|
1712
|
-
behavior.bind(this);
|
|
1713
|
-
behaviors[i] = behavior;
|
|
1714
|
-
}
|
|
1715
|
-
} else {
|
|
1716
|
-
if (this.source !== null) {
|
|
1717
|
-
this.evaluateUnbindables();
|
|
1718
|
-
}
|
|
1719
|
-
this.isBound = false;
|
|
1720
|
-
this.source = source;
|
|
1721
|
-
this.context = context;
|
|
1722
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
1723
|
-
behaviors[i].bind(this);
|
|
2202
|
+
createBehavior() {
|
|
2203
|
+
var _a;
|
|
2204
|
+
if (this.updateTarget === null) {
|
|
2205
|
+
const sink = sinkLookup[this.aspectType];
|
|
2206
|
+
const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
|
|
2207
|
+
if (!sink) {
|
|
2208
|
+
throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
|
|
1724
2209
|
}
|
|
2210
|
+
this.data = `${this.id}-d`;
|
|
2211
|
+
this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
|
|
1725
2212
|
}
|
|
1726
|
-
this
|
|
2213
|
+
return this;
|
|
1727
2214
|
}
|
|
1728
|
-
/**
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
2215
|
+
/** @internal */
|
|
2216
|
+
bind(controller) {
|
|
2217
|
+
var _a;
|
|
2218
|
+
const target = controller.targets[this.targetNodeId];
|
|
2219
|
+
const isHydrating = isHydratable(controller) && controller.hydrationStage && controller.hydrationStage !== HydrationStage.hydrated;
|
|
2220
|
+
switch (this.aspectType) {
|
|
2221
|
+
case DOMAspect.event:
|
|
2222
|
+
target[this.data] = controller;
|
|
2223
|
+
target.addEventListener(this.targetAspect, this, this.dataBinding.options);
|
|
2224
|
+
break;
|
|
2225
|
+
case DOMAspect.content:
|
|
2226
|
+
controller.onUnbind(this);
|
|
2227
|
+
// intentional fall through
|
|
2228
|
+
default:
|
|
2229
|
+
const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : target[this.data] = this.dataBinding.createObserver(this, this);
|
|
2230
|
+
observer.target = target;
|
|
2231
|
+
observer.controller = controller;
|
|
2232
|
+
if (isHydrating && (this.aspectType === DOMAspect.attribute || this.aspectType === DOMAspect.booleanAttribute)) {
|
|
2233
|
+
observer.bind(controller);
|
|
2234
|
+
// Skip updating target during bind for attributes
|
|
2235
|
+
break;
|
|
2236
|
+
}
|
|
2237
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
2238
|
+
break;
|
|
1734
2239
|
}
|
|
1735
|
-
this.evaluateUnbindables();
|
|
1736
|
-
this.source = null;
|
|
1737
|
-
this.context = this;
|
|
1738
|
-
this.isBound = false;
|
|
1739
2240
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
2241
|
+
/** @internal */
|
|
2242
|
+
unbind(controller) {
|
|
2243
|
+
const target = controller.targets[this.targetNodeId];
|
|
2244
|
+
const view = target.$fastView;
|
|
2245
|
+
if (view !== void 0 && view.isComposed) {
|
|
2246
|
+
view.unbind();
|
|
2247
|
+
view.needsBindOnly = true;
|
|
1744
2248
|
}
|
|
1745
|
-
unbindables.length = 0;
|
|
1746
2249
|
}
|
|
1747
|
-
/**
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
views[i].unbind();
|
|
2250
|
+
/** @internal */
|
|
2251
|
+
handleEvent(event) {
|
|
2252
|
+
const controller = event.currentTarget[this.data];
|
|
2253
|
+
if (controller.isBound) {
|
|
2254
|
+
ExecutionContext.setEvent(event);
|
|
2255
|
+
const result = this.dataBinding.evaluate(controller.source, controller.context);
|
|
2256
|
+
ExecutionContext.setEvent(null);
|
|
2257
|
+
if (result !== true) {
|
|
2258
|
+
event.preventDefault();
|
|
2259
|
+
}
|
|
1758
2260
|
}
|
|
1759
2261
|
}
|
|
2262
|
+
/** @internal */
|
|
2263
|
+
handleChange(binding, observer) {
|
|
2264
|
+
const target = observer.target;
|
|
2265
|
+
const controller = observer.controller;
|
|
2266
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
2267
|
+
}
|
|
1760
2268
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
2269
|
+
HTMLDirective.define(HTMLBindingDirective, {
|
|
2270
|
+
aspected: true
|
|
2271
|
+
});
|
|
1764
2272
|
|
|
1765
2273
|
const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
|
|
1766
2274
|
const descriptorCache = {};
|
|
@@ -2015,7 +2523,6 @@ const Compiler = {
|
|
|
2015
2523
|
return parts[0];
|
|
2016
2524
|
}
|
|
2017
2525
|
let sourceAspect;
|
|
2018
|
-
let binding;
|
|
2019
2526
|
let isVolatile = false;
|
|
2020
2527
|
let bindingPolicy = void 0;
|
|
2021
2528
|
const partCount = parts.length;
|
|
@@ -2024,7 +2531,6 @@ const Compiler = {
|
|
|
2024
2531
|
return () => x;
|
|
2025
2532
|
}
|
|
2026
2533
|
sourceAspect = x.sourceAspect || sourceAspect;
|
|
2027
|
-
binding = x.dataBinding || binding;
|
|
2028
2534
|
isVolatile = isVolatile || x.dataBinding.isVolatile;
|
|
2029
2535
|
bindingPolicy = bindingPolicy || x.dataBinding.policy;
|
|
2030
2536
|
return x.dataBinding.evaluate;
|
|
@@ -2036,17 +2542,14 @@ const Compiler = {
|
|
|
2036
2542
|
}
|
|
2037
2543
|
return output;
|
|
2038
2544
|
};
|
|
2039
|
-
|
|
2040
|
-
binding.isVolatile = isVolatile;
|
|
2041
|
-
binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
|
|
2042
|
-
const directive = new HTMLBindingDirective(binding);
|
|
2545
|
+
const directive = new HTMLBindingDirective(oneWay(expression, bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy, isVolatile));
|
|
2043
2546
|
HTMLDirective.assignAspect(directive, sourceAspect);
|
|
2044
2547
|
return directive;
|
|
2045
2548
|
}
|
|
2046
2549
|
};
|
|
2047
2550
|
|
|
2048
2551
|
// Much thanks to LitHTML for working this out!
|
|
2049
|
-
const lastAttributeNameRegex = /* eslint-disable-next-line no-control-regex */
|
|
2552
|
+
const lastAttributeNameRegex = /* eslint-disable-next-line no-control-regex, max-len */
|
|
2050
2553
|
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
2051
2554
|
const noFactories = Object.create(null);
|
|
2052
2555
|
/**
|
|
@@ -2106,14 +2609,20 @@ class ViewTemplate {
|
|
|
2106
2609
|
this.factories = factories;
|
|
2107
2610
|
}
|
|
2108
2611
|
/**
|
|
2109
|
-
*
|
|
2110
|
-
* @param hostBindingTarget - The element that host behaviors will be bound to.
|
|
2612
|
+
* @internal
|
|
2111
2613
|
*/
|
|
2112
|
-
|
|
2614
|
+
compile() {
|
|
2113
2615
|
if (this.result === null) {
|
|
2114
2616
|
this.result = Compiler.compile(this.html, this.factories, this.policy);
|
|
2115
2617
|
}
|
|
2116
|
-
return this.result
|
|
2618
|
+
return this.result;
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* Creates an HTMLView instance based on this template definition.
|
|
2622
|
+
* @param hostBindingTarget - The element that host behaviors will be bound to.
|
|
2623
|
+
*/
|
|
2624
|
+
create(hostBindingTarget) {
|
|
2625
|
+
return this.compile().createView(hostBindingTarget);
|
|
2117
2626
|
}
|
|
2118
2627
|
/**
|
|
2119
2628
|
* Returns a directive that can inline the template.
|
|
@@ -2718,6 +3227,100 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
|
|
|
2718
3227
|
*/
|
|
2719
3228
|
FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
|
|
2720
3229
|
|
|
3230
|
+
/**
|
|
3231
|
+
* An extension of MutationObserver that supports unobserving nodes.
|
|
3232
|
+
* @internal
|
|
3233
|
+
*/
|
|
3234
|
+
class UnobservableMutationObserver extends MutationObserver {
|
|
3235
|
+
/**
|
|
3236
|
+
* Creates an instance of UnobservableMutationObserver.
|
|
3237
|
+
* @param callback - The callback to invoke when observed nodes are changed.
|
|
3238
|
+
*/
|
|
3239
|
+
constructor(callback) {
|
|
3240
|
+
function handler(mutations) {
|
|
3241
|
+
this.callback.call(null, mutations.filter(record => this.observedNodes.has(record.target)));
|
|
3242
|
+
}
|
|
3243
|
+
super(handler);
|
|
3244
|
+
this.callback = callback;
|
|
3245
|
+
this.observedNodes = new Set();
|
|
3246
|
+
}
|
|
3247
|
+
observe(target, options) {
|
|
3248
|
+
this.observedNodes.add(target);
|
|
3249
|
+
super.observe(target, options);
|
|
3250
|
+
}
|
|
3251
|
+
unobserve(target) {
|
|
3252
|
+
this.observedNodes.delete(target);
|
|
3253
|
+
if (this.observedNodes.size < 1) {
|
|
3254
|
+
this.disconnect();
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
/**
|
|
3259
|
+
* Bridges between ViewBehaviors and HostBehaviors, enabling a host to
|
|
3260
|
+
* control ViewBehaviors.
|
|
3261
|
+
* @public
|
|
3262
|
+
*/
|
|
3263
|
+
Object.freeze({
|
|
3264
|
+
/**
|
|
3265
|
+
* Creates a ViewBehaviorOrchestrator.
|
|
3266
|
+
* @param source - The source to to associate behaviors with.
|
|
3267
|
+
* @returns A ViewBehaviorOrchestrator.
|
|
3268
|
+
*/
|
|
3269
|
+
create(source) {
|
|
3270
|
+
const behaviors = [];
|
|
3271
|
+
const targets = {};
|
|
3272
|
+
let unbindables = null;
|
|
3273
|
+
let isConnected = false;
|
|
3274
|
+
return {
|
|
3275
|
+
source,
|
|
3276
|
+
context: ExecutionContext.default,
|
|
3277
|
+
targets,
|
|
3278
|
+
get isBound() {
|
|
3279
|
+
return isConnected;
|
|
3280
|
+
},
|
|
3281
|
+
addBehaviorFactory(factory, target) {
|
|
3282
|
+
var _a, _b, _c, _d;
|
|
3283
|
+
const compiled = factory;
|
|
3284
|
+
compiled.id = (_a = compiled.id) !== null && _a !== void 0 ? _a : nextId();
|
|
3285
|
+
compiled.targetNodeId = (_b = compiled.targetNodeId) !== null && _b !== void 0 ? _b : nextId();
|
|
3286
|
+
compiled.targetTagName = (_c = target.tagName) !== null && _c !== void 0 ? _c : null;
|
|
3287
|
+
compiled.policy = (_d = compiled.policy) !== null && _d !== void 0 ? _d : DOM.policy;
|
|
3288
|
+
this.addTarget(compiled.targetNodeId, target);
|
|
3289
|
+
this.addBehavior(compiled.createBehavior());
|
|
3290
|
+
},
|
|
3291
|
+
addTarget(nodeId, target) {
|
|
3292
|
+
targets[nodeId] = target;
|
|
3293
|
+
},
|
|
3294
|
+
addBehavior(behavior) {
|
|
3295
|
+
behaviors.push(behavior);
|
|
3296
|
+
if (isConnected) {
|
|
3297
|
+
behavior.bind(this);
|
|
3298
|
+
}
|
|
3299
|
+
},
|
|
3300
|
+
onUnbind(unbindable) {
|
|
3301
|
+
if (unbindables === null) {
|
|
3302
|
+
unbindables = [];
|
|
3303
|
+
}
|
|
3304
|
+
unbindables.push(unbindable);
|
|
3305
|
+
},
|
|
3306
|
+
connectedCallback(controller) {
|
|
3307
|
+
if (!isConnected) {
|
|
3308
|
+
isConnected = true;
|
|
3309
|
+
behaviors.forEach(x => x.bind(this));
|
|
3310
|
+
}
|
|
3311
|
+
},
|
|
3312
|
+
disconnectedCallback(controller) {
|
|
3313
|
+
if (isConnected) {
|
|
3314
|
+
isConnected = false;
|
|
3315
|
+
if (unbindables !== null) {
|
|
3316
|
+
unbindables.forEach(x => x.unbind(this));
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
});
|
|
3323
|
+
|
|
2721
3324
|
const defaultEventOptions = {
|
|
2722
3325
|
bubbles: true,
|
|
2723
3326
|
composed: true,
|
|
@@ -2756,6 +3359,11 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
2756
3359
|
*/
|
|
2757
3360
|
this.guardBehaviorConnection = false;
|
|
2758
3361
|
this.behaviors = null;
|
|
3362
|
+
/**
|
|
3363
|
+
* Tracks whether behaviors are connected so that
|
|
3364
|
+
* behaviors cant be connected multiple times
|
|
3365
|
+
*/
|
|
3366
|
+
this.behaviorsConnected = false;
|
|
2759
3367
|
this._mainStyles = null;
|
|
2760
3368
|
/**
|
|
2761
3369
|
* This allows Observable.getNotifier(...) to return the Controller
|
|
@@ -2996,7 +3604,19 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
2996
3604
|
return;
|
|
2997
3605
|
}
|
|
2998
3606
|
this.stage = 0 /* Stages.connecting */;
|
|
2999
|
-
|
|
3607
|
+
this.bindObservables();
|
|
3608
|
+
this.connectBehaviors();
|
|
3609
|
+
if (this.needsInitialization) {
|
|
3610
|
+
this.renderTemplate(this.template);
|
|
3611
|
+
this.addStyles(this.mainStyles);
|
|
3612
|
+
this.needsInitialization = false;
|
|
3613
|
+
} else if (this.view !== null) {
|
|
3614
|
+
this.view.bind(this.source);
|
|
3615
|
+
}
|
|
3616
|
+
this.stage = 1 /* Stages.connected */;
|
|
3617
|
+
Observable.notify(this, isConnectedPropertyName);
|
|
3618
|
+
}
|
|
3619
|
+
bindObservables() {
|
|
3000
3620
|
if (this.boundObservables !== null) {
|
|
3001
3621
|
const element = this.source;
|
|
3002
3622
|
const boundObservables = this.boundObservables;
|
|
@@ -3007,23 +3627,30 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3007
3627
|
}
|
|
3008
3628
|
this.boundObservables = null;
|
|
3009
3629
|
}
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3630
|
+
}
|
|
3631
|
+
connectBehaviors() {
|
|
3632
|
+
if (this.behaviorsConnected === false) {
|
|
3633
|
+
const behaviors = this.behaviors;
|
|
3634
|
+
if (behaviors !== null) {
|
|
3635
|
+
this.guardBehaviorConnection = true;
|
|
3636
|
+
for (const key of behaviors.keys()) {
|
|
3637
|
+
key.connectedCallback && key.connectedCallback(this);
|
|
3638
|
+
}
|
|
3639
|
+
this.guardBehaviorConnection = false;
|
|
3015
3640
|
}
|
|
3016
|
-
this.
|
|
3641
|
+
this.behaviorsConnected = true;
|
|
3017
3642
|
}
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3643
|
+
}
|
|
3644
|
+
disconnectBehaviors() {
|
|
3645
|
+
if (this.behaviorsConnected === true) {
|
|
3646
|
+
const behaviors = this.behaviors;
|
|
3647
|
+
if (behaviors !== null) {
|
|
3648
|
+
for (const key of behaviors.keys()) {
|
|
3649
|
+
key.disconnectedCallback && key.disconnectedCallback(this);
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
this.behaviorsConnected = false;
|
|
3024
3653
|
}
|
|
3025
|
-
this.stage = 1 /* Stages.connected */;
|
|
3026
|
-
Observable.notify(this, isConnectedPropertyName);
|
|
3027
3654
|
}
|
|
3028
3655
|
/**
|
|
3029
3656
|
* Runs disconnected lifecycle behavior on the associated element.
|
|
@@ -3037,12 +3664,7 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3037
3664
|
if (this.view !== null) {
|
|
3038
3665
|
this.view.unbind();
|
|
3039
3666
|
}
|
|
3040
|
-
|
|
3041
|
-
if (behaviors !== null) {
|
|
3042
|
-
for (const key of behaviors.keys()) {
|
|
3043
|
-
key.disconnectedCallback && key.disconnectedCallback(this);
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3667
|
+
this.disconnectBehaviors();
|
|
3046
3668
|
this.stage = 3 /* Stages.disconnected */;
|
|
3047
3669
|
}
|
|
3048
3670
|
/**
|
|
@@ -3242,6 +3864,87 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
|
|
|
3242
3864
|
} else {
|
|
3243
3865
|
ElementStyles.setDefaultStrategy(StyleElementStrategy);
|
|
3244
3866
|
}
|
|
3867
|
+
const deferHydrationAttribute = "defer-hydration";
|
|
3868
|
+
const needsHydrationAttribute = "needs-hydration";
|
|
3869
|
+
/**
|
|
3870
|
+
* An ElementController capable of hydrating FAST elements from
|
|
3871
|
+
* Declarative Shadow DOM.
|
|
3872
|
+
*
|
|
3873
|
+
* @beta
|
|
3874
|
+
*/
|
|
3875
|
+
class HydratableElementController extends ElementController {
|
|
3876
|
+
static hydrationObserverHandler(records) {
|
|
3877
|
+
for (const record of records) {
|
|
3878
|
+
HydratableElementController.hydrationObserver.unobserve(record.target);
|
|
3879
|
+
record.target.$fastController.connect();
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
connect() {
|
|
3883
|
+
var _a, _b;
|
|
3884
|
+
// Initialize needsHydration on first connect
|
|
3885
|
+
if (this.needsHydration === undefined) {
|
|
3886
|
+
this.needsHydration = this.source.getAttribute(needsHydrationAttribute) !== null;
|
|
3887
|
+
}
|
|
3888
|
+
// If the `defer-hydration` attribute exists on the source,
|
|
3889
|
+
// wait for it to be removed before continuing connection behavior.
|
|
3890
|
+
if (this.source.hasAttribute(deferHydrationAttribute)) {
|
|
3891
|
+
HydratableElementController.hydrationObserver.observe(this.source, {
|
|
3892
|
+
attributeFilter: [deferHydrationAttribute]
|
|
3893
|
+
});
|
|
3894
|
+
return;
|
|
3895
|
+
}
|
|
3896
|
+
// If the controller does not need to be hydrated, defer connection behavior
|
|
3897
|
+
// to the base-class. This case handles element re-connection and initial connection
|
|
3898
|
+
// of elements that did not get declarative shadow-dom emitted, as well as if an extending
|
|
3899
|
+
// class
|
|
3900
|
+
if (!this.needsHydration) {
|
|
3901
|
+
super.connect();
|
|
3902
|
+
return;
|
|
3903
|
+
}
|
|
3904
|
+
if (this.stage !== 3 /* Stages.disconnected */) {
|
|
3905
|
+
return;
|
|
3906
|
+
}
|
|
3907
|
+
this.stage = 0 /* Stages.connecting */;
|
|
3908
|
+
this.bindObservables();
|
|
3909
|
+
this.connectBehaviors();
|
|
3910
|
+
const element = this.source;
|
|
3911
|
+
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
3912
|
+
if (this.template) {
|
|
3913
|
+
if (isHydratable(this.template)) {
|
|
3914
|
+
let firstChild = host.firstChild;
|
|
3915
|
+
let lastChild = host.lastChild;
|
|
3916
|
+
if (element.shadowRoot === null) {
|
|
3917
|
+
// handle element boundary markers when shadowRoot is not present
|
|
3918
|
+
if (HydrationMarkup.isElementBoundaryStartMarker(firstChild)) {
|
|
3919
|
+
firstChild.data = "";
|
|
3920
|
+
firstChild = firstChild.nextSibling;
|
|
3921
|
+
}
|
|
3922
|
+
if (HydrationMarkup.isElementBoundaryEndMarker(lastChild)) {
|
|
3923
|
+
lastChild.data = "";
|
|
3924
|
+
lastChild = lastChild.previousSibling;
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
this.view = this.template.hydrate(firstChild, lastChild, element);
|
|
3928
|
+
(_b = this.view) === null || _b === void 0 ? void 0 : _b.bind(this.source);
|
|
3929
|
+
} else {
|
|
3930
|
+
this.renderTemplate(this.template);
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
this.addStyles(this.mainStyles);
|
|
3934
|
+
this.stage = 1 /* Stages.connected */;
|
|
3935
|
+
this.source.removeAttribute(needsHydrationAttribute);
|
|
3936
|
+
this.needsInitialization = this.needsHydration = false;
|
|
3937
|
+
Observable.notify(this, isConnectedPropertyName);
|
|
3938
|
+
}
|
|
3939
|
+
disconnect() {
|
|
3940
|
+
super.disconnect();
|
|
3941
|
+
HydratableElementController.hydrationObserver.unobserve(this.source);
|
|
3942
|
+
}
|
|
3943
|
+
static install() {
|
|
3944
|
+
ElementController.setStrategy(HydratableElementController);
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
|
|
3245
3948
|
|
|
3246
3949
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
3247
3950
|
function createFASTElement(BaseType) {
|