rich-html-editor 0.2.1 → 1.0.0
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/README.md +17 -5
- package/dist/{chunk-ZDGUOGND.mjs → chunk-W3ULZ2LR.mjs} +23 -1
- package/dist/chunk-W3ULZ2LR.mjs.map +1 -0
- package/dist/index.js +413 -218
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +394 -219
- package/dist/index.mjs.map +1 -1
- package/dist/{state-CZIMHTJ3.mjs → state-DA3PGFTN.mjs} +2 -2
- package/package.json +2 -2
- package/dist/chunk-ZDGUOGND.mjs.map +0 -1
- /package/dist/{state-CZIMHTJ3.mjs.map → state-DA3PGFTN.mjs.map} +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# rich-html-editor
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/rich-html-editor)
|
|
4
|
+
|
|
3
5
|
A framework-agnostic, plug-and-play rich HTML editor library for adding WYSIWYG editing capabilities to your web applications.
|
|
4
6
|
|
|
5
7
|
## Features
|
|
@@ -12,9 +14,11 @@ A framework-agnostic, plug-and-play rich HTML editor library for adding WYSIWYG
|
|
|
12
14
|
- 🔒 **Security** - URL sanitization to prevent XSS attacks
|
|
13
15
|
- 📦 **Small Bundle** - ~13KB minified (ESM format)
|
|
14
16
|
|
|
17
|
+
Release: **v1.0.0** — Stable release (2025-12-22) including headings, lists (UL/OL), full toolbar formatting, color pickers, link sanitization, and undo/redo.
|
|
18
|
+
|
|
15
19
|
## Changelog
|
|
16
20
|
|
|
17
|
-
See the full changelog in [CHANGELOG.md](CHANGELOG.md).
|
|
21
|
+
See the full changelog in [CHANGELOG.md](CHANGELOG.md). Latest release: **v1.0.0** (2025-12-22).
|
|
18
22
|
|
|
19
23
|
## Installation
|
|
20
24
|
|
|
@@ -52,9 +56,11 @@ Vanilla JavaScript
|
|
|
52
56
|
```html
|
|
53
57
|
<iframe id="editor" srcdoc="<body><div class=\"editable\">Edit me</div></body>"></iframe>
|
|
54
58
|
<script type="module">
|
|
55
|
-
import { initRichEditor } from './dist/index.mjs';
|
|
59
|
+
import { initRichEditor, getCleanHTML } from './dist/index.mjs';
|
|
56
60
|
const iframe = document.getElementById('editor');
|
|
57
61
|
initRichEditor(iframe);
|
|
62
|
+
// later, retrieve the cleaned HTML from the editor
|
|
63
|
+
const html = getCleanHTML();
|
|
58
64
|
</script>
|
|
59
65
|
```
|
|
60
66
|
|
|
@@ -62,12 +68,14 @@ React (functional component)
|
|
|
62
68
|
|
|
63
69
|
```jsx
|
|
64
70
|
import { useEffect, useRef } from "react";
|
|
65
|
-
import { initRichEditor } from "rich-html-editor";
|
|
71
|
+
import { initRichEditor, getCleanHTML } from "rich-html-editor";
|
|
66
72
|
|
|
67
73
|
export default function Editor() {
|
|
68
74
|
const iframeRef = useRef(null);
|
|
69
75
|
useEffect(() => {
|
|
70
76
|
if (iframeRef.current) initRichEditor(iframeRef.current);
|
|
77
|
+
// later, read cleaned HTML from the editor
|
|
78
|
+
const html = getCleanHTML();
|
|
71
79
|
}, []);
|
|
72
80
|
return (
|
|
73
81
|
<iframe
|
|
@@ -86,12 +94,14 @@ Angular (simple component)
|
|
|
86
94
|
|
|
87
95
|
// in component.ts
|
|
88
96
|
import { AfterViewInit, ViewChild, ElementRef } from "@angular/core";
|
|
89
|
-
import { initRichEditor } from "rich-html-editor";
|
|
97
|
+
import { initRichEditor, getCleanHTML } from "rich-html-editor";
|
|
90
98
|
|
|
91
99
|
export class MyEditorComponent implements AfterViewInit {
|
|
92
100
|
@ViewChild("editor") editor!: ElementRef<HTMLIFrameElement>;
|
|
93
101
|
ngAfterViewInit() {
|
|
94
102
|
initRichEditor(this.editor.nativeElement);
|
|
103
|
+
// later, obtain cleaned HTML
|
|
104
|
+
const html = getCleanHTML();
|
|
95
105
|
}
|
|
96
106
|
}
|
|
97
107
|
```
|
|
@@ -104,12 +114,14 @@ Vue 3 (Composition API)
|
|
|
104
114
|
</template>
|
|
105
115
|
<script setup>
|
|
106
116
|
import { onMounted, ref } from "vue";
|
|
107
|
-
import { initRichEditor } from "rich-html-editor";
|
|
117
|
+
import { initRichEditor, getCleanHTML } from "rich-html-editor";
|
|
108
118
|
|
|
109
119
|
const editor = ref(null);
|
|
110
120
|
const srcdoc = '<body><div class="editable">Edit me</div></body>';
|
|
111
121
|
onMounted(() => {
|
|
112
122
|
if (editor.value) initRichEditor(editor.value);
|
|
123
|
+
// later, read cleaned HTML from the editor
|
|
124
|
+
const html = getCleanHTML();
|
|
113
125
|
});
|
|
114
126
|
</script>
|
|
115
127
|
```
|
|
@@ -74,6 +74,26 @@ var LABEL_STRIKETHROUGH = "<s>S</s>";
|
|
|
74
74
|
var LABEL_UNDO = "\u21BA";
|
|
75
75
|
var LABEL_REDO = "\u21BB";
|
|
76
76
|
var LABEL_LINK = "\u{1F517}";
|
|
77
|
+
var LABEL_UNORDERED_LIST = `
|
|
78
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
79
|
+
<circle cx="3" cy="4" r="1" fill="currentColor" />
|
|
80
|
+
<rect x="6" y="3" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
81
|
+
<circle cx="3" cy="8" r="1" fill="currentColor" />
|
|
82
|
+
<rect x="6" y="7" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
83
|
+
<circle cx="3" cy="12" r="1" fill="currentColor" />
|
|
84
|
+
<rect x="6" y="11" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
85
|
+
</svg>
|
|
86
|
+
`;
|
|
87
|
+
var LABEL_ORDERED_LIST = `
|
|
88
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
89
|
+
<text x="1" y="4" font-size="4" fill="currentColor">1.</text>
|
|
90
|
+
<rect x="6" y="3" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
91
|
+
<text x="1" y="8" font-size="4" fill="currentColor">2.</text>
|
|
92
|
+
<rect x="6" y="7" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
93
|
+
<text x="1" y="12" font-size="4" fill="currentColor">3.</text>
|
|
94
|
+
<rect x="6" y="11" width="9" height="2" rx="0.5" fill="currentColor" />
|
|
95
|
+
</svg>
|
|
96
|
+
`;
|
|
77
97
|
var LABEL_ALIGN_LEFT = `
|
|
78
98
|
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
79
99
|
<rect x="1" y="2" width="10" height="2" rx="0.5" fill="currentColor" />
|
|
@@ -420,6 +440,8 @@ export {
|
|
|
420
440
|
LABEL_UNDO,
|
|
421
441
|
LABEL_REDO,
|
|
422
442
|
LABEL_LINK,
|
|
443
|
+
LABEL_UNORDERED_LIST,
|
|
444
|
+
LABEL_ORDERED_LIST,
|
|
423
445
|
LABEL_ALIGN_LEFT,
|
|
424
446
|
LABEL_ALIGN_CENTER,
|
|
425
447
|
LABEL_ALIGN_RIGHT,
|
|
@@ -438,4 +460,4 @@ export {
|
|
|
438
460
|
pushStandaloneSnapshot,
|
|
439
461
|
setMaxStackSize
|
|
440
462
|
};
|
|
441
|
-
//# sourceMappingURL=chunk-
|
|
463
|
+
//# sourceMappingURL=chunk-W3ULZ2LR.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/events.ts","../src/core/constants.ts","../src/utils/sanitize.ts","../src/core/state.ts"],"sourcesContent":["import type { EditorEventType, EditorEvent, EditorEventHandler } from \"./types\";\r\n\r\nexport class EditorEventEmitter {\r\n private listeners: Map<EditorEventType, Set<EditorEventHandler>> = new Map();\r\n\r\n on(type: EditorEventType, handler: EditorEventHandler): () => void {\r\n if (!this.listeners.has(type)) {\r\n this.listeners.set(type, new Set());\r\n }\r\n this.listeners.get(type)!.add(handler);\r\n return () => this.off(type, handler);\r\n }\r\n\r\n once(type: EditorEventType, handler: EditorEventHandler): void {\r\n const unsubscribe = this.on(type, (event) => {\r\n handler(event);\r\n unsubscribe();\r\n });\r\n }\r\n\r\n off(type: EditorEventType, handler: EditorEventHandler): void {\r\n const handlers = this.listeners.get(type);\r\n if (handlers) {\r\n handlers.delete(handler);\r\n if (handlers.size === 0) this.listeners.delete(type);\r\n }\r\n }\r\n\r\n emit(event: EditorEvent): void {\r\n const handlers = this.listeners.get(event.type);\r\n if (handlers) {\r\n handlers.forEach((handler) => {\r\n try {\r\n handler(event);\r\n } catch (error) {\r\n console.error(\r\n `[rich-html-editor] Error in event handler for ${event.type}:`,\r\n error\r\n );\r\n }\r\n });\r\n }\r\n }\r\n\r\n removeAllListeners(type?: EditorEventType): void {\r\n if (type) this.listeners.delete(type);\r\n else this.listeners.clear();\r\n }\r\n\r\n listenerCount(type: EditorEventType): number {\r\n return this.listeners.get(type)?.size ?? 0;\r\n }\r\n}\r\n\r\nexport const editorEventEmitter = new EditorEventEmitter();\r\n\r\nexport function getEditorEventEmitter(): EditorEventEmitter {\r\n return editorEventEmitter;\r\n}\r\n","// Shared constants for the rich-html-editor (moved to core)\r\nexport const TOOLBAR_ID = \"editor-toolbar\";\r\nexport const STYLE_ID = \"editor-styles\";\r\nexport const CLASS_EDITABLE = \"editor-editable-element\";\r\nexport const CLASS_ACTIVE = \"editor-active-element\";\r\n\r\n// Default undo/redo stack size\r\nexport const DEFAULT_MAX_STACK = 60;\r\n\r\n// Toolbar styling constants\r\nexport const TOOLBAR_BG = \"#f8fafc\";\r\nexport const TOOLBAR_BORDER = \"#e5e7eb\";\r\nexport const BUTTON_BORDER = \"#d1d5db\";\r\nexport const BUTTON_ACTIVE_BG = \"#e0e7ff\";\r\nexport const BUTTON_BG = \"#fff\";\r\nexport const BUTTON_COLOR = \"#222\";\r\nexport const INFO_COLOR = \"#888\";\r\n\r\n// Outline colors used by injected styles\r\nexport const HOVER_OUTLINE = \"#2563eb\";\r\nexport const ACTIVE_OUTLINE = \"#16a34a\";\r\n\r\n// Toolbar label/icon constants (kept in core for reuse)\r\nexport const LABEL_BOLD = \"<b>B</b>\";\r\nexport const LABEL_ITALIC = \"<i>I</i>\";\r\nexport const LABEL_UNDERLINE = \"<u>U</u>\";\r\nexport const LABEL_STRIKETHROUGH = \"<s>S</s>\";\r\nexport const LABEL_UNDO = \"↺\";\r\nexport const LABEL_REDO = \"↻\";\r\nexport const LABEL_LINK = \"🔗\";\r\nexport const LABEL_UNORDERED_LIST = `\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n <circle cx=\"3\" cy=\"4\" r=\"1\" fill=\"currentColor\" />\r\n <rect x=\"6\" y=\"3\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n <circle cx=\"3\" cy=\"8\" r=\"1\" fill=\"currentColor\" />\r\n <rect x=\"6\" y=\"7\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n <circle cx=\"3\" cy=\"12\" r=\"1\" fill=\"currentColor\" />\r\n <rect x=\"6\" y=\"11\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n </svg>\r\n`;\r\nexport const LABEL_ORDERED_LIST = `\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n <text x=\"1\" y=\"4\" font-size=\"4\" fill=\"currentColor\">1.</text>\r\n <rect x=\"6\" y=\"3\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n <text x=\"1\" y=\"8\" font-size=\"4\" fill=\"currentColor\">2.</text>\r\n <rect x=\"6\" y=\"7\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n <text x=\"1\" y=\"12\" font-size=\"4\" fill=\"currentColor\">3.</text>\r\n <rect x=\"6\" y=\"11\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n </svg>\r\n`;\r\nexport const LABEL_ALIGN_LEFT = `\r\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n\t\t<rect x=\"1\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t</svg>\r\n`;\r\nexport const LABEL_ALIGN_CENTER = `\r\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n\t\t<rect x=\"3\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"3\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t</svg>\r\n`;\r\nexport const LABEL_ALIGN_RIGHT = `\r\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n\t\t<rect x=\"5\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"5\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\r\n\t</svg>\r\n`;\r\n\r\n// Font and size option lists used by the toolbar\r\nexport const FONT_OPTIONS: { label: string; value: string }[] = [\r\n { label: \"Arial\", value: \"Arial\" },\r\n { label: \"Helvetica\", value: \"Helvetica, Arial, sans-serif\" },\r\n { label: \"Verdana\", value: \"Verdana, Geneva, sans-serif\" },\r\n { label: \"Tahoma\", value: \"Tahoma, Geneva, sans-serif\" },\r\n { label: \"Trebuchet MS\", value: \"Trebuchet MS, Helvetica, sans-serif\" },\r\n { label: \"Georgia\", value: \"Georgia, serif\" },\r\n { label: \"Times New Roman\", value: \"Times New Roman, Times, serif\" },\r\n { label: \"Palatino\", value: \"Palatino, 'Palatino Linotype', serif\" },\r\n { label: \"Garamond\", value: \"Garamond, serif\" },\r\n { label: \"Book Antiqua\", value: \"'Book Antiqua', Palatino, serif\" },\r\n { label: \"Courier New\", value: \"'Courier New', Courier, monospace\" },\r\n { label: \"Lucida Console\", value: \"'Lucida Console', Monaco, monospace\" },\r\n { label: \"Impact\", value: \"Impact, Charcoal, sans-serif\" },\r\n { label: \"Comic Sans MS\", value: \"'Comic Sans MS', 'Comic Sans', cursive\" },\r\n { label: \"Segoe UI\", value: \"'Segoe UI', Tahoma, Geneva, sans-serif\" },\r\n {\r\n label: \"Roboto\",\r\n value: \"Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif\",\r\n },\r\n { label: \"Open Sans\", value: \"'Open Sans', Arial, sans-serif\" },\r\n { label: \"Lato\", value: \"Lato, 'Helvetica Neue', Arial, sans-serif\" },\r\n { label: \"Montserrat\", value: \"Montserrat, Arial, sans-serif\" },\r\n { label: \"Source Sans Pro\", value: \"'Source Sans Pro', Arial, sans-serif\" },\r\n { label: \"Fira Sans\", value: \"'Fira Sans', Arial, sans-serif\" },\r\n { label: \"Ubuntu\", value: \"Ubuntu, Arial, sans-serif\" },\r\n { label: \"Noto Sans\", value: \"'Noto Sans', Arial, sans-serif\" },\r\n { label: \"Droid Sans\", value: \"'Droid Sans', Arial, sans-serif\" },\r\n {\r\n label: \"Helvetica Neue\",\r\n value: \"'Helvetica Neue', Helvetica, Arial, sans-serif\",\r\n },\r\n {\r\n label: \"System UI\",\r\n value:\r\n \"system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\",\r\n },\r\n];\r\n\r\nexport const SIZE_OPTIONS: { label: string; value: string }[] = [\r\n { label: \"8\", value: \"8\" },\r\n { label: \"9\", value: \"9\" },\r\n { label: \"10\", value: \"10\" },\r\n { label: \"11\", value: \"11\" },\r\n { label: \"12\", value: \"12\" },\r\n { label: \"14\", value: \"14\" },\r\n { label: \"16\", value: \"16\" },\r\n { label: \"18\", value: \"18\" },\r\n { label: \"20\", value: \"20\" },\r\n { label: \"22\", value: \"22\" },\r\n { label: \"24\", value: \"24\" },\r\n { label: \"26\", value: \"26\" },\r\n { label: \"28\", value: \"28\" },\r\n { label: \"36\", value: \"36\" },\r\n { label: \"48\", value: \"48\" },\r\n { label: \"72\", value: \"72\" },\r\n];\r\n\r\n// Block format options (Paragraph + Headings)\r\nexport const FORMAT_OPTIONS: { label: string; value: string }[] = [\r\n { label: \"Heading 1\", value: \"h1\" },\r\n { label: \"Heading 2\", value: \"h2\" },\r\n { label: \"Heading 3\", value: \"h3\" },\r\n { label: \"Heading 4\", value: \"h4\" },\r\n { label: \"Heading 5\", value: \"h5\" },\r\n { label: \"Heading 6\", value: \"h6\" },\r\n];\r\n\r\n// Shared SVG used by toolbar color inputs (palette icon)\r\nexport const PALETTE_SVG = `\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\r\n <path d=\"M12 2C7 2 3 6 3 11c0 2.8 1.4 5.3 3.7 6.8.9.6 2 .9 3.3.9 1.6 0 3.1-.5 4.3-1.4.6-.4.9-1.1.6-1.8-.3-.7-1-1-1.7-.8-1.1.3-2.3.2-3.4-.3C10.3 14.6 9 13.6 9 12c0-1.7 1.3-3 3-3 .8 0 1.5.3 2.1.8.5.4 1.2.3 1.6-.2.9-1.1 1.4-2.4 1.4-3.8C20.9 6 16.9 2 12 2z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n <circle cx=\"7.5\" cy=\"10.5\" r=\"1\" fill=\"currentColor\"/>\r\n <circle cx=\"11.5\" cy=\"8.5\" r=\"1\" fill=\"currentColor\"/>\r\n <circle cx=\"14.5\" cy=\"11.5\" r=\"1\" fill=\"currentColor\"/>\r\n </svg>\r\n`;\r\n\r\n// MS Word-like icons for color controls\r\nexport const TEXT_COLOR_ICON_HTML = `\r\n <span class=\"text-color-wrapper\" aria-hidden=\"true\">\r\n <span class=\"text-A\">A</span>\r\n </span>\r\n`;\r\n\r\nexport const HIGHLIGHT_ICON_HTML = `\r\n <span class=\"highlight-wrapper\" aria-hidden=\"true\">\r\n <span class=\"highlight-bar\" data-role=\"highlight\"></span>\r\n <span class=\"text-A\">A</span>\r\n </span>\r\n`;\r\n","import createDOMPurify from \"dompurify\";\r\n\r\n/**\r\n * Sanitize HTML using DOMPurify.\r\n *\r\n * Works in browser and in jsdom (tests) by accepting either a `Window`\r\n * or a `Document` (from which `defaultView` is used).\r\n */\r\nexport function sanitizeHtml(\r\n html: string,\r\n ctx?: Window | Document | null\r\n): string {\r\n if (!html) return \"\";\r\n // Try to get a Window reference\r\n let win: Window | null = null;\r\n if (ctx && (ctx as Document).defaultView) {\r\n win = (ctx as Document).defaultView as Window;\r\n } else if (ctx && (ctx as Window).document) {\r\n win = ctx as Window;\r\n } else if (typeof window !== \"undefined\") {\r\n win = window as Window;\r\n }\r\n\r\n // If we have a window, use DOMPurify\r\n if (win) {\r\n try {\r\n const DOMPurify = createDOMPurify(win as any);\r\n // Preserve element ids and data-* attributes so restoring snapshots\r\n // during undo/redo does not break page scripts or CSS that rely on\r\n // those attributes. Still strip <script> and all inline event\r\n // handlers (on*) to avoid executing arbitrary script.\r\n // Use ADD_ATTR to allow `id`, and a hook to preserve any `data-` attrs.\r\n try {\r\n DOMPurify.addHook(\"uponSanitizeAttribute\", (node: any, data: any) => {\r\n try {\r\n if (data && data.attrName && data.attrName.startsWith(\"data-\")) {\r\n // Keep data-* attributes\r\n (data as any).keepAttr = true;\r\n }\r\n } catch (e) {\r\n /* ignore hook errors */\r\n }\r\n });\r\n } catch (e) {\r\n /* addHook may not be available in some environments; ignore */\r\n }\r\n\r\n return DOMPurify.sanitize(html, {\r\n // Use sensible defaults: allow common formatting tags but strip scripts\r\n ALLOWED_TAGS: [\r\n \"a\",\r\n \"b\",\r\n \"i\",\r\n \"em\",\r\n \"strong\",\r\n \"u\",\r\n \"p\",\r\n \"div\",\r\n \"span\",\r\n // Common semantic elements: preserve document structure so undo/redo\r\n // does not flatten header/section/nav into plain content.\r\n \"header\",\r\n \"nav\",\r\n \"section\",\r\n \"main\",\r\n \"footer\",\r\n \"article\",\r\n \"aside\",\r\n \"figure\",\r\n \"figcaption\",\r\n \"time\",\r\n // Interactive / form elements that may appear in content\r\n \"button\",\r\n \"input\",\r\n \"label\",\r\n \"select\",\r\n \"option\",\r\n \"textarea\",\r\n \"details\",\r\n \"summary\",\r\n // Allow <style> tags so user/content-provided CSS is preserved\r\n // when taking snapshots and during undo/redo operations.\r\n // DOMPurify will still sanitize the contents of style blocks.\r\n \"style\",\r\n // Preserve linked stylesheets so page/editor styling isn't lost\r\n \"link\",\r\n \"ul\",\r\n \"ol\",\r\n \"li\",\r\n \"br\",\r\n \"hr\",\r\n \"blockquote\",\r\n \"pre\",\r\n \"code\",\r\n \"h1\",\r\n \"h2\",\r\n \"h3\",\r\n \"h4\",\r\n \"h5\",\r\n \"h6\",\r\n \"img\",\r\n ],\r\n ALLOWED_ATTR: [\r\n \"href\",\r\n \"title\",\r\n \"alt\",\r\n \"src\",\r\n \"class\",\r\n \"style\",\r\n // Attributes used by <link> tags\r\n \"rel\",\r\n \"type\",\r\n \"media\",\r\n ],\r\n // Also allow `id` attributes so element ids survive sanitization.\r\n ADD_ATTR: [\"id\"],\r\n });\r\n } catch (e) {\r\n // If DOMPurify initialization fails, fall through to minimal stripping\r\n }\r\n }\r\n\r\n // Minimal fallback: remove <script> tags and on* attributes\r\n return html\r\n .replace(/<script[\\s\\S]*?>[\\s\\S]*?<\\/script>/gi, \"\")\r\n .replace(/on[a-z]+=\\\"[^\"]*\\\"/gi, \"\");\r\n}\r\n\r\nexport default sanitizeHtml;\r\n","import { editorEventEmitter } from \"./events\";\r\nimport {\r\n DEFAULT_MAX_STACK,\r\n TOOLBAR_ID,\r\n STYLE_ID,\r\n CLASS_EDITABLE,\r\n CLASS_ACTIVE,\r\n} from \"./constants\";\r\nimport { sanitizeHtml } from \"../utils/sanitize\";\r\n\r\nlet _doc: Document | null = null;\r\nlet _undoStack: string[] = [];\r\nlet _redoStack: string[] = [];\r\nlet _currentEditable: HTMLElement | null = null;\r\nlet _maxStackSize = DEFAULT_MAX_STACK;\r\n\r\nexport function _setDoc(doc: Document | null) {\r\n _doc = doc;\r\n}\r\nexport function _getDoc() {\r\n return _doc;\r\n}\r\nexport function _setUndoStack(stack: string[]) {\r\n _undoStack = stack;\r\n editorEventEmitter.emit({\r\n type: \"undoStateChanged\",\r\n timestamp: Date.now(),\r\n data: { canUndo: _undoStack.length > 1 },\r\n });\r\n}\r\nexport function _getUndoStack() {\r\n return _undoStack;\r\n}\r\nexport function _setRedoStack(stack: string[]) {\r\n _redoStack = stack;\r\n editorEventEmitter.emit({\r\n type: \"redoStateChanged\",\r\n timestamp: Date.now(),\r\n data: { canRedo: _redoStack.length > 0 },\r\n });\r\n}\r\nexport function _getRedoStack() {\r\n return _redoStack;\r\n}\r\nexport function _setCurrentEditable(el: HTMLElement | null) {\r\n _currentEditable = el;\r\n editorEventEmitter.emit({\r\n type: \"selectionChanged\",\r\n timestamp: Date.now(),\r\n data: { element: el?.tagName },\r\n });\r\n}\r\nexport function _getCurrentEditable() {\r\n return _currentEditable;\r\n}\r\nexport function pushStandaloneSnapshot(clearRedo = true) {\r\n if (!_doc) return;\r\n // Clone the documentElement and remove injected UI (toolbar/style)\r\n // so snapshots capture only the user's content.\r\n const clone = _doc.documentElement.cloneNode(true) as HTMLElement;\r\n const toolbarNode = clone.querySelector(`#${TOOLBAR_ID}`);\r\n if (toolbarNode && toolbarNode.parentNode)\r\n toolbarNode.parentNode.removeChild(toolbarNode);\r\n const styleNode = clone.querySelector(`#${STYLE_ID}`);\r\n if (styleNode && styleNode.parentNode)\r\n styleNode.parentNode.removeChild(styleNode);\r\n // Remove editor-specific attributes/classes so snapshots don't persist\r\n // transient editing state (contenteditable, toolbar classes, tabindex).\r\n try {\r\n const editableNodes = clone.querySelectorAll(\r\n \"[contenteditable], .\" + CLASS_EDITABLE + \", .\" + CLASS_ACTIVE\r\n );\r\n editableNodes.forEach((el) => {\r\n try {\r\n if (el instanceof Element) {\r\n if (el.hasAttribute(\"contenteditable\"))\r\n el.removeAttribute(\"contenteditable\");\r\n if (el.hasAttribute(\"tabindex\")) el.removeAttribute(\"tabindex\");\r\n el.classList.remove(CLASS_EDITABLE, CLASS_ACTIVE);\r\n }\r\n } catch (e) {\r\n /* ignore */\r\n }\r\n });\r\n } catch (e) {\r\n /* ignore */\r\n }\r\n // Preserve scripts by replacing them with a harmless placeholder\r\n // that contains the encoded script source/attributes. This allows the\r\n // sanitizer to run (which strips <script> tags) while keeping the\r\n // script content available to re-insert and execute on restore.\r\n try {\r\n const scripts = Array.from(\r\n clone.querySelectorAll<HTMLScriptElement>(\"script\")\r\n );\r\n scripts.forEach((s) => {\r\n try {\r\n const code = s.textContent || \"\";\r\n const attrs: Record<string, string> = {};\r\n Array.from(s.attributes).forEach((a) => (attrs[a.name] = a.value));\r\n const placeholder = clone.ownerDocument!.createElement(\"span\");\r\n // encode script body in base64 to survive sanitization\r\n try {\r\n // btoa may throw on Unicode; encodeURIComponent first\r\n const safe =\r\n typeof btoa !== \"undefined\"\r\n ? btoa(unescape(encodeURIComponent(code)))\r\n : encodeURIComponent(code);\r\n placeholder.setAttribute(\"data-rhe-script\", safe);\r\n } catch (e) {\r\n placeholder.setAttribute(\"data-rhe-script\", encodeURIComponent(code));\r\n }\r\n if (Object.keys(attrs).length) {\r\n placeholder.setAttribute(\r\n \"data-rhe-script-attrs\",\r\n encodeURIComponent(JSON.stringify(attrs))\r\n );\r\n }\r\n // mark parent editable region if present so we can reinsert in-place\r\n const parentMarker = s.closest(\"[data-rhe-id]\") as HTMLElement | null;\r\n if (parentMarker && parentMarker.getAttribute(\"data-rhe-id\")) {\r\n placeholder.setAttribute(\r\n \"data-rhe-script-parent\",\r\n parentMarker.getAttribute(\"data-rhe-id\")!\r\n );\r\n } else {\r\n placeholder.setAttribute(\"data-rhe-script-parent\", \"head\");\r\n }\r\n s.parentNode?.replaceChild(placeholder, s);\r\n } catch (e) {\r\n /* ignore per-script errors */\r\n }\r\n });\r\n } catch (e) {\r\n /* ignore script extraction errors */\r\n }\r\n const snapRaw = clone.outerHTML;\r\n const snap = sanitizeHtml(snapRaw, _doc);\r\n if (!_undoStack.length || _undoStack[_undoStack.length - 1] !== snap) {\r\n _undoStack.push(snap);\r\n if (_undoStack.length > _maxStackSize) _undoStack.shift();\r\n editorEventEmitter.emit({\r\n type: \"contentChanged\",\r\n timestamp: Date.now(),\r\n data: { htmlLength: snap.length },\r\n });\r\n }\r\n if (clearRedo) {\r\n _redoStack = [];\r\n editorEventEmitter.emit({\r\n type: \"redoStateChanged\",\r\n timestamp: Date.now(),\r\n data: { canRedo: false },\r\n });\r\n }\r\n}\r\nexport function setMaxStackSize(size: number) {\r\n _maxStackSize = Math.max(1, size);\r\n}\r\n"],"mappings":"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","names":[]}
|