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

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 (59) hide show
  1. package/CHANGELOG.json +36 -0
  2. package/CHANGELOG.md +18 -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 +24 -31
  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/components/fast-definitions.js +2 -2
  25. package/dist/esm/debug.js +4 -1
  26. package/dist/esm/dom-policy.js +337 -0
  27. package/dist/esm/dom.js +117 -0
  28. package/dist/esm/index.js +2 -1
  29. package/dist/esm/index.rollup.debug.js +3 -1
  30. package/dist/esm/index.rollup.js +3 -1
  31. package/dist/esm/interfaces.js +45 -0
  32. package/dist/esm/observation/observable.js +3 -3
  33. package/dist/esm/observation/update-queue.js +2 -2
  34. package/dist/esm/platform.js +1 -1
  35. package/dist/esm/polyfills.js +3 -7
  36. package/dist/esm/templating/binding-signal.js +3 -2
  37. package/dist/esm/templating/binding-two-way.js +3 -2
  38. package/dist/esm/templating/binding.js +31 -54
  39. package/dist/esm/templating/compiler.js +31 -38
  40. package/dist/esm/templating/dangerous-html.js +23 -0
  41. package/dist/esm/templating/html-directive.js +38 -135
  42. package/dist/esm/templating/node-observation.js +14 -8
  43. package/dist/esm/templating/ref.js +1 -1
  44. package/dist/esm/templating/render.js +17 -6
  45. package/dist/esm/templating/repeat.js +2 -6
  46. package/dist/esm/templating/template.js +81 -56
  47. package/dist/esm/testing/fixture.js +1 -1
  48. package/dist/esm/utilities.js +68 -0
  49. package/dist/fast-element.api.json +1088 -608
  50. package/dist/fast-element.d.ts +190 -147
  51. package/dist/fast-element.debug.js +756 -388
  52. package/dist/fast-element.debug.min.js +1 -1
  53. package/dist/fast-element.js +727 -362
  54. package/dist/fast-element.min.js +1 -1
  55. package/dist/fast-element.untrimmed.d.ts +190 -147
  56. package/docs/api-report.md +66 -56
  57. package/package.json +5 -1
  58. package/dist/dts/templating/dom.d.ts +0 -41
  59. package/dist/esm/templating/dom.js +0 -49
@@ -1,10 +1,70 @@
1
+ let kernelMode;
2
+ const kernelAttr = "fast-kernel";
3
+ try {
4
+ if (document.currentScript) {
5
+ kernelMode = document.currentScript.getAttribute(kernelAttr);
6
+ }
7
+ else {
8
+ const scripts = document.getElementsByTagName("script");
9
+ const currentScript = scripts[scripts.length - 1];
10
+ kernelMode = currentScript.getAttribute(kernelAttr);
11
+ }
12
+ }
13
+ catch (e) {
14
+ kernelMode = "isolate";
15
+ }
16
+ let KernelServiceId;
17
+ switch (kernelMode) {
18
+ case "share": // share the kernel across major versions
19
+ KernelServiceId = Object.freeze({
20
+ updateQueue: 1,
21
+ observable: 2,
22
+ contextEvent: 3,
23
+ elementRegistry: 4,
24
+ });
25
+ break;
26
+ case "share-v2": // only share the kernel with other v2 instances
27
+ KernelServiceId = Object.freeze({
28
+ updateQueue: 1.2,
29
+ observable: 2.2,
30
+ contextEvent: 3.2,
31
+ elementRegistry: 4.2,
32
+ });
33
+ break;
34
+ default:
35
+ // fully isolate the kernel from all other FAST instances
36
+ const postfix = `-${Math.random().toString(36).substring(2, 8)}`;
37
+ KernelServiceId = Object.freeze({
38
+ updateQueue: `1.2${postfix}`,
39
+ observable: `2.2${postfix}`,
40
+ contextEvent: `3.2${postfix}`,
41
+ elementRegistry: `4.2${postfix}`,
42
+ });
43
+ break;
44
+ }
45
+ /**
46
+ * @internal
47
+ */
48
+ const isFunction = (object) => typeof object === "function";
49
+ /**
50
+ * @internal
51
+ */
52
+ const isString = (object) => typeof object === "string";
53
+ /**
54
+ * @internal
55
+ */
56
+ const noop = () => void 0;
57
+
58
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
1
59
  (function ensureGlobalThis() {
2
60
  if (typeof globalThis !== "undefined") {
3
61
  // We're running in a modern environment.
4
62
  return;
5
63
  }
64
+ // @ts-ignore
6
65
  if (typeof global !== "undefined") {
7
66
  // We're running in NodeJS
67
+ // @ts-ignore
8
68
  global.globalThis = global;
9
69
  }
10
70
  else if (typeof self !== "undefined") {
@@ -22,14 +82,8 @@
22
82
  result.globalThis = result;
23
83
  }
24
84
  })();
25
- // API-only Polyfill for trustedTypes
26
- if (!globalThis.trustedTypes) {
27
- globalThis.trustedTypes = {
28
- createPolicy: (n, r) => r,
29
- };
30
- }
31
85
 
32
- // ensure FAST global - duplicated in polyfills.ts and debug.ts
86
+ // ensure FAST global - duplicated debug.ts
33
87
  const propConfig = {
34
88
  configurable: false,
35
89
  enumerable: false,
@@ -117,24 +171,11 @@ function createMetadataLocator() {
117
171
  };
118
172
  }
119
173
 
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
174
  /**
134
175
  * The default UpdateQueue.
135
176
  * @public
136
177
  */
137
- const Updates = FAST.getById(1 /* KernelServiceId.updateQueue */, () => {
178
+ const Updates = FAST.getById(KernelServiceId.updateQueue, () => {
138
179
  const tasks = [];
139
180
  const pendingErrors = [];
140
181
  const rAF = globalThis.requestAnimationFrame;
@@ -196,6 +237,456 @@ const Updates = FAST.getById(1 /* KernelServiceId.updateQueue */, () => {
196
237
  });
197
238
  });
198
239
 
240
+ /**
241
+ * The type of HTML aspect to target.
242
+ * @public
243
+ */
244
+ const DOMAspect = Object.freeze({
245
+ /**
246
+ * Not aspected.
247
+ */
248
+ none: 0,
249
+ /**
250
+ * An attribute.
251
+ */
252
+ attribute: 1,
253
+ /**
254
+ * A boolean attribute.
255
+ */
256
+ booleanAttribute: 2,
257
+ /**
258
+ * A property.
259
+ */
260
+ property: 3,
261
+ /**
262
+ * Content
263
+ */
264
+ content: 4,
265
+ /**
266
+ * A token list.
267
+ */
268
+ tokenList: 5,
269
+ /**
270
+ * An event.
271
+ */
272
+ event: 6,
273
+ });
274
+ const createHTML$1 = html => html;
275
+ const fastTrustedType = globalThis.trustedTypes
276
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML: createHTML$1 })
277
+ : { createHTML: createHTML$1 };
278
+ let defaultPolicy = Object.freeze({
279
+ createHTML(value) {
280
+ return fastTrustedType.createHTML(value);
281
+ },
282
+ protect(tagName, aspect, aspectName, sink) {
283
+ return sink;
284
+ },
285
+ });
286
+ const fastPolicy = defaultPolicy;
287
+ /**
288
+ * Common DOM APIs.
289
+ * @public
290
+ */
291
+ const DOM = Object.freeze({
292
+ /**
293
+ * @deprecated
294
+ * Use Updates.enqueue().
295
+ */
296
+ queueUpdate: Updates.enqueue,
297
+ /**
298
+ * @deprecated
299
+ * Use Updates.next()
300
+ */
301
+ nextUpdate: Updates.next,
302
+ /**
303
+ * @deprecated
304
+ * Use Updates.process()
305
+ */
306
+ processUpdates: Updates.process,
307
+ /**
308
+ * Gets the dom policy used by the templating system.
309
+ */
310
+ get policy() {
311
+ return defaultPolicy;
312
+ },
313
+ /**
314
+ * Sets the dom policy used by the templating system.
315
+ * @param policy - The policy to set.
316
+ * @remarks
317
+ * This API can only be called once, for security reasons. It should be
318
+ * called by the application developer at the start of their program.
319
+ */
320
+ setPolicy(value) {
321
+ if (defaultPolicy !== fastPolicy) {
322
+ throw FAST.error(1201 /* Message.onlySetDOMPolicyOnce */);
323
+ }
324
+ defaultPolicy = value;
325
+ },
326
+ /**
327
+ * Sets an attribute value on an element.
328
+ * @param element - The element to set the attribute value on.
329
+ * @param attributeName - The attribute name to set.
330
+ * @param value - The value of the attribute to set.
331
+ * @remarks
332
+ * If the value is `null` or `undefined`, the attribute is removed, otherwise
333
+ * it is set to the provided value using the standard `setAttribute` API.
334
+ */
335
+ setAttribute(element, attributeName, value) {
336
+ value === null || value === undefined
337
+ ? element.removeAttribute(attributeName)
338
+ : element.setAttribute(attributeName, value);
339
+ },
340
+ /**
341
+ * Sets a boolean attribute value.
342
+ * @param element - The element to set the boolean attribute value on.
343
+ * @param attributeName - The attribute name to set.
344
+ * @param value - The value of the attribute to set.
345
+ * @remarks
346
+ * If the value is true, the attribute is added; otherwise it is removed.
347
+ */
348
+ setBooleanAttribute(element, attributeName, value) {
349
+ value
350
+ ? element.setAttribute(attributeName, "")
351
+ : element.removeAttribute(attributeName);
352
+ },
353
+ });
354
+
355
+ function safeURL(tagName, aspect, aspectName, sink) {
356
+ return (target, name, value, ...rest) => {
357
+ if (isString(value)) {
358
+ value = value.replace("javascript:", "");
359
+ }
360
+ sink(target, name, value, ...rest);
361
+ };
362
+ }
363
+ function block(tagName, aspect, aspectName, sink) {
364
+ throw new Error(`${aspectName} on ${tagName !== null && tagName !== void 0 ? tagName : "text"} is blocked by the current DOMPolicy.`);
365
+ }
366
+ const defaultDOMElementGuards = {
367
+ a: {
368
+ [DOMAspect.attribute]: {
369
+ href: safeURL,
370
+ },
371
+ [DOMAspect.property]: {
372
+ href: safeURL,
373
+ },
374
+ },
375
+ area: {
376
+ [DOMAspect.attribute]: {
377
+ href: safeURL,
378
+ },
379
+ [DOMAspect.property]: {
380
+ href: safeURL,
381
+ },
382
+ },
383
+ button: {
384
+ [DOMAspect.attribute]: {
385
+ formaction: safeURL,
386
+ },
387
+ [DOMAspect.property]: {
388
+ formAction: safeURL,
389
+ },
390
+ },
391
+ embed: {
392
+ [DOMAspect.attribute]: {
393
+ src: block,
394
+ },
395
+ [DOMAspect.property]: {
396
+ src: block,
397
+ },
398
+ },
399
+ form: {
400
+ [DOMAspect.attribute]: {
401
+ action: safeURL,
402
+ },
403
+ [DOMAspect.property]: {
404
+ action: safeURL,
405
+ },
406
+ },
407
+ frame: {
408
+ [DOMAspect.attribute]: {
409
+ src: safeURL,
410
+ },
411
+ [DOMAspect.property]: {
412
+ src: safeURL,
413
+ },
414
+ },
415
+ iframe: {
416
+ [DOMAspect.attribute]: {
417
+ src: safeURL,
418
+ },
419
+ [DOMAspect.property]: {
420
+ src: safeURL,
421
+ srcdoc: block,
422
+ },
423
+ },
424
+ input: {
425
+ [DOMAspect.attribute]: {
426
+ formaction: safeURL,
427
+ },
428
+ [DOMAspect.property]: {
429
+ formAction: safeURL,
430
+ },
431
+ },
432
+ link: {
433
+ [DOMAspect.attribute]: {
434
+ href: block,
435
+ },
436
+ [DOMAspect.property]: {
437
+ href: block,
438
+ },
439
+ },
440
+ object: {
441
+ [DOMAspect.attribute]: {
442
+ codebase: block,
443
+ data: block,
444
+ },
445
+ [DOMAspect.property]: {
446
+ codeBase: block,
447
+ data: block,
448
+ },
449
+ },
450
+ script: {
451
+ [DOMAspect.attribute]: {
452
+ src: block,
453
+ text: block,
454
+ },
455
+ [DOMAspect.property]: {
456
+ src: block,
457
+ text: block,
458
+ innerText: block,
459
+ textContent: block,
460
+ },
461
+ },
462
+ style: {
463
+ [DOMAspect.property]: {
464
+ innerText: block,
465
+ textContent: block,
466
+ },
467
+ },
468
+ };
469
+ const blockedEvents = {
470
+ onabort: block,
471
+ onauxclick: block,
472
+ onbeforeinput: block,
473
+ onbeforematch: block,
474
+ onblur: block,
475
+ oncancel: block,
476
+ oncanplay: block,
477
+ oncanplaythrough: block,
478
+ onchange: block,
479
+ onclick: block,
480
+ onclose: block,
481
+ oncontextlost: block,
482
+ oncontextmenu: block,
483
+ oncontextrestored: block,
484
+ oncopy: block,
485
+ oncuechange: block,
486
+ oncut: block,
487
+ ondblclick: block,
488
+ ondrag: block,
489
+ ondragend: block,
490
+ ondragenter: block,
491
+ ondragleave: block,
492
+ ondragover: block,
493
+ ondragstart: block,
494
+ ondrop: block,
495
+ ondurationchange: block,
496
+ onemptied: block,
497
+ onended: block,
498
+ onerror: block,
499
+ onfocus: block,
500
+ onformdata: block,
501
+ oninput: block,
502
+ oninvalid: block,
503
+ onkeydown: block,
504
+ onkeypress: block,
505
+ onkeyup: block,
506
+ onload: block,
507
+ onloadeddata: block,
508
+ onloadedmetadata: block,
509
+ onloadstart: block,
510
+ onmousedown: block,
511
+ onmouseenter: block,
512
+ onmouseleave: block,
513
+ onmousemove: block,
514
+ onmouseout: block,
515
+ onmouseover: block,
516
+ onmouseup: block,
517
+ onpaste: block,
518
+ onpause: block,
519
+ onplay: block,
520
+ onplaying: block,
521
+ onprogress: block,
522
+ onratechange: block,
523
+ onreset: block,
524
+ onresize: block,
525
+ onscroll: block,
526
+ onsecuritypolicyviolation: block,
527
+ onseeked: block,
528
+ onseeking: block,
529
+ onselect: block,
530
+ onslotchange: block,
531
+ onstalled: block,
532
+ onsubmit: block,
533
+ onsuspend: block,
534
+ ontimeupdate: block,
535
+ ontoggle: block,
536
+ onvolumechange: block,
537
+ onwaiting: block,
538
+ onwebkitanimationend: block,
539
+ onwebkitanimationiteration: block,
540
+ onwebkitanimationstart: block,
541
+ onwebkittransitionend: block,
542
+ onwheel: block,
543
+ };
544
+ const defaultDOMGuards = {
545
+ elements: defaultDOMElementGuards,
546
+ aspects: {
547
+ [DOMAspect.attribute]: Object.assign({}, blockedEvents),
548
+ [DOMAspect.property]: Object.assign({ innerHTML: block }, blockedEvents),
549
+ [DOMAspect.event]: Object.assign({}, blockedEvents),
550
+ },
551
+ };
552
+ function createDomSinkGuards(config, defaults) {
553
+ const result = {};
554
+ for (const name in defaults) {
555
+ const overrideValue = config[name];
556
+ const defaultValue = defaults[name];
557
+ switch (overrideValue) {
558
+ case null:
559
+ // remove the default
560
+ break;
561
+ case undefined:
562
+ // keep the default
563
+ result[name] = defaultValue;
564
+ break;
565
+ default:
566
+ // override the default
567
+ result[name] = overrideValue;
568
+ break;
569
+ }
570
+ }
571
+ // add any new sinks that were not overrides
572
+ for (const name in config) {
573
+ if (!(name in result)) {
574
+ result[name] = config[name];
575
+ }
576
+ }
577
+ return Object.freeze(result);
578
+ }
579
+ function createDOMAspectGuards(config, defaults) {
580
+ const result = {};
581
+ for (const aspect in defaults) {
582
+ const overrideValue = config[aspect];
583
+ const defaultValue = defaults[aspect];
584
+ switch (overrideValue) {
585
+ case null:
586
+ // remove the default
587
+ break;
588
+ case undefined:
589
+ // keep the default
590
+ result[aspect] = createDomSinkGuards(defaultValue, {});
591
+ break;
592
+ default:
593
+ // override the default
594
+ result[aspect] = createDomSinkGuards(overrideValue, defaultValue);
595
+ break;
596
+ }
597
+ }
598
+ // add any new aspect guards that were not overrides
599
+ for (const aspect in config) {
600
+ if (!(aspect in result)) {
601
+ result[aspect] = createDomSinkGuards(config[aspect], {});
602
+ }
603
+ }
604
+ return Object.freeze(result);
605
+ }
606
+ function createElementGuards(config, defaults) {
607
+ const result = {};
608
+ for (const tag in defaults) {
609
+ const overrideValue = config[tag];
610
+ const defaultValue = defaults[tag];
611
+ switch (overrideValue) {
612
+ case null:
613
+ // remove the default
614
+ break;
615
+ case undefined:
616
+ // keep the default
617
+ result[tag] = createDOMAspectGuards(overrideValue, {});
618
+ break;
619
+ default:
620
+ // override the default aspects
621
+ result[tag] = createDOMAspectGuards(overrideValue, defaultValue);
622
+ break;
623
+ }
624
+ }
625
+ // Add any new element guards that were not overrides
626
+ for (const tag in config) {
627
+ if (!(tag in result)) {
628
+ result[tag] = createDOMAspectGuards(config[tag], {});
629
+ }
630
+ }
631
+ return Object.freeze(result);
632
+ }
633
+ function createDOMGuards(config, defaults) {
634
+ return Object.freeze({
635
+ elements: config.elements
636
+ ? createElementGuards(config.elements, defaults.elements)
637
+ : defaults.elements,
638
+ aspects: config.aspects
639
+ ? createDOMAspectGuards(config.aspects, defaults.aspects)
640
+ : defaults.aspects,
641
+ });
642
+ }
643
+ function createTrustedType() {
644
+ const createHTML = html => html;
645
+ return globalThis.trustedTypes
646
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML })
647
+ : { createHTML };
648
+ }
649
+ function tryGuard(aspectGuards, tagName, aspect, aspectName, sink) {
650
+ const sinkGuards = aspectGuards[aspect];
651
+ if (sinkGuards) {
652
+ const guard = sinkGuards[aspectName];
653
+ if (guard) {
654
+ return guard(tagName, aspect, aspectName, sink);
655
+ }
656
+ }
657
+ }
658
+ const DOMPolicy = Object.freeze({
659
+ /**
660
+ * Creates a new DOM Policy object.
661
+ * @param options The options to use in creating the policy.
662
+ * @returns The newly created DOMPolicy.
663
+ */
664
+ create(options = {}) {
665
+ var _a, _b;
666
+ const trustedType = (_a = options.trustedType) !== null && _a !== void 0 ? _a : createTrustedType();
667
+ const guards = createDOMGuards((_b = options.guards) !== null && _b !== void 0 ? _b : {}, defaultDOMGuards);
668
+ return Object.freeze({
669
+ createHTML(value) {
670
+ return trustedType.createHTML(value);
671
+ },
672
+ protect(tagName, aspect, aspectName, sink) {
673
+ var _a;
674
+ // Check for element-specific guards.
675
+ const key = (tagName !== null && tagName !== void 0 ? tagName : "").toLowerCase();
676
+ const elementGuards = guards.elements[key];
677
+ if (elementGuards) {
678
+ const guard = tryGuard(elementGuards, tagName, aspect, aspectName, sink);
679
+ if (guard) {
680
+ return guard;
681
+ }
682
+ }
683
+ // Check for guards applicable to all nodes.
684
+ return ((_a = tryGuard(guards.aspects, tagName, aspect, aspectName, sink)) !== null && _a !== void 0 ? _a : sink);
685
+ },
686
+ });
687
+ },
688
+ });
689
+
199
690
  /**
200
691
  * An implementation of {@link Notifier} that efficiently keeps track of
201
692
  * subscribers interested in a specific change notification on an
@@ -380,7 +871,7 @@ const SourceLifetime = Object.freeze({
380
871
  * Common Observable APIs.
381
872
  * @public
382
873
  */
383
- const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
874
+ const Observable = FAST.getById(KernelServiceId.observable, () => {
384
875
  const queueUpdate = Updates.enqueue;
385
876
  const volatileRegex = /(:|&&|\|\||if)/;
386
877
  const notifierLookup = new WeakMap();
@@ -651,7 +1142,7 @@ function volatile(target, name, descriptor) {
651
1142
  },
652
1143
  });
653
1144
  }
654
- const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
1145
+ const contextEvent = FAST.getById(KernelServiceId.contextEvent, () => {
655
1146
  let current = null;
656
1147
  return {
657
1148
  get() {
@@ -1482,55 +1973,6 @@ css.partial = (strings, ...values) => {
1482
1973
  */
1483
1974
  const cssPartial = css.partial;
1484
1975
 
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
1976
  const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
1535
1977
  const interpolationStart = `${marker}{`;
1536
1978
  const interpolationEnd = `}${marker}`;
@@ -1607,67 +2049,6 @@ const Parser = Object.freeze({
1607
2049
  },
1608
2050
  });
1609
2051
 
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
2052
  const registry = createTypeRegistry();
1672
2053
  /**
1673
2054
  * Instructs the template engine to apply behavior to a node.
@@ -1695,67 +2076,6 @@ const HTMLDirective = Object.freeze({
1695
2076
  registry.register(options);
1696
2077
  return type;
1697
2078
  },
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
2079
  /**
1760
2080
  *
1761
2081
  * @param directive - The directive to assign the aspect to.
@@ -1763,9 +2083,9 @@ const Aspect = Object.freeze({
1763
2083
  * @remarks
1764
2084
  * If a falsy value is provided, then the content aspect will be assigned.
1765
2085
  */
1766
- assign(directive, value) {
2086
+ assignAspect(directive, value) {
1767
2087
  if (!value) {
1768
- directive.aspectType = Aspect.content;
2088
+ directive.aspectType = DOMAspect.content;
1769
2089
  return;
1770
2090
  }
1771
2091
  directive.sourceAspect = value;
@@ -1774,24 +2094,53 @@ const Aspect = Object.freeze({
1774
2094
  directive.targetAspect = value.substring(1);
1775
2095
  directive.aspectType =
1776
2096
  directive.targetAspect === "classList"
1777
- ? Aspect.tokenList
1778
- : Aspect.property;
2097
+ ? DOMAspect.tokenList
2098
+ : DOMAspect.property;
1779
2099
  break;
1780
2100
  case "?":
1781
2101
  directive.targetAspect = value.substring(1);
1782
- directive.aspectType = Aspect.booleanAttribute;
2102
+ directive.aspectType = DOMAspect.booleanAttribute;
1783
2103
  break;
1784
2104
  case "@":
1785
2105
  directive.targetAspect = value.substring(1);
1786
- directive.aspectType = Aspect.event;
2106
+ directive.aspectType = DOMAspect.event;
1787
2107
  break;
1788
2108
  default:
1789
2109
  directive.targetAspect = value;
1790
- directive.aspectType = Aspect.attribute;
2110
+ directive.aspectType = DOMAspect.attribute;
1791
2111
  break;
1792
2112
  }
1793
2113
  },
1794
2114
  });
2115
+ /**
2116
+ * Decorator: Defines an HTMLDirective.
2117
+ * @param options - Provides options that specify the directive's application.
2118
+ * @public
2119
+ */
2120
+ function htmlDirective(options) {
2121
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
2122
+ return function (type) {
2123
+ HTMLDirective.define(type, options);
2124
+ };
2125
+ }
2126
+ /**
2127
+ * Captures a binding expression along with related information and capabilities.
2128
+ *
2129
+ * @public
2130
+ */
2131
+ class Binding {
2132
+ /**
2133
+ * Creates a binding.
2134
+ * @param evaluate - Evaluates the binding.
2135
+ * @param policy - The security policy to associate with this binding.
2136
+ * @param isVolatile - Indicates whether the binding is volatile.
2137
+ */
2138
+ constructor(evaluate, policy, isVolatile = false) {
2139
+ this.evaluate = evaluate;
2140
+ this.policy = policy;
2141
+ this.isVolatile = isVolatile;
2142
+ }
2143
+ }
1795
2144
  /**
1796
2145
  * A base class used for attribute directives that don't need internal state.
1797
2146
  * @public
@@ -1803,10 +2152,6 @@ class StatelessAttachedAttributeDirective {
1803
2152
  */
1804
2153
  constructor(options) {
1805
2154
  this.options = options;
1806
- /**
1807
- * The unique id of the factory.
1808
- */
1809
- this.id = nextId();
1810
2155
  /**
1811
2156
  * Opts out of JSON stringification.
1812
2157
  * @internal
@@ -1831,15 +2176,6 @@ class StatelessAttachedAttributeDirective {
1831
2176
  }
1832
2177
  }
1833
2178
 
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
2179
  class OnChangeBinding extends Binding {
1844
2180
  createObserver(_, subscriber) {
1845
2181
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
@@ -1952,8 +2288,14 @@ function updateTokenList(target, aspect, value) {
1952
2288
  }
1953
2289
  }
1954
2290
  }
1955
- const setProperty = (t, a, v) => (t[a] = v);
1956
- const eventTarget = () => void 0;
2291
+ const sinkLookup = {
2292
+ [DOMAspect.attribute]: DOM.setAttribute,
2293
+ [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
2294
+ [DOMAspect.property]: (t, a, v) => (t[a] = v),
2295
+ [DOMAspect.content]: updateContent,
2296
+ [DOMAspect.tokenList]: updateTokenList,
2297
+ [DOMAspect.event]: () => void 0,
2298
+ };
1957
2299
  /**
1958
2300
  * A directive that applies bindings.
1959
2301
  * @public
@@ -1966,15 +2308,10 @@ class HTMLBindingDirective {
1966
2308
  constructor(dataBinding) {
1967
2309
  this.dataBinding = dataBinding;
1968
2310
  this.updateTarget = null;
1969
- /**
1970
- * The unique id of the factory.
1971
- */
1972
- this.id = nextId();
1973
2311
  /**
1974
2312
  * The type of aspect to target.
1975
2313
  */
1976
- this.aspectType = Aspect.content;
1977
- this.data = `${this.id}-d`;
2314
+ this.aspectType = DOMAspect.content;
1978
2315
  }
1979
2316
  /**
1980
2317
  * Creates HTML to be used within a template.
@@ -1987,45 +2324,28 @@ class HTMLBindingDirective {
1987
2324
  * Creates a behavior.
1988
2325
  */
1989
2326
  createBehavior() {
2327
+ var _a;
1990
2328
  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 */);
2329
+ const sink = sinkLookup[this.aspectType];
2330
+ const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
2331
+ if (!sink) {
2332
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2015
2333
  }
2334
+ this.data = `${this.id}-d`;
2335
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
2016
2336
  }
2017
2337
  return this;
2018
2338
  }
2019
2339
  /** @internal */
2020
2340
  bind(controller) {
2021
2341
  var _a;
2022
- const target = controller.targets[this.nodeId];
2023
- switch (this.updateTarget) {
2024
- case eventTarget:
2342
+ const target = controller.targets[this.targetNodeId];
2343
+ switch (this.aspectType) {
2344
+ case DOMAspect.event:
2025
2345
  target[this.data] = controller;
2026
2346
  target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2027
2347
  break;
2028
- case updateContent:
2348
+ case DOMAspect.content:
2029
2349
  controller.onUnbind(this);
2030
2350
  // intentional fall through
2031
2351
  default:
@@ -2038,7 +2358,7 @@ class HTMLBindingDirective {
2038
2358
  }
2039
2359
  /** @internal */
2040
2360
  unbind(controller) {
2041
- const target = controller.targets[this.nodeId];
2361
+ const target = controller.targets[this.targetNodeId];
2042
2362
  const view = target.$fastView;
2043
2363
  if (view !== void 0 && view.isComposed) {
2044
2364
  view.unbind();
@@ -2068,21 +2388,23 @@ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2068
2388
  /**
2069
2389
  * Creates an standard binding.
2070
2390
  * @param expression - The binding to refresh when changed.
2391
+ * @param policy - The security policy to associate with th binding.
2071
2392
  * @param isVolatile - Indicates whether the binding is volatile or not.
2072
2393
  * @returns A binding configuration.
2073
2394
  * @public
2074
2395
  */
2075
- function bind(expression, isVolatile = Observable.isVolatileBinding(expression)) {
2076
- return new OnChangeBinding(expression, isVolatile);
2396
+ function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
2397
+ return new OnChangeBinding(expression, policy, isVolatile);
2077
2398
  }
2078
2399
  /**
2079
2400
  * Creates a one time binding
2080
2401
  * @param expression - The binding to refresh when signaled.
2402
+ * @param policy - The security policy to associate with th binding.
2081
2403
  * @returns A binding configuration.
2082
2404
  * @public
2083
2405
  */
2084
- function oneTime(expression) {
2085
- return new OneTimeBinding(expression);
2406
+ function oneTime(expression, policy) {
2407
+ return new OneTimeBinding(expression, policy);
2086
2408
  }
2087
2409
  /**
2088
2410
  * Creates an event listener binding.
@@ -2092,7 +2414,7 @@ function oneTime(expression) {
2092
2414
  * @public
2093
2415
  */
2094
2416
  function listener(expression, options) {
2095
- const config = new OnChangeBinding(expression, false);
2417
+ const config = new OnChangeBinding(expression);
2096
2418
  config.options = options;
2097
2419
  return config;
2098
2420
  }
@@ -2373,20 +2695,25 @@ const warningHost = new Proxy(document.createElement("div"), {
2373
2695
  },
2374
2696
  });
2375
2697
  class CompilationContext {
2376
- constructor(fragment, directives) {
2698
+ constructor(fragment, directives, policy) {
2377
2699
  this.fragment = fragment;
2378
2700
  this.directives = directives;
2701
+ this.policy = policy;
2379
2702
  this.proto = null;
2380
2703
  this.nodeIds = new Set();
2381
2704
  this.descriptors = {};
2382
2705
  this.factories = [];
2383
2706
  }
2384
- addFactory(factory, parentId, nodeId, targetIndex) {
2707
+ addFactory(factory, parentId, nodeId, targetIndex, tagName) {
2708
+ var _a, _b;
2385
2709
  if (!this.nodeIds.has(nodeId)) {
2386
2710
  this.nodeIds.add(nodeId);
2387
2711
  this.addTargetDescriptor(parentId, nodeId, targetIndex);
2388
2712
  }
2389
- factory.nodeId = nodeId;
2713
+ factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
2714
+ factory.targetNodeId = nodeId;
2715
+ factory.targetTagName = tagName;
2716
+ factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
2390
2717
  this.factories.push(factory);
2391
2718
  }
2392
2719
  freeze() {
@@ -2439,19 +2766,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2439
2766
  let result = null;
2440
2767
  if (parseResult === null) {
2441
2768
  if (includeBasicValues) {
2442
- result = new HTMLBindingDirective(oneTime(() => attrValue));
2443
- Aspect.assign(result, attr.name);
2769
+ result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
2770
+ HTMLDirective.assignAspect(result, attr.name);
2444
2771
  }
2445
2772
  }
2446
2773
  else {
2447
2774
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2448
- result = Compiler.aggregate(parseResult);
2775
+ result = Compiler.aggregate(parseResult, context.policy);
2449
2776
  }
2450
2777
  if (result !== null) {
2451
2778
  node.removeAttributeNode(attr);
2452
2779
  i--;
2453
2780
  ii--;
2454
- context.addFactory(result, parentId, nodeId, nodeIndex);
2781
+ context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
2455
2782
  }
2456
2783
  }
2457
2784
  }
@@ -2476,8 +2803,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2476
2803
  }
2477
2804
  else {
2478
2805
  currentNode.textContent = " ";
2479
- Aspect.assign(currentPart);
2480
- context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2806
+ HTMLDirective.assignAspect(currentPart);
2807
+ context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
2481
2808
  }
2482
2809
  lastNode = currentNode;
2483
2810
  }
@@ -2509,7 +2836,7 @@ function compileNode(context, parentId, node, nodeIndex) {
2509
2836
  if (parts !== null) {
2510
2837
  context.addFactory(
2511
2838
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2512
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
2839
+ Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
2513
2840
  }
2514
2841
  break;
2515
2842
  }
@@ -2523,45 +2850,28 @@ function isMarker(node, directives) {
2523
2850
  Parser.parse(node.data, directives) !== null);
2524
2851
  }
2525
2852
  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
2853
  /**
2532
2854
  * Common APIs related to compilation.
2533
2855
  * @public
2534
2856
  */
2535
2857
  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
2858
  /**
2550
2859
  * Compiles a template and associated directives into a compilation
2551
2860
  * result which can be used to create views.
2552
2861
  * @param html - The html string or template element to compile.
2553
- * @param directives - The directives referenced by the template.
2862
+ * @param factories - The behavior factories referenced by the template.
2863
+ * @param policy - The security policy to compile the html with.
2554
2864
  * @remarks
2555
2865
  * The template that is provided for compilation is altered in-place
2556
2866
  * and cannot be compiled again. If the original template must be preserved,
2557
2867
  * it is recommended that you clone the original and pass the clone to this API.
2558
2868
  * @public
2559
2869
  */
2560
- compile(html, directives) {
2870
+ compile(html, factories, policy = DOM.policy) {
2561
2871
  let template;
2562
2872
  if (isString(html)) {
2563
2873
  template = document.createElement(templateTag);
2564
- template.innerHTML = htmlPolicy.createHTML(html);
2874
+ template.innerHTML = policy.createHTML(html);
2565
2875
  const fec = template.content.firstElementChild;
2566
2876
  if (fec !== null && fec.tagName === templateTag) {
2567
2877
  template = fec;
@@ -2572,18 +2882,18 @@ const Compiler = {
2572
2882
  }
2573
2883
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
2574
2884
  const fragment = document.adoptNode(template.content);
2575
- const context = new CompilationContext(fragment, directives);
2885
+ const context = new CompilationContext(fragment, factories, policy);
2576
2886
  compileAttributes(context, "", template, /* host */ "h", 0, true);
2577
2887
  if (
2578
2888
  // If the first node in a fragment is a marker, that means it's an unstable first node,
2579
2889
  // because something like a when, repeat, etc. could add nodes before the marker.
2580
2890
  // To mitigate this, we insert a stable first node. However, if we insert a node,
2581
2891
  // that will alter the result of the TreeWalker. So, we also need to offset the target index.
2582
- isMarker(fragment.firstChild, directives) ||
2892
+ isMarker(fragment.firstChild, factories) ||
2583
2893
  // Or if there is only one node and a directive, it means the template's content
2584
2894
  // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
2585
2895
  // 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)) {
2896
+ (fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
2587
2897
  fragment.insertBefore(document.createComment(""), fragment.firstChild);
2588
2898
  }
2589
2899
  compileChildren(context, fragment, /* root */ "r");
@@ -2602,15 +2912,17 @@ const Compiler = {
2602
2912
  * Aggregates an array of strings and directives into a single directive.
2603
2913
  * @param parts - A heterogeneous array of static strings interspersed with
2604
2914
  * directives.
2915
+ * @param policy - The security policy to use with the aggregated bindings.
2605
2916
  * @returns A single inline directive that aggregates the behavior of all the parts.
2606
2917
  */
2607
- aggregate(parts) {
2918
+ aggregate(parts, policy = DOM.policy) {
2608
2919
  if (parts.length === 1) {
2609
2920
  return parts[0];
2610
2921
  }
2611
2922
  let sourceAspect;
2612
2923
  let binding;
2613
2924
  let isVolatile = false;
2925
+ let bindingPolicy = void 0;
2614
2926
  const partCount = parts.length;
2615
2927
  const finalParts = parts.map((x) => {
2616
2928
  if (isString(x)) {
@@ -2619,6 +2931,7 @@ const Compiler = {
2619
2931
  sourceAspect = x.sourceAspect || sourceAspect;
2620
2932
  binding = x.dataBinding || binding;
2621
2933
  isVolatile = isVolatile || x.dataBinding.isVolatile;
2934
+ bindingPolicy = bindingPolicy || x.dataBinding.policy;
2622
2935
  return x.dataBinding.evaluate;
2623
2936
  });
2624
2937
  const expression = (scope, context) => {
@@ -2630,12 +2943,26 @@ const Compiler = {
2630
2943
  };
2631
2944
  binding.evaluate = expression;
2632
2945
  binding.isVolatile = isVolatile;
2946
+ binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
2633
2947
  const directive = new HTMLBindingDirective(binding);
2634
- Aspect.assign(directive, sourceAspect);
2948
+ HTMLDirective.assignAspect(directive, sourceAspect);
2635
2949
  return directive;
2636
2950
  },
2637
2951
  };
2638
2952
 
2953
+ // Much thanks to LitHTML for working this out!
2954
+ const lastAttributeNameRegex =
2955
+ /* eslint-disable-next-line no-control-regex */
2956
+ /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2957
+ function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
2958
+ if (definition.aspected) {
2959
+ const match = lastAttributeNameRegex.exec(prevString);
2960
+ if (match !== null) {
2961
+ HTMLDirective.assignAspect(value, match[2]);
2962
+ }
2963
+ }
2964
+ return value.createHTML(add);
2965
+ }
2639
2966
  /**
2640
2967
  * A template capable of creating HTMLView instances or rendering directly to DOM.
2641
2968
  * @public
@@ -2645,8 +2972,10 @@ class ViewTemplate {
2645
2972
  * Creates an instance of ViewTemplate.
2646
2973
  * @param html - The html representing what this template will instantiate, including placeholders for directives.
2647
2974
  * @param factories - The directives that will be connected to placeholders in the html.
2975
+ * @param policy - The security policy to use when compiling this template.
2648
2976
  */
2649
- constructor(html, factories) {
2977
+ constructor(html, factories = {}, policy) {
2978
+ this.policy = policy;
2650
2979
  this.result = null;
2651
2980
  /**
2652
2981
  * Opts out of JSON stringification.
@@ -2662,10 +2991,28 @@ class ViewTemplate {
2662
2991
  */
2663
2992
  create(hostBindingTarget) {
2664
2993
  if (this.result === null) {
2665
- this.result = Compiler.compile(this.html, this.factories);
2994
+ this.result = Compiler.compile(this.html, this.factories, this.policy);
2666
2995
  }
2667
2996
  return this.result.createView(hostBindingTarget);
2668
2997
  }
2998
+ /**
2999
+ * Sets the DOMPolicy for this template.
3000
+ * @param policy - The policy to associated with this template.
3001
+ * @returns The modified template instance.
3002
+ * @remarks
3003
+ * The DOMPolicy can only be set once for a template and cannot be
3004
+ * set after the template is compiled.
3005
+ */
3006
+ withPolicy(policy) {
3007
+ if (this.result) {
3008
+ throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
3009
+ }
3010
+ if (this.policy) {
3011
+ throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
3012
+ }
3013
+ this.policy = policy;
3014
+ return this;
3015
+ }
2669
3016
  /**
2670
3017
  * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
2671
3018
  * @param source - The data source to bind the template to.
@@ -2679,17 +3026,47 @@ class ViewTemplate {
2679
3026
  view.appendTo(host);
2680
3027
  return view;
2681
3028
  }
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]);
3029
+ /**
3030
+ * Creates a template based on a set of static strings and dynamic values.
3031
+ * @param strings - The static strings to create the template with.
3032
+ * @param values - The dynamic values to create the template with.
3033
+ * @param policy - The DOMPolicy to associated with the template.
3034
+ * @returns A ViewTemplate.
3035
+ * @remarks
3036
+ * This API should not be used directly under normal circumstances because constructing
3037
+ * a template in this way, if not done properly, can open up the application to XSS
3038
+ * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
3039
+ * and also be sure to manually sanitize all static strings particularly if they can
3040
+ * come from user input.
3041
+ */
3042
+ static create(strings, values, policy) {
3043
+ let html = "";
3044
+ const factories = Object.create(null);
3045
+ const add = (factory) => {
3046
+ var _a;
3047
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
3048
+ factories[id] = factory;
3049
+ return id;
3050
+ };
3051
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
3052
+ const currentString = strings[i];
3053
+ let currentValue = values[i];
3054
+ let definition;
3055
+ html += currentString;
3056
+ if (isFunction(currentValue)) {
3057
+ currentValue = new HTMLBindingDirective(bind(currentValue));
3058
+ }
3059
+ else if (currentValue instanceof Binding) {
3060
+ currentValue = new HTMLBindingDirective(currentValue);
3061
+ }
3062
+ else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
3063
+ const staticValue = currentValue;
3064
+ currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
3065
+ }
3066
+ html += createHTML(currentValue, currentString, add, definition);
3067
+ }
3068
+ return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
2691
3069
  }
2692
- return value.createHTML(add);
2693
3070
  }
2694
3071
  /**
2695
3072
  * Transforms a template literal string into a ViewTemplate.
@@ -2701,49 +3078,10 @@ function createAspectedHTML(value, prevString, add) {
2701
3078
  * @public
2702
3079
  */
2703
3080
  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
- }
3081
+ if (Array.isArray(strings) && Array.isArray(strings.raw)) {
3082
+ return ViewTemplate.create(strings, values);
2745
3083
  }
2746
- return new ViewTemplate(html + strings[strings.length - 1], factories);
3084
+ throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
2747
3085
  }
2748
3086
 
2749
3087
  /**
@@ -2756,7 +3094,7 @@ class RefDirective extends StatelessAttachedAttributeDirective {
2756
3094
  * @param controller - The view controller that manages the lifecycle of this behavior.
2757
3095
  */
2758
3096
  bind(controller) {
2759
- controller.source[this.options] = controller.targets[this.nodeId];
3097
+ controller.source[this.options] = controller.targets[this.targetNodeId];
2760
3098
  }
2761
3099
  }
2762
3100
  HTMLDirective.define(RefDirective);
@@ -2830,7 +3168,7 @@ class RepeatBehavior {
2830
3168
  * @param controller - The view controller that manages the lifecycle of this behavior.
2831
3169
  */
2832
3170
  bind(controller) {
2833
- this.location = controller.targets[this.directive.nodeId];
3171
+ this.location = controller.targets[this.directive.targetNodeId];
2834
3172
  this.controller = controller;
2835
3173
  this.items = this.itemsBindingObserver.bind(controller);
2836
3174
  this.template = this.templateBindingObserver.bind(controller);
@@ -3010,10 +3348,6 @@ class RepeatDirective {
3010
3348
  this.dataBinding = dataBinding;
3011
3349
  this.templateBinding = templateBinding;
3012
3350
  this.options = options;
3013
- /**
3014
- * The unique id of the factory.
3015
- */
3016
- this.id = nextId();
3017
3351
  ArrayObserver.enable();
3018
3352
  }
3019
3353
  /**
@@ -3062,9 +3396,15 @@ const elements = (selector) => selector
3062
3396
  * Internally used by the SlottedDirective and the ChildrenDirective.
3063
3397
  */
3064
3398
  class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3065
- constructor() {
3066
- super(...arguments);
3067
- this.controllerProperty = `${this.id}-c`;
3399
+ /**
3400
+ * The unique id of the factory.
3401
+ */
3402
+ get id() {
3403
+ return this._id;
3404
+ }
3405
+ set id(value) {
3406
+ this._id = value;
3407
+ this._controllerProperty = `${value}-c`;
3068
3408
  }
3069
3409
  /**
3070
3410
  * Bind this behavior to the source.
@@ -3073,8 +3413,8 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3073
3413
  * @param targets - The targets that behaviors in a view can attach to.
3074
3414
  */
3075
3415
  bind(controller) {
3076
- const target = controller.targets[this.nodeId];
3077
- target[this.controllerProperty] = controller;
3416
+ const target = controller.targets[this.targetNodeId];
3417
+ target[this._controllerProperty] = controller;
3078
3418
  this.updateTarget(controller.source, this.computeNodes(target));
3079
3419
  this.observe(target);
3080
3420
  controller.onUnbind(this);
@@ -3086,10 +3426,10 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3086
3426
  * @param targets - The targets that behaviors in a view can attach to.
3087
3427
  */
3088
3428
  unbind(controller) {
3089
- const target = controller.targets[this.nodeId];
3429
+ const target = controller.targets[this.targetNodeId];
3090
3430
  this.updateTarget(controller.source, emptyArray);
3091
3431
  this.disconnect(target);
3092
- target[this.controllerProperty] = null;
3432
+ target[this._controllerProperty] = null;
3093
3433
  }
3094
3434
  /**
3095
3435
  * Gets the data source for the target.
@@ -3097,7 +3437,7 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3097
3437
  * @returns The source.
3098
3438
  */
3099
3439
  getSource(target) {
3100
- return target[this.controllerProperty].source;
3440
+ return target[this._controllerProperty].source;
3101
3441
  }
3102
3442
  /**
3103
3443
  * Updates the source property with the computed nodes.
@@ -3238,6 +3578,29 @@ function children(propertyOrOptions) {
3238
3578
  return new ChildrenDirective(propertyOrOptions);
3239
3579
  }
3240
3580
 
3581
+ /**
3582
+ * A directive capable of injecting static HTML platform runtime protection.
3583
+ * @public
3584
+ */
3585
+ class DangerousHTMLDirective {
3586
+ constructor(html) {
3587
+ this.html = html;
3588
+ }
3589
+ createHTML() {
3590
+ return this.html;
3591
+ }
3592
+ }
3593
+ HTMLDirective.define(DangerousHTMLDirective);
3594
+ /**
3595
+ * Injects static HTML without platform protection.
3596
+ * @param html - The html to injection.
3597
+ * @returns A DangerousHTMLDirective.
3598
+ * @public
3599
+ */
3600
+ function dangerousHTML(html) {
3601
+ return new DangerousHTMLDirective(html);
3602
+ }
3603
+
3241
3604
  const booleanMode = "boolean";
3242
3605
  const reflectMode = "reflect";
3243
3606
  /**
@@ -3436,7 +3799,7 @@ function attr(configOrTarget, prop) {
3436
3799
  const defaultShadowOptions = { mode: "open" };
3437
3800
  const defaultElementOptions = {};
3438
3801
  const fastElementBaseTypes = new Set();
3439
- const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */, () => createTypeRegistry());
3802
+ const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
3440
3803
  /**
3441
3804
  * Defines metadata for a FASTElement.
3442
3805
  * @public
@@ -4098,4 +4461,6 @@ function customElement(nameOrDef) {
4098
4461
  };
4099
4462
  }
4100
4463
 
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 };
4464
+ DOM.setPolicy(DOMPolicy.create());
4465
+
4466
+ 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 };