@memberjunction/ng-markdown 6.2.0-edge.0 → 6.2.0-edge.1
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.
- package/dist/__tests__/web-dom-utils.test.js +36 -36
- package/dist/__tests__/web-dom-utils.test.js.map +1 -1
- package/dist/lib/components/markdown.component.d.ts +133 -21
- package/dist/lib/components/markdown.component.d.ts.map +1 -1
- package/dist/lib/components/markdown.component.js +304 -60
- package/dist/lib/components/markdown.component.js.map +1 -1
- package/dist/lib/extensions/code-copy.extension.d.ts +4 -0
- package/dist/lib/extensions/code-copy.extension.d.ts.map +1 -1
- package/dist/lib/extensions/code-copy.extension.js +10 -2
- package/dist/lib/extensions/code-copy.extension.js.map +1 -1
- package/dist/lib/extensions/collapsible-headings.extension.d.ts +8 -0
- package/dist/lib/extensions/collapsible-headings.extension.d.ts.map +1 -1
- package/dist/lib/extensions/collapsible-headings.extension.js +20 -4
- package/dist/lib/extensions/collapsible-headings.extension.js.map +1 -1
- package/dist/lib/extensions/svg-renderer.extension.d.ts +2 -0
- package/dist/lib/extensions/svg-renderer.extension.d.ts.map +1 -1
- package/dist/lib/extensions/svg-renderer.extension.js +5 -1
- package/dist/lib/extensions/svg-renderer.extension.js.map +1 -1
- package/dist/lib/services/markdown.service.d.ts +26 -0
- package/dist/lib/services/markdown.service.d.ts.map +1 -1
- package/dist/lib/services/markdown.service.js +68 -16
- package/dist/lib/services/markdown.service.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { SanitizeSvgContent } from '../lib/extensions/svg-renderer.extension';
|
|
3
|
+
import { AddCopyButtonsToCodeBlocks, RemoveCopyButtonsFromCodeBlocks, } from '../lib/extensions/code-copy.extension';
|
|
4
|
+
import { ToggleCollapsibleSection, ExpandAllSections, CollapseAllSections, ExpandToHeading, } from '../lib/extensions/collapsible-headings.extension';
|
|
5
5
|
function container(html) {
|
|
6
6
|
const el = document.createElement('div');
|
|
7
7
|
el.innerHTML = html;
|
|
@@ -15,50 +15,50 @@ afterEach(() => {
|
|
|
15
15
|
describe('sanitizeSvgContent', () => {
|
|
16
16
|
it('removes <script> elements', () => {
|
|
17
17
|
const el = container('<svg><script>alert(1)</script><circle/></svg>');
|
|
18
|
-
|
|
18
|
+
SanitizeSvgContent(el);
|
|
19
19
|
expect(el.querySelector('script')).toBeNull();
|
|
20
20
|
expect(el.querySelector('circle')).not.toBeNull();
|
|
21
21
|
});
|
|
22
22
|
it('strips inline event-handler attributes', () => {
|
|
23
23
|
const el = container('<svg><rect onclick="hack()" onload="x()"/></svg>');
|
|
24
|
-
|
|
24
|
+
SanitizeSvgContent(el);
|
|
25
25
|
const rect = el.querySelector('rect');
|
|
26
26
|
expect(rect?.hasAttribute('onclick')).toBe(false);
|
|
27
27
|
expect(rect?.hasAttribute('onload')).toBe(false);
|
|
28
28
|
});
|
|
29
29
|
it('removes javascript: hrefs', () => {
|
|
30
30
|
const el = container('<svg><a href="javascript:evil()">x</a></svg>');
|
|
31
|
-
|
|
31
|
+
SanitizeSvgContent(el);
|
|
32
32
|
expect(el.querySelector('a')?.hasAttribute('href')).toBe(false);
|
|
33
33
|
});
|
|
34
34
|
it('removes foreignObject and external <use>', () => {
|
|
35
35
|
const el = container('<svg><foreignObject></foreignObject><use href="https://evil.test/x"/></svg>');
|
|
36
|
-
|
|
36
|
+
SanitizeSvgContent(el);
|
|
37
37
|
expect(el.querySelector('foreignObject')).toBeNull();
|
|
38
38
|
expect(el.querySelector('use')).toBeNull();
|
|
39
39
|
});
|
|
40
40
|
it('strips javascript: from xlink:href', () => {
|
|
41
41
|
const el = container('<svg><a xlink:href="javascript:evil()">x</a></svg>');
|
|
42
|
-
|
|
42
|
+
SanitizeSvgContent(el);
|
|
43
43
|
expect(el.querySelector('a')?.hasAttribute('xlink:href')).toBe(false);
|
|
44
44
|
});
|
|
45
45
|
it('keeps a local <use> reference (only external ones are removed)', () => {
|
|
46
46
|
const el = container('<svg><use href="#icon"/></svg>');
|
|
47
|
-
|
|
47
|
+
SanitizeSvgContent(el);
|
|
48
48
|
expect(el.querySelector('use')).not.toBeNull();
|
|
49
49
|
});
|
|
50
50
|
it('is a no-op on a container with no dangerous content', () => {
|
|
51
51
|
const el = container('<svg><circle cx="1" cy="1" r="1"/></svg>');
|
|
52
|
-
|
|
52
|
+
SanitizeSvgContent(el);
|
|
53
53
|
expect(el.querySelector('circle')).not.toBeNull();
|
|
54
54
|
});
|
|
55
55
|
it('does not throw on an empty container', () => {
|
|
56
56
|
const el = container('');
|
|
57
|
-
expect(() =>
|
|
57
|
+
expect(() => SanitizeSvgContent(el)).not.toThrow();
|
|
58
58
|
});
|
|
59
59
|
it('removes handler attributes that are not on a fixed list', () => {
|
|
60
60
|
const el = container('<svg><rect onbegin="a()" onpointerrawupdate="b()" ONANIMATIONEND="c()" OnMouseEnter="d()"/></svg>');
|
|
61
|
-
|
|
61
|
+
SanitizeSvgContent(el);
|
|
62
62
|
expect(el.querySelector('rect')?.attributes.length).toBe(0);
|
|
63
63
|
});
|
|
64
64
|
it.each([
|
|
@@ -73,7 +73,7 @@ describe('sanitizeSvgContent', () => {
|
|
|
73
73
|
])('removes href using %s', (_label, href) => {
|
|
74
74
|
const el = container('<svg><a>x</a></svg>');
|
|
75
75
|
el.querySelector('a').setAttribute('href', href);
|
|
76
|
-
|
|
76
|
+
SanitizeSvgContent(el);
|
|
77
77
|
expect(el.querySelector('a')?.hasAttribute('href')).toBe(false);
|
|
78
78
|
});
|
|
79
79
|
it('removes script URLs from xlink:href, src, action and formaction', () => {
|
|
@@ -82,7 +82,7 @@ describe('sanitizeSvgContent', () => {
|
|
|
82
82
|
el.querySelector('image').setAttribute('src', 'javascript:alert(1)');
|
|
83
83
|
el.querySelector('foo').setAttribute('action', 'javascript:alert(1)');
|
|
84
84
|
el.querySelector('foo').setAttribute('formaction', 'vbscript:alert(1)');
|
|
85
|
-
|
|
85
|
+
SanitizeSvgContent(el);
|
|
86
86
|
expect(el.querySelector('a')?.hasAttribute('xlink:href')).toBe(false);
|
|
87
87
|
expect(el.querySelector('image')?.hasAttribute('src')).toBe(false);
|
|
88
88
|
expect(el.querySelector('foo')?.attributes.length).toBe(0);
|
|
@@ -91,13 +91,13 @@ describe('sanitizeSvgContent', () => {
|
|
|
91
91
|
const el = container('<svg><image id="png"/><image id="svg"/></svg>');
|
|
92
92
|
el.querySelector('#png').setAttribute('href', 'data:image/png;base64,iVBORw0KGgo=');
|
|
93
93
|
el.querySelector('#svg').setAttribute('href', 'data:image/svg+xml;base64,PHN2Zy8+');
|
|
94
|
-
|
|
94
|
+
SanitizeSvgContent(el);
|
|
95
95
|
expect(el.querySelector('#png')?.hasAttribute('href')).toBe(true);
|
|
96
96
|
expect(el.querySelector('#svg')?.hasAttribute('href')).toBe(false);
|
|
97
97
|
});
|
|
98
98
|
it('keeps safe hrefs and non-URL attributes untouched', () => {
|
|
99
99
|
const el = container('<svg><a href="https://example.test/" class="k" data-x="1">x</a></svg>');
|
|
100
|
-
|
|
100
|
+
SanitizeSvgContent(el);
|
|
101
101
|
const a = el.querySelector('a');
|
|
102
102
|
expect(a.getAttribute('href')).toBe('https://example.test/');
|
|
103
103
|
expect(a.getAttribute('class')).toBe('k');
|
|
@@ -110,56 +110,56 @@ describe('sanitizeSvgContent', () => {
|
|
|
110
110
|
])('removes a <use> whose reference is not same-document (%s)', (_label, href) => {
|
|
111
111
|
const el = container('<svg><use/></svg>');
|
|
112
112
|
el.querySelector('use').setAttribute('href', href);
|
|
113
|
-
|
|
113
|
+
SanitizeSvgContent(el);
|
|
114
114
|
expect(el.querySelector('use')).toBeNull();
|
|
115
115
|
});
|
|
116
116
|
});
|
|
117
117
|
describe('addCopyButtonsToCodeBlocks / removeCopyButtonsFromCodeBlocks', () => {
|
|
118
118
|
it('adds a copy button with a formatted language label', () => {
|
|
119
119
|
const el = container('<pre><code class="language-ts">const x = 1;</code></pre>');
|
|
120
|
-
|
|
120
|
+
AddCopyButtonsToCodeBlocks(el);
|
|
121
121
|
expect(el.querySelector('.code-copy-btn')).not.toBeNull();
|
|
122
122
|
expect(el.querySelector('.code-language-label')?.textContent).toBe('TypeScript');
|
|
123
123
|
});
|
|
124
124
|
it('does not double-add a toolbar', () => {
|
|
125
125
|
const el = container('<pre><code class="language-js">a</code></pre>');
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
AddCopyButtonsToCodeBlocks(el);
|
|
127
|
+
AddCopyButtonsToCodeBlocks(el);
|
|
128
128
|
expect(el.querySelectorAll('.code-toolbar')).toHaveLength(1);
|
|
129
129
|
});
|
|
130
130
|
it('removes toolbars on cleanup', () => {
|
|
131
131
|
const el = container('<pre><code class="language-js">a</code></pre>');
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
AddCopyButtonsToCodeBlocks(el);
|
|
133
|
+
RemoveCopyButtonsFromCodeBlocks(el);
|
|
134
134
|
expect(el.querySelector('.code-toolbar')).toBeNull();
|
|
135
135
|
});
|
|
136
136
|
it('adds a copy button but no language label for a code block without a language class', () => {
|
|
137
137
|
const el = container('<pre><code>plain code</code></pre>');
|
|
138
|
-
|
|
138
|
+
AddCopyButtonsToCodeBlocks(el);
|
|
139
139
|
expect(el.querySelector('.code-copy-btn')).not.toBeNull();
|
|
140
140
|
expect(el.querySelector('.code-language-label')).toBeNull();
|
|
141
141
|
});
|
|
142
142
|
it('omits the language label when showLanguageLabel is false', () => {
|
|
143
143
|
const el = container('<pre><code class="language-ts">a</code></pre>');
|
|
144
|
-
|
|
144
|
+
AddCopyButtonsToCodeBlocks(el, { showLanguageLabel: false });
|
|
145
145
|
expect(el.querySelector('.code-copy-btn')).not.toBeNull();
|
|
146
146
|
expect(el.querySelector('.code-language-label')).toBeNull();
|
|
147
147
|
});
|
|
148
148
|
it('honors a custom button/toolbar class and can remove it again', () => {
|
|
149
149
|
const el = container('<pre><code class="language-js">a</code></pre>');
|
|
150
|
-
|
|
150
|
+
AddCopyButtonsToCodeBlocks(el, { toolbarClass: 'my-bar', buttonClass: 'my-btn' });
|
|
151
151
|
expect(el.querySelector('.my-btn')).not.toBeNull();
|
|
152
|
-
|
|
152
|
+
RemoveCopyButtonsFromCodeBlocks(el, 'my-bar');
|
|
153
153
|
expect(el.querySelector('.my-bar')).toBeNull();
|
|
154
154
|
});
|
|
155
155
|
it('is a no-op on a container with no code blocks', () => {
|
|
156
156
|
const el = container('<p>no code here</p>');
|
|
157
|
-
expect(() =>
|
|
157
|
+
expect(() => AddCopyButtonsToCodeBlocks(el)).not.toThrow();
|
|
158
158
|
expect(el.querySelector('.code-toolbar')).toBeNull();
|
|
159
159
|
});
|
|
160
160
|
it('removeCopyButtonsFromCodeBlocks is safe when there are no toolbars', () => {
|
|
161
161
|
const el = container('<pre><code>a</code></pre>');
|
|
162
|
-
expect(() =>
|
|
162
|
+
expect(() => RemoveCopyButtonsFromCodeBlocks(el)).not.toThrow();
|
|
163
163
|
});
|
|
164
164
|
});
|
|
165
165
|
describe('collapsible DOM helpers', () => {
|
|
@@ -177,41 +177,41 @@ describe('collapsible DOM helpers', () => {
|
|
|
177
177
|
});
|
|
178
178
|
it('toggleCollapsibleSection flips the collapsed class and aria-expanded', () => {
|
|
179
179
|
const parent = el.querySelector('.collapsible-section');
|
|
180
|
-
|
|
180
|
+
ToggleCollapsibleSection(parent);
|
|
181
181
|
expect(parent.classList.contains('collapsed')).toBe(true);
|
|
182
182
|
expect(parent.querySelector('.collapsible-toggle')?.getAttribute('aria-expanded')).toBe('false');
|
|
183
183
|
});
|
|
184
184
|
it('expandAllSections clears every collapsed section', () => {
|
|
185
|
-
|
|
185
|
+
ExpandAllSections(el);
|
|
186
186
|
expect(el.querySelectorAll('.collapsible-section.collapsed')).toHaveLength(0);
|
|
187
187
|
});
|
|
188
188
|
it('collapseAllSections collapses every section', () => {
|
|
189
|
-
|
|
189
|
+
CollapseAllSections(el);
|
|
190
190
|
expect(el.querySelectorAll('.collapsible-section:not(.collapsed)')).toHaveLength(0);
|
|
191
191
|
});
|
|
192
192
|
it('expandToHeading reveals ancestor sections of a heading', () => {
|
|
193
193
|
// child section starts collapsed; expanding to #child should clear it
|
|
194
|
-
|
|
194
|
+
ExpandToHeading(el, 'child');
|
|
195
195
|
const child = el.querySelector('[data-level="3"]');
|
|
196
196
|
expect(child.classList.contains('collapsed')).toBe(false);
|
|
197
197
|
});
|
|
198
198
|
it('toggleCollapsibleSection expands a section that starts collapsed', () => {
|
|
199
199
|
const child = el.querySelector('[data-level="3"]');
|
|
200
200
|
expect(child.classList.contains('collapsed')).toBe(true);
|
|
201
|
-
|
|
201
|
+
ToggleCollapsibleSection(child);
|
|
202
202
|
expect(child.classList.contains('collapsed')).toBe(false);
|
|
203
203
|
expect(child.querySelector('.collapsible-toggle')?.getAttribute('aria-expanded')).toBe('true');
|
|
204
204
|
});
|
|
205
205
|
it('expandToHeading is a no-op for an unknown heading id (no throw)', () => {
|
|
206
206
|
const child = el.querySelector('[data-level="3"]');
|
|
207
|
-
expect(() =>
|
|
207
|
+
expect(() => ExpandToHeading(el, 'does-not-exist')).not.toThrow();
|
|
208
208
|
// The child that was collapsed stays collapsed — nothing was touched.
|
|
209
209
|
expect(child.classList.contains('collapsed')).toBe(true);
|
|
210
210
|
});
|
|
211
211
|
it('expand/collapse helpers are safe on a container with no sections', () => {
|
|
212
212
|
const empty = container('<p>nothing collapsible</p>');
|
|
213
|
-
expect(() =>
|
|
214
|
-
expect(() =>
|
|
213
|
+
expect(() => ExpandAllSections(empty)).not.toThrow();
|
|
214
|
+
expect(() => CollapseAllSections(empty)).not.toThrow();
|
|
215
215
|
});
|
|
216
216
|
});
|
|
217
217
|
//# sourceMappingURL=web-dom-utils.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-dom-utils.test.js","sourceRoot":"","sources":["../../src/__tests__/web-dom-utils.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, beforeEach, afterEach } from 'vitest';\nimport { sanitizeSvgContent } from '../lib/extensions/svg-renderer.extension';\nimport {\n addCopyButtonsToCodeBlocks,\n removeCopyButtonsFromCodeBlocks,\n} from '../lib/extensions/code-copy.extension';\nimport {\n toggleCollapsibleSection,\n expandAllSections,\n collapseAllSections,\n expandToHeading,\n} from '../lib/extensions/collapsible-headings.extension';\n\nfunction container(html: string): HTMLElement {\n const el = document.createElement('div');\n el.innerHTML = html;\n document.body.appendChild(el);\n return el;\n}\n\n// Prevent fixtures (and their duplicate ids) from leaking across tests.\nafterEach(() => {\n document.body.innerHTML = '';\n});\n\ndescribe('sanitizeSvgContent', () => {\n it('removes <script> elements', () => {\n const el = container('<svg><script>alert(1)</script><circle/></svg>');\n sanitizeSvgContent(el);\n expect(el.querySelector('script')).toBeNull();\n expect(el.querySelector('circle')).not.toBeNull();\n });\n\n it('strips inline event-handler attributes', () => {\n const el = container('<svg><rect onclick=\"hack()\" onload=\"x()\"/></svg>');\n sanitizeSvgContent(el);\n const rect = el.querySelector('rect');\n expect(rect?.hasAttribute('onclick')).toBe(false);\n expect(rect?.hasAttribute('onload')).toBe(false);\n });\n\n it('removes javascript: hrefs', () => {\n const el = container('<svg><a href=\"javascript:evil()\">x</a></svg>');\n sanitizeSvgContent(el);\n expect(el.querySelector('a')?.hasAttribute('href')).toBe(false);\n });\n\n it('removes foreignObject and external <use>', () => {\n const el = container('<svg><foreignObject></foreignObject><use href=\"https://evil.test/x\"/></svg>');\n sanitizeSvgContent(el);\n expect(el.querySelector('foreignObject')).toBeNull();\n expect(el.querySelector('use')).toBeNull();\n });\n\n it('strips javascript: from xlink:href', () => {\n const el = container('<svg><a xlink:href=\"javascript:evil()\">x</a></svg>');\n sanitizeSvgContent(el);\n expect(el.querySelector('a')?.hasAttribute('xlink:href')).toBe(false);\n });\n\n it('keeps a local <use> reference (only external ones are removed)', () => {\n const el = container('<svg><use href=\"#icon\"/></svg>');\n sanitizeSvgContent(el);\n expect(el.querySelector('use')).not.toBeNull();\n });\n\n it('is a no-op on a container with no dangerous content', () => {\n const el = container('<svg><circle cx=\"1\" cy=\"1\" r=\"1\"/></svg>');\n sanitizeSvgContent(el);\n expect(el.querySelector('circle')).not.toBeNull();\n });\n\n it('does not throw on an empty container', () => {\n const el = container('');\n expect(() => sanitizeSvgContent(el)).not.toThrow();\n });\n\n it('removes handler attributes that are not on a fixed list', () => {\n const el = container('<svg><rect onbegin=\"a()\" onpointerrawupdate=\"b()\" ONANIMATIONEND=\"c()\" OnMouseEnter=\"d()\"/></svg>');\n sanitizeSvgContent(el);\n expect(el.querySelector('rect')?.attributes.length).toBe(0);\n });\n\n it.each([\n ['vbscript:', 'vbscript:msgbox(1)'],\n ['data:text/html', 'data:text/html,<script>alert(1)</script>'],\n ['data:image/svg+xml', 'data:image/svg+xml,<svg onload=alert(1)>'],\n ['tab inside scheme', 'java\\tscript:alert(1)'],\n ['newline inside scheme', 'java\\nscript:alert(1)'],\n ['leading control character', '\\u0001javascript:alert(1)'],\n ['mixed case', 'JaVaScRiPt:alert(1)'],\n ['leading whitespace', ' javascript:alert(1)'],\n ])('removes href using %s', (_label, href) => {\n const el = container('<svg><a>x</a></svg>');\n el.querySelector('a')!.setAttribute('href', href);\n sanitizeSvgContent(el);\n expect(el.querySelector('a')?.hasAttribute('href')).toBe(false);\n });\n\n it('removes script URLs from xlink:href, src, action and formaction', () => {\n const el = container('<svg><a>x</a><image/><foo/></svg>');\n el.querySelector('a')!.setAttribute('xlink:href', 'javascript:alert(1)');\n el.querySelector('image')!.setAttribute('src', 'javascript:alert(1)');\n el.querySelector('foo')!.setAttribute('action', 'javascript:alert(1)');\n el.querySelector('foo')!.setAttribute('formaction', 'vbscript:alert(1)');\n sanitizeSvgContent(el);\n expect(el.querySelector('a')?.hasAttribute('xlink:href')).toBe(false);\n expect(el.querySelector('image')?.hasAttribute('src')).toBe(false);\n expect(el.querySelector('foo')?.attributes.length).toBe(0);\n });\n\n it('keeps a raster data: image on <image> but not an SVG one', () => {\n const el = container('<svg><image id=\"png\"/><image id=\"svg\"/></svg>');\n el.querySelector('#png')!.setAttribute('href', 'data:image/png;base64,iVBORw0KGgo=');\n el.querySelector('#svg')!.setAttribute('href', 'data:image/svg+xml;base64,PHN2Zy8+');\n sanitizeSvgContent(el);\n expect(el.querySelector('#png')?.hasAttribute('href')).toBe(true);\n expect(el.querySelector('#svg')?.hasAttribute('href')).toBe(false);\n });\n\n it('keeps safe hrefs and non-URL attributes untouched', () => {\n const el = container('<svg><a href=\"https://example.test/\" class=\"k\" data-x=\"1\">x</a></svg>');\n sanitizeSvgContent(el);\n const a = el.querySelector('a')!;\n expect(a.getAttribute('href')).toBe('https://example.test/');\n expect(a.getAttribute('class')).toBe('k');\n expect(a.getAttribute('data-x')).toBe('1');\n });\n\n it.each([\n ['protocol-relative', '//evil.test/sprite.svg#x'],\n ['obfuscated scheme', 'ht\\ttp://evil.test/sprite.svg#x'],\n ['relative path', 'sprites.svg#x'],\n ])('removes a <use> whose reference is not same-document (%s)', (_label, href) => {\n const el = container('<svg><use/></svg>');\n el.querySelector('use')!.setAttribute('href', href);\n sanitizeSvgContent(el);\n expect(el.querySelector('use')).toBeNull();\n });\n});\n\ndescribe('addCopyButtonsToCodeBlocks / removeCopyButtonsFromCodeBlocks', () => {\n it('adds a copy button with a formatted language label', () => {\n const el = container('<pre><code class=\"language-ts\">const x = 1;</code></pre>');\n addCopyButtonsToCodeBlocks(el);\n expect(el.querySelector('.code-copy-btn')).not.toBeNull();\n expect(el.querySelector('.code-language-label')?.textContent).toBe('TypeScript');\n });\n\n it('does not double-add a toolbar', () => {\n const el = container('<pre><code class=\"language-js\">a</code></pre>');\n addCopyButtonsToCodeBlocks(el);\n addCopyButtonsToCodeBlocks(el);\n expect(el.querySelectorAll('.code-toolbar')).toHaveLength(1);\n });\n\n it('removes toolbars on cleanup', () => {\n const el = container('<pre><code class=\"language-js\">a</code></pre>');\n addCopyButtonsToCodeBlocks(el);\n removeCopyButtonsFromCodeBlocks(el);\n expect(el.querySelector('.code-toolbar')).toBeNull();\n });\n\n it('adds a copy button but no language label for a code block without a language class', () => {\n const el = container('<pre><code>plain code</code></pre>');\n addCopyButtonsToCodeBlocks(el);\n expect(el.querySelector('.code-copy-btn')).not.toBeNull();\n expect(el.querySelector('.code-language-label')).toBeNull();\n });\n\n it('omits the language label when showLanguageLabel is false', () => {\n const el = container('<pre><code class=\"language-ts\">a</code></pre>');\n addCopyButtonsToCodeBlocks(el, { showLanguageLabel: false });\n expect(el.querySelector('.code-copy-btn')).not.toBeNull();\n expect(el.querySelector('.code-language-label')).toBeNull();\n });\n\n it('honors a custom button/toolbar class and can remove it again', () => {\n const el = container('<pre><code class=\"language-js\">a</code></pre>');\n addCopyButtonsToCodeBlocks(el, { toolbarClass: 'my-bar', buttonClass: 'my-btn' });\n expect(el.querySelector('.my-btn')).not.toBeNull();\n removeCopyButtonsFromCodeBlocks(el, 'my-bar');\n expect(el.querySelector('.my-bar')).toBeNull();\n });\n\n it('is a no-op on a container with no code blocks', () => {\n const el = container('<p>no code here</p>');\n expect(() => addCopyButtonsToCodeBlocks(el)).not.toThrow();\n expect(el.querySelector('.code-toolbar')).toBeNull();\n });\n\n it('removeCopyButtonsFromCodeBlocks is safe when there are no toolbars', () => {\n const el = container('<pre><code>a</code></pre>');\n expect(() => removeCopyButtonsFromCodeBlocks(el)).not.toThrow();\n });\n});\n\ndescribe('collapsible DOM helpers', () => {\n let el: HTMLElement;\n\n beforeEach(() => {\n el = container(\n '<div class=\"collapsible-section\" data-level=\"2\">' +\n '<div class=\"collapsible-heading-wrapper\"><button class=\"collapsible-toggle\" aria-expanded=\"true\"></button><h2 id=\"parent\">Parent</h2></div>' +\n '<div class=\"collapsible-content\">' +\n '<div class=\"collapsible-section collapsed\" data-level=\"3\">' +\n '<div class=\"collapsible-heading-wrapper\"><button class=\"collapsible-toggle\" aria-expanded=\"false\"></button><h3 id=\"child\">Child</h3></div>' +\n '<div class=\"collapsible-content\"><p>c</p></div>' +\n '</div>' +\n '</div>' +\n '</div>'\n );\n });\n\n it('toggleCollapsibleSection flips the collapsed class and aria-expanded', () => {\n const parent = el.querySelector('.collapsible-section') as HTMLElement;\n toggleCollapsibleSection(parent);\n expect(parent.classList.contains('collapsed')).toBe(true);\n expect(parent.querySelector('.collapsible-toggle')?.getAttribute('aria-expanded')).toBe('false');\n });\n\n it('expandAllSections clears every collapsed section', () => {\n expandAllSections(el);\n expect(el.querySelectorAll('.collapsible-section.collapsed')).toHaveLength(0);\n });\n\n it('collapseAllSections collapses every section', () => {\n collapseAllSections(el);\n expect(el.querySelectorAll('.collapsible-section:not(.collapsed)')).toHaveLength(0);\n });\n\n it('expandToHeading reveals ancestor sections of a heading', () => {\n // child section starts collapsed; expanding to #child should clear it\n expandToHeading(el, 'child');\n const child = el.querySelector('[data-level=\"3\"]') as HTMLElement;\n expect(child.classList.contains('collapsed')).toBe(false);\n });\n\n it('toggleCollapsibleSection expands a section that starts collapsed', () => {\n const child = el.querySelector('[data-level=\"3\"]') as HTMLElement;\n expect(child.classList.contains('collapsed')).toBe(true);\n toggleCollapsibleSection(child);\n expect(child.classList.contains('collapsed')).toBe(false);\n expect(child.querySelector('.collapsible-toggle')?.getAttribute('aria-expanded')).toBe('true');\n });\n\n it('expandToHeading is a no-op for an unknown heading id (no throw)', () => {\n const child = el.querySelector('[data-level=\"3\"]') as HTMLElement;\n expect(() => expandToHeading(el, 'does-not-exist')).not.toThrow();\n // The child that was collapsed stays collapsed — nothing was touched.\n expect(child.classList.contains('collapsed')).toBe(true);\n });\n\n it('expand/collapse helpers are safe on a container with no sections', () => {\n const empty = container('<p>nothing collapsible</p>');\n expect(() => expandAllSections(empty)).not.toThrow();\n expect(() => collapseAllSections(empty)).not.toThrow();\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"web-dom-utils.test.js","sourceRoot":"","sources":["../../src/__tests__/web-dom-utils.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, beforeEach, afterEach } from 'vitest';\nimport { SanitizeSvgContent } from '../lib/extensions/svg-renderer.extension';\nimport {\n AddCopyButtonsToCodeBlocks,\n RemoveCopyButtonsFromCodeBlocks,\n} from '../lib/extensions/code-copy.extension';\nimport {\n ToggleCollapsibleSection,\n ExpandAllSections,\n CollapseAllSections,\n ExpandToHeading,\n} from '../lib/extensions/collapsible-headings.extension';\n\nfunction container(html: string): HTMLElement {\n const el = document.createElement('div');\n el.innerHTML = html;\n document.body.appendChild(el);\n return el;\n}\n\n// Prevent fixtures (and their duplicate ids) from leaking across tests.\nafterEach(() => {\n document.body.innerHTML = '';\n});\n\ndescribe('sanitizeSvgContent', () => {\n it('removes <script> elements', () => {\n const el = container('<svg><script>alert(1)</script><circle/></svg>');\n SanitizeSvgContent(el);\n expect(el.querySelector('script')).toBeNull();\n expect(el.querySelector('circle')).not.toBeNull();\n });\n\n it('strips inline event-handler attributes', () => {\n const el = container('<svg><rect onclick=\"hack()\" onload=\"x()\"/></svg>');\n SanitizeSvgContent(el);\n const rect = el.querySelector('rect');\n expect(rect?.hasAttribute('onclick')).toBe(false);\n expect(rect?.hasAttribute('onload')).toBe(false);\n });\n\n it('removes javascript: hrefs', () => {\n const el = container('<svg><a href=\"javascript:evil()\">x</a></svg>');\n SanitizeSvgContent(el);\n expect(el.querySelector('a')?.hasAttribute('href')).toBe(false);\n });\n\n it('removes foreignObject and external <use>', () => {\n const el = container('<svg><foreignObject></foreignObject><use href=\"https://evil.test/x\"/></svg>');\n SanitizeSvgContent(el);\n expect(el.querySelector('foreignObject')).toBeNull();\n expect(el.querySelector('use')).toBeNull();\n });\n\n it('strips javascript: from xlink:href', () => {\n const el = container('<svg><a xlink:href=\"javascript:evil()\">x</a></svg>');\n SanitizeSvgContent(el);\n expect(el.querySelector('a')?.hasAttribute('xlink:href')).toBe(false);\n });\n\n it('keeps a local <use> reference (only external ones are removed)', () => {\n const el = container('<svg><use href=\"#icon\"/></svg>');\n SanitizeSvgContent(el);\n expect(el.querySelector('use')).not.toBeNull();\n });\n\n it('is a no-op on a container with no dangerous content', () => {\n const el = container('<svg><circle cx=\"1\" cy=\"1\" r=\"1\"/></svg>');\n SanitizeSvgContent(el);\n expect(el.querySelector('circle')).not.toBeNull();\n });\n\n it('does not throw on an empty container', () => {\n const el = container('');\n expect(() => SanitizeSvgContent(el)).not.toThrow();\n });\n\n it('removes handler attributes that are not on a fixed list', () => {\n const el = container('<svg><rect onbegin=\"a()\" onpointerrawupdate=\"b()\" ONANIMATIONEND=\"c()\" OnMouseEnter=\"d()\"/></svg>');\n SanitizeSvgContent(el);\n expect(el.querySelector('rect')?.attributes.length).toBe(0);\n });\n\n it.each([\n ['vbscript:', 'vbscript:msgbox(1)'],\n ['data:text/html', 'data:text/html,<script>alert(1)</script>'],\n ['data:image/svg+xml', 'data:image/svg+xml,<svg onload=alert(1)>'],\n ['tab inside scheme', 'java\\tscript:alert(1)'],\n ['newline inside scheme', 'java\\nscript:alert(1)'],\n ['leading control character', '\\u0001javascript:alert(1)'],\n ['mixed case', 'JaVaScRiPt:alert(1)'],\n ['leading whitespace', ' javascript:alert(1)'],\n ])('removes href using %s', (_label, href) => {\n const el = container('<svg><a>x</a></svg>');\n el.querySelector('a')!.setAttribute('href', href);\n SanitizeSvgContent(el);\n expect(el.querySelector('a')?.hasAttribute('href')).toBe(false);\n });\n\n it('removes script URLs from xlink:href, src, action and formaction', () => {\n const el = container('<svg><a>x</a><image/><foo/></svg>');\n el.querySelector('a')!.setAttribute('xlink:href', 'javascript:alert(1)');\n el.querySelector('image')!.setAttribute('src', 'javascript:alert(1)');\n el.querySelector('foo')!.setAttribute('action', 'javascript:alert(1)');\n el.querySelector('foo')!.setAttribute('formaction', 'vbscript:alert(1)');\n SanitizeSvgContent(el);\n expect(el.querySelector('a')?.hasAttribute('xlink:href')).toBe(false);\n expect(el.querySelector('image')?.hasAttribute('src')).toBe(false);\n expect(el.querySelector('foo')?.attributes.length).toBe(0);\n });\n\n it('keeps a raster data: image on <image> but not an SVG one', () => {\n const el = container('<svg><image id=\"png\"/><image id=\"svg\"/></svg>');\n el.querySelector('#png')!.setAttribute('href', 'data:image/png;base64,iVBORw0KGgo=');\n el.querySelector('#svg')!.setAttribute('href', 'data:image/svg+xml;base64,PHN2Zy8+');\n SanitizeSvgContent(el);\n expect(el.querySelector('#png')?.hasAttribute('href')).toBe(true);\n expect(el.querySelector('#svg')?.hasAttribute('href')).toBe(false);\n });\n\n it('keeps safe hrefs and non-URL attributes untouched', () => {\n const el = container('<svg><a href=\"https://example.test/\" class=\"k\" data-x=\"1\">x</a></svg>');\n SanitizeSvgContent(el);\n const a = el.querySelector('a')!;\n expect(a.getAttribute('href')).toBe('https://example.test/');\n expect(a.getAttribute('class')).toBe('k');\n expect(a.getAttribute('data-x')).toBe('1');\n });\n\n it.each([\n ['protocol-relative', '//evil.test/sprite.svg#x'],\n ['obfuscated scheme', 'ht\\ttp://evil.test/sprite.svg#x'],\n ['relative path', 'sprites.svg#x'],\n ])('removes a <use> whose reference is not same-document (%s)', (_label, href) => {\n const el = container('<svg><use/></svg>');\n el.querySelector('use')!.setAttribute('href', href);\n SanitizeSvgContent(el);\n expect(el.querySelector('use')).toBeNull();\n });\n});\n\ndescribe('addCopyButtonsToCodeBlocks / removeCopyButtonsFromCodeBlocks', () => {\n it('adds a copy button with a formatted language label', () => {\n const el = container('<pre><code class=\"language-ts\">const x = 1;</code></pre>');\n AddCopyButtonsToCodeBlocks(el);\n expect(el.querySelector('.code-copy-btn')).not.toBeNull();\n expect(el.querySelector('.code-language-label')?.textContent).toBe('TypeScript');\n });\n\n it('does not double-add a toolbar', () => {\n const el = container('<pre><code class=\"language-js\">a</code></pre>');\n AddCopyButtonsToCodeBlocks(el);\n AddCopyButtonsToCodeBlocks(el);\n expect(el.querySelectorAll('.code-toolbar')).toHaveLength(1);\n });\n\n it('removes toolbars on cleanup', () => {\n const el = container('<pre><code class=\"language-js\">a</code></pre>');\n AddCopyButtonsToCodeBlocks(el);\n RemoveCopyButtonsFromCodeBlocks(el);\n expect(el.querySelector('.code-toolbar')).toBeNull();\n });\n\n it('adds a copy button but no language label for a code block without a language class', () => {\n const el = container('<pre><code>plain code</code></pre>');\n AddCopyButtonsToCodeBlocks(el);\n expect(el.querySelector('.code-copy-btn')).not.toBeNull();\n expect(el.querySelector('.code-language-label')).toBeNull();\n });\n\n it('omits the language label when showLanguageLabel is false', () => {\n const el = container('<pre><code class=\"language-ts\">a</code></pre>');\n AddCopyButtonsToCodeBlocks(el, { showLanguageLabel: false });\n expect(el.querySelector('.code-copy-btn')).not.toBeNull();\n expect(el.querySelector('.code-language-label')).toBeNull();\n });\n\n it('honors a custom button/toolbar class and can remove it again', () => {\n const el = container('<pre><code class=\"language-js\">a</code></pre>');\n AddCopyButtonsToCodeBlocks(el, { toolbarClass: 'my-bar', buttonClass: 'my-btn' });\n expect(el.querySelector('.my-btn')).not.toBeNull();\n RemoveCopyButtonsFromCodeBlocks(el, 'my-bar');\n expect(el.querySelector('.my-bar')).toBeNull();\n });\n\n it('is a no-op on a container with no code blocks', () => {\n const el = container('<p>no code here</p>');\n expect(() => AddCopyButtonsToCodeBlocks(el)).not.toThrow();\n expect(el.querySelector('.code-toolbar')).toBeNull();\n });\n\n it('removeCopyButtonsFromCodeBlocks is safe when there are no toolbars', () => {\n const el = container('<pre><code>a</code></pre>');\n expect(() => RemoveCopyButtonsFromCodeBlocks(el)).not.toThrow();\n });\n});\n\ndescribe('collapsible DOM helpers', () => {\n let el: HTMLElement;\n\n beforeEach(() => {\n el = container(\n '<div class=\"collapsible-section\" data-level=\"2\">' +\n '<div class=\"collapsible-heading-wrapper\"><button class=\"collapsible-toggle\" aria-expanded=\"true\"></button><h2 id=\"parent\">Parent</h2></div>' +\n '<div class=\"collapsible-content\">' +\n '<div class=\"collapsible-section collapsed\" data-level=\"3\">' +\n '<div class=\"collapsible-heading-wrapper\"><button class=\"collapsible-toggle\" aria-expanded=\"false\"></button><h3 id=\"child\">Child</h3></div>' +\n '<div class=\"collapsible-content\"><p>c</p></div>' +\n '</div>' +\n '</div>' +\n '</div>'\n );\n });\n\n it('toggleCollapsibleSection flips the collapsed class and aria-expanded', () => {\n const parent = el.querySelector('.collapsible-section') as HTMLElement;\n ToggleCollapsibleSection(parent);\n expect(parent.classList.contains('collapsed')).toBe(true);\n expect(parent.querySelector('.collapsible-toggle')?.getAttribute('aria-expanded')).toBe('false');\n });\n\n it('expandAllSections clears every collapsed section', () => {\n ExpandAllSections(el);\n expect(el.querySelectorAll('.collapsible-section.collapsed')).toHaveLength(0);\n });\n\n it('collapseAllSections collapses every section', () => {\n CollapseAllSections(el);\n expect(el.querySelectorAll('.collapsible-section:not(.collapsed)')).toHaveLength(0);\n });\n\n it('expandToHeading reveals ancestor sections of a heading', () => {\n // child section starts collapsed; expanding to #child should clear it\n ExpandToHeading(el, 'child');\n const child = el.querySelector('[data-level=\"3\"]') as HTMLElement;\n expect(child.classList.contains('collapsed')).toBe(false);\n });\n\n it('toggleCollapsibleSection expands a section that starts collapsed', () => {\n const child = el.querySelector('[data-level=\"3\"]') as HTMLElement;\n expect(child.classList.contains('collapsed')).toBe(true);\n ToggleCollapsibleSection(child);\n expect(child.classList.contains('collapsed')).toBe(false);\n expect(child.querySelector('.collapsible-toggle')?.getAttribute('aria-expanded')).toBe('true');\n });\n\n it('expandToHeading is a no-op for an unknown heading id (no throw)', () => {\n const child = el.querySelector('[data-level=\"3\"]') as HTMLElement;\n expect(() => ExpandToHeading(el, 'does-not-exist')).not.toThrow();\n // The child that was collapsed stays collapsed — nothing was touched.\n expect(child.classList.contains('collapsed')).toBe(true);\n });\n\n it('expand/collapse helpers are safe on a container with no sections', () => {\n const empty = container('<p>nothing collapsible</p>');\n expect(() => ExpandAllSections(empty)).not.toThrow();\n expect(() => CollapseAllSections(empty)).not.toThrow();\n });\n});\n"]}
|
|
@@ -33,31 +33,59 @@ export declare class MarkdownComponent implements OnChanges, AfterViewInit, OnDe
|
|
|
33
33
|
/**
|
|
34
34
|
* The markdown content to render
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
Data: string;
|
|
37
|
+
/** @deprecated Use {@link Data}. */
|
|
38
|
+
set data(value: string);
|
|
39
|
+
/** @deprecated Use {@link Data}. */
|
|
40
|
+
get data(): string;
|
|
37
41
|
/**
|
|
38
42
|
* Enable syntax highlighting
|
|
39
43
|
*/
|
|
40
|
-
|
|
44
|
+
EnableHighlight: boolean;
|
|
45
|
+
/** @deprecated Use {@link EnableHighlight}. */
|
|
46
|
+
set enableHighlight(value: boolean);
|
|
47
|
+
/** @deprecated Use {@link EnableHighlight}. */
|
|
48
|
+
get enableHighlight(): boolean;
|
|
41
49
|
/**
|
|
42
50
|
* Enable Mermaid diagram rendering
|
|
43
51
|
*/
|
|
44
|
-
|
|
52
|
+
EnableMermaid: boolean;
|
|
53
|
+
/** @deprecated Use {@link EnableMermaid}. */
|
|
54
|
+
set enableMermaid(value: boolean);
|
|
55
|
+
/** @deprecated Use {@link EnableMermaid}. */
|
|
56
|
+
get enableMermaid(): boolean;
|
|
45
57
|
/**
|
|
46
58
|
* Enable copy button on code blocks
|
|
47
59
|
*/
|
|
48
|
-
|
|
60
|
+
EnableCodeCopy: boolean;
|
|
61
|
+
/** @deprecated Use {@link EnableCodeCopy}. */
|
|
62
|
+
set enableCodeCopy(value: boolean);
|
|
63
|
+
/** @deprecated Use {@link EnableCodeCopy}. */
|
|
64
|
+
get enableCodeCopy(): boolean;
|
|
49
65
|
/**
|
|
50
66
|
* Enable collapsible heading sections
|
|
51
67
|
*/
|
|
52
|
-
|
|
68
|
+
EnableCollapsibleHeadings: boolean;
|
|
69
|
+
/** @deprecated Use {@link EnableCollapsibleHeadings}. */
|
|
70
|
+
set enableCollapsibleHeadings(value: boolean);
|
|
71
|
+
/** @deprecated Use {@link EnableCollapsibleHeadings}. */
|
|
72
|
+
get enableCollapsibleHeadings(): boolean;
|
|
53
73
|
/**
|
|
54
74
|
* Heading level at which to start collapsing
|
|
55
75
|
*/
|
|
56
|
-
|
|
76
|
+
CollapsibleHeadingLevel: 1 | 2 | 3 | 4 | 5 | 6;
|
|
77
|
+
/** @deprecated Use {@link CollapsibleHeadingLevel}. */
|
|
78
|
+
set collapsibleHeadingLevel(value: 1 | 2 | 3 | 4 | 5 | 6);
|
|
79
|
+
/** @deprecated Use {@link CollapsibleHeadingLevel}. */
|
|
80
|
+
get collapsibleHeadingLevel(): 1 | 2 | 3 | 4 | 5 | 6;
|
|
57
81
|
/**
|
|
58
82
|
* Whether collapsible sections should be expanded by default
|
|
59
83
|
*/
|
|
60
|
-
|
|
84
|
+
CollapsibleDefaultExpanded: boolean;
|
|
85
|
+
/** @deprecated Use {@link CollapsibleDefaultExpanded}. */
|
|
86
|
+
set collapsibleDefaultExpanded(value: boolean);
|
|
87
|
+
/** @deprecated Use {@link CollapsibleDefaultExpanded}. */
|
|
88
|
+
get collapsibleDefaultExpanded(): boolean;
|
|
61
89
|
/**
|
|
62
90
|
* Specify which heading levels should start expanded.
|
|
63
91
|
* Array of heading levels (2-6) that should be expanded by default.
|
|
@@ -69,75 +97,153 @@ export declare class MarkdownComponent implements OnChanges, AfterViewInit, OnDe
|
|
|
69
97
|
* - [] = All collapsed
|
|
70
98
|
* - undefined = Uses collapsibleDefaultExpanded for all levels
|
|
71
99
|
*/
|
|
72
|
-
|
|
100
|
+
AutoExpandLevels?: number[];
|
|
101
|
+
/** @deprecated Use {@link AutoExpandLevels}. */
|
|
102
|
+
set autoExpandLevels(value: number[] | undefined);
|
|
103
|
+
/** @deprecated Use {@link AutoExpandLevels}. */
|
|
104
|
+
get autoExpandLevels(): number[] | undefined;
|
|
73
105
|
/**
|
|
74
106
|
* Enable GitHub-style alerts
|
|
75
107
|
*/
|
|
76
|
-
|
|
108
|
+
EnableAlerts: boolean;
|
|
109
|
+
/** @deprecated Use {@link EnableAlerts}. */
|
|
110
|
+
set enableAlerts(value: boolean);
|
|
111
|
+
/** @deprecated Use {@link EnableAlerts}. */
|
|
112
|
+
get enableAlerts(): boolean;
|
|
77
113
|
/**
|
|
78
114
|
* Enable smartypants for typography (curly quotes, em/en dashes, ellipses)
|
|
79
115
|
*/
|
|
80
|
-
|
|
116
|
+
EnableSmartypants: boolean;
|
|
117
|
+
/** @deprecated Use {@link EnableSmartypants}. */
|
|
118
|
+
set enableSmartypants(value: boolean);
|
|
119
|
+
/** @deprecated Use {@link EnableSmartypants}. */
|
|
120
|
+
get enableSmartypants(): boolean;
|
|
81
121
|
/**
|
|
82
122
|
* Enable SVG code block rendering
|
|
83
123
|
* When enabled, ```svg code blocks are rendered as actual SVG images
|
|
84
124
|
*/
|
|
85
|
-
|
|
125
|
+
EnableSvgRenderer: boolean;
|
|
126
|
+
/** @deprecated Use {@link EnableSvgRenderer}. */
|
|
127
|
+
set enableSvgRenderer(value: boolean);
|
|
128
|
+
/** @deprecated Use {@link EnableSvgRenderer}. */
|
|
129
|
+
get enableSvgRenderer(): boolean;
|
|
86
130
|
/**
|
|
87
131
|
* Enable raw HTML passthrough in markdown content.
|
|
88
132
|
* Scripts and event handlers are still stripped unless enableJavaScript is true.
|
|
89
133
|
*/
|
|
90
|
-
|
|
134
|
+
EnableHtml: boolean;
|
|
135
|
+
/** @deprecated Use {@link EnableHtml}. */
|
|
136
|
+
set enableHtml(value: boolean);
|
|
137
|
+
/** @deprecated Use {@link EnableHtml}. */
|
|
138
|
+
get enableHtml(): boolean;
|
|
91
139
|
/**
|
|
92
140
|
* Enable JavaScript in HTML content (<script> tags and on* handlers).
|
|
93
141
|
* WARNING: Major security risk - only enable for fully trusted content.
|
|
94
142
|
*/
|
|
95
|
-
|
|
143
|
+
EnableJavaScript: boolean;
|
|
144
|
+
/** @deprecated Use {@link EnableJavaScript}. */
|
|
145
|
+
set enableJavaScript(value: boolean);
|
|
146
|
+
/** @deprecated Use {@link EnableJavaScript}. */
|
|
147
|
+
get enableJavaScript(): boolean;
|
|
96
148
|
/**
|
|
97
149
|
* Enable heading IDs for anchor links
|
|
98
150
|
*/
|
|
99
|
-
|
|
151
|
+
EnableHeadingIds: boolean;
|
|
152
|
+
/** @deprecated Use {@link EnableHeadingIds}. */
|
|
153
|
+
set enableHeadingIds(value: boolean);
|
|
154
|
+
/** @deprecated Use {@link EnableHeadingIds}. */
|
|
155
|
+
get enableHeadingIds(): boolean;
|
|
100
156
|
/**
|
|
101
157
|
* Prefix for heading IDs
|
|
102
158
|
*/
|
|
103
|
-
|
|
159
|
+
HeadingIdPrefix: string;
|
|
160
|
+
/** @deprecated Use {@link HeadingIdPrefix}. */
|
|
161
|
+
set headingIdPrefix(value: string);
|
|
162
|
+
/** @deprecated Use {@link HeadingIdPrefix}. */
|
|
163
|
+
get headingIdPrefix(): string;
|
|
104
164
|
/**
|
|
105
165
|
* Enable line numbers in code blocks
|
|
106
166
|
*/
|
|
107
|
-
|
|
167
|
+
EnableLineNumbers: boolean;
|
|
168
|
+
/** @deprecated Use {@link EnableLineNumbers}. */
|
|
169
|
+
set enableLineNumbers(value: boolean);
|
|
170
|
+
/** @deprecated Use {@link EnableLineNumbers}. */
|
|
171
|
+
get enableLineNumbers(): boolean;
|
|
108
172
|
/**
|
|
109
173
|
* Custom CSS class for the container
|
|
110
174
|
*/
|
|
111
|
-
|
|
175
|
+
ContainerClass: string;
|
|
176
|
+
/** @deprecated Use {@link ContainerClass}. */
|
|
177
|
+
set containerClass(value: string);
|
|
178
|
+
/** @deprecated Use {@link ContainerClass}. */
|
|
179
|
+
get containerClass(): string;
|
|
112
180
|
/**
|
|
113
181
|
* Mermaid theme.
|
|
114
182
|
* 'auto' (default) detects light/dark from the document's data-theme attribute.
|
|
115
183
|
*/
|
|
116
|
-
|
|
184
|
+
MermaidTheme: 'auto' | 'default' | 'dark' | 'forest' | 'neutral' | 'base';
|
|
185
|
+
/** @deprecated Use {@link MermaidTheme}. */
|
|
186
|
+
set mermaidTheme(value: 'auto' | 'default' | 'dark' | 'forest' | 'neutral' | 'base');
|
|
187
|
+
/** @deprecated Use {@link MermaidTheme}. */
|
|
188
|
+
get mermaidTheme(): 'auto' | 'default' | 'dark' | 'forest' | 'neutral' | 'base';
|
|
117
189
|
/**
|
|
118
190
|
* Whether to sanitize HTML output
|
|
119
191
|
*/
|
|
120
|
-
|
|
192
|
+
Sanitize: boolean;
|
|
193
|
+
/** @deprecated Use {@link Sanitize}. */
|
|
194
|
+
set sanitize(value: boolean);
|
|
195
|
+
/** @deprecated Use {@link Sanitize}. */
|
|
196
|
+
get sanitize(): boolean;
|
|
121
197
|
/**
|
|
122
198
|
* Emitted when rendering is complete
|
|
123
199
|
*/
|
|
200
|
+
Rendered: EventEmitter<MarkdownRenderEvent>;
|
|
201
|
+
/**
|
|
202
|
+
* @deprecated Use {@link Rendered}.
|
|
203
|
+
*
|
|
204
|
+
* The same emitter under the old binding name, so a template still binding
|
|
205
|
+
* (rendered) keeps working. Must stay AFTER Rendered: class fields
|
|
206
|
+
* initialise in order, and the other way round this captures undefined.
|
|
207
|
+
*/
|
|
124
208
|
rendered: EventEmitter<MarkdownRenderEvent>;
|
|
125
209
|
/**
|
|
126
210
|
* Emitted when a heading anchor is clicked
|
|
127
211
|
*/
|
|
212
|
+
HeadingClick: EventEmitter<HeadingInfo>;
|
|
213
|
+
/**
|
|
214
|
+
* @deprecated Use {@link HeadingClick}.
|
|
215
|
+
*
|
|
216
|
+
* The same emitter under the old binding name, so a template still binding
|
|
217
|
+
* (headingClick) keeps working. Must stay AFTER HeadingClick: class fields
|
|
218
|
+
* initialise in order, and the other way round this captures undefined.
|
|
219
|
+
*/
|
|
128
220
|
headingClick: EventEmitter<HeadingInfo>;
|
|
129
221
|
/**
|
|
130
222
|
* Emitted when code is copied to clipboard
|
|
131
223
|
*/
|
|
224
|
+
CodeCopied: EventEmitter<string>;
|
|
225
|
+
/**
|
|
226
|
+
* @deprecated Use {@link CodeCopied}.
|
|
227
|
+
*
|
|
228
|
+
* The same emitter under the old binding name, so a template still binding
|
|
229
|
+
* (codeCopied) keeps working. Must stay AFTER CodeCopied: class fields
|
|
230
|
+
* initialise in order, and the other way round this captures undefined.
|
|
231
|
+
*/
|
|
132
232
|
codeCopied: EventEmitter<string>;
|
|
133
233
|
/**
|
|
134
234
|
* The sanitized HTML content to display
|
|
135
235
|
*/
|
|
136
|
-
|
|
236
|
+
RenderedContent: SafeHtml;
|
|
237
|
+
/** @deprecated Use {@link RenderedContent}. */
|
|
238
|
+
get renderedContent(): SafeHtml;
|
|
239
|
+
/** @deprecated Use {@link RenderedContent}. */
|
|
240
|
+
set renderedContent(value: SafeHtml);
|
|
137
241
|
/**
|
|
138
242
|
* Public accessor for the component's element reference.
|
|
139
243
|
* Provided for backward compatibility with ngx-markdown API.
|
|
140
244
|
*/
|
|
245
|
+
get Element(): ElementRef<HTMLElement>;
|
|
246
|
+
/** @deprecated Use {@link Element}. */
|
|
141
247
|
get element(): ElementRef<HTMLElement>;
|
|
142
248
|
private renderStartTime;
|
|
143
249
|
private hasMermaid;
|
|
@@ -204,16 +310,22 @@ export declare class MarkdownComponent implements OnChanges, AfterViewInit, OnDe
|
|
|
204
310
|
/**
|
|
205
311
|
* Force a re-render of the markdown content
|
|
206
312
|
*/
|
|
313
|
+
Refresh(): void;
|
|
314
|
+
/** @deprecated Use {@link Refresh}. */
|
|
207
315
|
refresh(): void;
|
|
208
316
|
/**
|
|
209
317
|
* Get the current heading list (for TOC building)
|
|
210
318
|
*/
|
|
319
|
+
GetHeadings(): HeadingInfo[];
|
|
320
|
+
/** @deprecated Use {@link GetHeadings}. */
|
|
211
321
|
getHeadings(): HeadingInfo[];
|
|
212
322
|
/**
|
|
213
323
|
* Scroll to a heading by ID
|
|
214
324
|
*/
|
|
325
|
+
ScrollToHeading(headingId: string): void;
|
|
326
|
+
/** @deprecated Use {@link ScrollToHeading}. */
|
|
215
327
|
scrollToHeading(headingId: string): void;
|
|
216
328
|
static ɵfac: i0.ɵɵFactoryDeclaration<MarkdownComponent, never>;
|
|
217
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<MarkdownComponent, "mj-markdown", never, { "data": { "alias": "data"; "required": false; }; "enableHighlight": { "alias": "enableHighlight"; "required": false; }; "enableMermaid": { "alias": "enableMermaid"; "required": false; }; "enableCodeCopy": { "alias": "enableCodeCopy"; "required": false; }; "enableCollapsibleHeadings": { "alias": "enableCollapsibleHeadings"; "required": false; }; "collapsibleHeadingLevel": { "alias": "collapsibleHeadingLevel"; "required": false; }; "collapsibleDefaultExpanded": { "alias": "collapsibleDefaultExpanded"; "required": false; }; "autoExpandLevels": { "alias": "autoExpandLevels"; "required": false; }; "enableAlerts": { "alias": "enableAlerts"; "required": false; }; "enableSmartypants": { "alias": "enableSmartypants"; "required": false; }; "enableSvgRenderer": { "alias": "enableSvgRenderer"; "required": false; }; "enableHtml": { "alias": "enableHtml"; "required": false; }; "enableJavaScript": { "alias": "enableJavaScript"; "required": false; }; "enableHeadingIds": { "alias": "enableHeadingIds"; "required": false; }; "headingIdPrefix": { "alias": "headingIdPrefix"; "required": false; }; "enableLineNumbers": { "alias": "enableLineNumbers"; "required": false; }; "containerClass": { "alias": "containerClass"; "required": false; }; "mermaidTheme": { "alias": "mermaidTheme"; "required": false; }; "sanitize": { "alias": "sanitize"; "required": false; }; }, { "rendered": "rendered"; "headingClick": "headingClick"; "codeCopied": "codeCopied"; }, never, never, false, never>;
|
|
329
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MarkdownComponent, "mj-markdown", never, { "Data": { "alias": "Data"; "required": false; }; "data": { "alias": "data"; "required": false; }; "EnableHighlight": { "alias": "EnableHighlight"; "required": false; }; "enableHighlight": { "alias": "enableHighlight"; "required": false; }; "EnableMermaid": { "alias": "EnableMermaid"; "required": false; }; "enableMermaid": { "alias": "enableMermaid"; "required": false; }; "EnableCodeCopy": { "alias": "EnableCodeCopy"; "required": false; }; "enableCodeCopy": { "alias": "enableCodeCopy"; "required": false; }; "EnableCollapsibleHeadings": { "alias": "EnableCollapsibleHeadings"; "required": false; }; "enableCollapsibleHeadings": { "alias": "enableCollapsibleHeadings"; "required": false; }; "CollapsibleHeadingLevel": { "alias": "CollapsibleHeadingLevel"; "required": false; }; "collapsibleHeadingLevel": { "alias": "collapsibleHeadingLevel"; "required": false; }; "CollapsibleDefaultExpanded": { "alias": "CollapsibleDefaultExpanded"; "required": false; }; "collapsibleDefaultExpanded": { "alias": "collapsibleDefaultExpanded"; "required": false; }; "AutoExpandLevels": { "alias": "AutoExpandLevels"; "required": false; }; "autoExpandLevels": { "alias": "autoExpandLevels"; "required": false; }; "EnableAlerts": { "alias": "EnableAlerts"; "required": false; }; "enableAlerts": { "alias": "enableAlerts"; "required": false; }; "EnableSmartypants": { "alias": "EnableSmartypants"; "required": false; }; "enableSmartypants": { "alias": "enableSmartypants"; "required": false; }; "EnableSvgRenderer": { "alias": "EnableSvgRenderer"; "required": false; }; "enableSvgRenderer": { "alias": "enableSvgRenderer"; "required": false; }; "EnableHtml": { "alias": "EnableHtml"; "required": false; }; "enableHtml": { "alias": "enableHtml"; "required": false; }; "EnableJavaScript": { "alias": "EnableJavaScript"; "required": false; }; "enableJavaScript": { "alias": "enableJavaScript"; "required": false; }; "EnableHeadingIds": { "alias": "EnableHeadingIds"; "required": false; }; "enableHeadingIds": { "alias": "enableHeadingIds"; "required": false; }; "HeadingIdPrefix": { "alias": "HeadingIdPrefix"; "required": false; }; "headingIdPrefix": { "alias": "headingIdPrefix"; "required": false; }; "EnableLineNumbers": { "alias": "EnableLineNumbers"; "required": false; }; "enableLineNumbers": { "alias": "enableLineNumbers"; "required": false; }; "ContainerClass": { "alias": "ContainerClass"; "required": false; }; "containerClass": { "alias": "containerClass"; "required": false; }; "MermaidTheme": { "alias": "MermaidTheme"; "required": false; }; "mermaidTheme": { "alias": "mermaidTheme"; "required": false; }; "Sanitize": { "alias": "Sanitize"; "required": false; }; "sanitize": { "alias": "sanitize"; "required": false; }; }, { "Rendered": "Rendered"; "rendered": "rendered"; "HeadingClick": "HeadingClick"; "headingClick": "headingClick"; "CodeCopied": "CodeCopied"; "codeCopied": "codeCopied"; }, never, never, false, never>;
|
|
218
330
|
}
|
|
219
331
|
//# sourceMappingURL=markdown.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/markdown.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,SAAS,EACT,aAAa,EAIb,iBAAiB,EACjB,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAA2C,mBAAmB,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;;AAG1H;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAQa,iBAAkB,YAAW,SAAS,EAAE,aAAa,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"markdown.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/markdown.component.ts"],"names":[],"mappings":"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"}
|