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