@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,34 +1,3 @@
1
- (function ensureGlobalThis() {
2
- if (typeof globalThis !== "undefined") {
3
- // We're running in a modern environment.
4
- return;
5
- }
6
- if (typeof global !== "undefined") {
7
- // We're running in NodeJS
8
- global.globalThis = global;
9
- }
10
- else if (typeof self !== "undefined") {
11
- self.globalThis = self;
12
- }
13
- else if (typeof window !== "undefined") {
14
- // We're running in the browser's main thread.
15
- window.globalThis = window;
16
- }
17
- else {
18
- // Hopefully we never get here...
19
- // Not all environments allow eval and Function. Use only as a last resort:
20
- // eslint-disable-next-line no-new-func
21
- const result = new Function("return this")();
22
- result.globalThis = result;
23
- }
24
- })();
25
- // API-only Polyfill for trustedTypes
26
- if (!globalThis.trustedTypes) {
27
- globalThis.trustedTypes = {
28
- createPolicy: (n, r) => r,
29
- };
30
- }
31
-
32
1
  if (globalThis.FAST === void 0) {
33
2
  Reflect.defineProperty(globalThis, "FAST", {
34
3
  value: Object.create(null),
@@ -40,11 +9,14 @@ if (globalThis.FAST === void 0) {
40
9
  const FAST$1 = globalThis.FAST;
41
10
  const debugMessages = {
42
11
  [1101 /* needsArrayObservation */]: "Must call enableArrayObservation before observing arrays.",
43
- [1201 /* onlySetHTMLPolicyOnce */]: "The HTML policy can only be set once.",
12
+ [1201 /* onlySetDOMPolicyOnce */]: "The DOM Policy can only be set once.",
44
13
  [1202 /* bindingInnerHTMLRequiresTrustedTypes */]: "To bind innerHTML, you must use a TrustedTypesPolicy.",
45
14
  [1203 /* twoWayBindingRequiresObservables */]: "View=>Model update skipped. To use twoWay binding, the target property must be observable.",
46
15
  [1204 /* hostBindingWithoutHost */]: "No host element is present. Cannot bind host with ${name}.",
47
16
  [1205 /* unsupportedBindingBehavior */]: "The requested binding behavior is not supported by the binding engine.",
17
+ [1206 /* directCallToHTMLTagNotAllowed */]: "Calling html`` as a normal function invalidates the security guarantees provided by FAST.",
18
+ [1207 /* onlySetTemplatePolicyOnce */]: "The DOM Policy for an HTML template can only be set once.",
19
+ [1208 /* cannotSetTemplatePolicyAfterCompilation */]: "The DOM Policy cannot be set after a template is compiled.",
48
20
  [1401 /* missingElementDefinition */]: "Missing FASTElement definition.",
49
21
  [1501 /* noRegistrationForContext */]: "No registration for Context/Interface '${name}'.",
50
22
  [1502 /* noFactoryForResolver */]: "Dependency injection resolver for '${key}' returned a null factory.",
@@ -90,7 +62,48 @@ Object.assign(FAST$1, {
90
62
  },
91
63
  });
92
64
 
93
- // ensure FAST global - duplicated in polyfills.ts and debug.ts
65
+ /**
66
+ * @internal
67
+ */
68
+ const isFunction = (object) => typeof object === "function";
69
+ /**
70
+ * @internal
71
+ */
72
+ const isString = (object) => typeof object === "string";
73
+ /**
74
+ * @internal
75
+ */
76
+ const noop = () => void 0;
77
+
78
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
79
+ (function ensureGlobalThis() {
80
+ if (typeof globalThis !== "undefined") {
81
+ // We're running in a modern environment.
82
+ return;
83
+ }
84
+ // @ts-ignore
85
+ if (typeof global !== "undefined") {
86
+ // We're running in NodeJS
87
+ // @ts-ignore
88
+ global.globalThis = global;
89
+ }
90
+ else if (typeof self !== "undefined") {
91
+ self.globalThis = self;
92
+ }
93
+ else if (typeof window !== "undefined") {
94
+ // We're running in the browser's main thread.
95
+ window.globalThis = window;
96
+ }
97
+ else {
98
+ // Hopefully we never get here...
99
+ // Not all environments allow eval and Function. Use only as a last resort:
100
+ // eslint-disable-next-line no-new-func
101
+ const result = new Function("return this")();
102
+ result.globalThis = result;
103
+ }
104
+ })();
105
+
106
+ // ensure FAST global - duplicated debug.ts
94
107
  const propConfig = {
95
108
  configurable: false,
96
109
  enumerable: false,
@@ -178,19 +191,6 @@ function createMetadataLocator() {
178
191
  };
179
192
  }
180
193
 
181
- /**
182
- * @internal
183
- */
184
- const isFunction = (object) => typeof object === "function";
185
- /**
186
- * @internal
187
- */
188
- const isString = (object) => typeof object === "string";
189
- /**
190
- * @internal
191
- */
192
- const noop = () => void 0;
193
-
194
194
  /**
195
195
  * The default UpdateQueue.
196
196
  * @public
@@ -257,6 +257,456 @@ const Updates = FAST.getById(1 /* KernelServiceId.updateQueue */, () => {
257
257
  });
258
258
  });
259
259
 
260
+ /**
261
+ * The type of HTML aspect to target.
262
+ * @public
263
+ */
264
+ const DOMAspect = Object.freeze({
265
+ /**
266
+ * Not aspected.
267
+ */
268
+ none: 0,
269
+ /**
270
+ * An attribute.
271
+ */
272
+ attribute: 1,
273
+ /**
274
+ * A boolean attribute.
275
+ */
276
+ booleanAttribute: 2,
277
+ /**
278
+ * A property.
279
+ */
280
+ property: 3,
281
+ /**
282
+ * Content
283
+ */
284
+ content: 4,
285
+ /**
286
+ * A token list.
287
+ */
288
+ tokenList: 5,
289
+ /**
290
+ * An event.
291
+ */
292
+ event: 6,
293
+ });
294
+ const createHTML$1 = html => html;
295
+ const fastTrustedType = globalThis.trustedTypes
296
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML: createHTML$1 })
297
+ : { createHTML: createHTML$1 };
298
+ let defaultPolicy = Object.freeze({
299
+ createHTML(value) {
300
+ return fastTrustedType.createHTML(value);
301
+ },
302
+ protect(tagName, aspect, aspectName, sink) {
303
+ return sink;
304
+ },
305
+ });
306
+ const fastPolicy = defaultPolicy;
307
+ /**
308
+ * Common DOM APIs.
309
+ * @public
310
+ */
311
+ const DOM = Object.freeze({
312
+ /**
313
+ * @deprecated
314
+ * Use Updates.enqueue().
315
+ */
316
+ queueUpdate: Updates.enqueue,
317
+ /**
318
+ * @deprecated
319
+ * Use Updates.next()
320
+ */
321
+ nextUpdate: Updates.next,
322
+ /**
323
+ * @deprecated
324
+ * Use Updates.process()
325
+ */
326
+ processUpdates: Updates.process,
327
+ /**
328
+ * Gets the dom policy used by the templating system.
329
+ */
330
+ get policy() {
331
+ return defaultPolicy;
332
+ },
333
+ /**
334
+ * Sets the dom policy used by the templating system.
335
+ * @param policy - The policy to set.
336
+ * @remarks
337
+ * This API can only be called once, for security reasons. It should be
338
+ * called by the application developer at the start of their program.
339
+ */
340
+ setPolicy(value) {
341
+ if (defaultPolicy !== fastPolicy) {
342
+ throw FAST.error(1201 /* Message.onlySetDOMPolicyOnce */);
343
+ }
344
+ defaultPolicy = value;
345
+ },
346
+ /**
347
+ * Sets an attribute value on an element.
348
+ * @param element - The element to set the attribute value on.
349
+ * @param attributeName - The attribute name to set.
350
+ * @param value - The value of the attribute to set.
351
+ * @remarks
352
+ * If the value is `null` or `undefined`, the attribute is removed, otherwise
353
+ * it is set to the provided value using the standard `setAttribute` API.
354
+ */
355
+ setAttribute(element, attributeName, value) {
356
+ value === null || value === undefined
357
+ ? element.removeAttribute(attributeName)
358
+ : element.setAttribute(attributeName, value);
359
+ },
360
+ /**
361
+ * Sets a boolean attribute value.
362
+ * @param element - The element to set the boolean attribute value on.
363
+ * @param attributeName - The attribute name to set.
364
+ * @param value - The value of the attribute to set.
365
+ * @remarks
366
+ * If the value is true, the attribute is added; otherwise it is removed.
367
+ */
368
+ setBooleanAttribute(element, attributeName, value) {
369
+ value
370
+ ? element.setAttribute(attributeName, "")
371
+ : element.removeAttribute(attributeName);
372
+ },
373
+ });
374
+
375
+ function safeURL(tagName, aspect, aspectName, sink) {
376
+ return (target, name, value, ...rest) => {
377
+ if (isString(value)) {
378
+ value = value.replace("javascript:", "");
379
+ }
380
+ sink(target, name, value, ...rest);
381
+ };
382
+ }
383
+ function block(tagName, aspect, aspectName, sink) {
384
+ throw new Error(`${aspectName} on ${tagName !== null && tagName !== void 0 ? tagName : "text"} is blocked by the current DOMPolicy.`);
385
+ }
386
+ const defaultDOMElementGuards = {
387
+ a: {
388
+ [DOMAspect.attribute]: {
389
+ href: safeURL,
390
+ },
391
+ [DOMAspect.property]: {
392
+ href: safeURL,
393
+ },
394
+ },
395
+ area: {
396
+ [DOMAspect.attribute]: {
397
+ href: safeURL,
398
+ },
399
+ [DOMAspect.property]: {
400
+ href: safeURL,
401
+ },
402
+ },
403
+ button: {
404
+ [DOMAspect.attribute]: {
405
+ formaction: safeURL,
406
+ },
407
+ [DOMAspect.property]: {
408
+ formAction: safeURL,
409
+ },
410
+ },
411
+ embed: {
412
+ [DOMAspect.attribute]: {
413
+ src: block,
414
+ },
415
+ [DOMAspect.property]: {
416
+ src: block,
417
+ },
418
+ },
419
+ form: {
420
+ [DOMAspect.attribute]: {
421
+ action: safeURL,
422
+ },
423
+ [DOMAspect.property]: {
424
+ action: safeURL,
425
+ },
426
+ },
427
+ frame: {
428
+ [DOMAspect.attribute]: {
429
+ src: safeURL,
430
+ },
431
+ [DOMAspect.property]: {
432
+ src: safeURL,
433
+ },
434
+ },
435
+ iframe: {
436
+ [DOMAspect.attribute]: {
437
+ src: safeURL,
438
+ },
439
+ [DOMAspect.property]: {
440
+ src: safeURL,
441
+ srcdoc: block,
442
+ },
443
+ },
444
+ input: {
445
+ [DOMAspect.attribute]: {
446
+ formaction: safeURL,
447
+ },
448
+ [DOMAspect.property]: {
449
+ formAction: safeURL,
450
+ },
451
+ },
452
+ link: {
453
+ [DOMAspect.attribute]: {
454
+ href: block,
455
+ },
456
+ [DOMAspect.property]: {
457
+ href: block,
458
+ },
459
+ },
460
+ object: {
461
+ [DOMAspect.attribute]: {
462
+ codebase: block,
463
+ data: block,
464
+ },
465
+ [DOMAspect.property]: {
466
+ codeBase: block,
467
+ data: block,
468
+ },
469
+ },
470
+ script: {
471
+ [DOMAspect.attribute]: {
472
+ src: block,
473
+ text: block,
474
+ },
475
+ [DOMAspect.property]: {
476
+ src: block,
477
+ text: block,
478
+ innerText: block,
479
+ textContent: block,
480
+ },
481
+ },
482
+ style: {
483
+ [DOMAspect.property]: {
484
+ innerText: block,
485
+ textContent: block,
486
+ },
487
+ },
488
+ };
489
+ const blockedEvents = {
490
+ onabort: block,
491
+ onauxclick: block,
492
+ onbeforeinput: block,
493
+ onbeforematch: block,
494
+ onblur: block,
495
+ oncancel: block,
496
+ oncanplay: block,
497
+ oncanplaythrough: block,
498
+ onchange: block,
499
+ onclick: block,
500
+ onclose: block,
501
+ oncontextlost: block,
502
+ oncontextmenu: block,
503
+ oncontextrestored: block,
504
+ oncopy: block,
505
+ oncuechange: block,
506
+ oncut: block,
507
+ ondblclick: block,
508
+ ondrag: block,
509
+ ondragend: block,
510
+ ondragenter: block,
511
+ ondragleave: block,
512
+ ondragover: block,
513
+ ondragstart: block,
514
+ ondrop: block,
515
+ ondurationchange: block,
516
+ onemptied: block,
517
+ onended: block,
518
+ onerror: block,
519
+ onfocus: block,
520
+ onformdata: block,
521
+ oninput: block,
522
+ oninvalid: block,
523
+ onkeydown: block,
524
+ onkeypress: block,
525
+ onkeyup: block,
526
+ onload: block,
527
+ onloadeddata: block,
528
+ onloadedmetadata: block,
529
+ onloadstart: block,
530
+ onmousedown: block,
531
+ onmouseenter: block,
532
+ onmouseleave: block,
533
+ onmousemove: block,
534
+ onmouseout: block,
535
+ onmouseover: block,
536
+ onmouseup: block,
537
+ onpaste: block,
538
+ onpause: block,
539
+ onplay: block,
540
+ onplaying: block,
541
+ onprogress: block,
542
+ onratechange: block,
543
+ onreset: block,
544
+ onresize: block,
545
+ onscroll: block,
546
+ onsecuritypolicyviolation: block,
547
+ onseeked: block,
548
+ onseeking: block,
549
+ onselect: block,
550
+ onslotchange: block,
551
+ onstalled: block,
552
+ onsubmit: block,
553
+ onsuspend: block,
554
+ ontimeupdate: block,
555
+ ontoggle: block,
556
+ onvolumechange: block,
557
+ onwaiting: block,
558
+ onwebkitanimationend: block,
559
+ onwebkitanimationiteration: block,
560
+ onwebkitanimationstart: block,
561
+ onwebkittransitionend: block,
562
+ onwheel: block,
563
+ };
564
+ const defaultDOMGuards = {
565
+ elements: defaultDOMElementGuards,
566
+ aspects: {
567
+ [DOMAspect.attribute]: Object.assign({}, blockedEvents),
568
+ [DOMAspect.property]: Object.assign({ innerHTML: block }, blockedEvents),
569
+ [DOMAspect.event]: Object.assign({}, blockedEvents),
570
+ },
571
+ };
572
+ function createDomSinkGuards(config, defaults) {
573
+ const result = {};
574
+ for (const name in defaults) {
575
+ const overrideValue = config[name];
576
+ const defaultValue = defaults[name];
577
+ switch (overrideValue) {
578
+ case null:
579
+ // remove the default
580
+ break;
581
+ case undefined:
582
+ // keep the default
583
+ result[name] = defaultValue;
584
+ break;
585
+ default:
586
+ // override the default
587
+ result[name] = overrideValue;
588
+ break;
589
+ }
590
+ }
591
+ // add any new sinks that were not overrides
592
+ for (const name in config) {
593
+ if (!(name in result)) {
594
+ result[name] = config[name];
595
+ }
596
+ }
597
+ return Object.freeze(result);
598
+ }
599
+ function createDOMAspectGuards(config, defaults) {
600
+ const result = {};
601
+ for (const aspect in defaults) {
602
+ const overrideValue = config[aspect];
603
+ const defaultValue = defaults[aspect];
604
+ switch (overrideValue) {
605
+ case null:
606
+ // remove the default
607
+ break;
608
+ case undefined:
609
+ // keep the default
610
+ result[aspect] = createDomSinkGuards(defaultValue, {});
611
+ break;
612
+ default:
613
+ // override the default
614
+ result[aspect] = createDomSinkGuards(overrideValue, defaultValue);
615
+ break;
616
+ }
617
+ }
618
+ // add any new aspect guards that were not overrides
619
+ for (const aspect in config) {
620
+ if (!(aspect in result)) {
621
+ result[aspect] = createDomSinkGuards(config[aspect], {});
622
+ }
623
+ }
624
+ return Object.freeze(result);
625
+ }
626
+ function createElementGuards(config, defaults) {
627
+ const result = {};
628
+ for (const tag in defaults) {
629
+ const overrideValue = config[tag];
630
+ const defaultValue = defaults[tag];
631
+ switch (overrideValue) {
632
+ case null:
633
+ // remove the default
634
+ break;
635
+ case undefined:
636
+ // keep the default
637
+ result[tag] = createDOMAspectGuards(overrideValue, {});
638
+ break;
639
+ default:
640
+ // override the default aspects
641
+ result[tag] = createDOMAspectGuards(overrideValue, defaultValue);
642
+ break;
643
+ }
644
+ }
645
+ // Add any new element guards that were not overrides
646
+ for (const tag in config) {
647
+ if (!(tag in result)) {
648
+ result[tag] = createDOMAspectGuards(config[tag], {});
649
+ }
650
+ }
651
+ return Object.freeze(result);
652
+ }
653
+ function createDOMGuards(config, defaults) {
654
+ return Object.freeze({
655
+ elements: config.elements
656
+ ? createElementGuards(config.elements, defaults.elements)
657
+ : defaults.elements,
658
+ aspects: config.aspects
659
+ ? createDOMAspectGuards(config.aspects, defaults.aspects)
660
+ : defaults.aspects,
661
+ });
662
+ }
663
+ function createTrustedType() {
664
+ const createHTML = html => html;
665
+ return globalThis.trustedTypes
666
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML })
667
+ : { createHTML };
668
+ }
669
+ function tryGuard(aspectGuards, tagName, aspect, aspectName, sink) {
670
+ const sinkGuards = aspectGuards[aspect];
671
+ if (sinkGuards) {
672
+ const guard = sinkGuards[aspectName];
673
+ if (guard) {
674
+ return guard(tagName, aspect, aspectName, sink);
675
+ }
676
+ }
677
+ }
678
+ const DOMPolicy = Object.freeze({
679
+ /**
680
+ * Creates a new DOM Policy object.
681
+ * @param options The options to use in creating the policy.
682
+ * @returns The newly created DOMPolicy.
683
+ */
684
+ create(options = {}) {
685
+ var _a, _b;
686
+ const trustedType = (_a = options.trustedType) !== null && _a !== void 0 ? _a : createTrustedType();
687
+ const guards = createDOMGuards((_b = options.guards) !== null && _b !== void 0 ? _b : {}, defaultDOMGuards);
688
+ return Object.freeze({
689
+ createHTML(value) {
690
+ return trustedType.createHTML(value);
691
+ },
692
+ protect(tagName, aspect, aspectName, sink) {
693
+ var _a;
694
+ // Check for element-specific guards.
695
+ const key = (tagName !== null && tagName !== void 0 ? tagName : "").toLowerCase();
696
+ const elementGuards = guards.elements[key];
697
+ if (elementGuards) {
698
+ const guard = tryGuard(elementGuards, tagName, aspect, aspectName, sink);
699
+ if (guard) {
700
+ return guard;
701
+ }
702
+ }
703
+ // Check for guards applicable to all nodes.
704
+ return ((_a = tryGuard(guards.aspects, tagName, aspect, aspectName, sink)) !== null && _a !== void 0 ? _a : sink);
705
+ },
706
+ });
707
+ },
708
+ });
709
+
260
710
  /**
261
711
  * An implementation of {@link Notifier} that efficiently keeps track of
262
712
  * subscribers interested in a specific change notification on an
@@ -1543,55 +1993,6 @@ css.partial = (strings, ...values) => {
1543
1993
  */
1544
1994
  const cssPartial = css.partial;
1545
1995
 
1546
- /**
1547
- * Common DOM APIs.
1548
- * @public
1549
- */
1550
- const DOM = Object.freeze({
1551
- /**
1552
- * @deprecated
1553
- * Use Updates.enqueue().
1554
- */
1555
- queueUpdate: Updates.enqueue,
1556
- /**
1557
- * @deprecated
1558
- * Use Updates.next()
1559
- */
1560
- nextUpdate: Updates.next,
1561
- /**
1562
- * @deprecated
1563
- * Use Updates.process()
1564
- */
1565
- processUpdates: Updates.process,
1566
- /**
1567
- * Sets an attribute value on an element.
1568
- * @param element - The element to set the attribute value on.
1569
- * @param attributeName - The attribute name to set.
1570
- * @param value - The value of the attribute to set.
1571
- * @remarks
1572
- * If the value is `null` or `undefined`, the attribute is removed, otherwise
1573
- * it is set to the provided value using the standard `setAttribute` API.
1574
- */
1575
- setAttribute(element, attributeName, value) {
1576
- value === null || value === undefined
1577
- ? element.removeAttribute(attributeName)
1578
- : element.setAttribute(attributeName, value);
1579
- },
1580
- /**
1581
- * Sets a boolean attribute value.
1582
- * @param element - The element to set the boolean attribute value on.
1583
- * @param attributeName - The attribute name to set.
1584
- * @param value - The value of the attribute to set.
1585
- * @remarks
1586
- * If the value is true, the attribute is added; otherwise it is removed.
1587
- */
1588
- setBooleanAttribute(element, attributeName, value) {
1589
- value
1590
- ? element.setAttribute(attributeName, "")
1591
- : element.removeAttribute(attributeName);
1592
- },
1593
- });
1594
-
1595
1996
  const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
1596
1997
  const interpolationStart = `${marker}{`;
1597
1998
  const interpolationEnd = `}${marker}`;
@@ -1668,67 +2069,6 @@ const Parser = Object.freeze({
1668
2069
  },
1669
2070
  });
1670
2071
 
1671
- /**
1672
- * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1673
- * control ViewBehaviors.
1674
- * @public
1675
- */
1676
- const ViewBehaviorOrchestrator = Object.freeze({
1677
- /**
1678
- * Creates a ViewBehaviorOrchestrator.
1679
- * @param source - The source to to associate behaviors with.
1680
- * @returns A ViewBehaviorOrchestrator.
1681
- */
1682
- create(source) {
1683
- const behaviors = [];
1684
- const targets = {};
1685
- let unbindables = null;
1686
- let isConnected = false;
1687
- return {
1688
- source,
1689
- context: ExecutionContext.default,
1690
- targets,
1691
- get isBound() {
1692
- return isConnected;
1693
- },
1694
- addBehaviorFactory(factory, target) {
1695
- const nodeId = factory.nodeId || (factory.nodeId = nextId());
1696
- factory.id || (factory.id = nextId());
1697
- this.addTarget(nodeId, target);
1698
- this.addBehavior(factory.createBehavior());
1699
- },
1700
- addTarget(nodeId, target) {
1701
- targets[nodeId] = target;
1702
- },
1703
- addBehavior(behavior) {
1704
- behaviors.push(behavior);
1705
- if (isConnected) {
1706
- behavior.bind(this);
1707
- }
1708
- },
1709
- onUnbind(unbindable) {
1710
- if (unbindables === null) {
1711
- unbindables = [];
1712
- }
1713
- unbindables.push(unbindable);
1714
- },
1715
- connectedCallback(controller) {
1716
- if (!isConnected) {
1717
- isConnected = true;
1718
- behaviors.forEach(x => x.bind(this));
1719
- }
1720
- },
1721
- disconnectedCallback(controller) {
1722
- if (isConnected) {
1723
- isConnected = false;
1724
- if (unbindables !== null) {
1725
- unbindables.forEach(x => x.unbind(this));
1726
- }
1727
- }
1728
- },
1729
- };
1730
- },
1731
- });
1732
2072
  const registry = createTypeRegistry();
1733
2073
  /**
1734
2074
  * Instructs the template engine to apply behavior to a node.
@@ -1756,67 +2096,6 @@ const HTMLDirective = Object.freeze({
1756
2096
  registry.register(options);
1757
2097
  return type;
1758
2098
  },
1759
- });
1760
- /**
1761
- * Decorator: Defines an HTMLDirective.
1762
- * @param options - Provides options that specify the directive's application.
1763
- * @public
1764
- */
1765
- function htmlDirective(options) {
1766
- /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
1767
- return function (type) {
1768
- HTMLDirective.define(type, options);
1769
- };
1770
- }
1771
- /**
1772
- * Captures a binding expression along with related information and capabilities.
1773
- *
1774
- * @public
1775
- */
1776
- class Binding {
1777
- /**
1778
- * Creates a binding.
1779
- * @param evaluate - Evaluates the binding.
1780
- * @param isVolatile - Indicates whether the binding is volatile.
1781
- */
1782
- constructor(evaluate, isVolatile = false) {
1783
- this.evaluate = evaluate;
1784
- this.isVolatile = isVolatile;
1785
- }
1786
- }
1787
- /**
1788
- * The type of HTML aspect to target.
1789
- * @public
1790
- */
1791
- const Aspect = Object.freeze({
1792
- /**
1793
- * Not aspected.
1794
- */
1795
- none: 0,
1796
- /**
1797
- * An attribute.
1798
- */
1799
- attribute: 1,
1800
- /**
1801
- * A boolean attribute.
1802
- */
1803
- booleanAttribute: 2,
1804
- /**
1805
- * A property.
1806
- */
1807
- property: 3,
1808
- /**
1809
- * Content
1810
- */
1811
- content: 4,
1812
- /**
1813
- * A token list.
1814
- */
1815
- tokenList: 5,
1816
- /**
1817
- * An event.
1818
- */
1819
- event: 6,
1820
2099
  /**
1821
2100
  *
1822
2101
  * @param directive - The directive to assign the aspect to.
@@ -1824,9 +2103,9 @@ const Aspect = Object.freeze({
1824
2103
  * @remarks
1825
2104
  * If a falsy value is provided, then the content aspect will be assigned.
1826
2105
  */
1827
- assign(directive, value) {
2106
+ assignAspect(directive, value) {
1828
2107
  if (!value) {
1829
- directive.aspectType = Aspect.content;
2108
+ directive.aspectType = DOMAspect.content;
1830
2109
  return;
1831
2110
  }
1832
2111
  directive.sourceAspect = value;
@@ -1835,24 +2114,53 @@ const Aspect = Object.freeze({
1835
2114
  directive.targetAspect = value.substring(1);
1836
2115
  directive.aspectType =
1837
2116
  directive.targetAspect === "classList"
1838
- ? Aspect.tokenList
1839
- : Aspect.property;
2117
+ ? DOMAspect.tokenList
2118
+ : DOMAspect.property;
1840
2119
  break;
1841
2120
  case "?":
1842
2121
  directive.targetAspect = value.substring(1);
1843
- directive.aspectType = Aspect.booleanAttribute;
2122
+ directive.aspectType = DOMAspect.booleanAttribute;
1844
2123
  break;
1845
2124
  case "@":
1846
2125
  directive.targetAspect = value.substring(1);
1847
- directive.aspectType = Aspect.event;
2126
+ directive.aspectType = DOMAspect.event;
1848
2127
  break;
1849
2128
  default:
1850
2129
  directive.targetAspect = value;
1851
- directive.aspectType = Aspect.attribute;
2130
+ directive.aspectType = DOMAspect.attribute;
1852
2131
  break;
1853
2132
  }
1854
2133
  },
1855
2134
  });
2135
+ /**
2136
+ * Decorator: Defines an HTMLDirective.
2137
+ * @param options - Provides options that specify the directive's application.
2138
+ * @public
2139
+ */
2140
+ function htmlDirective(options) {
2141
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
2142
+ return function (type) {
2143
+ HTMLDirective.define(type, options);
2144
+ };
2145
+ }
2146
+ /**
2147
+ * Captures a binding expression along with related information and capabilities.
2148
+ *
2149
+ * @public
2150
+ */
2151
+ class Binding {
2152
+ /**
2153
+ * Creates a binding.
2154
+ * @param evaluate - Evaluates the binding.
2155
+ * @param policy - The security policy to associate with this binding.
2156
+ * @param isVolatile - Indicates whether the binding is volatile.
2157
+ */
2158
+ constructor(evaluate, policy, isVolatile = false) {
2159
+ this.evaluate = evaluate;
2160
+ this.policy = policy;
2161
+ this.isVolatile = isVolatile;
2162
+ }
2163
+ }
1856
2164
  /**
1857
2165
  * A base class used for attribute directives that don't need internal state.
1858
2166
  * @public
@@ -1864,10 +2172,6 @@ class StatelessAttachedAttributeDirective {
1864
2172
  */
1865
2173
  constructor(options) {
1866
2174
  this.options = options;
1867
- /**
1868
- * The unique id of the factory.
1869
- */
1870
- this.id = nextId();
1871
2175
  /**
1872
2176
  * Opts out of JSON stringification.
1873
2177
  * @internal
@@ -1892,15 +2196,6 @@ class StatelessAttachedAttributeDirective {
1892
2196
  }
1893
2197
  }
1894
2198
 
1895
- const createInnerHTMLBinding = globalThis.TrustedHTML
1896
- ? (binding) => (s, c) => {
1897
- const value = binding(s, c);
1898
- if (value instanceof TrustedHTML) {
1899
- return value;
1900
- }
1901
- throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1902
- }
1903
- : (binding) => binding;
1904
2199
  class OnChangeBinding extends Binding {
1905
2200
  createObserver(_, subscriber) {
1906
2201
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
@@ -2013,8 +2308,14 @@ function updateTokenList(target, aspect, value) {
2013
2308
  }
2014
2309
  }
2015
2310
  }
2016
- const setProperty = (t, a, v) => (t[a] = v);
2017
- const eventTarget = () => void 0;
2311
+ const sinkLookup = {
2312
+ [DOMAspect.attribute]: DOM.setAttribute,
2313
+ [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
2314
+ [DOMAspect.property]: (t, a, v) => (t[a] = v),
2315
+ [DOMAspect.content]: updateContent,
2316
+ [DOMAspect.tokenList]: updateTokenList,
2317
+ [DOMAspect.event]: () => void 0,
2318
+ };
2018
2319
  /**
2019
2320
  * A directive that applies bindings.
2020
2321
  * @public
@@ -2027,15 +2328,10 @@ class HTMLBindingDirective {
2027
2328
  constructor(dataBinding) {
2028
2329
  this.dataBinding = dataBinding;
2029
2330
  this.updateTarget = null;
2030
- /**
2031
- * The unique id of the factory.
2032
- */
2033
- this.id = nextId();
2034
2331
  /**
2035
2332
  * The type of aspect to target.
2036
2333
  */
2037
- this.aspectType = Aspect.content;
2038
- this.data = `${this.id}-d`;
2334
+ this.aspectType = DOMAspect.content;
2039
2335
  }
2040
2336
  /**
2041
2337
  * Creates HTML to be used within a template.
@@ -2048,45 +2344,28 @@ class HTMLBindingDirective {
2048
2344
  * Creates a behavior.
2049
2345
  */
2050
2346
  createBehavior() {
2347
+ var _a;
2051
2348
  if (this.updateTarget === null) {
2052
- if (this.targetAspect === "innerHTML") {
2053
- this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2054
- }
2055
- switch (this.aspectType) {
2056
- case 1:
2057
- this.updateTarget = DOM.setAttribute;
2058
- break;
2059
- case 2:
2060
- this.updateTarget = DOM.setBooleanAttribute;
2061
- break;
2062
- case 3:
2063
- this.updateTarget = setProperty;
2064
- break;
2065
- case 4:
2066
- this.updateTarget = updateContent;
2067
- break;
2068
- case 5:
2069
- this.updateTarget = updateTokenList;
2070
- break;
2071
- case 6:
2072
- this.updateTarget = eventTarget;
2073
- break;
2074
- default:
2075
- throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2349
+ const sink = sinkLookup[this.aspectType];
2350
+ const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
2351
+ if (!sink) {
2352
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2076
2353
  }
2354
+ this.data = `${this.id}-d`;
2355
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
2077
2356
  }
2078
2357
  return this;
2079
2358
  }
2080
2359
  /** @internal */
2081
2360
  bind(controller) {
2082
2361
  var _a;
2083
- const target = controller.targets[this.nodeId];
2084
- switch (this.updateTarget) {
2085
- case eventTarget:
2362
+ const target = controller.targets[this.targetNodeId];
2363
+ switch (this.aspectType) {
2364
+ case DOMAspect.event:
2086
2365
  target[this.data] = controller;
2087
2366
  target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2088
2367
  break;
2089
- case updateContent:
2368
+ case DOMAspect.content:
2090
2369
  controller.onUnbind(this);
2091
2370
  // intentional fall through
2092
2371
  default:
@@ -2099,7 +2378,7 @@ class HTMLBindingDirective {
2099
2378
  }
2100
2379
  /** @internal */
2101
2380
  unbind(controller) {
2102
- const target = controller.targets[this.nodeId];
2381
+ const target = controller.targets[this.targetNodeId];
2103
2382
  const view = target.$fastView;
2104
2383
  if (view !== void 0 && view.isComposed) {
2105
2384
  view.unbind();
@@ -2129,21 +2408,23 @@ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2129
2408
  /**
2130
2409
  * Creates an standard binding.
2131
2410
  * @param expression - The binding to refresh when changed.
2411
+ * @param policy - The security policy to associate with th binding.
2132
2412
  * @param isVolatile - Indicates whether the binding is volatile or not.
2133
2413
  * @returns A binding configuration.
2134
2414
  * @public
2135
2415
  */
2136
- function bind(expression, isVolatile = Observable.isVolatileBinding(expression)) {
2137
- return new OnChangeBinding(expression, isVolatile);
2416
+ function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
2417
+ return new OnChangeBinding(expression, policy, isVolatile);
2138
2418
  }
2139
2419
  /**
2140
2420
  * Creates a one time binding
2141
2421
  * @param expression - The binding to refresh when signaled.
2422
+ * @param policy - The security policy to associate with th binding.
2142
2423
  * @returns A binding configuration.
2143
2424
  * @public
2144
2425
  */
2145
- function oneTime(expression) {
2146
- return new OneTimeBinding(expression);
2426
+ function oneTime(expression, policy) {
2427
+ return new OneTimeBinding(expression, policy);
2147
2428
  }
2148
2429
  /**
2149
2430
  * Creates an event listener binding.
@@ -2153,7 +2434,7 @@ function oneTime(expression) {
2153
2434
  * @public
2154
2435
  */
2155
2436
  function listener(expression, options) {
2156
- const config = new OnChangeBinding(expression, false);
2437
+ const config = new OnChangeBinding(expression);
2157
2438
  config.options = options;
2158
2439
  return config;
2159
2440
  }
@@ -2434,20 +2715,25 @@ const warningHost = new Proxy(document.createElement("div"), {
2434
2715
  },
2435
2716
  });
2436
2717
  class CompilationContext {
2437
- constructor(fragment, directives) {
2718
+ constructor(fragment, directives, policy) {
2438
2719
  this.fragment = fragment;
2439
2720
  this.directives = directives;
2721
+ this.policy = policy;
2440
2722
  this.proto = null;
2441
2723
  this.nodeIds = new Set();
2442
2724
  this.descriptors = {};
2443
2725
  this.factories = [];
2444
2726
  }
2445
- addFactory(factory, parentId, nodeId, targetIndex) {
2727
+ addFactory(factory, parentId, nodeId, targetIndex, tagName) {
2728
+ var _a, _b;
2446
2729
  if (!this.nodeIds.has(nodeId)) {
2447
2730
  this.nodeIds.add(nodeId);
2448
2731
  this.addTargetDescriptor(parentId, nodeId, targetIndex);
2449
2732
  }
2450
- factory.nodeId = nodeId;
2733
+ factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
2734
+ factory.targetNodeId = nodeId;
2735
+ factory.targetTagName = tagName;
2736
+ factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
2451
2737
  this.factories.push(factory);
2452
2738
  }
2453
2739
  freeze() {
@@ -2500,19 +2786,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2500
2786
  let result = null;
2501
2787
  if (parseResult === null) {
2502
2788
  if (includeBasicValues) {
2503
- result = new HTMLBindingDirective(oneTime(() => attrValue));
2504
- Aspect.assign(result, attr.name);
2789
+ result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
2790
+ HTMLDirective.assignAspect(result, attr.name);
2505
2791
  }
2506
2792
  }
2507
2793
  else {
2508
2794
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2509
- result = Compiler.aggregate(parseResult);
2795
+ result = Compiler.aggregate(parseResult, context.policy);
2510
2796
  }
2511
2797
  if (result !== null) {
2512
2798
  node.removeAttributeNode(attr);
2513
2799
  i--;
2514
2800
  ii--;
2515
- context.addFactory(result, parentId, nodeId, nodeIndex);
2801
+ context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
2516
2802
  }
2517
2803
  }
2518
2804
  }
@@ -2537,8 +2823,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2537
2823
  }
2538
2824
  else {
2539
2825
  currentNode.textContent = " ";
2540
- Aspect.assign(currentPart);
2541
- context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2826
+ HTMLDirective.assignAspect(currentPart);
2827
+ context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
2542
2828
  }
2543
2829
  lastNode = currentNode;
2544
2830
  }
@@ -2570,7 +2856,7 @@ function compileNode(context, parentId, node, nodeIndex) {
2570
2856
  if (parts !== null) {
2571
2857
  context.addFactory(
2572
2858
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2573
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
2859
+ Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
2574
2860
  }
2575
2861
  break;
2576
2862
  }
@@ -2584,45 +2870,28 @@ function isMarker(node, directives) {
2584
2870
  Parser.parse(node.data, directives) !== null);
2585
2871
  }
2586
2872
  const templateTag = "TEMPLATE";
2587
- const policyOptions = { createHTML: html => html };
2588
- let htmlPolicy = globalThis.trustedTypes
2589
- ? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
2590
- : policyOptions;
2591
- const fastHTMLPolicy = htmlPolicy;
2592
2873
  /**
2593
2874
  * Common APIs related to compilation.
2594
2875
  * @public
2595
2876
  */
2596
2877
  const Compiler = {
2597
- /**
2598
- * Sets the HTML trusted types policy used by the compiler.
2599
- * @param policy - The policy to set for HTML.
2600
- * @remarks
2601
- * This API can only be called once, for security reasons. It should be
2602
- * called by the application developer at the start of their program.
2603
- */
2604
- setHTMLPolicy(policy) {
2605
- if (htmlPolicy !== fastHTMLPolicy) {
2606
- throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
2607
- }
2608
- htmlPolicy = policy;
2609
- },
2610
2878
  /**
2611
2879
  * Compiles a template and associated directives into a compilation
2612
2880
  * result which can be used to create views.
2613
2881
  * @param html - The html string or template element to compile.
2614
- * @param directives - The directives referenced by the template.
2882
+ * @param factories - The behavior factories referenced by the template.
2883
+ * @param policy - The security policy to compile the html with.
2615
2884
  * @remarks
2616
2885
  * The template that is provided for compilation is altered in-place
2617
2886
  * and cannot be compiled again. If the original template must be preserved,
2618
2887
  * it is recommended that you clone the original and pass the clone to this API.
2619
2888
  * @public
2620
2889
  */
2621
- compile(html, directives) {
2890
+ compile(html, factories, policy = DOM.policy) {
2622
2891
  let template;
2623
2892
  if (isString(html)) {
2624
2893
  template = document.createElement(templateTag);
2625
- template.innerHTML = htmlPolicy.createHTML(html);
2894
+ template.innerHTML = policy.createHTML(html);
2626
2895
  const fec = template.content.firstElementChild;
2627
2896
  if (fec !== null && fec.tagName === templateTag) {
2628
2897
  template = fec;
@@ -2633,18 +2902,18 @@ const Compiler = {
2633
2902
  }
2634
2903
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
2635
2904
  const fragment = document.adoptNode(template.content);
2636
- const context = new CompilationContext(fragment, directives);
2905
+ const context = new CompilationContext(fragment, factories, policy);
2637
2906
  compileAttributes(context, "", template, /* host */ "h", 0, true);
2638
2907
  if (
2639
2908
  // If the first node in a fragment is a marker, that means it's an unstable first node,
2640
2909
  // because something like a when, repeat, etc. could add nodes before the marker.
2641
2910
  // To mitigate this, we insert a stable first node. However, if we insert a node,
2642
2911
  // that will alter the result of the TreeWalker. So, we also need to offset the target index.
2643
- isMarker(fragment.firstChild, directives) ||
2912
+ isMarker(fragment.firstChild, factories) ||
2644
2913
  // Or if there is only one node and a directive, it means the template's content
2645
2914
  // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
2646
2915
  // the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
2647
- (fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
2916
+ (fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
2648
2917
  fragment.insertBefore(document.createComment(""), fragment.firstChild);
2649
2918
  }
2650
2919
  compileChildren(context, fragment, /* root */ "r");
@@ -2663,15 +2932,17 @@ const Compiler = {
2663
2932
  * Aggregates an array of strings and directives into a single directive.
2664
2933
  * @param parts - A heterogeneous array of static strings interspersed with
2665
2934
  * directives.
2935
+ * @param policy - The security policy to use with the aggregated bindings.
2666
2936
  * @returns A single inline directive that aggregates the behavior of all the parts.
2667
2937
  */
2668
- aggregate(parts) {
2938
+ aggregate(parts, policy = DOM.policy) {
2669
2939
  if (parts.length === 1) {
2670
2940
  return parts[0];
2671
2941
  }
2672
2942
  let sourceAspect;
2673
2943
  let binding;
2674
2944
  let isVolatile = false;
2945
+ let bindingPolicy = void 0;
2675
2946
  const partCount = parts.length;
2676
2947
  const finalParts = parts.map((x) => {
2677
2948
  if (isString(x)) {
@@ -2680,6 +2951,7 @@ const Compiler = {
2680
2951
  sourceAspect = x.sourceAspect || sourceAspect;
2681
2952
  binding = x.dataBinding || binding;
2682
2953
  isVolatile = isVolatile || x.dataBinding.isVolatile;
2954
+ bindingPolicy = bindingPolicy || x.dataBinding.policy;
2683
2955
  return x.dataBinding.evaluate;
2684
2956
  });
2685
2957
  const expression = (scope, context) => {
@@ -2691,12 +2963,26 @@ const Compiler = {
2691
2963
  };
2692
2964
  binding.evaluate = expression;
2693
2965
  binding.isVolatile = isVolatile;
2966
+ binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
2694
2967
  const directive = new HTMLBindingDirective(binding);
2695
- Aspect.assign(directive, sourceAspect);
2968
+ HTMLDirective.assignAspect(directive, sourceAspect);
2696
2969
  return directive;
2697
2970
  },
2698
2971
  };
2699
2972
 
2973
+ // Much thanks to LitHTML for working this out!
2974
+ const lastAttributeNameRegex =
2975
+ /* eslint-disable-next-line no-control-regex */
2976
+ /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2977
+ function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
2978
+ if (definition.aspected) {
2979
+ const match = lastAttributeNameRegex.exec(prevString);
2980
+ if (match !== null) {
2981
+ HTMLDirective.assignAspect(value, match[2]);
2982
+ }
2983
+ }
2984
+ return value.createHTML(add);
2985
+ }
2700
2986
  /**
2701
2987
  * A template capable of creating HTMLView instances or rendering directly to DOM.
2702
2988
  * @public
@@ -2706,8 +2992,10 @@ class ViewTemplate {
2706
2992
  * Creates an instance of ViewTemplate.
2707
2993
  * @param html - The html representing what this template will instantiate, including placeholders for directives.
2708
2994
  * @param factories - The directives that will be connected to placeholders in the html.
2995
+ * @param policy - The security policy to use when compiling this template.
2709
2996
  */
2710
- constructor(html, factories) {
2997
+ constructor(html, factories = {}, policy) {
2998
+ this.policy = policy;
2711
2999
  this.result = null;
2712
3000
  /**
2713
3001
  * Opts out of JSON stringification.
@@ -2723,10 +3011,28 @@ class ViewTemplate {
2723
3011
  */
2724
3012
  create(hostBindingTarget) {
2725
3013
  if (this.result === null) {
2726
- this.result = Compiler.compile(this.html, this.factories);
3014
+ this.result = Compiler.compile(this.html, this.factories, this.policy);
2727
3015
  }
2728
3016
  return this.result.createView(hostBindingTarget);
2729
3017
  }
3018
+ /**
3019
+ * Sets the DOMPolicy for this template.
3020
+ * @param policy - The policy to associated with this template.
3021
+ * @returns The modified template instance.
3022
+ * @remarks
3023
+ * The DOMPolicy can only be set once for a template and cannot be
3024
+ * set after the template is compiled.
3025
+ */
3026
+ withPolicy(policy) {
3027
+ if (this.result) {
3028
+ throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
3029
+ }
3030
+ if (this.policy) {
3031
+ throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
3032
+ }
3033
+ this.policy = policy;
3034
+ return this;
3035
+ }
2730
3036
  /**
2731
3037
  * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
2732
3038
  * @param source - The data source to bind the template to.
@@ -2740,17 +3046,47 @@ class ViewTemplate {
2740
3046
  view.appendTo(host);
2741
3047
  return view;
2742
3048
  }
2743
- }
2744
- // Much thanks to LitHTML for working this out!
2745
- const lastAttributeNameRegex =
2746
- /* eslint-disable-next-line no-control-regex */
2747
- /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2748
- function createAspectedHTML(value, prevString, add) {
2749
- const match = lastAttributeNameRegex.exec(prevString);
2750
- if (match !== null) {
2751
- Aspect.assign(value, match[2]);
3049
+ /**
3050
+ * Creates a template based on a set of static strings and dynamic values.
3051
+ * @param strings - The static strings to create the template with.
3052
+ * @param values - The dynamic values to create the template with.
3053
+ * @param policy - The DOMPolicy to associated with the template.
3054
+ * @returns A ViewTemplate.
3055
+ * @remarks
3056
+ * This API should not be used directly under normal circumstances because constructing
3057
+ * a template in this way, if not done properly, can open up the application to XSS
3058
+ * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
3059
+ * and also be sure to manually sanitize all static strings particularly if they can
3060
+ * come from user input.
3061
+ */
3062
+ static create(strings, values, policy) {
3063
+ let html = "";
3064
+ const factories = Object.create(null);
3065
+ const add = (factory) => {
3066
+ var _a;
3067
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
3068
+ factories[id] = factory;
3069
+ return id;
3070
+ };
3071
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
3072
+ const currentString = strings[i];
3073
+ let currentValue = values[i];
3074
+ let definition;
3075
+ html += currentString;
3076
+ if (isFunction(currentValue)) {
3077
+ currentValue = new HTMLBindingDirective(bind(currentValue));
3078
+ }
3079
+ else if (currentValue instanceof Binding) {
3080
+ currentValue = new HTMLBindingDirective(currentValue);
3081
+ }
3082
+ else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
3083
+ const staticValue = currentValue;
3084
+ currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
3085
+ }
3086
+ html += createHTML(currentValue, currentString, add, definition);
3087
+ }
3088
+ return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
2752
3089
  }
2753
- return value.createHTML(add);
2754
3090
  }
2755
3091
  /**
2756
3092
  * Transforms a template literal string into a ViewTemplate.
@@ -2762,49 +3098,10 @@ function createAspectedHTML(value, prevString, add) {
2762
3098
  * @public
2763
3099
  */
2764
3100
  function html(strings, ...values) {
2765
- let html = "";
2766
- const factories = Object.create(null);
2767
- const add = (factory) => {
2768
- var _a;
2769
- const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
2770
- factories[id] = factory;
2771
- return id;
2772
- };
2773
- for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
2774
- const currentString = strings[i];
2775
- const currentValue = values[i];
2776
- let definition;
2777
- html += currentString;
2778
- if (isFunction(currentValue)) {
2779
- html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2780
- }
2781
- else if (isString(currentValue)) {
2782
- const match = lastAttributeNameRegex.exec(currentString);
2783
- if (match !== null) {
2784
- const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2785
- Aspect.assign(directive, match[2]);
2786
- html += directive.createHTML(add);
2787
- }
2788
- else {
2789
- html += currentValue;
2790
- }
2791
- }
2792
- else if (currentValue instanceof Binding) {
2793
- html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2794
- }
2795
- else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2796
- html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2797
- }
2798
- else {
2799
- if (definition.aspected) {
2800
- html += createAspectedHTML(currentValue, currentString, add);
2801
- }
2802
- else {
2803
- html += currentValue.createHTML(add);
2804
- }
2805
- }
3101
+ if (Array.isArray(strings) && Array.isArray(strings.raw)) {
3102
+ return ViewTemplate.create(strings, values);
2806
3103
  }
2807
- return new ViewTemplate(html + strings[strings.length - 1], factories);
3104
+ throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
2808
3105
  }
2809
3106
 
2810
3107
  /**
@@ -2817,7 +3114,7 @@ class RefDirective extends StatelessAttachedAttributeDirective {
2817
3114
  * @param controller - The view controller that manages the lifecycle of this behavior.
2818
3115
  */
2819
3116
  bind(controller) {
2820
- controller.source[this.options] = controller.targets[this.nodeId];
3117
+ controller.source[this.options] = controller.targets[this.targetNodeId];
2821
3118
  }
2822
3119
  }
2823
3120
  HTMLDirective.define(RefDirective);
@@ -2891,7 +3188,7 @@ class RepeatBehavior {
2891
3188
  * @param controller - The view controller that manages the lifecycle of this behavior.
2892
3189
  */
2893
3190
  bind(controller) {
2894
- this.location = controller.targets[this.directive.nodeId];
3191
+ this.location = controller.targets[this.directive.targetNodeId];
2895
3192
  this.controller = controller;
2896
3193
  this.items = this.itemsBindingObserver.bind(controller);
2897
3194
  this.template = this.templateBindingObserver.bind(controller);
@@ -3071,10 +3368,6 @@ class RepeatDirective {
3071
3368
  this.dataBinding = dataBinding;
3072
3369
  this.templateBinding = templateBinding;
3073
3370
  this.options = options;
3074
- /**
3075
- * The unique id of the factory.
3076
- */
3077
- this.id = nextId();
3078
3371
  ArrayObserver.enable();
3079
3372
  }
3080
3373
  /**
@@ -3123,9 +3416,15 @@ const elements = (selector) => selector
3123
3416
  * Internally used by the SlottedDirective and the ChildrenDirective.
3124
3417
  */
3125
3418
  class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3126
- constructor() {
3127
- super(...arguments);
3128
- this.controllerProperty = `${this.id}-c`;
3419
+ /**
3420
+ * The unique id of the factory.
3421
+ */
3422
+ get id() {
3423
+ return this._id;
3424
+ }
3425
+ set id(value) {
3426
+ this._id = value;
3427
+ this._controllerProperty = `${value}-c`;
3129
3428
  }
3130
3429
  /**
3131
3430
  * Bind this behavior to the source.
@@ -3134,8 +3433,8 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3134
3433
  * @param targets - The targets that behaviors in a view can attach to.
3135
3434
  */
3136
3435
  bind(controller) {
3137
- const target = controller.targets[this.nodeId];
3138
- target[this.controllerProperty] = controller;
3436
+ const target = controller.targets[this.targetNodeId];
3437
+ target[this._controllerProperty] = controller;
3139
3438
  this.updateTarget(controller.source, this.computeNodes(target));
3140
3439
  this.observe(target);
3141
3440
  controller.onUnbind(this);
@@ -3147,10 +3446,10 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3147
3446
  * @param targets - The targets that behaviors in a view can attach to.
3148
3447
  */
3149
3448
  unbind(controller) {
3150
- const target = controller.targets[this.nodeId];
3449
+ const target = controller.targets[this.targetNodeId];
3151
3450
  this.updateTarget(controller.source, emptyArray);
3152
3451
  this.disconnect(target);
3153
- target[this.controllerProperty] = null;
3452
+ target[this._controllerProperty] = null;
3154
3453
  }
3155
3454
  /**
3156
3455
  * Gets the data source for the target.
@@ -3158,7 +3457,7 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3158
3457
  * @returns The source.
3159
3458
  */
3160
3459
  getSource(target) {
3161
- return target[this.controllerProperty].source;
3460
+ return target[this._controllerProperty].source;
3162
3461
  }
3163
3462
  /**
3164
3463
  * Updates the source property with the computed nodes.
@@ -3299,6 +3598,29 @@ function children(propertyOrOptions) {
3299
3598
  return new ChildrenDirective(propertyOrOptions);
3300
3599
  }
3301
3600
 
3601
+ /**
3602
+ * A directive capable of injecting static HTML platform runtime protection.
3603
+ * @public
3604
+ */
3605
+ class DangerousHTMLDirective {
3606
+ constructor(html) {
3607
+ this.html = html;
3608
+ }
3609
+ createHTML() {
3610
+ return this.html;
3611
+ }
3612
+ }
3613
+ HTMLDirective.define(DangerousHTMLDirective);
3614
+ /**
3615
+ * Injects static HTML without platform protection.
3616
+ * @param html - The html to injection.
3617
+ * @returns A DangerousHTMLDirective.
3618
+ * @public
3619
+ */
3620
+ function dangerousHTML(html) {
3621
+ return new DangerousHTMLDirective(html);
3622
+ }
3623
+
3302
3624
  const booleanMode = "boolean";
3303
3625
  const reflectMode = "reflect";
3304
3626
  /**
@@ -4159,4 +4481,6 @@ function customElement(nameOrDef) {
4159
4481
  };
4160
4482
  }
4161
4483
 
4162
- 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 };
4484
+ DOM.setPolicy(DOMPolicy.create());
4485
+
4486
+ 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 };