@microsoft/fast-element 2.0.0-beta.17 → 2.0.0-beta.18

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.
Files changed (56) hide show
  1. package/CHANGELOG.json +21 -0
  2. package/CHANGELOG.md +10 -1
  3. package/dist/dts/dom-policy.d.ts +68 -0
  4. package/dist/dts/dom.d.ts +116 -0
  5. package/dist/dts/index.d.ts +3 -2
  6. package/dist/dts/index.rollup.d.ts +0 -1
  7. package/dist/dts/index.rollup.debug.d.ts +0 -1
  8. package/dist/dts/interfaces.d.ts +15 -24
  9. package/dist/dts/polyfills.d.ts +0 -1
  10. package/dist/dts/templating/binding-signal.d.ts +3 -1
  11. package/dist/dts/templating/binding-two-way.d.ts +3 -1
  12. package/dist/dts/templating/binding.d.ts +16 -5
  13. package/dist/dts/templating/compiler.d.ts +11 -13
  14. package/dist/dts/templating/dangerous-html.d.ts +18 -0
  15. package/dist/dts/templating/html-directive.d.ts +50 -119
  16. package/dist/dts/templating/node-observation.d.ts +11 -1
  17. package/dist/dts/templating/ref.d.ts +4 -0
  18. package/dist/dts/templating/render.d.ts +30 -6
  19. package/dist/dts/templating/repeat.d.ts +1 -5
  20. package/dist/dts/templating/template.d.ts +39 -13
  21. package/dist/dts/templating/view.d.ts +2 -2
  22. package/dist/dts/utilities.d.ts +39 -0
  23. package/dist/esm/components/attributes.js +1 -1
  24. package/dist/esm/debug.js +4 -1
  25. package/dist/esm/dom-policy.js +337 -0
  26. package/dist/esm/dom.js +117 -0
  27. package/dist/esm/index.js +2 -1
  28. package/dist/esm/index.rollup.debug.js +3 -1
  29. package/dist/esm/index.rollup.js +3 -1
  30. package/dist/esm/platform.js +1 -1
  31. package/dist/esm/polyfills.js +3 -7
  32. package/dist/esm/templating/binding-signal.js +3 -2
  33. package/dist/esm/templating/binding-two-way.js +3 -2
  34. package/dist/esm/templating/binding.js +31 -54
  35. package/dist/esm/templating/compiler.js +31 -38
  36. package/dist/esm/templating/dangerous-html.js +23 -0
  37. package/dist/esm/templating/html-directive.js +38 -135
  38. package/dist/esm/templating/node-observation.js +14 -8
  39. package/dist/esm/templating/ref.js +1 -1
  40. package/dist/esm/templating/render.js +17 -6
  41. package/dist/esm/templating/repeat.js +2 -6
  42. package/dist/esm/templating/template.js +81 -56
  43. package/dist/esm/testing/fixture.js +1 -1
  44. package/dist/esm/utilities.js +68 -0
  45. package/dist/fast-element.api.json +1088 -608
  46. package/dist/fast-element.d.ts +190 -147
  47. package/dist/fast-element.debug.js +708 -384
  48. package/dist/fast-element.debug.min.js +1 -1
  49. package/dist/fast-element.js +679 -358
  50. package/dist/fast-element.min.js +1 -1
  51. package/dist/fast-element.untrimmed.d.ts +190 -147
  52. package/docs/api-report.md +66 -56
  53. package/package.json +5 -1
  54. package/yarn-error.log +177 -0
  55. package/dist/dts/templating/dom.d.ts +0 -41
  56. package/dist/esm/templating/dom.js +0 -49
@@ -1,10 +1,26 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ const isFunction = (object) => typeof object === "function";
5
+ /**
6
+ * @internal
7
+ */
8
+ const isString = (object) => typeof object === "string";
9
+ /**
10
+ * @internal
11
+ */
12
+ const noop = () => void 0;
13
+
14
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
1
15
  (function ensureGlobalThis() {
2
16
  if (typeof globalThis !== "undefined") {
3
17
  // We're running in a modern environment.
4
18
  return;
5
19
  }
20
+ // @ts-ignore
6
21
  if (typeof global !== "undefined") {
7
22
  // We're running in NodeJS
23
+ // @ts-ignore
8
24
  global.globalThis = global;
9
25
  }
10
26
  else if (typeof self !== "undefined") {
@@ -22,14 +38,8 @@
22
38
  result.globalThis = result;
23
39
  }
24
40
  })();
25
- // API-only Polyfill for trustedTypes
26
- if (!globalThis.trustedTypes) {
27
- globalThis.trustedTypes = {
28
- createPolicy: (n, r) => r,
29
- };
30
- }
31
41
 
32
- // ensure FAST global - duplicated in polyfills.ts and debug.ts
42
+ // ensure FAST global - duplicated debug.ts
33
43
  const propConfig = {
34
44
  configurable: false,
35
45
  enumerable: false,
@@ -117,19 +127,6 @@ function createMetadataLocator() {
117
127
  };
118
128
  }
119
129
 
120
- /**
121
- * @internal
122
- */
123
- const isFunction = (object) => typeof object === "function";
124
- /**
125
- * @internal
126
- */
127
- const isString = (object) => typeof object === "string";
128
- /**
129
- * @internal
130
- */
131
- const noop = () => void 0;
132
-
133
130
  /**
134
131
  * The default UpdateQueue.
135
132
  * @public
@@ -196,6 +193,456 @@ const Updates = FAST.getById(1 /* KernelServiceId.updateQueue */, () => {
196
193
  });
197
194
  });
198
195
 
196
+ /**
197
+ * The type of HTML aspect to target.
198
+ * @public
199
+ */
200
+ const DOMAspect = Object.freeze({
201
+ /**
202
+ * Not aspected.
203
+ */
204
+ none: 0,
205
+ /**
206
+ * An attribute.
207
+ */
208
+ attribute: 1,
209
+ /**
210
+ * A boolean attribute.
211
+ */
212
+ booleanAttribute: 2,
213
+ /**
214
+ * A property.
215
+ */
216
+ property: 3,
217
+ /**
218
+ * Content
219
+ */
220
+ content: 4,
221
+ /**
222
+ * A token list.
223
+ */
224
+ tokenList: 5,
225
+ /**
226
+ * An event.
227
+ */
228
+ event: 6,
229
+ });
230
+ const createHTML$1 = html => html;
231
+ const fastTrustedType = globalThis.trustedTypes
232
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML: createHTML$1 })
233
+ : { createHTML: createHTML$1 };
234
+ let defaultPolicy = Object.freeze({
235
+ createHTML(value) {
236
+ return fastTrustedType.createHTML(value);
237
+ },
238
+ protect(tagName, aspect, aspectName, sink) {
239
+ return sink;
240
+ },
241
+ });
242
+ const fastPolicy = defaultPolicy;
243
+ /**
244
+ * Common DOM APIs.
245
+ * @public
246
+ */
247
+ const DOM = Object.freeze({
248
+ /**
249
+ * @deprecated
250
+ * Use Updates.enqueue().
251
+ */
252
+ queueUpdate: Updates.enqueue,
253
+ /**
254
+ * @deprecated
255
+ * Use Updates.next()
256
+ */
257
+ nextUpdate: Updates.next,
258
+ /**
259
+ * @deprecated
260
+ * Use Updates.process()
261
+ */
262
+ processUpdates: Updates.process,
263
+ /**
264
+ * Gets the dom policy used by the templating system.
265
+ */
266
+ get policy() {
267
+ return defaultPolicy;
268
+ },
269
+ /**
270
+ * Sets the dom policy used by the templating system.
271
+ * @param policy - The policy to set.
272
+ * @remarks
273
+ * This API can only be called once, for security reasons. It should be
274
+ * called by the application developer at the start of their program.
275
+ */
276
+ setPolicy(value) {
277
+ if (defaultPolicy !== fastPolicy) {
278
+ throw FAST.error(1201 /* Message.onlySetDOMPolicyOnce */);
279
+ }
280
+ defaultPolicy = value;
281
+ },
282
+ /**
283
+ * Sets an attribute value on an element.
284
+ * @param element - The element to set the attribute value on.
285
+ * @param attributeName - The attribute name to set.
286
+ * @param value - The value of the attribute to set.
287
+ * @remarks
288
+ * If the value is `null` or `undefined`, the attribute is removed, otherwise
289
+ * it is set to the provided value using the standard `setAttribute` API.
290
+ */
291
+ setAttribute(element, attributeName, value) {
292
+ value === null || value === undefined
293
+ ? element.removeAttribute(attributeName)
294
+ : element.setAttribute(attributeName, value);
295
+ },
296
+ /**
297
+ * Sets a boolean attribute value.
298
+ * @param element - The element to set the boolean attribute value on.
299
+ * @param attributeName - The attribute name to set.
300
+ * @param value - The value of the attribute to set.
301
+ * @remarks
302
+ * If the value is true, the attribute is added; otherwise it is removed.
303
+ */
304
+ setBooleanAttribute(element, attributeName, value) {
305
+ value
306
+ ? element.setAttribute(attributeName, "")
307
+ : element.removeAttribute(attributeName);
308
+ },
309
+ });
310
+
311
+ function safeURL(tagName, aspect, aspectName, sink) {
312
+ return (target, name, value, ...rest) => {
313
+ if (isString(value)) {
314
+ value = value.replace("javascript:", "");
315
+ }
316
+ sink(target, name, value, ...rest);
317
+ };
318
+ }
319
+ function block(tagName, aspect, aspectName, sink) {
320
+ throw new Error(`${aspectName} on ${tagName !== null && tagName !== void 0 ? tagName : "text"} is blocked by the current DOMPolicy.`);
321
+ }
322
+ const defaultDOMElementGuards = {
323
+ a: {
324
+ [DOMAspect.attribute]: {
325
+ href: safeURL,
326
+ },
327
+ [DOMAspect.property]: {
328
+ href: safeURL,
329
+ },
330
+ },
331
+ area: {
332
+ [DOMAspect.attribute]: {
333
+ href: safeURL,
334
+ },
335
+ [DOMAspect.property]: {
336
+ href: safeURL,
337
+ },
338
+ },
339
+ button: {
340
+ [DOMAspect.attribute]: {
341
+ formaction: safeURL,
342
+ },
343
+ [DOMAspect.property]: {
344
+ formAction: safeURL,
345
+ },
346
+ },
347
+ embed: {
348
+ [DOMAspect.attribute]: {
349
+ src: block,
350
+ },
351
+ [DOMAspect.property]: {
352
+ src: block,
353
+ },
354
+ },
355
+ form: {
356
+ [DOMAspect.attribute]: {
357
+ action: safeURL,
358
+ },
359
+ [DOMAspect.property]: {
360
+ action: safeURL,
361
+ },
362
+ },
363
+ frame: {
364
+ [DOMAspect.attribute]: {
365
+ src: safeURL,
366
+ },
367
+ [DOMAspect.property]: {
368
+ src: safeURL,
369
+ },
370
+ },
371
+ iframe: {
372
+ [DOMAspect.attribute]: {
373
+ src: safeURL,
374
+ },
375
+ [DOMAspect.property]: {
376
+ src: safeURL,
377
+ srcdoc: block,
378
+ },
379
+ },
380
+ input: {
381
+ [DOMAspect.attribute]: {
382
+ formaction: safeURL,
383
+ },
384
+ [DOMAspect.property]: {
385
+ formAction: safeURL,
386
+ },
387
+ },
388
+ link: {
389
+ [DOMAspect.attribute]: {
390
+ href: block,
391
+ },
392
+ [DOMAspect.property]: {
393
+ href: block,
394
+ },
395
+ },
396
+ object: {
397
+ [DOMAspect.attribute]: {
398
+ codebase: block,
399
+ data: block,
400
+ },
401
+ [DOMAspect.property]: {
402
+ codeBase: block,
403
+ data: block,
404
+ },
405
+ },
406
+ script: {
407
+ [DOMAspect.attribute]: {
408
+ src: block,
409
+ text: block,
410
+ },
411
+ [DOMAspect.property]: {
412
+ src: block,
413
+ text: block,
414
+ innerText: block,
415
+ textContent: block,
416
+ },
417
+ },
418
+ style: {
419
+ [DOMAspect.property]: {
420
+ innerText: block,
421
+ textContent: block,
422
+ },
423
+ },
424
+ };
425
+ const blockedEvents = {
426
+ onabort: block,
427
+ onauxclick: block,
428
+ onbeforeinput: block,
429
+ onbeforematch: block,
430
+ onblur: block,
431
+ oncancel: block,
432
+ oncanplay: block,
433
+ oncanplaythrough: block,
434
+ onchange: block,
435
+ onclick: block,
436
+ onclose: block,
437
+ oncontextlost: block,
438
+ oncontextmenu: block,
439
+ oncontextrestored: block,
440
+ oncopy: block,
441
+ oncuechange: block,
442
+ oncut: block,
443
+ ondblclick: block,
444
+ ondrag: block,
445
+ ondragend: block,
446
+ ondragenter: block,
447
+ ondragleave: block,
448
+ ondragover: block,
449
+ ondragstart: block,
450
+ ondrop: block,
451
+ ondurationchange: block,
452
+ onemptied: block,
453
+ onended: block,
454
+ onerror: block,
455
+ onfocus: block,
456
+ onformdata: block,
457
+ oninput: block,
458
+ oninvalid: block,
459
+ onkeydown: block,
460
+ onkeypress: block,
461
+ onkeyup: block,
462
+ onload: block,
463
+ onloadeddata: block,
464
+ onloadedmetadata: block,
465
+ onloadstart: block,
466
+ onmousedown: block,
467
+ onmouseenter: block,
468
+ onmouseleave: block,
469
+ onmousemove: block,
470
+ onmouseout: block,
471
+ onmouseover: block,
472
+ onmouseup: block,
473
+ onpaste: block,
474
+ onpause: block,
475
+ onplay: block,
476
+ onplaying: block,
477
+ onprogress: block,
478
+ onratechange: block,
479
+ onreset: block,
480
+ onresize: block,
481
+ onscroll: block,
482
+ onsecuritypolicyviolation: block,
483
+ onseeked: block,
484
+ onseeking: block,
485
+ onselect: block,
486
+ onslotchange: block,
487
+ onstalled: block,
488
+ onsubmit: block,
489
+ onsuspend: block,
490
+ ontimeupdate: block,
491
+ ontoggle: block,
492
+ onvolumechange: block,
493
+ onwaiting: block,
494
+ onwebkitanimationend: block,
495
+ onwebkitanimationiteration: block,
496
+ onwebkitanimationstart: block,
497
+ onwebkittransitionend: block,
498
+ onwheel: block,
499
+ };
500
+ const defaultDOMGuards = {
501
+ elements: defaultDOMElementGuards,
502
+ aspects: {
503
+ [DOMAspect.attribute]: Object.assign({}, blockedEvents),
504
+ [DOMAspect.property]: Object.assign({ innerHTML: block }, blockedEvents),
505
+ [DOMAspect.event]: Object.assign({}, blockedEvents),
506
+ },
507
+ };
508
+ function createDomSinkGuards(config, defaults) {
509
+ const result = {};
510
+ for (const name in defaults) {
511
+ const overrideValue = config[name];
512
+ const defaultValue = defaults[name];
513
+ switch (overrideValue) {
514
+ case null:
515
+ // remove the default
516
+ break;
517
+ case undefined:
518
+ // keep the default
519
+ result[name] = defaultValue;
520
+ break;
521
+ default:
522
+ // override the default
523
+ result[name] = overrideValue;
524
+ break;
525
+ }
526
+ }
527
+ // add any new sinks that were not overrides
528
+ for (const name in config) {
529
+ if (!(name in result)) {
530
+ result[name] = config[name];
531
+ }
532
+ }
533
+ return Object.freeze(result);
534
+ }
535
+ function createDOMAspectGuards(config, defaults) {
536
+ const result = {};
537
+ for (const aspect in defaults) {
538
+ const overrideValue = config[aspect];
539
+ const defaultValue = defaults[aspect];
540
+ switch (overrideValue) {
541
+ case null:
542
+ // remove the default
543
+ break;
544
+ case undefined:
545
+ // keep the default
546
+ result[aspect] = createDomSinkGuards(defaultValue, {});
547
+ break;
548
+ default:
549
+ // override the default
550
+ result[aspect] = createDomSinkGuards(overrideValue, defaultValue);
551
+ break;
552
+ }
553
+ }
554
+ // add any new aspect guards that were not overrides
555
+ for (const aspect in config) {
556
+ if (!(aspect in result)) {
557
+ result[aspect] = createDomSinkGuards(config[aspect], {});
558
+ }
559
+ }
560
+ return Object.freeze(result);
561
+ }
562
+ function createElementGuards(config, defaults) {
563
+ const result = {};
564
+ for (const tag in defaults) {
565
+ const overrideValue = config[tag];
566
+ const defaultValue = defaults[tag];
567
+ switch (overrideValue) {
568
+ case null:
569
+ // remove the default
570
+ break;
571
+ case undefined:
572
+ // keep the default
573
+ result[tag] = createDOMAspectGuards(overrideValue, {});
574
+ break;
575
+ default:
576
+ // override the default aspects
577
+ result[tag] = createDOMAspectGuards(overrideValue, defaultValue);
578
+ break;
579
+ }
580
+ }
581
+ // Add any new element guards that were not overrides
582
+ for (const tag in config) {
583
+ if (!(tag in result)) {
584
+ result[tag] = createDOMAspectGuards(config[tag], {});
585
+ }
586
+ }
587
+ return Object.freeze(result);
588
+ }
589
+ function createDOMGuards(config, defaults) {
590
+ return Object.freeze({
591
+ elements: config.elements
592
+ ? createElementGuards(config.elements, defaults.elements)
593
+ : defaults.elements,
594
+ aspects: config.aspects
595
+ ? createDOMAspectGuards(config.aspects, defaults.aspects)
596
+ : defaults.aspects,
597
+ });
598
+ }
599
+ function createTrustedType() {
600
+ const createHTML = html => html;
601
+ return globalThis.trustedTypes
602
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML })
603
+ : { createHTML };
604
+ }
605
+ function tryGuard(aspectGuards, tagName, aspect, aspectName, sink) {
606
+ const sinkGuards = aspectGuards[aspect];
607
+ if (sinkGuards) {
608
+ const guard = sinkGuards[aspectName];
609
+ if (guard) {
610
+ return guard(tagName, aspect, aspectName, sink);
611
+ }
612
+ }
613
+ }
614
+ const DOMPolicy = Object.freeze({
615
+ /**
616
+ * Creates a new DOM Policy object.
617
+ * @param options The options to use in creating the policy.
618
+ * @returns The newly created DOMPolicy.
619
+ */
620
+ create(options = {}) {
621
+ var _a, _b;
622
+ const trustedType = (_a = options.trustedType) !== null && _a !== void 0 ? _a : createTrustedType();
623
+ const guards = createDOMGuards((_b = options.guards) !== null && _b !== void 0 ? _b : {}, defaultDOMGuards);
624
+ return Object.freeze({
625
+ createHTML(value) {
626
+ return trustedType.createHTML(value);
627
+ },
628
+ protect(tagName, aspect, aspectName, sink) {
629
+ var _a;
630
+ // Check for element-specific guards.
631
+ const key = (tagName !== null && tagName !== void 0 ? tagName : "").toLowerCase();
632
+ const elementGuards = guards.elements[key];
633
+ if (elementGuards) {
634
+ const guard = tryGuard(elementGuards, tagName, aspect, aspectName, sink);
635
+ if (guard) {
636
+ return guard;
637
+ }
638
+ }
639
+ // Check for guards applicable to all nodes.
640
+ return ((_a = tryGuard(guards.aspects, tagName, aspect, aspectName, sink)) !== null && _a !== void 0 ? _a : sink);
641
+ },
642
+ });
643
+ },
644
+ });
645
+
199
646
  /**
200
647
  * An implementation of {@link Notifier} that efficiently keeps track of
201
648
  * subscribers interested in a specific change notification on an
@@ -1482,55 +1929,6 @@ css.partial = (strings, ...values) => {
1482
1929
  */
1483
1930
  const cssPartial = css.partial;
1484
1931
 
1485
- /**
1486
- * Common DOM APIs.
1487
- * @public
1488
- */
1489
- const DOM = Object.freeze({
1490
- /**
1491
- * @deprecated
1492
- * Use Updates.enqueue().
1493
- */
1494
- queueUpdate: Updates.enqueue,
1495
- /**
1496
- * @deprecated
1497
- * Use Updates.next()
1498
- */
1499
- nextUpdate: Updates.next,
1500
- /**
1501
- * @deprecated
1502
- * Use Updates.process()
1503
- */
1504
- processUpdates: Updates.process,
1505
- /**
1506
- * Sets an attribute value on an element.
1507
- * @param element - The element to set the attribute value on.
1508
- * @param attributeName - The attribute name to set.
1509
- * @param value - The value of the attribute to set.
1510
- * @remarks
1511
- * If the value is `null` or `undefined`, the attribute is removed, otherwise
1512
- * it is set to the provided value using the standard `setAttribute` API.
1513
- */
1514
- setAttribute(element, attributeName, value) {
1515
- value === null || value === undefined
1516
- ? element.removeAttribute(attributeName)
1517
- : element.setAttribute(attributeName, value);
1518
- },
1519
- /**
1520
- * Sets a boolean attribute value.
1521
- * @param element - The element to set the boolean attribute value on.
1522
- * @param attributeName - The attribute name to set.
1523
- * @param value - The value of the attribute to set.
1524
- * @remarks
1525
- * If the value is true, the attribute is added; otherwise it is removed.
1526
- */
1527
- setBooleanAttribute(element, attributeName, value) {
1528
- value
1529
- ? element.setAttribute(attributeName, "")
1530
- : element.removeAttribute(attributeName);
1531
- },
1532
- });
1533
-
1534
1932
  const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
1535
1933
  const interpolationStart = `${marker}{`;
1536
1934
  const interpolationEnd = `}${marker}`;
@@ -1607,67 +2005,6 @@ const Parser = Object.freeze({
1607
2005
  },
1608
2006
  });
1609
2007
 
1610
- /**
1611
- * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1612
- * control ViewBehaviors.
1613
- * @public
1614
- */
1615
- const ViewBehaviorOrchestrator = Object.freeze({
1616
- /**
1617
- * Creates a ViewBehaviorOrchestrator.
1618
- * @param source - The source to to associate behaviors with.
1619
- * @returns A ViewBehaviorOrchestrator.
1620
- */
1621
- create(source) {
1622
- const behaviors = [];
1623
- const targets = {};
1624
- let unbindables = null;
1625
- let isConnected = false;
1626
- return {
1627
- source,
1628
- context: ExecutionContext.default,
1629
- targets,
1630
- get isBound() {
1631
- return isConnected;
1632
- },
1633
- addBehaviorFactory(factory, target) {
1634
- const nodeId = factory.nodeId || (factory.nodeId = nextId());
1635
- factory.id || (factory.id = nextId());
1636
- this.addTarget(nodeId, target);
1637
- this.addBehavior(factory.createBehavior());
1638
- },
1639
- addTarget(nodeId, target) {
1640
- targets[nodeId] = target;
1641
- },
1642
- addBehavior(behavior) {
1643
- behaviors.push(behavior);
1644
- if (isConnected) {
1645
- behavior.bind(this);
1646
- }
1647
- },
1648
- onUnbind(unbindable) {
1649
- if (unbindables === null) {
1650
- unbindables = [];
1651
- }
1652
- unbindables.push(unbindable);
1653
- },
1654
- connectedCallback(controller) {
1655
- if (!isConnected) {
1656
- isConnected = true;
1657
- behaviors.forEach(x => x.bind(this));
1658
- }
1659
- },
1660
- disconnectedCallback(controller) {
1661
- if (isConnected) {
1662
- isConnected = false;
1663
- if (unbindables !== null) {
1664
- unbindables.forEach(x => x.unbind(this));
1665
- }
1666
- }
1667
- },
1668
- };
1669
- },
1670
- });
1671
2008
  const registry = createTypeRegistry();
1672
2009
  /**
1673
2010
  * Instructs the template engine to apply behavior to a node.
@@ -1695,67 +2032,6 @@ const HTMLDirective = Object.freeze({
1695
2032
  registry.register(options);
1696
2033
  return type;
1697
2034
  },
1698
- });
1699
- /**
1700
- * Decorator: Defines an HTMLDirective.
1701
- * @param options - Provides options that specify the directive's application.
1702
- * @public
1703
- */
1704
- function htmlDirective(options) {
1705
- /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
1706
- return function (type) {
1707
- HTMLDirective.define(type, options);
1708
- };
1709
- }
1710
- /**
1711
- * Captures a binding expression along with related information and capabilities.
1712
- *
1713
- * @public
1714
- */
1715
- class Binding {
1716
- /**
1717
- * Creates a binding.
1718
- * @param evaluate - Evaluates the binding.
1719
- * @param isVolatile - Indicates whether the binding is volatile.
1720
- */
1721
- constructor(evaluate, isVolatile = false) {
1722
- this.evaluate = evaluate;
1723
- this.isVolatile = isVolatile;
1724
- }
1725
- }
1726
- /**
1727
- * The type of HTML aspect to target.
1728
- * @public
1729
- */
1730
- const Aspect = Object.freeze({
1731
- /**
1732
- * Not aspected.
1733
- */
1734
- none: 0,
1735
- /**
1736
- * An attribute.
1737
- */
1738
- attribute: 1,
1739
- /**
1740
- * A boolean attribute.
1741
- */
1742
- booleanAttribute: 2,
1743
- /**
1744
- * A property.
1745
- */
1746
- property: 3,
1747
- /**
1748
- * Content
1749
- */
1750
- content: 4,
1751
- /**
1752
- * A token list.
1753
- */
1754
- tokenList: 5,
1755
- /**
1756
- * An event.
1757
- */
1758
- event: 6,
1759
2035
  /**
1760
2036
  *
1761
2037
  * @param directive - The directive to assign the aspect to.
@@ -1763,9 +2039,9 @@ const Aspect = Object.freeze({
1763
2039
  * @remarks
1764
2040
  * If a falsy value is provided, then the content aspect will be assigned.
1765
2041
  */
1766
- assign(directive, value) {
2042
+ assignAspect(directive, value) {
1767
2043
  if (!value) {
1768
- directive.aspectType = Aspect.content;
2044
+ directive.aspectType = DOMAspect.content;
1769
2045
  return;
1770
2046
  }
1771
2047
  directive.sourceAspect = value;
@@ -1774,24 +2050,53 @@ const Aspect = Object.freeze({
1774
2050
  directive.targetAspect = value.substring(1);
1775
2051
  directive.aspectType =
1776
2052
  directive.targetAspect === "classList"
1777
- ? Aspect.tokenList
1778
- : Aspect.property;
2053
+ ? DOMAspect.tokenList
2054
+ : DOMAspect.property;
1779
2055
  break;
1780
2056
  case "?":
1781
2057
  directive.targetAspect = value.substring(1);
1782
- directive.aspectType = Aspect.booleanAttribute;
2058
+ directive.aspectType = DOMAspect.booleanAttribute;
1783
2059
  break;
1784
2060
  case "@":
1785
2061
  directive.targetAspect = value.substring(1);
1786
- directive.aspectType = Aspect.event;
2062
+ directive.aspectType = DOMAspect.event;
1787
2063
  break;
1788
2064
  default:
1789
2065
  directive.targetAspect = value;
1790
- directive.aspectType = Aspect.attribute;
2066
+ directive.aspectType = DOMAspect.attribute;
1791
2067
  break;
1792
2068
  }
1793
2069
  },
1794
2070
  });
2071
+ /**
2072
+ * Decorator: Defines an HTMLDirective.
2073
+ * @param options - Provides options that specify the directive's application.
2074
+ * @public
2075
+ */
2076
+ function htmlDirective(options) {
2077
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
2078
+ return function (type) {
2079
+ HTMLDirective.define(type, options);
2080
+ };
2081
+ }
2082
+ /**
2083
+ * Captures a binding expression along with related information and capabilities.
2084
+ *
2085
+ * @public
2086
+ */
2087
+ class Binding {
2088
+ /**
2089
+ * Creates a binding.
2090
+ * @param evaluate - Evaluates the binding.
2091
+ * @param policy - The security policy to associate with this binding.
2092
+ * @param isVolatile - Indicates whether the binding is volatile.
2093
+ */
2094
+ constructor(evaluate, policy, isVolatile = false) {
2095
+ this.evaluate = evaluate;
2096
+ this.policy = policy;
2097
+ this.isVolatile = isVolatile;
2098
+ }
2099
+ }
1795
2100
  /**
1796
2101
  * A base class used for attribute directives that don't need internal state.
1797
2102
  * @public
@@ -1803,10 +2108,6 @@ class StatelessAttachedAttributeDirective {
1803
2108
  */
1804
2109
  constructor(options) {
1805
2110
  this.options = options;
1806
- /**
1807
- * The unique id of the factory.
1808
- */
1809
- this.id = nextId();
1810
2111
  /**
1811
2112
  * Opts out of JSON stringification.
1812
2113
  * @internal
@@ -1831,15 +2132,6 @@ class StatelessAttachedAttributeDirective {
1831
2132
  }
1832
2133
  }
1833
2134
 
1834
- const createInnerHTMLBinding = globalThis.TrustedHTML
1835
- ? (binding) => (s, c) => {
1836
- const value = binding(s, c);
1837
- if (value instanceof TrustedHTML) {
1838
- return value;
1839
- }
1840
- throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1841
- }
1842
- : (binding) => binding;
1843
2135
  class OnChangeBinding extends Binding {
1844
2136
  createObserver(_, subscriber) {
1845
2137
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
@@ -1952,8 +2244,14 @@ function updateTokenList(target, aspect, value) {
1952
2244
  }
1953
2245
  }
1954
2246
  }
1955
- const setProperty = (t, a, v) => (t[a] = v);
1956
- const eventTarget = () => void 0;
2247
+ const sinkLookup = {
2248
+ [DOMAspect.attribute]: DOM.setAttribute,
2249
+ [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
2250
+ [DOMAspect.property]: (t, a, v) => (t[a] = v),
2251
+ [DOMAspect.content]: updateContent,
2252
+ [DOMAspect.tokenList]: updateTokenList,
2253
+ [DOMAspect.event]: () => void 0,
2254
+ };
1957
2255
  /**
1958
2256
  * A directive that applies bindings.
1959
2257
  * @public
@@ -1966,15 +2264,10 @@ class HTMLBindingDirective {
1966
2264
  constructor(dataBinding) {
1967
2265
  this.dataBinding = dataBinding;
1968
2266
  this.updateTarget = null;
1969
- /**
1970
- * The unique id of the factory.
1971
- */
1972
- this.id = nextId();
1973
2267
  /**
1974
2268
  * The type of aspect to target.
1975
2269
  */
1976
- this.aspectType = Aspect.content;
1977
- this.data = `${this.id}-d`;
2270
+ this.aspectType = DOMAspect.content;
1978
2271
  }
1979
2272
  /**
1980
2273
  * Creates HTML to be used within a template.
@@ -1987,45 +2280,28 @@ class HTMLBindingDirective {
1987
2280
  * Creates a behavior.
1988
2281
  */
1989
2282
  createBehavior() {
2283
+ var _a;
1990
2284
  if (this.updateTarget === null) {
1991
- if (this.targetAspect === "innerHTML") {
1992
- this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
1993
- }
1994
- switch (this.aspectType) {
1995
- case 1:
1996
- this.updateTarget = DOM.setAttribute;
1997
- break;
1998
- case 2:
1999
- this.updateTarget = DOM.setBooleanAttribute;
2000
- break;
2001
- case 3:
2002
- this.updateTarget = setProperty;
2003
- break;
2004
- case 4:
2005
- this.updateTarget = updateContent;
2006
- break;
2007
- case 5:
2008
- this.updateTarget = updateTokenList;
2009
- break;
2010
- case 6:
2011
- this.updateTarget = eventTarget;
2012
- break;
2013
- default:
2014
- throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2285
+ const sink = sinkLookup[this.aspectType];
2286
+ const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
2287
+ if (!sink) {
2288
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2015
2289
  }
2290
+ this.data = `${this.id}-d`;
2291
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
2016
2292
  }
2017
2293
  return this;
2018
2294
  }
2019
2295
  /** @internal */
2020
2296
  bind(controller) {
2021
2297
  var _a;
2022
- const target = controller.targets[this.nodeId];
2023
- switch (this.updateTarget) {
2024
- case eventTarget:
2298
+ const target = controller.targets[this.targetNodeId];
2299
+ switch (this.aspectType) {
2300
+ case DOMAspect.event:
2025
2301
  target[this.data] = controller;
2026
2302
  target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2027
2303
  break;
2028
- case updateContent:
2304
+ case DOMAspect.content:
2029
2305
  controller.onUnbind(this);
2030
2306
  // intentional fall through
2031
2307
  default:
@@ -2038,7 +2314,7 @@ class HTMLBindingDirective {
2038
2314
  }
2039
2315
  /** @internal */
2040
2316
  unbind(controller) {
2041
- const target = controller.targets[this.nodeId];
2317
+ const target = controller.targets[this.targetNodeId];
2042
2318
  const view = target.$fastView;
2043
2319
  if (view !== void 0 && view.isComposed) {
2044
2320
  view.unbind();
@@ -2068,21 +2344,23 @@ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2068
2344
  /**
2069
2345
  * Creates an standard binding.
2070
2346
  * @param expression - The binding to refresh when changed.
2347
+ * @param policy - The security policy to associate with th binding.
2071
2348
  * @param isVolatile - Indicates whether the binding is volatile or not.
2072
2349
  * @returns A binding configuration.
2073
2350
  * @public
2074
2351
  */
2075
- function bind(expression, isVolatile = Observable.isVolatileBinding(expression)) {
2076
- return new OnChangeBinding(expression, isVolatile);
2352
+ function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
2353
+ return new OnChangeBinding(expression, policy, isVolatile);
2077
2354
  }
2078
2355
  /**
2079
2356
  * Creates a one time binding
2080
2357
  * @param expression - The binding to refresh when signaled.
2358
+ * @param policy - The security policy to associate with th binding.
2081
2359
  * @returns A binding configuration.
2082
2360
  * @public
2083
2361
  */
2084
- function oneTime(expression) {
2085
- return new OneTimeBinding(expression);
2362
+ function oneTime(expression, policy) {
2363
+ return new OneTimeBinding(expression, policy);
2086
2364
  }
2087
2365
  /**
2088
2366
  * Creates an event listener binding.
@@ -2092,7 +2370,7 @@ function oneTime(expression) {
2092
2370
  * @public
2093
2371
  */
2094
2372
  function listener(expression, options) {
2095
- const config = new OnChangeBinding(expression, false);
2373
+ const config = new OnChangeBinding(expression);
2096
2374
  config.options = options;
2097
2375
  return config;
2098
2376
  }
@@ -2373,20 +2651,25 @@ const warningHost = new Proxy(document.createElement("div"), {
2373
2651
  },
2374
2652
  });
2375
2653
  class CompilationContext {
2376
- constructor(fragment, directives) {
2654
+ constructor(fragment, directives, policy) {
2377
2655
  this.fragment = fragment;
2378
2656
  this.directives = directives;
2657
+ this.policy = policy;
2379
2658
  this.proto = null;
2380
2659
  this.nodeIds = new Set();
2381
2660
  this.descriptors = {};
2382
2661
  this.factories = [];
2383
2662
  }
2384
- addFactory(factory, parentId, nodeId, targetIndex) {
2663
+ addFactory(factory, parentId, nodeId, targetIndex, tagName) {
2664
+ var _a, _b;
2385
2665
  if (!this.nodeIds.has(nodeId)) {
2386
2666
  this.nodeIds.add(nodeId);
2387
2667
  this.addTargetDescriptor(parentId, nodeId, targetIndex);
2388
2668
  }
2389
- factory.nodeId = nodeId;
2669
+ factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
2670
+ factory.targetNodeId = nodeId;
2671
+ factory.targetTagName = tagName;
2672
+ factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
2390
2673
  this.factories.push(factory);
2391
2674
  }
2392
2675
  freeze() {
@@ -2439,19 +2722,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2439
2722
  let result = null;
2440
2723
  if (parseResult === null) {
2441
2724
  if (includeBasicValues) {
2442
- result = new HTMLBindingDirective(oneTime(() => attrValue));
2443
- Aspect.assign(result, attr.name);
2725
+ result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
2726
+ HTMLDirective.assignAspect(result, attr.name);
2444
2727
  }
2445
2728
  }
2446
2729
  else {
2447
2730
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2448
- result = Compiler.aggregate(parseResult);
2731
+ result = Compiler.aggregate(parseResult, context.policy);
2449
2732
  }
2450
2733
  if (result !== null) {
2451
2734
  node.removeAttributeNode(attr);
2452
2735
  i--;
2453
2736
  ii--;
2454
- context.addFactory(result, parentId, nodeId, nodeIndex);
2737
+ context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
2455
2738
  }
2456
2739
  }
2457
2740
  }
@@ -2476,8 +2759,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2476
2759
  }
2477
2760
  else {
2478
2761
  currentNode.textContent = " ";
2479
- Aspect.assign(currentPart);
2480
- context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2762
+ HTMLDirective.assignAspect(currentPart);
2763
+ context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
2481
2764
  }
2482
2765
  lastNode = currentNode;
2483
2766
  }
@@ -2509,7 +2792,7 @@ function compileNode(context, parentId, node, nodeIndex) {
2509
2792
  if (parts !== null) {
2510
2793
  context.addFactory(
2511
2794
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2512
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
2795
+ Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
2513
2796
  }
2514
2797
  break;
2515
2798
  }
@@ -2523,45 +2806,28 @@ function isMarker(node, directives) {
2523
2806
  Parser.parse(node.data, directives) !== null);
2524
2807
  }
2525
2808
  const templateTag = "TEMPLATE";
2526
- const policyOptions = { createHTML: html => html };
2527
- let htmlPolicy = globalThis.trustedTypes
2528
- ? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
2529
- : policyOptions;
2530
- const fastHTMLPolicy = htmlPolicy;
2531
2809
  /**
2532
2810
  * Common APIs related to compilation.
2533
2811
  * @public
2534
2812
  */
2535
2813
  const Compiler = {
2536
- /**
2537
- * Sets the HTML trusted types policy used by the compiler.
2538
- * @param policy - The policy to set for HTML.
2539
- * @remarks
2540
- * This API can only be called once, for security reasons. It should be
2541
- * called by the application developer at the start of their program.
2542
- */
2543
- setHTMLPolicy(policy) {
2544
- if (htmlPolicy !== fastHTMLPolicy) {
2545
- throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
2546
- }
2547
- htmlPolicy = policy;
2548
- },
2549
2814
  /**
2550
2815
  * Compiles a template and associated directives into a compilation
2551
2816
  * result which can be used to create views.
2552
2817
  * @param html - The html string or template element to compile.
2553
- * @param directives - The directives referenced by the template.
2818
+ * @param factories - The behavior factories referenced by the template.
2819
+ * @param policy - The security policy to compile the html with.
2554
2820
  * @remarks
2555
2821
  * The template that is provided for compilation is altered in-place
2556
2822
  * and cannot be compiled again. If the original template must be preserved,
2557
2823
  * it is recommended that you clone the original and pass the clone to this API.
2558
2824
  * @public
2559
2825
  */
2560
- compile(html, directives) {
2826
+ compile(html, factories, policy = DOM.policy) {
2561
2827
  let template;
2562
2828
  if (isString(html)) {
2563
2829
  template = document.createElement(templateTag);
2564
- template.innerHTML = htmlPolicy.createHTML(html);
2830
+ template.innerHTML = policy.createHTML(html);
2565
2831
  const fec = template.content.firstElementChild;
2566
2832
  if (fec !== null && fec.tagName === templateTag) {
2567
2833
  template = fec;
@@ -2572,18 +2838,18 @@ const Compiler = {
2572
2838
  }
2573
2839
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
2574
2840
  const fragment = document.adoptNode(template.content);
2575
- const context = new CompilationContext(fragment, directives);
2841
+ const context = new CompilationContext(fragment, factories, policy);
2576
2842
  compileAttributes(context, "", template, /* host */ "h", 0, true);
2577
2843
  if (
2578
2844
  // If the first node in a fragment is a marker, that means it's an unstable first node,
2579
2845
  // because something like a when, repeat, etc. could add nodes before the marker.
2580
2846
  // To mitigate this, we insert a stable first node. However, if we insert a node,
2581
2847
  // that will alter the result of the TreeWalker. So, we also need to offset the target index.
2582
- isMarker(fragment.firstChild, directives) ||
2848
+ isMarker(fragment.firstChild, factories) ||
2583
2849
  // Or if there is only one node and a directive, it means the template's content
2584
2850
  // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
2585
2851
  // the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
2586
- (fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
2852
+ (fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
2587
2853
  fragment.insertBefore(document.createComment(""), fragment.firstChild);
2588
2854
  }
2589
2855
  compileChildren(context, fragment, /* root */ "r");
@@ -2602,15 +2868,17 @@ const Compiler = {
2602
2868
  * Aggregates an array of strings and directives into a single directive.
2603
2869
  * @param parts - A heterogeneous array of static strings interspersed with
2604
2870
  * directives.
2871
+ * @param policy - The security policy to use with the aggregated bindings.
2605
2872
  * @returns A single inline directive that aggregates the behavior of all the parts.
2606
2873
  */
2607
- aggregate(parts) {
2874
+ aggregate(parts, policy = DOM.policy) {
2608
2875
  if (parts.length === 1) {
2609
2876
  return parts[0];
2610
2877
  }
2611
2878
  let sourceAspect;
2612
2879
  let binding;
2613
2880
  let isVolatile = false;
2881
+ let bindingPolicy = void 0;
2614
2882
  const partCount = parts.length;
2615
2883
  const finalParts = parts.map((x) => {
2616
2884
  if (isString(x)) {
@@ -2619,6 +2887,7 @@ const Compiler = {
2619
2887
  sourceAspect = x.sourceAspect || sourceAspect;
2620
2888
  binding = x.dataBinding || binding;
2621
2889
  isVolatile = isVolatile || x.dataBinding.isVolatile;
2890
+ bindingPolicy = bindingPolicy || x.dataBinding.policy;
2622
2891
  return x.dataBinding.evaluate;
2623
2892
  });
2624
2893
  const expression = (scope, context) => {
@@ -2630,12 +2899,26 @@ const Compiler = {
2630
2899
  };
2631
2900
  binding.evaluate = expression;
2632
2901
  binding.isVolatile = isVolatile;
2902
+ binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
2633
2903
  const directive = new HTMLBindingDirective(binding);
2634
- Aspect.assign(directive, sourceAspect);
2904
+ HTMLDirective.assignAspect(directive, sourceAspect);
2635
2905
  return directive;
2636
2906
  },
2637
2907
  };
2638
2908
 
2909
+ // Much thanks to LitHTML for working this out!
2910
+ const lastAttributeNameRegex =
2911
+ /* eslint-disable-next-line no-control-regex */
2912
+ /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2913
+ function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
2914
+ if (definition.aspected) {
2915
+ const match = lastAttributeNameRegex.exec(prevString);
2916
+ if (match !== null) {
2917
+ HTMLDirective.assignAspect(value, match[2]);
2918
+ }
2919
+ }
2920
+ return value.createHTML(add);
2921
+ }
2639
2922
  /**
2640
2923
  * A template capable of creating HTMLView instances or rendering directly to DOM.
2641
2924
  * @public
@@ -2645,8 +2928,10 @@ class ViewTemplate {
2645
2928
  * Creates an instance of ViewTemplate.
2646
2929
  * @param html - The html representing what this template will instantiate, including placeholders for directives.
2647
2930
  * @param factories - The directives that will be connected to placeholders in the html.
2931
+ * @param policy - The security policy to use when compiling this template.
2648
2932
  */
2649
- constructor(html, factories) {
2933
+ constructor(html, factories = {}, policy) {
2934
+ this.policy = policy;
2650
2935
  this.result = null;
2651
2936
  /**
2652
2937
  * Opts out of JSON stringification.
@@ -2662,10 +2947,28 @@ class ViewTemplate {
2662
2947
  */
2663
2948
  create(hostBindingTarget) {
2664
2949
  if (this.result === null) {
2665
- this.result = Compiler.compile(this.html, this.factories);
2950
+ this.result = Compiler.compile(this.html, this.factories, this.policy);
2666
2951
  }
2667
2952
  return this.result.createView(hostBindingTarget);
2668
2953
  }
2954
+ /**
2955
+ * Sets the DOMPolicy for this template.
2956
+ * @param policy - The policy to associated with this template.
2957
+ * @returns The modified template instance.
2958
+ * @remarks
2959
+ * The DOMPolicy can only be set once for a template and cannot be
2960
+ * set after the template is compiled.
2961
+ */
2962
+ withPolicy(policy) {
2963
+ if (this.result) {
2964
+ throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
2965
+ }
2966
+ if (this.policy) {
2967
+ throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
2968
+ }
2969
+ this.policy = policy;
2970
+ return this;
2971
+ }
2669
2972
  /**
2670
2973
  * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
2671
2974
  * @param source - The data source to bind the template to.
@@ -2679,17 +2982,47 @@ class ViewTemplate {
2679
2982
  view.appendTo(host);
2680
2983
  return view;
2681
2984
  }
2682
- }
2683
- // Much thanks to LitHTML for working this out!
2684
- const lastAttributeNameRegex =
2685
- /* eslint-disable-next-line no-control-regex */
2686
- /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2687
- function createAspectedHTML(value, prevString, add) {
2688
- const match = lastAttributeNameRegex.exec(prevString);
2689
- if (match !== null) {
2690
- Aspect.assign(value, match[2]);
2985
+ /**
2986
+ * Creates a template based on a set of static strings and dynamic values.
2987
+ * @param strings - The static strings to create the template with.
2988
+ * @param values - The dynamic values to create the template with.
2989
+ * @param policy - The DOMPolicy to associated with the template.
2990
+ * @returns A ViewTemplate.
2991
+ * @remarks
2992
+ * This API should not be used directly under normal circumstances because constructing
2993
+ * a template in this way, if not done properly, can open up the application to XSS
2994
+ * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
2995
+ * and also be sure to manually sanitize all static strings particularly if they can
2996
+ * come from user input.
2997
+ */
2998
+ static create(strings, values, policy) {
2999
+ let html = "";
3000
+ const factories = Object.create(null);
3001
+ const add = (factory) => {
3002
+ var _a;
3003
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
3004
+ factories[id] = factory;
3005
+ return id;
3006
+ };
3007
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
3008
+ const currentString = strings[i];
3009
+ let currentValue = values[i];
3010
+ let definition;
3011
+ html += currentString;
3012
+ if (isFunction(currentValue)) {
3013
+ currentValue = new HTMLBindingDirective(bind(currentValue));
3014
+ }
3015
+ else if (currentValue instanceof Binding) {
3016
+ currentValue = new HTMLBindingDirective(currentValue);
3017
+ }
3018
+ else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
3019
+ const staticValue = currentValue;
3020
+ currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
3021
+ }
3022
+ html += createHTML(currentValue, currentString, add, definition);
3023
+ }
3024
+ return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
2691
3025
  }
2692
- return value.createHTML(add);
2693
3026
  }
2694
3027
  /**
2695
3028
  * Transforms a template literal string into a ViewTemplate.
@@ -2701,49 +3034,10 @@ function createAspectedHTML(value, prevString, add) {
2701
3034
  * @public
2702
3035
  */
2703
3036
  function html(strings, ...values) {
2704
- let html = "";
2705
- const factories = Object.create(null);
2706
- const add = (factory) => {
2707
- var _a;
2708
- const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
2709
- factories[id] = factory;
2710
- return id;
2711
- };
2712
- for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
2713
- const currentString = strings[i];
2714
- const currentValue = values[i];
2715
- let definition;
2716
- html += currentString;
2717
- if (isFunction(currentValue)) {
2718
- html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2719
- }
2720
- else if (isString(currentValue)) {
2721
- const match = lastAttributeNameRegex.exec(currentString);
2722
- if (match !== null) {
2723
- const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2724
- Aspect.assign(directive, match[2]);
2725
- html += directive.createHTML(add);
2726
- }
2727
- else {
2728
- html += currentValue;
2729
- }
2730
- }
2731
- else if (currentValue instanceof Binding) {
2732
- html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2733
- }
2734
- else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2735
- html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2736
- }
2737
- else {
2738
- if (definition.aspected) {
2739
- html += createAspectedHTML(currentValue, currentString, add);
2740
- }
2741
- else {
2742
- html += currentValue.createHTML(add);
2743
- }
2744
- }
3037
+ if (Array.isArray(strings) && Array.isArray(strings.raw)) {
3038
+ return ViewTemplate.create(strings, values);
2745
3039
  }
2746
- return new ViewTemplate(html + strings[strings.length - 1], factories);
3040
+ throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
2747
3041
  }
2748
3042
 
2749
3043
  /**
@@ -2756,7 +3050,7 @@ class RefDirective extends StatelessAttachedAttributeDirective {
2756
3050
  * @param controller - The view controller that manages the lifecycle of this behavior.
2757
3051
  */
2758
3052
  bind(controller) {
2759
- controller.source[this.options] = controller.targets[this.nodeId];
3053
+ controller.source[this.options] = controller.targets[this.targetNodeId];
2760
3054
  }
2761
3055
  }
2762
3056
  HTMLDirective.define(RefDirective);
@@ -2830,7 +3124,7 @@ class RepeatBehavior {
2830
3124
  * @param controller - The view controller that manages the lifecycle of this behavior.
2831
3125
  */
2832
3126
  bind(controller) {
2833
- this.location = controller.targets[this.directive.nodeId];
3127
+ this.location = controller.targets[this.directive.targetNodeId];
2834
3128
  this.controller = controller;
2835
3129
  this.items = this.itemsBindingObserver.bind(controller);
2836
3130
  this.template = this.templateBindingObserver.bind(controller);
@@ -3010,10 +3304,6 @@ class RepeatDirective {
3010
3304
  this.dataBinding = dataBinding;
3011
3305
  this.templateBinding = templateBinding;
3012
3306
  this.options = options;
3013
- /**
3014
- * The unique id of the factory.
3015
- */
3016
- this.id = nextId();
3017
3307
  ArrayObserver.enable();
3018
3308
  }
3019
3309
  /**
@@ -3062,9 +3352,15 @@ const elements = (selector) => selector
3062
3352
  * Internally used by the SlottedDirective and the ChildrenDirective.
3063
3353
  */
3064
3354
  class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3065
- constructor() {
3066
- super(...arguments);
3067
- this.controllerProperty = `${this.id}-c`;
3355
+ /**
3356
+ * The unique id of the factory.
3357
+ */
3358
+ get id() {
3359
+ return this._id;
3360
+ }
3361
+ set id(value) {
3362
+ this._id = value;
3363
+ this._controllerProperty = `${value}-c`;
3068
3364
  }
3069
3365
  /**
3070
3366
  * Bind this behavior to the source.
@@ -3073,8 +3369,8 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3073
3369
  * @param targets - The targets that behaviors in a view can attach to.
3074
3370
  */
3075
3371
  bind(controller) {
3076
- const target = controller.targets[this.nodeId];
3077
- target[this.controllerProperty] = controller;
3372
+ const target = controller.targets[this.targetNodeId];
3373
+ target[this._controllerProperty] = controller;
3078
3374
  this.updateTarget(controller.source, this.computeNodes(target));
3079
3375
  this.observe(target);
3080
3376
  controller.onUnbind(this);
@@ -3086,10 +3382,10 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3086
3382
  * @param targets - The targets that behaviors in a view can attach to.
3087
3383
  */
3088
3384
  unbind(controller) {
3089
- const target = controller.targets[this.nodeId];
3385
+ const target = controller.targets[this.targetNodeId];
3090
3386
  this.updateTarget(controller.source, emptyArray);
3091
3387
  this.disconnect(target);
3092
- target[this.controllerProperty] = null;
3388
+ target[this._controllerProperty] = null;
3093
3389
  }
3094
3390
  /**
3095
3391
  * Gets the data source for the target.
@@ -3097,7 +3393,7 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3097
3393
  * @returns The source.
3098
3394
  */
3099
3395
  getSource(target) {
3100
- return target[this.controllerProperty].source;
3396
+ return target[this._controllerProperty].source;
3101
3397
  }
3102
3398
  /**
3103
3399
  * Updates the source property with the computed nodes.
@@ -3238,6 +3534,29 @@ function children(propertyOrOptions) {
3238
3534
  return new ChildrenDirective(propertyOrOptions);
3239
3535
  }
3240
3536
 
3537
+ /**
3538
+ * A directive capable of injecting static HTML platform runtime protection.
3539
+ * @public
3540
+ */
3541
+ class DangerousHTMLDirective {
3542
+ constructor(html) {
3543
+ this.html = html;
3544
+ }
3545
+ createHTML() {
3546
+ return this.html;
3547
+ }
3548
+ }
3549
+ HTMLDirective.define(DangerousHTMLDirective);
3550
+ /**
3551
+ * Injects static HTML without platform protection.
3552
+ * @param html - The html to injection.
3553
+ * @returns A DangerousHTMLDirective.
3554
+ * @public
3555
+ */
3556
+ function dangerousHTML(html) {
3557
+ return new DangerousHTMLDirective(html);
3558
+ }
3559
+
3241
3560
  const booleanMode = "boolean";
3242
3561
  const reflectMode = "reflect";
3243
3562
  /**
@@ -4098,4 +4417,6 @@ function customElement(nameOrDef) {
4098
4417
  };
4099
4418
  }
4100
4419
 
4101
- export { ArrayObserver, Aspect, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewBehaviorOrchestrator, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };
4420
+ DOM.setPolicy(DOMPolicy.create());
4421
+
4422
+ export { ArrayObserver, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, DOMAspect, DangerousHTMLDirective, 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, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, cssPartial, customElement, dangerousHTML, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };