botframework-webchat 4.15.10-main.20230908.de79e0e → 4.15.10-main.20230915.bb5898e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/webchat-es5.js +1 -1
  2. package/dist/webchat-minimal.js +1 -1
  3. package/dist/webchat.js +1 -1
  4. package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.d.ts.map +1 -1
  5. package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.js +5 -4
  6. package/lib/adaptiveCards/Attachment/AnimationCardContent.js +4 -3
  7. package/lib/adaptiveCards/Attachment/AudioCardContent.js +4 -3
  8. package/lib/adaptiveCards/Attachment/SignInCardContent.d.ts.map +1 -1
  9. package/lib/adaptiveCards/Attachment/SignInCardContent.js +3 -5
  10. package/lib/adaptiveCards/Attachment/VideoCardContent.js +4 -3
  11. package/lib/addVersion.js +1 -1
  12. package/lib/createFullStyleSet.d.ts +531 -233
  13. package/lib/createFullStyleSet.d.ts.map +1 -1
  14. package/lib/hooks/useStyleSet.d.ts +6 -0
  15. package/lib/hooks/useStyleSet.d.ts.map +1 -0
  16. package/lib/hooks/useStyleSet.js +23 -0
  17. package/lib/index.d.ts +3 -2
  18. package/lib/index.d.ts.map +1 -1
  19. package/lib/index.js +6 -3
  20. package/lib/markdown/markdownItPlugins/ariaLabel.d.ts +6 -0
  21. package/lib/markdown/markdownItPlugins/ariaLabel.d.ts.map +1 -0
  22. package/lib/markdown/markdownItPlugins/ariaLabel.js +64 -0
  23. package/lib/markdown/markdownItPlugins/betterLink.d.ts +22 -0
  24. package/lib/markdown/markdownItPlugins/betterLink.d.ts.map +1 -0
  25. package/lib/markdown/markdownItPlugins/betterLink.js +114 -0
  26. package/lib/markdown/markdownItPlugins/respectCRLF.d.ts +3 -0
  27. package/lib/markdown/markdownItPlugins/respectCRLF.d.ts.map +1 -0
  28. package/lib/markdown/markdownItPlugins/respectCRLF.js +19 -0
  29. package/lib/markdown/private/iterateLinkDefinitions.d.ts +3 -0
  30. package/lib/markdown/private/iterateLinkDefinitions.d.ts.map +1 -0
  31. package/lib/markdown/private/iterateLinkDefinitions.js +84 -0
  32. package/lib/markdown/renderMarkdown.d.ts +8 -0
  33. package/lib/markdown/renderMarkdown.d.ts.map +1 -0
  34. package/lib/markdown/renderMarkdown.js +119 -0
  35. package/lib/useComposerProps.js +2 -2
  36. package/package.json +9 -7
  37. package/src/__tests__/renderMarkdown.spec.js +3 -3
  38. package/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +2 -1
  39. package/src/adaptiveCards/Attachment/AnimationCardContent.tsx +2 -2
  40. package/src/adaptiveCards/Attachment/AudioCardContent.tsx +2 -2
  41. package/src/adaptiveCards/Attachment/SignInCardContent.tsx +1 -3
  42. package/src/adaptiveCards/Attachment/VideoCardContent.tsx +2 -2
  43. package/src/hooks/useStyleSet.ts +13 -0
  44. package/src/index.ts +4 -2
  45. package/src/markdown/markdownItPlugins/ariaLabel.spec.ts +27 -0
  46. package/src/markdown/markdownItPlugins/ariaLabel.ts +52 -0
  47. package/src/markdown/markdownItPlugins/betterLink.spec.ts +80 -0
  48. package/src/markdown/markdownItPlugins/betterLink.ts +113 -0
  49. package/src/markdown/markdownItPlugins/respectCRLF.ts +7 -0
  50. package/src/markdown/private/iterateLinkDefinitions.ts +14 -0
  51. package/src/markdown/renderMarkdown.ts +146 -0
  52. package/src/useComposerProps.ts +1 -1
  53. package/lib/renderMarkdown.d.ts +0 -6
  54. package/lib/renderMarkdown.d.ts.map +0 -1
  55. package/lib/renderMarkdown.js +0 -115
  56. package/src/renderMarkdown.ts +0 -140
@@ -0,0 +1,7 @@
1
+ // TODO: We should find some other ways to do this.
2
+ const pre = (markdown: string): string =>
3
+ // IE11 does not support "u" flag and Babel could not remove it. We intentionally omitting the "u" flag here.
4
+ // eslint-disable-next-line require-unicode-regexp
5
+ markdown.replace(/\n\r|\r\n/g, carriageReturn => (carriageReturn === '\n\r' ? '\r\n' : '\n\r'));
6
+
7
+ export { pre };
@@ -0,0 +1,14 @@
1
+ import { fromMarkdown } from 'mdast-util-from-markdown';
2
+ import { type Definition, type Node } from 'mdast';
3
+
4
+ function isDefinition(node: Node): node is Definition {
5
+ return node.type === 'definition';
6
+ }
7
+
8
+ export default function* iterateLinkDefinitions(markdown: string): Generator<Definition, void, void> {
9
+ for (const topLevelNode of fromMarkdown(markdown).children) {
10
+ if (isDefinition(topLevelNode)) {
11
+ yield topLevelNode;
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,146 @@
1
+ import { onErrorResumeNext } from 'botframework-webchat-core';
2
+ import MarkdownIt from 'markdown-it';
3
+ import sanitizeHTML from 'sanitize-html';
4
+
5
+ import { pre as respectCRLFPre } from './markdownItPlugins/respectCRLF';
6
+ import ariaLabel, { post as ariaLabelPost, pre as ariaLabelPre } from './markdownItPlugins/ariaLabel';
7
+ import betterLink from './markdownItPlugins/betterLink';
8
+ import iterateLinkDefinitions from './private/iterateLinkDefinitions';
9
+
10
+ const SANITIZE_HTML_OPTIONS = Object.freeze({
11
+ allowedAttributes: {
12
+ a: ['aria-label', 'class', 'href', 'name', 'rel', 'target'],
13
+ button: ['aria-label', 'class', 'type', 'value'],
14
+ img: ['alt', 'class', 'src', 'title'],
15
+ span: ['aria-label']
16
+ },
17
+ allowedSchemes: ['data', 'http', 'https', 'ftp', 'mailto', 'sip', 'tel'],
18
+ allowedTags: [
19
+ 'a',
20
+ 'b',
21
+ 'blockquote',
22
+ 'br',
23
+ 'button',
24
+ 'caption',
25
+ 'code',
26
+ 'del',
27
+ 'div',
28
+ 'em',
29
+ 'h1',
30
+ 'h2',
31
+ 'h3',
32
+ 'h4',
33
+ 'h5',
34
+ 'h6',
35
+ 'hr',
36
+ 'i',
37
+ 'img',
38
+ 'ins',
39
+ 'li',
40
+ 'nl',
41
+ 'ol',
42
+ 'p',
43
+ 'pre',
44
+ 's',
45
+ 'span',
46
+ 'strike',
47
+ 'strong',
48
+ 'table',
49
+ 'tbody',
50
+ 'td',
51
+ 'tfoot',
52
+ 'th',
53
+ 'thead',
54
+ 'tr',
55
+ 'ul'
56
+ ]
57
+ });
58
+
59
+ const MARKDOWN_IT_INIT = Object.freeze({
60
+ breaks: false,
61
+ html: false,
62
+ linkify: true,
63
+ typographer: true,
64
+ xhtmlOut: true
65
+ });
66
+
67
+ type BetterLinkDecoration = Exclude<ReturnType<Parameters<typeof betterLink>[1]>, undefined>;
68
+ type RenderInit = { externalLinkAlt?: string };
69
+
70
+ export default function render(
71
+ markdown: string,
72
+ { markdownRespectCRLF }: Readonly<{ markdownRespectCRLF: boolean }>,
73
+ { externalLinkAlt = '' }: Readonly<RenderInit> = Object.freeze({})
74
+ ): string {
75
+ const linkDefinitions = Array.from(iterateLinkDefinitions(markdown));
76
+
77
+ if (markdownRespectCRLF) {
78
+ markdown = respectCRLFPre(markdown);
79
+ }
80
+
81
+ markdown = ariaLabelPre(markdown);
82
+
83
+ const markdownIt = new MarkdownIt(MARKDOWN_IT_INIT)
84
+ .use(ariaLabel)
85
+ .use(betterLink, (href: string, textContent: string): BetterLinkDecoration | undefined => {
86
+ const decoration: BetterLinkDecoration = {
87
+ rel: 'noopener noreferrer',
88
+ target: '_blank'
89
+ };
90
+
91
+ const ariaLabelSegments: string[] = [textContent];
92
+ const classes: Set<string> = new Set();
93
+ const linkDefinition = linkDefinitions.find(({ url }) => url === href);
94
+ const protocol = onErrorResumeNext(() => new URL(href).protocol);
95
+
96
+ if (linkDefinition) {
97
+ ariaLabelSegments.push(
98
+ linkDefinition.title || onErrorResumeNext(() => new URL(linkDefinition.url).host) || linkDefinition.url
99
+ );
100
+
101
+ linkDefinition.identifier === textContent && classes.add('webchat__render-markdown__pure-identifier');
102
+ }
103
+
104
+ if (protocol === 'cite:') {
105
+ decoration.asButton = true;
106
+
107
+ classes.add('webchat__render-markdown__citation');
108
+ } else if (protocol === 'http:' || protocol === 'https:') {
109
+ decoration.iconAlt = externalLinkAlt;
110
+ decoration.iconClassName = 'webchat__render-markdown__external-link-icon';
111
+
112
+ ariaLabelSegments.push(externalLinkAlt);
113
+ }
114
+
115
+ // The first segment is textContent. Putting textContent is aria-label is useless.
116
+ if (ariaLabelSegments.length > 1) {
117
+ // If "aria-label" is already applied, do not overwrite it.
118
+ decoration.ariaLabel = (value: string) => value || ariaLabelSegments.join(' ');
119
+ }
120
+
121
+ decoration.className = Array.from(classes).join(' ');
122
+
123
+ // By default, Markdown-It will set "title" to the link title in link definition.
124
+
125
+ // However, "title" may be narrated by screen reader:
126
+ // - Edge
127
+ // - <a> will narrate "aria-label" but not "title"
128
+ // - <button> will narrate both "aria-label" and "title"
129
+ // - NVDA
130
+ // - <a> will narrate both "aria-label" and "title"
131
+ // - <button> will narrate both "aria-label" and "title"
132
+
133
+ // Title makes it very difficult to control narrations by the screen reader. Thus, we are disabling it in favor of "aria-label".
134
+ // This will not affect our accessibility compliance but UX. We could use a non-native tooltip or other forms of visual hint.
135
+
136
+ decoration.title = false;
137
+
138
+ return decoration;
139
+ });
140
+
141
+ let html = markdownIt.render(markdown);
142
+
143
+ html = ariaLabelPost(html);
144
+
145
+ return sanitizeHTML(html, SANITIZE_HTML_OPTIONS);
146
+ }
@@ -4,7 +4,7 @@ import { useMemo } from 'react';
4
4
  import createAdaptiveCardsAttachmentForScreenReaderMiddleware from './adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware';
5
5
  import createAdaptiveCardsAttachmentMiddleware from './adaptiveCards/createAdaptiveCardsAttachmentMiddleware';
6
6
  import createAdaptiveCardsStyleSet from './adaptiveCards/Styles/createAdaptiveCardsStyleSet';
7
- import defaultRenderMarkdown from './renderMarkdown';
7
+ import defaultRenderMarkdown from './markdown/renderMarkdown';
8
8
 
9
9
  export default function useComposerProps({
10
10
  attachmentForScreenReaderMiddleware,
@@ -1,6 +0,0 @@
1
- export default function render(markdown: string, { markdownRespectCRLF }: {
2
- markdownRespectCRLF: boolean;
3
- }, { externalLinkAlt }?: {
4
- externalLinkAlt?: string;
5
- }): string;
6
- //# sourceMappingURL=renderMarkdown.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"renderMarkdown.d.ts","sourceRoot":"","sources":["../src/renderMarkdown.ts"],"names":[],"mappings":"AAuEA,MAAM,CAAC,OAAO,UAAU,MAAM,CAC5B,QAAQ,EAAE,MAAM,EAChB,EAAE,mBAAmB,EAAE,EAAE;IAAE,mBAAmB,EAAE,OAAO,CAAA;CAAE,EACzD,EAAE,eAAoB,EAAE,GAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1D,MAAM,CAgER"}
@@ -1,115 +0,0 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
- Object.defineProperty(exports, "__esModule", {
6
- value: true
7
- });
8
- exports.default = render;
9
-
10
- var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
-
12
- var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
-
14
- var _markdownItForInline = _interopRequireDefault(require("markdown-it-for-inline"));
15
-
16
- var _markdownIt = _interopRequireDefault(require("markdown-it"));
17
-
18
- var _markdownItAttrsEs = _interopRequireDefault(require("markdown-it-attrs-es5"));
19
-
20
- var _sanitizeHtml = _interopRequireDefault(require("sanitize-html"));
21
-
22
- /* eslint no-magic-numbers: ["error", { "ignore": [0, 1, 2] }] */
23
- var SANITIZE_HTML_OPTIONS = {
24
- allowedAttributes: {
25
- a: ['aria-label', 'href', 'name', 'rel', 'target', 'title'],
26
- img: ['alt', 'class', 'src']
27
- },
28
- allowedSchemes: ['data', 'http', 'https', 'ftp', 'mailto', 'sip', 'tel'],
29
- allowedTags: ['a', 'b', 'blockquote', 'br', 'caption', 'code', 'del', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'ins', 'li', 'nl', 'ol', 'p', 'pre', 's', 'span', 'strike', 'strong', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'ul']
30
- }; // Put a transparent pixel instead of the "open in new window" icon, so developers can easily modify the icon in CSS.
31
-
32
- var TRANSPARENT_GIF = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; // This is used for parsing Markdown for external links.
33
-
34
- var internalMarkdownIt = new _markdownIt.default();
35
- var MARKDOWN_ATTRS_LEFT_DELIMITER = '⟬'; // Make sure the delimiter is free from any RegExp characters, such as *, ?, etc.
36
- // IE11 does not support "u" flag and Babel could not remove it. We intentionally omitting the "u" flag here.
37
- // eslint-disable-next-line require-unicode-regexp
38
-
39
- var MARKDOWN_ATTRS_LEFT_DELIMITER_PATTERN = new RegExp(MARKDOWN_ATTRS_LEFT_DELIMITER, 'g');
40
- var MARKDOWN_ATTRS_RIGHT_DELIMITER = '⟭'; // Make sure the delimiter is free from any RegExp characters, such as *, ?, etc.
41
- // IE11 does not support "u" flag and Babel could not remove it. We intentionally omitting the "u" flag here.
42
- // eslint-disable-next-line require-unicode-regexp
43
-
44
- var MARKDOWN_ATTRS_RIGHT_DELIMITER_PATTERN = new RegExp(MARKDOWN_ATTRS_RIGHT_DELIMITER, 'g');
45
-
46
- function render(markdown, _ref) {
47
- var markdownRespectCRLF = _ref.markdownRespectCRLF;
48
-
49
- var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
50
- _ref2$externalLinkAlt = _ref2.externalLinkAlt,
51
- externalLinkAlt = _ref2$externalLinkAlt === void 0 ? '' : _ref2$externalLinkAlt;
52
-
53
- if (markdownRespectCRLF) {
54
- markdown = markdown.replace(/\n\r|\r\n/g, function (carriageReturn) {
55
- return carriageReturn === '\n\r' ? '\r\n' : '\n\r';
56
- });
57
- } // Related to #3165.
58
- // We only support attributes "aria-label" and should leave other attributes as-is.
59
- // However, `markdown-it-attrs` remove unrecognized attributes, such as {hello}.
60
- // Before passing to `markdown-it-attrs`, we will convert known attributes from {aria-label="..."} into ⟬aria-label="..."⟭ (using white tortoise shell brackets).
61
- // Then, we ask `markdown-it-attrs` to only process the new brackets, so it should only try to process things that we allowlisted.
62
- // Lastly, we revert tortoise shell brackets back to curly brackets, for unprocessed attributes.
63
-
64
-
65
- markdown = markdown.replace(/\{[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*aria\x2Dlabel()[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*\}/gi, "".concat(MARKDOWN_ATTRS_LEFT_DELIMITER, "aria-label").concat(MARKDOWN_ATTRS_RIGHT_DELIMITER)).replace(/\{[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*aria\x2Dlabel=("(?:(?!")[\s\S])*"|(?:(?![\t-\r \}\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF])[\s\S])*)[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*\}/gi, function (_, valueInsideQuotes) {
66
- return "".concat(MARKDOWN_ATTRS_LEFT_DELIMITER, "aria-label=").concat(valueInsideQuotes).concat(MARKDOWN_ATTRS_RIGHT_DELIMITER);
67
- });
68
- var html = new _markdownIt.default({
69
- breaks: false,
70
- html: false,
71
- linkify: true,
72
- typographer: true,
73
- xhtmlOut: true
74
- }).use(_markdownItAttrsEs.default, {
75
- // `markdown-it-attrs` is added for accessibility and allow bot developers to specify `aria-label`.
76
- // We are allowlisting `aria-label` only as it is allowlisted in `sanitize-html`.
77
- // Other `aria-*` will be sanitized even we allowlisted here.
78
- allowedAttributes: ['aria-label'],
79
- leftDelimiter: MARKDOWN_ATTRS_LEFT_DELIMITER,
80
- rightDelimiter: MARKDOWN_ATTRS_RIGHT_DELIMITER
81
- }).use(_markdownItForInline.default, 'url_new_win', 'link_open', function (tokens, index) {
82
- var token = tokens[+index];
83
- token.attrSet('rel', 'noopener noreferrer');
84
- token.attrSet('target', '_blank');
85
- var linkOpenToken = tokens.find(function (_ref3) {
86
- var type = _ref3.type;
87
- return type === 'link_open';
88
- });
89
-
90
- var _linkOpenToken$attrs$ = linkOpenToken.attrs.find(function (_ref4) {
91
- var _ref5 = (0, _slicedToArray2.default)(_ref4, 1),
92
- name = _ref5[0];
93
-
94
- return name === 'href';
95
- }),
96
- _linkOpenToken$attrs$2 = (0, _slicedToArray2.default)(_linkOpenToken$attrs$, 2),
97
- href = _linkOpenToken$attrs$2[1]; // Adds a new icon if the link is http: or https:.
98
- // Don't add if it's a phone number, etc.
99
-
100
-
101
- if (/^http[s\u017F]?:/i.test(href)) {
102
- externalLinkAlt && token.attrSet('title', externalLinkAlt);
103
- var iconTokens = internalMarkdownIt.parseInline("![".concat(externalLinkAlt, "](").concat(TRANSPARENT_GIF, ")"))[0].children;
104
- iconTokens[0].attrJoin('class', 'webchat__markdown__external-link-icon');
105
- tokens.splice.apply(tokens, [index + 2, 0].concat((0, _toConsumableArray2.default)(iconTokens)));
106
- }
107
- }).render(markdown); // Restore attributes not processed by `markdown-it-attrs`.
108
- // TODO: [P2] #2511 After we fixed our polyfill story, we should use "String.prototype.replaceAll" instead of RegExp for replace all occurrences.
109
-
110
- html = html.replace(MARKDOWN_ATTRS_LEFT_DELIMITER_PATTERN, '{').replace(MARKDOWN_ATTRS_RIGHT_DELIMITER_PATTERN, '}'); // The signature from "sanitize-html" module is not correct.
111
- // @ts-ignore
112
-
113
- return (0, _sanitizeHtml.default)(html, SANITIZE_HTML_OPTIONS);
114
- }
115
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTQU5JVElaRV9IVE1MX09QVElPTlMiLCJhbGxvd2VkQXR0cmlidXRlcyIsImEiLCJpbWciLCJhbGxvd2VkU2NoZW1lcyIsImFsbG93ZWRUYWdzIiwiVFJBTlNQQVJFTlRfR0lGIiwiaW50ZXJuYWxNYXJrZG93bkl0IiwiTWFya2Rvd25JdCIsIk1BUktET1dOX0FUVFJTX0xFRlRfREVMSU1JVEVSIiwiTUFSS0RPV05fQVRUUlNfTEVGVF9ERUxJTUlURVJfUEFUVEVSTiIsIlJlZ0V4cCIsIk1BUktET1dOX0FUVFJTX1JJR0hUX0RFTElNSVRFUiIsIk1BUktET1dOX0FUVFJTX1JJR0hUX0RFTElNSVRFUl9QQVRURVJOIiwicmVuZGVyIiwibWFya2Rvd24iLCJtYXJrZG93blJlc3BlY3RDUkxGIiwiZXh0ZXJuYWxMaW5rQWx0IiwicmVwbGFjZSIsImNhcnJpYWdlUmV0dXJuIiwiXyIsInZhbHVlSW5zaWRlUXVvdGVzIiwiaHRtbCIsImJyZWFrcyIsImxpbmtpZnkiLCJ0eXBvZ3JhcGhlciIsInhodG1sT3V0IiwidXNlIiwibWFya2Rvd25JdEF0dHJzIiwibGVmdERlbGltaXRlciIsInJpZ2h0RGVsaW1pdGVyIiwiaXRlcmF0b3IiLCJ0b2tlbnMiLCJpbmRleCIsInRva2VuIiwiYXR0clNldCIsImxpbmtPcGVuVG9rZW4iLCJmaW5kIiwidHlwZSIsImF0dHJzIiwibmFtZSIsImhyZWYiLCJ0ZXN0IiwiaWNvblRva2VucyIsInBhcnNlSW5saW5lIiwiY2hpbGRyZW4iLCJhdHRySm9pbiIsInNwbGljZSIsInNhbml0aXplSFRNTCJdLCJzb3VyY2VSb290IjoiYnVuZGxlOi8vLyIsInNvdXJjZXMiOlsiLi4vc3JjL3JlbmRlck1hcmtkb3duLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludCBuby1tYWdpYy1udW1iZXJzOiBbXCJlcnJvclwiLCB7IFwiaWdub3JlXCI6IFswLCAxLCAyXSB9XSAqL1xuXG5pbXBvcnQgaXRlcmF0b3IgZnJvbSAnbWFya2Rvd24taXQtZm9yLWlubGluZSc7XG5pbXBvcnQgTWFya2Rvd25JdCBmcm9tICdtYXJrZG93bi1pdCc7XG5pbXBvcnQgbWFya2Rvd25JdEF0dHJzIGZyb20gJ21hcmtkb3duLWl0LWF0dHJzLWVzNSc7XG5pbXBvcnQgc2FuaXRpemVIVE1MIGZyb20gJ3Nhbml0aXplLWh0bWwnO1xuXG5jb25zdCBTQU5JVElaRV9IVE1MX09QVElPTlMgPSB7XG4gIGFsbG93ZWRBdHRyaWJ1dGVzOiB7XG4gICAgYTogWydhcmlhLWxhYmVsJywgJ2hyZWYnLCAnbmFtZScsICdyZWwnLCAndGFyZ2V0JywgJ3RpdGxlJ10sXG4gICAgaW1nOiBbJ2FsdCcsICdjbGFzcycsICdzcmMnXVxuICB9LFxuICBhbGxvd2VkU2NoZW1lczogWydkYXRhJywgJ2h0dHAnLCAnaHR0cHMnLCAnZnRwJywgJ21haWx0bycsICdzaXAnLCAndGVsJ10sXG4gIGFsbG93ZWRUYWdzOiBbXG4gICAgJ2EnLFxuICAgICdiJyxcbiAgICAnYmxvY2txdW90ZScsXG4gICAgJ2JyJyxcbiAgICAnY2FwdGlvbicsXG4gICAgJ2NvZGUnLFxuICAgICdkZWwnLFxuICAgICdkaXYnLFxuICAgICdlbScsXG4gICAgJ2gxJyxcbiAgICAnaDInLFxuICAgICdoMycsXG4gICAgJ2g0JyxcbiAgICAnaDUnLFxuICAgICdoNicsXG4gICAgJ2hyJyxcbiAgICAnaScsXG4gICAgJ2ltZycsXG4gICAgJ2lucycsXG4gICAgJ2xpJyxcbiAgICAnbmwnLFxuICAgICdvbCcsXG4gICAgJ3AnLFxuICAgICdwcmUnLFxuICAgICdzJyxcbiAgICAnc3BhbicsXG4gICAgJ3N0cmlrZScsXG4gICAgJ3N0cm9uZycsXG4gICAgJ3RhYmxlJyxcbiAgICAndGJvZHknLFxuICAgICd0ZCcsXG4gICAgJ3Rmb290JyxcbiAgICAndGgnLFxuICAgICd0aGVhZCcsXG4gICAgJ3RyJyxcbiAgICAndWwnXG4gIF1cbn07XG5cbi8vIFB1dCBhIHRyYW5zcGFyZW50IHBpeGVsIGluc3RlYWQgb2YgdGhlIFwib3BlbiBpbiBuZXcgd2luZG93XCIgaWNvbiwgc28gZGV2ZWxvcGVycyBjYW4gZWFzaWx5IG1vZGlmeSB0aGUgaWNvbiBpbiBDU1MuXG5jb25zdCBUUkFOU1BBUkVOVF9HSUYgPSAnZGF0YTppbWFnZS9naWY7YmFzZTY0LFIwbEdPRGxoQVFBQkFJQUFBQUFBQVAvLy95SDVCQUVBQUFBQUxBQUFBQUFCQUFFQUFBSUJSQUE3JztcblxuLy8gVGhpcyBpcyB1c2VkIGZvciBwYXJzaW5nIE1hcmtkb3duIGZvciBleHRlcm5hbCBsaW5rcy5cbmNvbnN0IGludGVybmFsTWFya2Rvd25JdCA9IG5ldyBNYXJrZG93bkl0KCk7XG5cbmNvbnN0IE1BUktET1dOX0FUVFJTX0xFRlRfREVMSU1JVEVSID0gJ+KfrCc7XG4vLyBNYWtlIHN1cmUgdGhlIGRlbGltaXRlciBpcyBmcmVlIGZyb20gYW55IFJlZ0V4cCBjaGFyYWN0ZXJzLCBzdWNoIGFzICosID8sIGV0Yy5cbi8vIElFMTEgZG9lcyBub3Qgc3VwcG9ydCBcInVcIiBmbGFnIGFuZCBCYWJlbCBjb3VsZCBub3QgcmVtb3ZlIGl0LiBXZSBpbnRlbnRpb25hbGx5IG9taXR0aW5nIHRoZSBcInVcIiBmbGFnIGhlcmUuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcmVxdWlyZS11bmljb2RlLXJlZ2V4cFxuY29uc3QgTUFSS0RPV05fQVRUUlNfTEVGVF9ERUxJTUlURVJfUEFUVEVSTiA9IG5ldyBSZWdFeHAoTUFSS0RPV05fQVRUUlNfTEVGVF9ERUxJTUlURVIsICdnJyk7XG5cbmNvbnN0IE1BUktET1dOX0FUVFJTX1JJR0hUX0RFTElNSVRFUiA9ICfin60nO1xuLy8gTWFrZSBzdXJlIHRoZSBkZWxpbWl0ZXIgaXMgZnJlZSBmcm9tIGFueSBSZWdFeHAgY2hhcmFjdGVycywgc3VjaCBhcyAqLCA/LCBldGMuXG4vLyBJRTExIGRvZXMgbm90IHN1cHBvcnQgXCJ1XCIgZmxhZyBhbmQgQmFiZWwgY291bGQgbm90IHJlbW92ZSBpdC4gV2UgaW50ZW50aW9uYWxseSBvbWl0dGluZyB0aGUgXCJ1XCIgZmxhZyBoZXJlLlxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlcXVpcmUtdW5pY29kZS1yZWdleHBcbmNvbnN0IE1BUktET1dOX0FUVFJTX1JJR0hUX0RFTElNSVRFUl9QQVRURVJOID0gbmV3IFJlZ0V4cChNQVJLRE9XTl9BVFRSU19SSUdIVF9ERUxJTUlURVIsICdnJyk7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJlbmRlcihcbiAgbWFya2Rvd246IHN0cmluZyxcbiAgeyBtYXJrZG93blJlc3BlY3RDUkxGIH06IHsgbWFya2Rvd25SZXNwZWN0Q1JMRjogYm9vbGVhbiB9LFxuICB7IGV4dGVybmFsTGlua0FsdCA9ICcnIH06IHsgZXh0ZXJuYWxMaW5rQWx0Pzogc3RyaW5nIH0gPSB7fVxuKTogc3RyaW5nIHtcbiAgaWYgKG1hcmtkb3duUmVzcGVjdENSTEYpIHtcbiAgICBtYXJrZG93biA9IG1hcmtkb3duLnJlcGxhY2UoL1xcblxccnxcXHJcXG4vZ3UsIGNhcnJpYWdlUmV0dXJuID0+IChjYXJyaWFnZVJldHVybiA9PT0gJ1xcblxccicgPyAnXFxyXFxuJyA6ICdcXG5cXHInKSk7XG4gIH1cblxuICAvLyBSZWxhdGVkIHRvICMzMTY1LlxuICAvLyBXZSBvbmx5IHN1cHBvcnQgYXR0cmlidXRlcyBcImFyaWEtbGFiZWxcIiBhbmQgc2hvdWxkIGxlYXZlIG90aGVyIGF0dHJpYnV0ZXMgYXMtaXMuXG4gIC8vIEhvd2V2ZXIsIGBtYXJrZG93bi1pdC1hdHRyc2AgcmVtb3ZlIHVucmVjb2duaXplZCBhdHRyaWJ1dGVzLCBzdWNoIGFzIHtoZWxsb30uXG4gIC8vIEJlZm9yZSBwYXNzaW5nIHRvIGBtYXJrZG93bi1pdC1hdHRyc2AsIHdlIHdpbGwgY29udmVydCBrbm93biBhdHRyaWJ1dGVzIGZyb20ge2FyaWEtbGFiZWw9XCIuLi5cIn0gaW50byDin6xhcmlhLWxhYmVsPVwiLi4uXCLin60gKHVzaW5nIHdoaXRlIHRvcnRvaXNlIHNoZWxsIGJyYWNrZXRzKS5cbiAgLy8gVGhlbiwgd2UgYXNrIGBtYXJrZG93bi1pdC1hdHRyc2AgdG8gb25seSBwcm9jZXNzIHRoZSBuZXcgYnJhY2tldHMsIHNvIGl0IHNob3VsZCBvbmx5IHRyeSB0byBwcm9jZXNzIHRoaW5ncyB0aGF0IHdlIGFsbG93bGlzdGVkLlxuICAvLyBMYXN0bHksIHdlIHJldmVydCB0b3J0b2lzZSBzaGVsbCBicmFja2V0cyBiYWNrIHRvIGN1cmx5IGJyYWNrZXRzLCBmb3IgdW5wcm9jZXNzZWQgYXR0cmlidXRlcy5cbiAgbWFya2Rvd24gPSBtYXJrZG93blxuICAgIC5yZXBsYWNlKC9cXHtcXHMqYXJpYS1sYWJlbCgpXFxzKlxcfS9naXUsIGAke01BUktET1dOX0FUVFJTX0xFRlRfREVMSU1JVEVSfWFyaWEtbGFiZWwke01BUktET1dOX0FUVFJTX1JJR0hUX0RFTElNSVRFUn1gKVxuICAgIC5yZXBsYWNlKFxuICAgICAgL1xce1xccyphcmlhLWxhYmVsPShcIlteXCJdKlwifFteXFxzfV0qKVxccypcXH0vZ2l1LFxuICAgICAgKF8sIHZhbHVlSW5zaWRlUXVvdGVzKSA9PlxuICAgICAgICBgJHtNQVJLRE9XTl9BVFRSU19MRUZUX0RFTElNSVRFUn1hcmlhLWxhYmVsPSR7dmFsdWVJbnNpZGVRdW90ZXN9JHtNQVJLRE9XTl9BVFRSU19SSUdIVF9ERUxJTUlURVJ9YFxuICAgICk7XG5cbiAgbGV0IGh0bWwgPSBuZXcgTWFya2Rvd25JdCh7XG4gICAgYnJlYWtzOiBmYWxzZSxcbiAgICBodG1sOiBmYWxzZSxcbiAgICBsaW5raWZ5OiB0cnVlLFxuICAgIHR5cG9ncmFwaGVyOiB0cnVlLFxuICAgIHhodG1sT3V0OiB0cnVlXG4gIH0pXG4gICAgLnVzZShtYXJrZG93bkl0QXR0cnMsIHtcbiAgICAgIC8vIGBtYXJrZG93bi1pdC1hdHRyc2AgaXMgYWRkZWQgZm9yIGFjY2Vzc2liaWxpdHkgYW5kIGFsbG93IGJvdCBkZXZlbG9wZXJzIHRvIHNwZWNpZnkgYGFyaWEtbGFiZWxgLlxuICAgICAgLy8gV2UgYXJlIGFsbG93bGlzdGluZyBgYXJpYS1sYWJlbGAgb25seSBhcyBpdCBpcyBhbGxvd2xpc3RlZCBpbiBgc2FuaXRpemUtaHRtbGAuXG4gICAgICAvLyBPdGhlciBgYXJpYS0qYCB3aWxsIGJlIHNhbml0aXplZCBldmVuIHdlIGFsbG93bGlzdGVkIGhlcmUuXG4gICAgICBhbGxvd2VkQXR0cmlidXRlczogWydhcmlhLWxhYmVsJ10sXG4gICAgICBsZWZ0RGVsaW1pdGVyOiBNQVJLRE9XTl9BVFRSU19MRUZUX0RFTElNSVRFUixcbiAgICAgIHJpZ2h0RGVsaW1pdGVyOiBNQVJLRE9XTl9BVFRSU19SSUdIVF9ERUxJTUlURVJcbiAgICB9KVxuICAgIC51c2UoaXRlcmF0b3IsICd1cmxfbmV3X3dpbicsICdsaW5rX29wZW4nLCAodG9rZW5zLCBpbmRleCkgPT4ge1xuICAgICAgY29uc3QgdG9rZW4gPSB0b2tlbnNbK2luZGV4XTtcblxuICAgICAgdG9rZW4uYXR0clNldCgncmVsJywgJ25vb3BlbmVyIG5vcmVmZXJyZXInKTtcbiAgICAgIHRva2VuLmF0dHJTZXQoJ3RhcmdldCcsICdfYmxhbmsnKTtcblxuICAgICAgY29uc3QgbGlua09wZW5Ub2tlbiA9IHRva2Vucy5maW5kKCh7IHR5cGUgfSkgPT4gdHlwZSA9PT0gJ2xpbmtfb3BlbicpO1xuICAgICAgY29uc3QgWywgaHJlZl0gPSBsaW5rT3BlblRva2VuLmF0dHJzLmZpbmQoKFtuYW1lXSkgPT4gbmFtZSA9PT0gJ2hyZWYnKTtcblxuICAgICAgLy8gQWRkcyBhIG5ldyBpY29uIGlmIHRoZSBsaW5rIGlzIGh0dHA6IG9yIGh0dHBzOi5cbiAgICAgIC8vIERvbid0IGFkZCBpZiBpdCdzIGEgcGhvbmUgbnVtYmVyLCBldGMuXG4gICAgICBpZiAoL15odHRwcz86L2l1LnRlc3QoaHJlZikpIHtcbiAgICAgICAgZXh0ZXJuYWxMaW5rQWx0ICYmIHRva2VuLmF0dHJTZXQoJ3RpdGxlJywgZXh0ZXJuYWxMaW5rQWx0KTtcblxuICAgICAgICBjb25zdCBpY29uVG9rZW5zID0gaW50ZXJuYWxNYXJrZG93bkl0LnBhcnNlSW5saW5lKGAhWyR7ZXh0ZXJuYWxMaW5rQWx0fV0oJHtUUkFOU1BBUkVOVF9HSUZ9KWApWzBdLmNoaWxkcmVuO1xuXG4gICAgICAgIGljb25Ub2tlbnNbMF0uYXR0ckpvaW4oJ2NsYXNzJywgJ3dlYmNoYXRfX21hcmtkb3duX19leHRlcm5hbC1saW5rLWljb24nKTtcblxuICAgICAgICB0b2tlbnMuc3BsaWNlKGluZGV4ICsgMiwgMCwgLi4uaWNvblRva2Vucyk7XG4gICAgICB9XG4gICAgfSlcbiAgICAucmVuZGVyKG1hcmtkb3duKTtcblxuICAvLyBSZXN0b3JlIGF0dHJpYnV0ZXMgbm90IHByb2Nlc3NlZCBieSBgbWFya2Rvd24taXQtYXR0cnNgLlxuICAvLyBUT0RPOiBbUDJdICMyNTExIEFmdGVyIHdlIGZpeGVkIG91ciBwb2x5ZmlsbCBzdG9yeSwgd2Ugc2hvdWxkIHVzZSBcIlN0cmluZy5wcm90b3R5cGUucmVwbGFjZUFsbFwiIGluc3RlYWQgb2YgUmVnRXhwIGZvciByZXBsYWNlIGFsbCBvY2N1cnJlbmNlcy5cbiAgaHRtbCA9IGh0bWwucmVwbGFjZShNQVJLRE9XTl9BVFRSU19MRUZUX0RFTElNSVRFUl9QQVRURVJOLCAneycpLnJlcGxhY2UoTUFSS0RPV05fQVRUUlNfUklHSFRfREVMSU1JVEVSX1BBVFRFUk4sICd9Jyk7XG5cbiAgLy8gVGhlIHNpZ25hdHVyZSBmcm9tIFwic2FuaXRpemUtaHRtbFwiIG1vZHVsZSBpcyBub3QgY29ycmVjdC5cbiAgLy8gQHRzLWlnbm9yZVxuICByZXR1cm4gc2FuaXRpemVIVE1MKGh0bWwsIFNBTklUSVpFX0hUTUxfT1BUSU9OUyk7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7QUFFQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFMQTtBQU9BLElBQU1BLHFCQUFxQixHQUFHO0VBQzVCQyxpQkFBaUIsRUFBRTtJQUNqQkMsQ0FBQyxFQUFFLENBQUMsWUFBRCxFQUFlLE1BQWYsRUFBdUIs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
@@ -1,140 +0,0 @@
1
- /* eslint no-magic-numbers: ["error", { "ignore": [0, 1, 2] }] */
2
-
3
- import iterator from 'markdown-it-for-inline';
4
- import MarkdownIt from 'markdown-it';
5
- import markdownItAttrs from 'markdown-it-attrs-es5';
6
- import sanitizeHTML from 'sanitize-html';
7
-
8
- const SANITIZE_HTML_OPTIONS = {
9
- allowedAttributes: {
10
- a: ['aria-label', 'href', 'name', 'rel', 'target', 'title'],
11
- img: ['alt', 'class', 'src']
12
- },
13
- allowedSchemes: ['data', 'http', 'https', 'ftp', 'mailto', 'sip', 'tel'],
14
- allowedTags: [
15
- 'a',
16
- 'b',
17
- 'blockquote',
18
- 'br',
19
- 'caption',
20
- 'code',
21
- 'del',
22
- 'div',
23
- 'em',
24
- 'h1',
25
- 'h2',
26
- 'h3',
27
- 'h4',
28
- 'h5',
29
- 'h6',
30
- 'hr',
31
- 'i',
32
- 'img',
33
- 'ins',
34
- 'li',
35
- 'nl',
36
- 'ol',
37
- 'p',
38
- 'pre',
39
- 's',
40
- 'span',
41
- 'strike',
42
- 'strong',
43
- 'table',
44
- 'tbody',
45
- 'td',
46
- 'tfoot',
47
- 'th',
48
- 'thead',
49
- 'tr',
50
- 'ul'
51
- ]
52
- };
53
-
54
- // Put a transparent pixel instead of the "open in new window" icon, so developers can easily modify the icon in CSS.
55
- const TRANSPARENT_GIF = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
56
-
57
- // This is used for parsing Markdown for external links.
58
- const internalMarkdownIt = new MarkdownIt();
59
-
60
- const MARKDOWN_ATTRS_LEFT_DELIMITER = '⟬';
61
- // Make sure the delimiter is free from any RegExp characters, such as *, ?, etc.
62
- // IE11 does not support "u" flag and Babel could not remove it. We intentionally omitting the "u" flag here.
63
- // eslint-disable-next-line require-unicode-regexp
64
- const MARKDOWN_ATTRS_LEFT_DELIMITER_PATTERN = new RegExp(MARKDOWN_ATTRS_LEFT_DELIMITER, 'g');
65
-
66
- const MARKDOWN_ATTRS_RIGHT_DELIMITER = '⟭';
67
- // Make sure the delimiter is free from any RegExp characters, such as *, ?, etc.
68
- // IE11 does not support "u" flag and Babel could not remove it. We intentionally omitting the "u" flag here.
69
- // eslint-disable-next-line require-unicode-regexp
70
- const MARKDOWN_ATTRS_RIGHT_DELIMITER_PATTERN = new RegExp(MARKDOWN_ATTRS_RIGHT_DELIMITER, 'g');
71
-
72
- export default function render(
73
- markdown: string,
74
- { markdownRespectCRLF }: { markdownRespectCRLF: boolean },
75
- { externalLinkAlt = '' }: { externalLinkAlt?: string } = {}
76
- ): string {
77
- if (markdownRespectCRLF) {
78
- markdown = markdown.replace(/\n\r|\r\n/gu, carriageReturn => (carriageReturn === '\n\r' ? '\r\n' : '\n\r'));
79
- }
80
-
81
- // Related to #3165.
82
- // We only support attributes "aria-label" and should leave other attributes as-is.
83
- // However, `markdown-it-attrs` remove unrecognized attributes, such as {hello}.
84
- // Before passing to `markdown-it-attrs`, we will convert known attributes from {aria-label="..."} into ⟬aria-label="..."⟭ (using white tortoise shell brackets).
85
- // Then, we ask `markdown-it-attrs` to only process the new brackets, so it should only try to process things that we allowlisted.
86
- // Lastly, we revert tortoise shell brackets back to curly brackets, for unprocessed attributes.
87
- markdown = markdown
88
- .replace(/\{\s*aria-label()\s*\}/giu, `${MARKDOWN_ATTRS_LEFT_DELIMITER}aria-label${MARKDOWN_ATTRS_RIGHT_DELIMITER}`)
89
- .replace(
90
- /\{\s*aria-label=("[^"]*"|[^\s}]*)\s*\}/giu,
91
- (_, valueInsideQuotes) =>
92
- `${MARKDOWN_ATTRS_LEFT_DELIMITER}aria-label=${valueInsideQuotes}${MARKDOWN_ATTRS_RIGHT_DELIMITER}`
93
- );
94
-
95
- let html = new MarkdownIt({
96
- breaks: false,
97
- html: false,
98
- linkify: true,
99
- typographer: true,
100
- xhtmlOut: true
101
- })
102
- .use(markdownItAttrs, {
103
- // `markdown-it-attrs` is added for accessibility and allow bot developers to specify `aria-label`.
104
- // We are allowlisting `aria-label` only as it is allowlisted in `sanitize-html`.
105
- // Other `aria-*` will be sanitized even we allowlisted here.
106
- allowedAttributes: ['aria-label'],
107
- leftDelimiter: MARKDOWN_ATTRS_LEFT_DELIMITER,
108
- rightDelimiter: MARKDOWN_ATTRS_RIGHT_DELIMITER
109
- })
110
- .use(iterator, 'url_new_win', 'link_open', (tokens, index) => {
111
- const token = tokens[+index];
112
-
113
- token.attrSet('rel', 'noopener noreferrer');
114
- token.attrSet('target', '_blank');
115
-
116
- const linkOpenToken = tokens.find(({ type }) => type === 'link_open');
117
- const [, href] = linkOpenToken.attrs.find(([name]) => name === 'href');
118
-
119
- // Adds a new icon if the link is http: or https:.
120
- // Don't add if it's a phone number, etc.
121
- if (/^https?:/iu.test(href)) {
122
- externalLinkAlt && token.attrSet('title', externalLinkAlt);
123
-
124
- const iconTokens = internalMarkdownIt.parseInline(`![${externalLinkAlt}](${TRANSPARENT_GIF})`)[0].children;
125
-
126
- iconTokens[0].attrJoin('class', 'webchat__markdown__external-link-icon');
127
-
128
- tokens.splice(index + 2, 0, ...iconTokens);
129
- }
130
- })
131
- .render(markdown);
132
-
133
- // Restore attributes not processed by `markdown-it-attrs`.
134
- // TODO: [P2] #2511 After we fixed our polyfill story, we should use "String.prototype.replaceAll" instead of RegExp for replace all occurrences.
135
- html = html.replace(MARKDOWN_ATTRS_LEFT_DELIMITER_PATTERN, '{').replace(MARKDOWN_ATTRS_RIGHT_DELIMITER_PATTERN, '}');
136
-
137
- // The signature from "sanitize-html" module is not correct.
138
- // @ts-ignore
139
- return sanitizeHTML(html, SANITIZE_HTML_OPTIONS);
140
- }