jotterjs 0.1.2 → 0.2.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 CHANGED
@@ -1,122 +1,144 @@
1
- # JotterJS
2
-
3
- A lightweight, vanilla JS rich-text editor component built on `contenteditable`. No dependencies at runtime.
4
-
5
- ## Features
6
-
7
- - Full formatting toolbar (bold, italic, underline, strikethrough, subscript/superscript, inline code)
8
- - Block format, font family, font size, and colour pickers
9
- - Lists, indentation, alignment, tables, links, and image insertion
10
- - Source (raw HTML) toggle
11
- - Content themes: `default`, `warm`, `ink`, `forest`
12
- - Pre-built toolbar presets (`minimal`, `writing`) and fully custom toolbar support
13
- - Custom toolbar buttons with `onClick` callbacks
14
- - Event system (`change`, `focus`, `blur`)
15
- - Simple chainable API
16
-
17
- ## Installation
18
-
19
- ```bash
20
- npm install jotterjs
21
- ```
22
-
23
- Or use the built files from `dist/` directly in a `<script>` tag.
24
-
25
- ## Usage
26
-
27
- ### ES Module
28
-
29
- ```js
30
- import JotterJS from 'jotterjs';
31
-
32
- const editor = new JotterJS('#my-editor', {
33
- height: '320px',
34
- placeholder: 'Start typing…',
35
- onChange: (html) => console.log(html),
36
- });
37
- ```
38
-
39
- ### IIFE (script tag)
40
-
41
- ```html
42
- <link rel="stylesheet" href="dist/jotter.min.css" />
43
- <script src="dist/jotter.iife.min.js"></script>
44
- <script>
45
- const editor = new JotterJS('#my-editor');
46
- </script>
47
- ```
48
-
49
- ## Options
50
-
51
- | Option | Type | Default | Description |
52
- |---------------|------------|------------------|--------------------------------------------------|
53
- | `placeholder` | `string` | `'Start typing…'`| Placeholder text shown when the editor is empty |
54
- | `height` | `string` | `'320px'` | Min-height of the editable area |
55
- | `theme` | `string` | `'default'` | Content theme: `default`, `warm`, `ink`, `forest`|
56
- | `toolbar` | `Array` | Full toolbar | Array of action descriptors |
57
- | `onChange` | `Function` | — | Callback `(html)` fired on every content change |
58
- | `onFocus` | `Function` | — | Callback fired on editor focus |
59
- | `onBlur` | `Function` | — | Callback fired on editor blur |
60
-
61
- ## API
62
-
63
- ```js
64
- editor.getHTML() // → string — raw innerHTML
65
- editor.getText() // → string — plain text
66
- editor.setHTML(html) // replace content
67
- editor.insertHTML(html) // insert HTML at caret
68
- editor.insertText(text) // insert plain text at caret
69
- editor.clear() // empty the editor
70
- editor.focus() // focus the editable area
71
- editor.setTheme(name) // change theme at runtime
72
- editor.setEnabled(bool) // toggle contenteditable
73
- editor.toggleSource() // switch rich-text ↔ HTML source view
74
- editor.isSourceMode() // → boolean
75
- editor.on(event, fn) // subscribe to 'change' | 'focus' | 'blur'
76
- editor.off(event, fn) // unsubscribe
77
- editor.destroy() // unmount and return final HTML
78
- ```
79
-
80
- Methods return `this` for chaining (except `getHTML`, `getText`, `isSourceMode`, and `destroy`).
81
-
82
- ## Toolbar Presets
83
-
84
- ```js
85
- new JotterJS('#el', { toolbar: JotterJS.presets.minimal });
86
- new JotterJS('#el', { toolbar: JotterJS.presets.writing });
87
- ```
88
-
89
- ## Custom Toolbar
90
-
91
- ```js
92
- const { actions } = JotterJS;
93
-
94
- new JotterJS('#el', {
95
- toolbar: [
96
- actions.bold,
97
- actions.italic,
98
- actions.sep,
99
- {
100
- icon: 'star',
101
- title: 'Insert signature',
102
- onClick: (editor) => editor.insertHTML('<p><em>— Sent with JotterJS</em></p>'),
103
- },
104
- {
105
- label: 'Clear',
106
- title: 'Clear content',
107
- onClick: (editor) => editor.clear(),
108
- },
109
- ],
110
- });
111
- ```
112
-
113
- ## Development
114
-
115
- ```bash
116
- npm run dev # start dev server
117
- npm run build # build to dist/
118
- ```
119
-
120
- ## License
121
-
122
- MIT
1
+ # JotterJS
2
+
3
+ A lightweight, vanilla JS rich-text editor component built on `contenteditable`. No dependencies at runtime.
4
+
5
+ ## Features
6
+
7
+ - Full formatting toolbar (bold, italic, underline, strikethrough, subscript/superscript, inline code)
8
+ - Block format, font family, font size, and colour pickers
9
+ - Lists, indentation, alignment, tables, links, and image insertion
10
+ - Source (raw HTML) toggle
11
+ - Content themes: `default`, `warm`, `ink`, `forest`
12
+ - Pre-built toolbar presets (`minimal`, `writing`, `full`) and fully custom toolbar support
13
+ - Custom toolbar buttons with `onClick` callbacks
14
+ - Event system (`change`, `focus`, `blur`)
15
+ - Simple chainable API
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install jotterjs
21
+ ```
22
+
23
+ Or use the built files from `dist/` directly in a `<script>` tag.
24
+
25
+ ## Usage
26
+
27
+ ### ES Module
28
+
29
+ ```js
30
+ import JotterJS from 'jotterjs';
31
+
32
+ const editor = new JotterJS('#my-editor', {
33
+ height: '320px',
34
+ placeholder: 'Start typing…',
35
+ onChange: (html) => console.log(html),
36
+ });
37
+ ```
38
+
39
+ ### IIFE (script tag)
40
+
41
+ ```html
42
+ <link rel="stylesheet" href="dist/jotter.min.css" />
43
+ <script src="dist/jotter.iife.min.js"></script>
44
+ <script>
45
+ const editor = new JotterJS('#my-editor');
46
+ </script>
47
+ ```
48
+
49
+ ## Options
50
+
51
+ | Option | Type | Default | Description |
52
+ |---------------|------------|------------------|--------------------------------------------------|
53
+ | `placeholder` | `string` | `'Start typing…'`| Placeholder text shown when the editor is empty |
54
+ | `height` | `string` | `'320px'` | Min-height of the editable area |
55
+ | `theme` | `string` | `'default'` | Content theme: `default`, `warm`, `ink`, `forest`|
56
+ | `toolbar` | `Array` | Full toolbar | Array of action descriptors |
57
+ | `onChange` | `Function` | — | Callback `(html)` fired on every content change |
58
+ | `onFocus` | `Function` | — | Callback fired on editor focus |
59
+ | `onBlur` | `Function` | — | Callback fired on editor blur |
60
+
61
+ ## API
62
+
63
+ ```js
64
+ editor.getHTML() // → string — raw innerHTML
65
+ editor.getText() // → string — plain text
66
+ editor.setHTML(html) // replace content
67
+ editor.insertHTML(html) // insert HTML at caret
68
+ editor.insertText(text) // insert plain text at caret
69
+ editor.clear() // empty the editor
70
+ editor.focus() // focus the editable area
71
+ editor.setTheme(name) // change theme at runtime
72
+ editor.setEnabled(bool) // toggle contenteditable
73
+ editor.toggleSource() // switch rich-text ↔ HTML source view
74
+ editor.isSourceMode() // → boolean
75
+ editor.on(event, fn) // subscribe to 'change' | 'focus' | 'blur'
76
+ editor.off(event, fn) // unsubscribe
77
+ editor.destroy() // unmount and return final HTML
78
+ ```
79
+
80
+ Methods return `this` for chaining (except `getHTML`, `getText`, `isSourceMode`, and `destroy`).
81
+
82
+ ## Toolbar Presets
83
+
84
+ ```js
85
+ new JotterJS('#el', { toolbar: JotterJS.presets.minimal });
86
+ new JotterJS('#el', { toolbar: JotterJS.presets.writing });
87
+ new JotterJS('#el', { toolbar: JotterJS.presets.full }); // the default toolbar
88
+ ```
89
+
90
+ Every preset is composed from `JotterJS.actions`, so a given command has the
91
+ same icon and tooltip whichever toolbar it appears in. Extend one by spreading:
92
+
93
+ ```js
94
+ new JotterJS('#el', {
95
+ toolbar: [
96
+ ...JotterJS.presets.minimal,
97
+ JotterJS.actions.sep,
98
+ JotterJS.actions.image,
99
+ ],
100
+ });
101
+ ```
102
+
103
+ ## Custom Toolbar
104
+
105
+ Action descriptors and preset arrays are frozen and shared between presets, so
106
+ customise by copying rather than mutating in place:
107
+
108
+ ```js
109
+ { ...JotterJS.actions.image, onClick: fn } // ✓
110
+ JotterJS.actions.image.onClick = fn // ✗ throws — would leak everywhere
111
+ ```
112
+
113
+ ```js
114
+ const { actions } = JotterJS;
115
+
116
+ new JotterJS('#el', {
117
+ toolbar: [
118
+ actions.bold,
119
+ actions.italic,
120
+ actions.sep,
121
+ {
122
+ icon: 'star',
123
+ title: 'Insert signature',
124
+ onClick: (editor) => editor.insertHTML('<p><em>— Sent with JotterJS</em></p>'),
125
+ },
126
+ {
127
+ label: 'Clear',
128
+ title: 'Clear content',
129
+ onClick: (editor) => editor.clear(),
130
+ },
131
+ ],
132
+ });
133
+ ```
134
+
135
+ ## Development
136
+
137
+ ```bash
138
+ npm run dev # start dev server
139
+ npm run build # build to dist/
140
+ ```
141
+
142
+ ## License
143
+
144
+ MIT
@@ -1,5 +1,5 @@
1
- var JotterJS=(()=>{var _=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var v=(p,t)=>{for(var e in t)_(p,e,{get:t[e],enumerable:!0})},E=(p,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of L(t))!y.call(p,o)&&o!==e&&_(p,o,{get:()=>t[o],enumerable:!(i=C(t,o))||i.enumerable});return p};var T=p=>E(_({},"__esModule",{value:!0}),p);var H={};v(H,{JotterJS:()=>u,default:()=>j});var b=[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},{type:"sep"},{cmd:"copy",icon:"content_copy",title:"Copy"},{cmd:"cut",icon:"content_cut",title:"Cut"},{cmd:"paste",icon:"content_paste",title:"Paste"},{type:"sep"},{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},{type:"sep"},{type:"blockformat"},{type:"fontfamily"},{type:"fontsize"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{cmd:"subscript",icon:"subscript",title:"Subscript"},{cmd:"superscript",icon:"superscript",title:"Superscript"},{custom:"code",icon:"code",title:"Inline Code"},{type:"sep"},{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},{type:"sep"},{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"},{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"},{type:"sep"},{type:"theme"},{type:"sep"},{type:"theme"}],g=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],S=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],x=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],k=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],M=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],f=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],w=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],u=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||b).forEach(e=>{let i=this._buildAction(e);i&&t.appendChild(i)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,e.appendChild(i)}return e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let o=window.prompt(t.prompt);o&&document.execCommand(t.cmd,!1,o)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",g.forEach(({label:e,tag:i})=>{let o=document.createElement("option");o.value=i,o.textContent=e,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),S.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=i,o.style.fontFamily=i,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),x.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=`${i}px`,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",f.forEach(({id:e,label:i})=>{let o=document.createElement("option");o.value=e,o.textContent=i,t.appendChild(o)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=t.cmd,i.title=t.title,i.setAttribute("aria-label",t.title);let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let n=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=n,i.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=n,a.tabIndex=-1,a.addEventListener("change",()=>{let r=a.value;s.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),i.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),a.click()}),e.appendChild(i),e.appendChild(a),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let i=document.createElement("span");return i.className="material-icons",i.textContent=t.icon,e.appendChild(i),e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,i){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=t.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`;let n=document.createElement("div");n.className="jotter-popup-hint",n.textContent="Hover to select size";let a=[];for(let r=0;r<o;r++)for(let c=0;c<i;c++){let l=document.createElement("span");l.className="jotter-table-cell",l.dataset.r=r,l.dataset.c=c,l.addEventListener("mouseenter",()=>{n.textContent=`${r+1} \xD7 ${c+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=r&&+d.dataset.c<=c)})}),l.addEventListener("click",()=>{this._insertTable(r+1,c+1),this._hidePopup()}),a.push(l),s.appendChild(l)}return t.appendChild(s),t.appendChild(n),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let i="<table><tbody>";for(let o=0;o<t;o++){i+="<tr>";for(let s=0;s<e;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,i),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,i="";if(this._savedRange){let c=window.getSelection();if(c&&c.rangeCount){i=c.toString();let l=c.anchorNode;for(;l&&l!==this._editor;){if(l.nodeName==="A"){e=l;break}l=l.parentNode}}}let o=this._makeField(t,"URL","url","https://"),s=this._makeField(t,"Link text (leave blank to keep selection)","text",""),n=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([c,l])=>{let d=document.createElement("option");d.value=l,d.textContent=c,r.appendChild(d)}),t.appendChild(a),t.appendChild(r),e?(o.value=e.getAttribute("href")||"",s.value=e.textContent||"",n.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):i&&(s.value=i),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let c=o.value.trim();if(!c)return;let l=s.value.trim()||i||c,d=n.value.trim(),h=r.value,m=`href="${this._esc(c)}"`;h&&(m+=` target="${this._esc(h)}"`),d&&(m+=` title="${this._esc(d)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=c,h?e.target=h:e.removeAttribute("target"),d?e.title=d:e.removeAttribute("title"),e.textContent=l):document.execCommand("insertHTML",!1,`<a ${m}>${this._esc(l)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),i=this._makeField(t,"Alt text","text","Descriptive text"),o=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=e.value.trim();if(!s)return;let n=i.value.trim(),a=o.value.trim(),r=a?`max-width:${a}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(s)}" alt="${this._esc(n)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let i=this._ytId(e.value.trim());if(!i){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let o=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${i}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let i=t.match(e);if(i)return i[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,t.appendChild(e),t.appendChild(i),t.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(k)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(M)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,o.addEventListener("mousedown",s=>{s.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(o)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),w.forEach(e=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=e.label,i.addEventListener("mousedown",o=>{o.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(i)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=e;let n=document.createElement("input");return n.type=i,n.className="jotter-popup-input",n.placeholder=o,t.appendChild(s),t.appendChild(n),n}_makeSubmitBtn(t,e){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=t,i.addEventListener("click",e),i}_esc(t){return t.replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),i=null;if(e&&e.rangeCount){let s=e.getRangeAt(0);s.startContainer===t&&(i=s.startOffset)}let o=document.createElement("p");if(t.replaceWith(o),o.appendChild(t),i!==null){let s=document.createRange();s.setStart(t,i),s.collapse(!0),e.removeAllRanges(),e.addRange(s)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1,"&nbsp;&nbsp;&nbsp;&nbsp;"))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
1
+ var JotterJS=(()=>{var b=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var y=(u,t)=>{for(var e in t)b(u,e,{get:t[e],enumerable:!0})},S=(u,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of E(t))!T.call(u,i)&&i!==e&&b(u,i,{get:()=>t[i],enumerable:!(o=L(t,i))||o.enumerable});return u};var x=u=>S(b({},"__esModule",{value:!0}),u);var I={};y(I,{JotterJS:()=>h,default:()=>R});var g={source:{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},sep:{type:"sep"},blockformat:{type:"blockformat"},fontfamily:{type:"fontfamily"},fontsize:{type:"fontsize"},theme:{type:"theme"},undo:{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},redo:{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},bold:{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},italic:{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},underline:{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},strike:{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},subscript:{cmd:"subscript",icon:"subscript",title:"Subscript"},superscript:{cmd:"superscript",icon:"superscript",title:"Superscript"},code:{custom:"code",icon:"code",title:"Inline Code"},copy:{cmd:"copy",icon:"content_copy",title:"Copy"},cut:{cmd:"cut",icon:"content_cut",title:"Cut"},paste:{cmd:"paste",icon:"content_paste",title:"Paste"},clearFormat:{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},alignLeft:{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},alignCenter:{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},alignRight:{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},bullets:{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},numbered:{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},link:{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},unlink:{cmd:"unlink",icon:"link_off",title:"Remove Link"},foreColor:{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},hiliteColor:{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},image:{type:"popup",id:"image",icon:"image",title:"Insert Image"},video:{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},table:{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},embed:{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},symbol:{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},specialChar:{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},lorem:{type:"popup",id:"lorem",icon:"history_edu",title:"Insert Lorem Ipsum"}};Object.values(g).forEach(Object.freeze);Object.freeze(g);var s=g,_={minimal:[s.source,s.sep,s.bold,s.italic,s.underline,s.sep,s.link,s.unlink],writing:[s.source,s.sep,s.undo,s.redo,s.sep,s.blockformat,s.sep,s.bold,s.italic,s.underline,s.strike,s.sep,s.bullets,s.numbered,s.sep,s.link,s.unlink,s.sep,s.image],full:[s.source,s.sep,s.undo,s.redo,s.sep,s.copy,s.cut,s.paste,s.sep,s.clearFormat,s.sep,s.blockformat,s.fontfamily,s.fontsize,s.sep,s.bold,s.italic,s.underline,s.strike,s.subscript,s.superscript,s.code,s.sep,s.foreColor,s.hiliteColor,s.sep,s.alignLeft,s.alignCenter,s.alignRight,s.sep,s.bullets,s.numbered,s.sep,s.link,s.unlink,s.sep,s.image,s.video,s.table,s.embed,s.symbol,s.specialChar,s.lorem,s.sep,s.theme]};Object.values(_).forEach(Object.freeze);Object.freeze(_);var M=_.full,C=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],w=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],k=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],j=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],H=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],v=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],N=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],h=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||M).forEach(e=>{let o=this._buildAction(e);o&&t.appendChild(o)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,e.appendChild(o)}return e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let i=window.prompt(t.prompt);i&&document.execCommand(t.cmd,!1,i)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",C.forEach(({label:e,tag:o})=>{let i=document.createElement("option");i.value=o,i.textContent=e,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),w.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=o,i.style.fontFamily=o,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),k.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=`${o}px`,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",v.forEach(({id:e,label:o})=>{let i=document.createElement("option");i.value=e,i.textContent=o,t.appendChild(i)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let o=document.createElement("button");o.type="button",o.className="jotter-btn jotter-color-btn",o.dataset.cmd=t.cmd,o.title=t.title,o.setAttribute("aria-label",t.title);let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,o.appendChild(i);let n=document.createElement("span");n.className="jotter-color-swatch";let a=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;n.style.background=a,o.appendChild(n);let l=document.createElement("input");return l.type="color",l.className="jotter-color-input",l.value=a,l.tabIndex=-1,l.addEventListener("change",()=>{let r=l.value;n.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),o.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),l.click()}),e.appendChild(o),e.appendChild(l),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let o=document.createElement("span");return o.className="material-icons",o.textContent=t.icon,e.appendChild(o),e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,o){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=o,this._popup.classList.add("jotter-popup--visible");let i=t.getBoundingClientRect();this._popup.style.top=i.bottom+6+"px",this._popup.style.left=i.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let n=this._popup.getBoundingClientRect();n.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,i.left-(n.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let o=10,i=8,n=document.createElement("div");n.className="jotter-table-grid",n.style.gridTemplateColumns=`repeat(${o}, 1fr)`;let a=document.createElement("div");a.className="jotter-popup-hint",a.textContent="Hover to select size";let l=[];for(let r=0;r<i;r++)for(let d=0;d<o;d++){let c=document.createElement("span");c.className="jotter-table-cell",c.dataset.r=r,c.dataset.c=d,c.addEventListener("mouseenter",()=>{a.textContent=`${r+1} \xD7 ${d+1} table`,l.forEach(p=>{p.classList.toggle("jotter-table-cell--active",+p.dataset.r<=r&&+p.dataset.c<=d)})}),c.addEventListener("click",()=>{this._insertTable(r+1,d+1),this._hidePopup()}),l.push(c),n.appendChild(c)}return t.appendChild(n),t.appendChild(a),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let o="<table><tbody>";for(let i=0;i<t;i++){o+="<tr>";for(let n=0;n<e;n++)o+=i===0?"<th><br></th>":"<td><br></td>";o+="</tr>"}o+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,o),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,o="";if(this._savedRange){let d=window.getSelection();if(d&&d.rangeCount){o=d.toString();let c=d.anchorNode;for(;c&&c!==this._editor;){if(c.nodeName==="A"){e=c;break}c=c.parentNode}}}let i=this._makeField(t,"URL","url","https://"),n=this._makeField(t,"Link text (leave blank to keep selection)","text",""),a=this._makeField(t,"Title / tooltip","text",""),l=document.createElement("label");l.className="jotter-popup-label",l.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([d,c])=>{let p=document.createElement("option");p.value=c,p.textContent=d,r.appendChild(p)}),t.appendChild(l),t.appendChild(r),e?(i.value=e.getAttribute("href")||"",n.value=e.textContent||"",a.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):o&&(n.value=o),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let d=i.value.trim();if(!d)return;let c=n.value.trim()||o||d,p=a.value.trim(),m=r.value,f=`href="${this._esc(d)}"`;m&&(f+=` target="${this._esc(m)}"`),p&&(f+=` title="${this._esc(p)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=d,m?e.target=m:e.removeAttribute("target"),p?e.title=p:e.removeAttribute("title"),e.textContent=c):document.execCommand("insertHTML",!1,`<a ${f}>${this._esc(c)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),o=this._makeField(t,"Alt text","text","Descriptive text"),i=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let n=e.value.trim();if(!n)return;let a=o.value.trim(),l=i.value.trim(),r=l?`max-width:${l}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(n)}" alt="${this._esc(a)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let o=this._ytId(e.value.trim());if(!o){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let i=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${o}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let o=t.match(e);if(o)return o[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let o=document.createElement("textarea");return o.className="jotter-popup-textarea",o.placeholder='<iframe src="..." ...></iframe>',o.rows=4,t.appendChild(e),t.appendChild(o),t.appendChild(this._makeSubmitBtn("Insert",()=>{let i=o.value.trim();i&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,i+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(j)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(H)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(o=>{let i=document.createElement("button");i.type="button",i.className="jotter-char-btn",i.textContent=o,i.title=`U+${o.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,i.addEventListener("mousedown",n=>{n.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(i)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),N.forEach(e=>{let o=document.createElement("button");o.type="button",o.className="jotter-lorem-btn",o.textContent=e.label,o.addEventListener("mousedown",i=>{i.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(o)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,o,i){let n=document.createElement("label");n.className="jotter-popup-label",n.textContent=e;let a=document.createElement("input");return a.type=o,a.className="jotter-popup-input",a.placeholder=i,t.appendChild(n),t.appendChild(a),a}_makeSubmitBtn(t,e){let o=document.createElement("button");return o.type="button",o.className="jotter-popup-submit",o.textContent=t,o.addEventListener("click",e),o}_esc(t){return t.replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{if(t.attributes.length>0)return;let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),o=null;if(e&&e.rangeCount){let n=e.getRangeAt(0);n.startContainer===t&&(o=n.startOffset)}let i=document.createElement("p");if(t.replaceWith(i),i.appendChild(t),o!==null){let n=document.createRange();n.setStart(t,o),n.collapse(!0),e.removeAllRanges(),e.addRange(n)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1,"&nbsp;&nbsp;&nbsp;&nbsp;"))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,o=" ",i=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),n=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
2
2
  $1
3
3
  `).split(`
4
- `).map(n=>n.trim()).filter(n=>n.length>0).map(n=>{let a=n.match(/^<\/(\w+)/),r=n.match(/^<(\w+)/),c=n.endsWith("/>"),l=r?r[1].toLowerCase():null,d=a?a[1].toLowerCase():null;d&&!s.has(d)&&(e=Math.max(0,e-1));let h=i.repeat(e)+n;return l&&!c&&!o.has(l)&&!d&&!s.has(l)&&e++,h}).join(`
5
- `)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(i=>i.remove()),e.querySelectorAll("*").forEach(i=>{Array.from(i.attributes).forEach(o=>{(o.name.startsWith("on")||["href","src","action","formaction","data"].includes(o.name)&&/^\s*javascript:/i.test(o.value))&&i.removeAttribute(o.name)})}),e.body.innerHTML}_toggleInlineCode(){let t=window.getSelection();if(!t||t.rangeCount===0)return;let e=t.getRangeAt(0),i=e.commonAncestorContainer;i.nodeType===3&&(i=i.parentNode);let o=i.closest?i.closest("code"):null;if(o){let s=o.parentNode;for(;o.firstChild;)s.insertBefore(o.firstChild,o);s.removeChild(o)}else if(e.collapsed){let s=document.createElement("code");s.innerHTML="&#8203;",e.insertNode(s);let n=document.createRange();n.setStart(s,0),n.setEnd(s,s.childNodes.length),t.removeAllRanges(),t.addRange(n)}else{let s=document.createElement("code");try{e.surroundContents(s)}catch{let a=e.extractContents();s.appendChild(a),e.insertNode(s)}}}_applyFontSize(t){let e=window.getSelection();if(!e||e.rangeCount===0)return;let i=e.getRangeAt(0);if(i.collapsed)return;let o=document.createElement("span");o.style.fontSize=t+"px";try{i.surroundContents(o)}catch{let n=i.extractContents();o.appendChild(n),i.insertNode(o)}}async _pasteFromClipboard(){try{if(navigator.clipboard&&navigator.clipboard.readText){let t=await navigator.clipboard.readText();document.execCommand("insertText",!1,t)}else document.execCommand("paste")}catch{}}_updateToolbarState(){this._toolbarEl.querySelectorAll(".jotter-btn[data-cmd]").forEach(e=>{try{e.classList.toggle("jotter-btn--active",document.queryCommandState(e.dataset.cmd))}catch{}});let t=this._toolbarEl.querySelector('[data-id="blockformat"]');if(t){let e=document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g,""),i=g.find(o=>o.tag===e);i&&(t.value=i.tag)}}_updateStatus(){let t=this._editor.innerText||"",e=t.trim()===""?0:t.trim().split(/\s+/).length,i=t.replace(/\n/g,"").length;this._wordCountEl.textContent=`${e} word${e!==1?"s":""}`,this._charCountEl.textContent=`${i} char${i!==1?"s":""}`}_saveRange(){let t=window.getSelection();return t&&t.rangeCount>0?t.getRangeAt(0).cloneRange():null}_restoreRange(t){if(!t)return;let e=window.getSelection();e.removeAllRanges(),e.addRange(t)}_emit(t,e){(this._listeners[t]||[]).forEach(i=>i(e))}getHTML(){return this._sourceMode?this._source.value:this._editor.innerHTML}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(t){return this._editor.focus(),document.execCommand("insertHTML",!1,this._sanitize(t)),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}insertText(t){return this._editor.focus(),document.execCommand("insertText",!1,t),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}setHTML(t){return this._editor.innerHTML=this._sanitize(t),this._updateStatus(),this}setTheme(t){let i=f.find(o=>o.id===t)?t:"default";return this._editor.dataset.theme=i,this._options.theme=i,this._themeSelect&&(this._themeSelect.value=i),this}setEnabled(t){return this._editor.contentEditable=String(t),this._root.classList.toggle("jotter--disabled",!t),this}on(t,e){return this._listeners[t]||(this._listeners[t]=[]),this._listeners[t].push(e),this}off(t,e){return this._listeners[t]&&(this._listeners[t]=this._listeners[t].filter(i=>i!==e)),this}destroy(){let t=this.getHTML();return this._hidePopup(),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=t,this._listeners={},t}};u.toolbar=b;u.actions={source:{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},sep:{type:"sep"},blockformat:{type:"blockformat"},fontfamily:{type:"fontfamily"},fontsize:{type:"fontsize"},theme:{type:"theme"},undo:{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},redo:{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},bold:{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},italic:{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},underline:{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},strike:{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},subscript:{cmd:"subscript",icon:"subscript",title:"Subscript"},superscript:{cmd:"superscript",icon:"superscript",title:"Superscript"},code:{custom:"code",icon:"code",title:"Inline Code"},copy:{cmd:"copy",icon:"content_copy",title:"Copy"},cut:{cmd:"cut",icon:"content_cut",title:"Cut"},paste:{cmd:"paste",icon:"content_paste",title:"Paste"},clearFormat:{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},alignLeft:{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},alignCenter:{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},alignRight:{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},bullets:{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},numbered:{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},link:{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},unlink:{cmd:"unlink",icon:"link_off",title:"Remove Link"},foreColor:{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},hiliteColor:{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},image:{type:"popup",id:"image",icon:"image",title:"Insert Image"},video:{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},table:{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},embed:{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},symbol:{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},specialChar:{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},lorem:{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"}};u.presets={minimal:[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold"},{cmd:"italic",icon:"format_italic",title:"Italic"},{cmd:"underline",icon:"format_underlined",title:"Underline"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"}],writing:[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo"},{cmd:"redo",icon:"redo",title:"Redo"},{type:"sep"},{type:"blockformat"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold"},{cmd:"italic",icon:"format_italic",title:"Italic"},{cmd:"underline",icon:"format_underlined",title:"Underline"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Ordered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"}]};var j=u;return T(H);})();
4
+ `).map(a=>a.trim()).filter(a=>a.length>0).map(a=>{let l=a.match(/^<\/(\w+)/),r=a.match(/^<(\w+)/),d=a.endsWith("/>"),c=r?r[1].toLowerCase():null,p=l?l[1].toLowerCase():null;p&&!n.has(p)&&(e=Math.max(0,e-1));let m=o.repeat(e)+a;return c&&!d&&!i.has(c)&&!p&&!n.has(c)&&e++,m}).join(`
5
+ `)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(o=>o.remove()),e.querySelectorAll("*").forEach(o=>{Array.from(o.attributes).forEach(i=>{(i.name.startsWith("on")||["href","src","action","formaction","data"].includes(i.name)&&/^\s*javascript:/i.test(i.value))&&o.removeAttribute(i.name)})}),e.body.innerHTML}_toggleInlineCode(){let t=window.getSelection();if(!t||t.rangeCount===0)return;let e=t.getRangeAt(0),o=e.commonAncestorContainer;o.nodeType===3&&(o=o.parentNode);let i=o.closest?o.closest("code"):null;if(i){let n=i.parentNode;for(;i.firstChild;)n.insertBefore(i.firstChild,i);n.removeChild(i)}else if(e.collapsed){let n=document.createElement("code");n.innerHTML="&#8203;",e.insertNode(n);let a=document.createRange();a.setStart(n,0),a.setEnd(n,n.childNodes.length),t.removeAllRanges(),t.addRange(a)}else{let n=document.createElement("code");try{e.surroundContents(n)}catch{let l=e.extractContents();n.appendChild(l),e.insertNode(n)}}}_applyFontSize(t){let e=window.getSelection();if(!e||e.rangeCount===0)return;let o=e.getRangeAt(0);if(o.collapsed)return;let i=document.createElement("span");i.style.fontSize=t+"px";try{o.surroundContents(i)}catch{let a=o.extractContents();i.appendChild(a),o.insertNode(i)}}async _pasteFromClipboard(){try{if(navigator.clipboard&&navigator.clipboard.readText){let t=await navigator.clipboard.readText();document.execCommand("insertText",!1,t)}else document.execCommand("paste")}catch{}}_updateToolbarState(){this._toolbarEl.querySelectorAll(".jotter-btn[data-cmd]").forEach(e=>{try{e.classList.toggle("jotter-btn--active",document.queryCommandState(e.dataset.cmd))}catch{}});let t=this._toolbarEl.querySelector('[data-id="blockformat"]');if(t){let e=document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g,""),o=C.find(i=>i.tag===e);o&&(t.value=o.tag)}}_updateStatus(){let t=this._editor.innerText||"",e=t.trim()===""?0:t.trim().split(/\s+/).length,o=t.replace(/\n/g,"").length;this._wordCountEl.textContent=`${e} word${e!==1?"s":""}`,this._charCountEl.textContent=`${o} char${o!==1?"s":""}`}_saveRange(){let t=window.getSelection();return t&&t.rangeCount>0?t.getRangeAt(0).cloneRange():null}_restoreRange(t){if(!t)return;let e=window.getSelection();e.removeAllRanges(),e.addRange(t)}_emit(t,e){(this._listeners[t]||[]).forEach(o=>o(e))}getHTML(){return this._sourceMode?this._source.value:this._editor.innerHTML}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(t){return this._editor.focus(),document.execCommand("insertHTML",!1,this._sanitize(t)),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}insertText(t){return this._editor.focus(),document.execCommand("insertText",!1,t),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}setHTML(t){return this._editor.innerHTML=this._sanitize(t),this._updateStatus(),this}setTheme(t){let o=v.find(i=>i.id===t)?t:"default";return this._editor.dataset.theme=o,this._options.theme=o,this._themeSelect&&(this._themeSelect.value=o),this}setEnabled(t){return this._editor.contentEditable=String(t),this._root.classList.toggle("jotter--disabled",!t),this}on(t,e){return this._listeners[t]||(this._listeners[t]=[]),this._listeners[t].push(e),this}off(t,e){return this._listeners[t]&&(this._listeners[t]=this._listeners[t].filter(o=>o!==e)),this}destroy(){let t=this.getHTML();return this._hidePopup(),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=t,this._listeners={},t}};h.toolbar=_.full;h.actions=g;h.presets=_;var R=h;return x(I);})();
package/dist/jotter.js CHANGED
@@ -1,53 +1,127 @@
1
1
  // src/jotter.js
2
- var TOOLBAR_ACTIONS = [
3
- { custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
4
- { type: "sep" },
5
- { cmd: "undo", icon: "undo", title: "Undo (Ctrl+Z)" },
6
- { cmd: "redo", icon: "redo", title: "Redo (Ctrl+Y)" },
7
- { type: "sep" },
8
- { cmd: "copy", icon: "content_copy", title: "Copy" },
9
- { cmd: "cut", icon: "content_cut", title: "Cut" },
10
- { cmd: "paste", icon: "content_paste", title: "Paste" },
11
- { type: "sep" },
12
- { cmd: "removeFormat", icon: "format_clear", title: "Clear Formatting" },
13
- { type: "sep" },
14
- { type: "blockformat" },
15
- { type: "fontfamily" },
16
- { type: "fontsize" },
17
- { type: "sep" },
18
- { cmd: "bold", icon: "format_bold", title: "Bold (Ctrl+B)" },
19
- { cmd: "italic", icon: "format_italic", title: "Italic (Ctrl+I)" },
20
- { cmd: "underline", icon: "format_underlined", title: "Underline (Ctrl+U)" },
21
- { cmd: "strikeThrough", icon: "strikethrough_s", title: "Strikethrough" },
22
- { cmd: "subscript", icon: "subscript", title: "Subscript" },
23
- { cmd: "superscript", icon: "superscript", title: "Superscript" },
24
- { custom: "code", icon: "code", title: "Inline Code" },
25
- { type: "sep" },
26
- { type: "color", cmd: "foreColor", icon: "format_color_text", title: "Text Color" },
27
- { type: "color", cmd: "hiliteColor", icon: "format_color_fill", title: "Background Color" },
28
- { type: "sep" },
29
- { cmd: "justifyLeft", icon: "format_align_left", title: "Align Left" },
30
- { cmd: "justifyCenter", icon: "format_align_center", title: "Align Center" },
31
- { cmd: "justifyRight", icon: "format_align_right", title: "Align Right" },
32
- { type: "sep" },
33
- { cmd: "insertUnorderedList", icon: "format_list_bulleted", title: "Bullet List" },
34
- { cmd: "insertOrderedList", icon: "format_list_numbered", title: "Numbered List" },
35
- { type: "sep" },
36
- { type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
37
- { cmd: "unlink", icon: "link_off", title: "Remove Link" },
38
- { type: "sep" },
39
- { type: "popup", id: "image", icon: "image", title: "Insert Image" },
40
- { type: "popup", id: "video", icon: "smart_display", title: "Insert YouTube Video" },
41
- { type: "popup", id: "table", icon: "table_chart", title: "Insert Table" },
42
- { type: "popup", id: "embed", icon: "html", title: "Insert Embed" },
43
- { type: "popup", id: "symbol", icon: "emoji_symbols", title: "Insert Symbol" },
44
- { type: "popup", id: "specialchar", icon: "format_shapes", title: "Special Characters" },
45
- { type: "popup", id: "lorem", icon: "article", title: "Insert Lorem Ipsum" },
46
- { type: "sep" },
47
- { type: "theme" },
48
- { type: "sep" },
49
- { type: "theme" }
50
- ];
2
+ var ACTIONS = {
3
+ source: { custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
4
+ sep: { type: "sep" },
5
+ blockformat: { type: "blockformat" },
6
+ fontfamily: { type: "fontfamily" },
7
+ fontsize: { type: "fontsize" },
8
+ theme: { type: "theme" },
9
+ undo: { cmd: "undo", icon: "undo", title: "Undo (Ctrl+Z)" },
10
+ redo: { cmd: "redo", icon: "redo", title: "Redo (Ctrl+Y)" },
11
+ bold: { cmd: "bold", icon: "format_bold", title: "Bold (Ctrl+B)" },
12
+ italic: { cmd: "italic", icon: "format_italic", title: "Italic (Ctrl+I)" },
13
+ underline: { cmd: "underline", icon: "format_underlined", title: "Underline (Ctrl+U)" },
14
+ strike: { cmd: "strikeThrough", icon: "strikethrough_s", title: "Strikethrough" },
15
+ subscript: { cmd: "subscript", icon: "subscript", title: "Subscript" },
16
+ superscript: { cmd: "superscript", icon: "superscript", title: "Superscript" },
17
+ code: { custom: "code", icon: "code", title: "Inline Code" },
18
+ copy: { cmd: "copy", icon: "content_copy", title: "Copy" },
19
+ cut: { cmd: "cut", icon: "content_cut", title: "Cut" },
20
+ paste: { cmd: "paste", icon: "content_paste", title: "Paste" },
21
+ clearFormat: { cmd: "removeFormat", icon: "format_clear", title: "Clear Formatting" },
22
+ alignLeft: { cmd: "justifyLeft", icon: "format_align_left", title: "Align Left" },
23
+ alignCenter: { cmd: "justifyCenter", icon: "format_align_center", title: "Align Center" },
24
+ alignRight: { cmd: "justifyRight", icon: "format_align_right", title: "Align Right" },
25
+ bullets: { cmd: "insertUnorderedList", icon: "format_list_bulleted", title: "Bullet List" },
26
+ numbered: { cmd: "insertOrderedList", icon: "format_list_numbered", title: "Numbered List" },
27
+ link: { type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
28
+ unlink: { cmd: "unlink", icon: "link_off", title: "Remove Link" },
29
+ foreColor: { type: "color", cmd: "foreColor", icon: "format_color_text", title: "Text Color" },
30
+ hiliteColor: { type: "color", cmd: "hiliteColor", icon: "format_color_fill", title: "Background Color" },
31
+ image: { type: "popup", id: "image", icon: "image", title: "Insert Image" },
32
+ video: { type: "popup", id: "video", icon: "smart_display", title: "Insert YouTube Video" },
33
+ table: { type: "popup", id: "table", icon: "table_chart", title: "Insert Table" },
34
+ embed: { type: "popup", id: "embed", icon: "html", title: "Insert Embed" },
35
+ symbol: { type: "popup", id: "symbol", icon: "emoji_symbols", title: "Insert Symbol" },
36
+ specialChar: { type: "popup", id: "specialchar", icon: "format_shapes", title: "Special Characters" },
37
+ lorem: { type: "popup", id: "lorem", icon: "history_edu", title: "Insert Lorem Ipsum" }
38
+ };
39
+ Object.values(ACTIONS).forEach(Object.freeze);
40
+ Object.freeze(ACTIONS);
41
+ var A = ACTIONS;
42
+ var PRESETS = {
43
+ minimal: [
44
+ A.source,
45
+ A.sep,
46
+ A.bold,
47
+ A.italic,
48
+ A.underline,
49
+ A.sep,
50
+ A.link,
51
+ A.unlink
52
+ ],
53
+ writing: [
54
+ A.source,
55
+ A.sep,
56
+ A.undo,
57
+ A.redo,
58
+ A.sep,
59
+ A.blockformat,
60
+ A.sep,
61
+ A.bold,
62
+ A.italic,
63
+ A.underline,
64
+ A.strike,
65
+ A.sep,
66
+ A.bullets,
67
+ A.numbered,
68
+ A.sep,
69
+ A.link,
70
+ A.unlink,
71
+ A.sep,
72
+ A.image
73
+ ],
74
+ full: [
75
+ A.source,
76
+ A.sep,
77
+ A.undo,
78
+ A.redo,
79
+ A.sep,
80
+ A.copy,
81
+ A.cut,
82
+ A.paste,
83
+ A.sep,
84
+ A.clearFormat,
85
+ A.sep,
86
+ A.blockformat,
87
+ A.fontfamily,
88
+ A.fontsize,
89
+ A.sep,
90
+ A.bold,
91
+ A.italic,
92
+ A.underline,
93
+ A.strike,
94
+ A.subscript,
95
+ A.superscript,
96
+ A.code,
97
+ A.sep,
98
+ A.foreColor,
99
+ A.hiliteColor,
100
+ A.sep,
101
+ A.alignLeft,
102
+ A.alignCenter,
103
+ A.alignRight,
104
+ A.sep,
105
+ A.bullets,
106
+ A.numbered,
107
+ A.sep,
108
+ A.link,
109
+ A.unlink,
110
+ A.sep,
111
+ A.image,
112
+ A.video,
113
+ A.table,
114
+ A.embed,
115
+ A.symbol,
116
+ A.specialChar,
117
+ A.lorem,
118
+ A.sep,
119
+ A.theme
120
+ ]
121
+ };
122
+ Object.values(PRESETS).forEach(Object.freeze);
123
+ Object.freeze(PRESETS);
124
+ var TOOLBAR_ACTIONS = PRESETS.full;
51
125
  var HEADING_OPTIONS = [
52
126
  { label: "Paragraph", tag: "p" },
53
127
  { label: "Heading 1", tag: "h1" },
@@ -983,6 +1057,8 @@ var JotterJS = class {
983
1057
  _bindEvents() {
984
1058
  this._editor.addEventListener("input", () => {
985
1059
  this._editor.querySelectorAll(":scope > div").forEach((d) => {
1060
+ if (d.attributes.length > 0)
1061
+ return;
986
1062
  const p = document.createElement("p");
987
1063
  p.innerHTML = d.innerHTML;
988
1064
  d.replaceWith(p);
@@ -1319,77 +1395,9 @@ var JotterJS = class {
1319
1395
  return html;
1320
1396
  }
1321
1397
  };
1322
- JotterJS.toolbar = TOOLBAR_ACTIONS;
1323
- JotterJS.actions = {
1324
- source: { custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
1325
- sep: { type: "sep" },
1326
- blockformat: { type: "blockformat" },
1327
- fontfamily: { type: "fontfamily" },
1328
- fontsize: { type: "fontsize" },
1329
- theme: { type: "theme" },
1330
- undo: { cmd: "undo", icon: "undo", title: "Undo (Ctrl+Z)" },
1331
- redo: { cmd: "redo", icon: "redo", title: "Redo (Ctrl+Y)" },
1332
- bold: { cmd: "bold", icon: "format_bold", title: "Bold (Ctrl+B)" },
1333
- italic: { cmd: "italic", icon: "format_italic", title: "Italic (Ctrl+I)" },
1334
- underline: { cmd: "underline", icon: "format_underlined", title: "Underline (Ctrl+U)" },
1335
- strike: { cmd: "strikeThrough", icon: "strikethrough_s", title: "Strikethrough" },
1336
- subscript: { cmd: "subscript", icon: "subscript", title: "Subscript" },
1337
- superscript: { cmd: "superscript", icon: "superscript", title: "Superscript" },
1338
- code: { custom: "code", icon: "code", title: "Inline Code" },
1339
- copy: { cmd: "copy", icon: "content_copy", title: "Copy" },
1340
- cut: { cmd: "cut", icon: "content_cut", title: "Cut" },
1341
- paste: { cmd: "paste", icon: "content_paste", title: "Paste" },
1342
- clearFormat: { cmd: "removeFormat", icon: "format_clear", title: "Clear Formatting" },
1343
- alignLeft: { cmd: "justifyLeft", icon: "format_align_left", title: "Align Left" },
1344
- alignCenter: { cmd: "justifyCenter", icon: "format_align_center", title: "Align Center" },
1345
- alignRight: { cmd: "justifyRight", icon: "format_align_right", title: "Align Right" },
1346
- bullets: { cmd: "insertUnorderedList", icon: "format_list_bulleted", title: "Bullet List" },
1347
- numbered: { cmd: "insertOrderedList", icon: "format_list_numbered", title: "Numbered List" },
1348
- link: { type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
1349
- unlink: { cmd: "unlink", icon: "link_off", title: "Remove Link" },
1350
- foreColor: { type: "color", cmd: "foreColor", icon: "format_color_text", title: "Text Color" },
1351
- hiliteColor: { type: "color", cmd: "hiliteColor", icon: "format_color_fill", title: "Background Color" },
1352
- image: { type: "popup", id: "image", icon: "image", title: "Insert Image" },
1353
- video: { type: "popup", id: "video", icon: "smart_display", title: "Insert YouTube Video" },
1354
- table: { type: "popup", id: "table", icon: "table_chart", title: "Insert Table" },
1355
- embed: { type: "popup", id: "embed", icon: "html", title: "Insert Embed" },
1356
- symbol: { type: "popup", id: "symbol", icon: "emoji_symbols", title: "Insert Symbol" },
1357
- specialChar: { type: "popup", id: "specialchar", icon: "format_shapes", title: "Special Characters" },
1358
- lorem: { type: "popup", id: "lorem", icon: "article", title: "Insert Lorem Ipsum" }
1359
- };
1360
- JotterJS.presets = {
1361
- minimal: [
1362
- { custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
1363
- { type: "sep" },
1364
- { cmd: "bold", icon: "format_bold", title: "Bold" },
1365
- { cmd: "italic", icon: "format_italic", title: "Italic" },
1366
- { cmd: "underline", icon: "format_underlined", title: "Underline" },
1367
- { type: "sep" },
1368
- { type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
1369
- { cmd: "unlink", icon: "link_off", title: "Remove Link" }
1370
- ],
1371
- writing: [
1372
- { custom: "toggleSource", label: "Source", title: "Edit HTML Source" },
1373
- { type: "sep" },
1374
- { cmd: "undo", icon: "undo", title: "Undo" },
1375
- { cmd: "redo", icon: "redo", title: "Redo" },
1376
- { type: "sep" },
1377
- { type: "blockformat" },
1378
- { type: "sep" },
1379
- { cmd: "bold", icon: "format_bold", title: "Bold" },
1380
- { cmd: "italic", icon: "format_italic", title: "Italic" },
1381
- { cmd: "underline", icon: "format_underlined", title: "Underline" },
1382
- { cmd: "strikeThrough", icon: "strikethrough_s", title: "Strikethrough" },
1383
- { type: "sep" },
1384
- { cmd: "insertUnorderedList", icon: "format_list_bulleted", title: "Bullet List" },
1385
- { cmd: "insertOrderedList", icon: "format_list_numbered", title: "Ordered List" },
1386
- { type: "sep" },
1387
- { type: "popup", id: "link", icon: "insert_link", title: "Insert Link" },
1388
- { cmd: "unlink", icon: "link_off", title: "Remove Link" },
1389
- { type: "sep" },
1390
- { type: "popup", id: "image", icon: "image", title: "Insert Image" }
1391
- ]
1392
- };
1398
+ JotterJS.toolbar = PRESETS.full;
1399
+ JotterJS.actions = ACTIONS;
1400
+ JotterJS.presets = PRESETS;
1393
1401
  var jotter_default = JotterJS;
1394
1402
  export {
1395
1403
  JotterJS,
@@ -1,5 +1,5 @@
1
- var g=[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},{type:"sep"},{cmd:"copy",icon:"content_copy",title:"Copy"},{cmd:"cut",icon:"content_cut",title:"Cut"},{cmd:"paste",icon:"content_paste",title:"Paste"},{type:"sep"},{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},{type:"sep"},{type:"blockformat"},{type:"fontfamily"},{type:"fontsize"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{cmd:"subscript",icon:"subscript",title:"Subscript"},{cmd:"superscript",icon:"superscript",title:"Superscript"},{custom:"code",icon:"code",title:"Inline Code"},{type:"sep"},{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},{type:"sep"},{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"},{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"},{type:"sep"},{type:"theme"},{type:"sep"},{type:"theme"}],m=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],f=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],b=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],C=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],L=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],_=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],y=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],u=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||g).forEach(e=>{let i=this._buildAction(e);i&&t.appendChild(i)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,e.appendChild(i)}return e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let o=window.prompt(t.prompt);o&&document.execCommand(t.cmd,!1,o)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",m.forEach(({label:e,tag:i})=>{let o=document.createElement("option");o.value=i,o.textContent=e,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),f.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=i,o.style.fontFamily=i,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),b.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=`${i}px`,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",_.forEach(({id:e,label:i})=>{let o=document.createElement("option");o.value=e,o.textContent=i,t.appendChild(o)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=t.cmd,i.title=t.title,i.setAttribute("aria-label",t.title);let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let n=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=n,i.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=n,a.tabIndex=-1,a.addEventListener("change",()=>{let r=a.value;s.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),i.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),a.click()}),e.appendChild(i),e.appendChild(a),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let i=document.createElement("span");return i.className="material-icons",i.textContent=t.icon,e.appendChild(i),e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,i){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=t.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`;let n=document.createElement("div");n.className="jotter-popup-hint",n.textContent="Hover to select size";let a=[];for(let r=0;r<o;r++)for(let c=0;c<i;c++){let l=document.createElement("span");l.className="jotter-table-cell",l.dataset.r=r,l.dataset.c=c,l.addEventListener("mouseenter",()=>{n.textContent=`${r+1} \xD7 ${c+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=r&&+d.dataset.c<=c)})}),l.addEventListener("click",()=>{this._insertTable(r+1,c+1),this._hidePopup()}),a.push(l),s.appendChild(l)}return t.appendChild(s),t.appendChild(n),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let i="<table><tbody>";for(let o=0;o<t;o++){i+="<tr>";for(let s=0;s<e;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,i),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,i="";if(this._savedRange){let c=window.getSelection();if(c&&c.rangeCount){i=c.toString();let l=c.anchorNode;for(;l&&l!==this._editor;){if(l.nodeName==="A"){e=l;break}l=l.parentNode}}}let o=this._makeField(t,"URL","url","https://"),s=this._makeField(t,"Link text (leave blank to keep selection)","text",""),n=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([c,l])=>{let d=document.createElement("option");d.value=l,d.textContent=c,r.appendChild(d)}),t.appendChild(a),t.appendChild(r),e?(o.value=e.getAttribute("href")||"",s.value=e.textContent||"",n.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):i&&(s.value=i),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let c=o.value.trim();if(!c)return;let l=s.value.trim()||i||c,d=n.value.trim(),p=r.value,h=`href="${this._esc(c)}"`;p&&(h+=` target="${this._esc(p)}"`),d&&(h+=` title="${this._esc(d)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=c,p?e.target=p:e.removeAttribute("target"),d?e.title=d:e.removeAttribute("title"),e.textContent=l):document.execCommand("insertHTML",!1,`<a ${h}>${this._esc(l)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),i=this._makeField(t,"Alt text","text","Descriptive text"),o=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=e.value.trim();if(!s)return;let n=i.value.trim(),a=o.value.trim(),r=a?`max-width:${a}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(s)}" alt="${this._esc(n)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let i=this._ytId(e.value.trim());if(!i){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let o=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${i}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let i=t.match(e);if(i)return i[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,t.appendChild(e),t.appendChild(i),t.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(C)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(L)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,o.addEventListener("mousedown",s=>{s.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(o)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),y.forEach(e=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=e.label,i.addEventListener("mousedown",o=>{o.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(i)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=e;let n=document.createElement("input");return n.type=i,n.className="jotter-popup-input",n.placeholder=o,t.appendChild(s),t.appendChild(n),n}_makeSubmitBtn(t,e){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=t,i.addEventListener("click",e),i}_esc(t){return t.replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),i=null;if(e&&e.rangeCount){let s=e.getRangeAt(0);s.startContainer===t&&(i=s.startOffset)}let o=document.createElement("p");if(t.replaceWith(o),o.appendChild(t),i!==null){let s=document.createRange();s.setStart(t,i),s.collapse(!0),e.removeAllRanges(),e.addRange(s)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1,"&nbsp;&nbsp;&nbsp;&nbsp;"))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
1
+ var _={source:{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},sep:{type:"sep"},blockformat:{type:"blockformat"},fontfamily:{type:"fontfamily"},fontsize:{type:"fontsize"},theme:{type:"theme"},undo:{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},redo:{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},bold:{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},italic:{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},underline:{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},strike:{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},subscript:{cmd:"subscript",icon:"subscript",title:"Subscript"},superscript:{cmd:"superscript",icon:"superscript",title:"Superscript"},code:{custom:"code",icon:"code",title:"Inline Code"},copy:{cmd:"copy",icon:"content_copy",title:"Copy"},cut:{cmd:"cut",icon:"content_cut",title:"Cut"},paste:{cmd:"paste",icon:"content_paste",title:"Paste"},clearFormat:{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},alignLeft:{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},alignCenter:{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},alignRight:{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},bullets:{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},numbered:{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},link:{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},unlink:{cmd:"unlink",icon:"link_off",title:"Remove Link"},foreColor:{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},hiliteColor:{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},image:{type:"popup",id:"image",icon:"image",title:"Insert Image"},video:{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},table:{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},embed:{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},symbol:{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},specialChar:{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},lorem:{type:"popup",id:"lorem",icon:"history_edu",title:"Insert Lorem Ipsum"}};Object.values(_).forEach(Object.freeze);Object.freeze(_);var s=_,m={minimal:[s.source,s.sep,s.bold,s.italic,s.underline,s.sep,s.link,s.unlink],writing:[s.source,s.sep,s.undo,s.redo,s.sep,s.blockformat,s.sep,s.bold,s.italic,s.underline,s.strike,s.sep,s.bullets,s.numbered,s.sep,s.link,s.unlink,s.sep,s.image],full:[s.source,s.sep,s.undo,s.redo,s.sep,s.copy,s.cut,s.paste,s.sep,s.clearFormat,s.sep,s.blockformat,s.fontfamily,s.fontsize,s.sep,s.bold,s.italic,s.underline,s.strike,s.subscript,s.superscript,s.code,s.sep,s.foreColor,s.hiliteColor,s.sep,s.alignLeft,s.alignCenter,s.alignRight,s.sep,s.bullets,s.numbered,s.sep,s.link,s.unlink,s.sep,s.image,s.video,s.table,s.embed,s.symbol,s.specialChar,s.lorem,s.sep,s.theme]};Object.values(m).forEach(Object.freeze);Object.freeze(m);var C=m.full,f=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],v=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],L=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],E=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],T=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],b=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],y=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],h=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||C).forEach(e=>{let o=this._buildAction(e);o&&t.appendChild(o)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,e.appendChild(o)}return e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let i=window.prompt(t.prompt);i&&document.execCommand(t.cmd,!1,i)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",f.forEach(({label:e,tag:o})=>{let i=document.createElement("option");i.value=o,i.textContent=e,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),v.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=o,i.style.fontFamily=o,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),L.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=`${o}px`,t.appendChild(i)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",b.forEach(({id:e,label:o})=>{let i=document.createElement("option");i.value=e,i.textContent=o,t.appendChild(i)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let o=document.createElement("button");o.type="button",o.className="jotter-btn jotter-color-btn",o.dataset.cmd=t.cmd,o.title=t.title,o.setAttribute("aria-label",t.title);let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,o.appendChild(i);let n=document.createElement("span");n.className="jotter-color-swatch";let a=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;n.style.background=a,o.appendChild(n);let l=document.createElement("input");return l.type="color",l.className="jotter-color-input",l.value=a,l.tabIndex=-1,l.addEventListener("change",()=>{let r=l.value;n.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),o.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),l.click()}),e.appendChild(o),e.appendChild(l),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let o=document.createElement("span");return o.className="material-icons",o.textContent=t.icon,e.appendChild(o),e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,o){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=o,this._popup.classList.add("jotter-popup--visible");let i=t.getBoundingClientRect();this._popup.style.top=i.bottom+6+"px",this._popup.style.left=i.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let n=this._popup.getBoundingClientRect();n.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,i.left-(n.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let o=10,i=8,n=document.createElement("div");n.className="jotter-table-grid",n.style.gridTemplateColumns=`repeat(${o}, 1fr)`;let a=document.createElement("div");a.className="jotter-popup-hint",a.textContent="Hover to select size";let l=[];for(let r=0;r<i;r++)for(let d=0;d<o;d++){let c=document.createElement("span");c.className="jotter-table-cell",c.dataset.r=r,c.dataset.c=d,c.addEventListener("mouseenter",()=>{a.textContent=`${r+1} \xD7 ${d+1} table`,l.forEach(p=>{p.classList.toggle("jotter-table-cell--active",+p.dataset.r<=r&&+p.dataset.c<=d)})}),c.addEventListener("click",()=>{this._insertTable(r+1,d+1),this._hidePopup()}),l.push(c),n.appendChild(c)}return t.appendChild(n),t.appendChild(a),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let o="<table><tbody>";for(let i=0;i<t;i++){o+="<tr>";for(let n=0;n<e;n++)o+=i===0?"<th><br></th>":"<td><br></td>";o+="</tr>"}o+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,o),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,o="";if(this._savedRange){let d=window.getSelection();if(d&&d.rangeCount){o=d.toString();let c=d.anchorNode;for(;c&&c!==this._editor;){if(c.nodeName==="A"){e=c;break}c=c.parentNode}}}let i=this._makeField(t,"URL","url","https://"),n=this._makeField(t,"Link text (leave blank to keep selection)","text",""),a=this._makeField(t,"Title / tooltip","text",""),l=document.createElement("label");l.className="jotter-popup-label",l.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([d,c])=>{let p=document.createElement("option");p.value=c,p.textContent=d,r.appendChild(p)}),t.appendChild(l),t.appendChild(r),e?(i.value=e.getAttribute("href")||"",n.value=e.textContent||"",a.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):o&&(n.value=o),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let d=i.value.trim();if(!d)return;let c=n.value.trim()||o||d,p=a.value.trim(),u=r.value,g=`href="${this._esc(d)}"`;u&&(g+=` target="${this._esc(u)}"`),p&&(g+=` title="${this._esc(p)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=d,u?e.target=u:e.removeAttribute("target"),p?e.title=p:e.removeAttribute("title"),e.textContent=c):document.execCommand("insertHTML",!1,`<a ${g}>${this._esc(c)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),o=this._makeField(t,"Alt text","text","Descriptive text"),i=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let n=e.value.trim();if(!n)return;let a=o.value.trim(),l=i.value.trim(),r=l?`max-width:${l}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(n)}" alt="${this._esc(a)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let o=this._ytId(e.value.trim());if(!o){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let i=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${o}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let o=t.match(e);if(o)return o[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let o=document.createElement("textarea");return o.className="jotter-popup-textarea",o.placeholder='<iframe src="..." ...></iframe>',o.rows=4,t.appendChild(e),t.appendChild(o),t.appendChild(this._makeSubmitBtn("Insert",()=>{let i=o.value.trim();i&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,i+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(E)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(T)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(o=>{let i=document.createElement("button");i.type="button",i.className="jotter-char-btn",i.textContent=o,i.title=`U+${o.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,i.addEventListener("mousedown",n=>{n.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(i)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),y.forEach(e=>{let o=document.createElement("button");o.type="button",o.className="jotter-lorem-btn",o.textContent=e.label,o.addEventListener("mousedown",i=>{i.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(o)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,o,i){let n=document.createElement("label");n.className="jotter-popup-label",n.textContent=e;let a=document.createElement("input");return a.type=o,a.className="jotter-popup-input",a.placeholder=i,t.appendChild(n),t.appendChild(a),a}_makeSubmitBtn(t,e){let o=document.createElement("button");return o.type="button",o.className="jotter-popup-submit",o.textContent=t,o.addEventListener("click",e),o}_esc(t){return t.replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{if(t.attributes.length>0)return;let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),o=null;if(e&&e.rangeCount){let n=e.getRangeAt(0);n.startContainer===t&&(o=n.startOffset)}let i=document.createElement("p");if(t.replaceWith(i),i.appendChild(t),o!==null){let n=document.createRange();n.setStart(t,o),n.collapse(!0),e.removeAllRanges(),e.addRange(n)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1,"&nbsp;&nbsp;&nbsp;&nbsp;"))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,o=" ",i=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),n=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
2
2
  $1
3
3
  `).split(`
4
- `).map(n=>n.trim()).filter(n=>n.length>0).map(n=>{let a=n.match(/^<\/(\w+)/),r=n.match(/^<(\w+)/),c=n.endsWith("/>"),l=r?r[1].toLowerCase():null,d=a?a[1].toLowerCase():null;d&&!s.has(d)&&(e=Math.max(0,e-1));let p=i.repeat(e)+n;return l&&!c&&!o.has(l)&&!d&&!s.has(l)&&e++,p}).join(`
5
- `)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(i=>i.remove()),e.querySelectorAll("*").forEach(i=>{Array.from(i.attributes).forEach(o=>{(o.name.startsWith("on")||["href","src","action","formaction","data"].includes(o.name)&&/^\s*javascript:/i.test(o.value))&&i.removeAttribute(o.name)})}),e.body.innerHTML}_toggleInlineCode(){let t=window.getSelection();if(!t||t.rangeCount===0)return;let e=t.getRangeAt(0),i=e.commonAncestorContainer;i.nodeType===3&&(i=i.parentNode);let o=i.closest?i.closest("code"):null;if(o){let s=o.parentNode;for(;o.firstChild;)s.insertBefore(o.firstChild,o);s.removeChild(o)}else if(e.collapsed){let s=document.createElement("code");s.innerHTML="&#8203;",e.insertNode(s);let n=document.createRange();n.setStart(s,0),n.setEnd(s,s.childNodes.length),t.removeAllRanges(),t.addRange(n)}else{let s=document.createElement("code");try{e.surroundContents(s)}catch{let a=e.extractContents();s.appendChild(a),e.insertNode(s)}}}_applyFontSize(t){let e=window.getSelection();if(!e||e.rangeCount===0)return;let i=e.getRangeAt(0);if(i.collapsed)return;let o=document.createElement("span");o.style.fontSize=t+"px";try{i.surroundContents(o)}catch{let n=i.extractContents();o.appendChild(n),i.insertNode(o)}}async _pasteFromClipboard(){try{if(navigator.clipboard&&navigator.clipboard.readText){let t=await navigator.clipboard.readText();document.execCommand("insertText",!1,t)}else document.execCommand("paste")}catch{}}_updateToolbarState(){this._toolbarEl.querySelectorAll(".jotter-btn[data-cmd]").forEach(e=>{try{e.classList.toggle("jotter-btn--active",document.queryCommandState(e.dataset.cmd))}catch{}});let t=this._toolbarEl.querySelector('[data-id="blockformat"]');if(t){let e=document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g,""),i=m.find(o=>o.tag===e);i&&(t.value=i.tag)}}_updateStatus(){let t=this._editor.innerText||"",e=t.trim()===""?0:t.trim().split(/\s+/).length,i=t.replace(/\n/g,"").length;this._wordCountEl.textContent=`${e} word${e!==1?"s":""}`,this._charCountEl.textContent=`${i} char${i!==1?"s":""}`}_saveRange(){let t=window.getSelection();return t&&t.rangeCount>0?t.getRangeAt(0).cloneRange():null}_restoreRange(t){if(!t)return;let e=window.getSelection();e.removeAllRanges(),e.addRange(t)}_emit(t,e){(this._listeners[t]||[]).forEach(i=>i(e))}getHTML(){return this._sourceMode?this._source.value:this._editor.innerHTML}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(t){return this._editor.focus(),document.execCommand("insertHTML",!1,this._sanitize(t)),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}insertText(t){return this._editor.focus(),document.execCommand("insertText",!1,t),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}setHTML(t){return this._editor.innerHTML=this._sanitize(t),this._updateStatus(),this}setTheme(t){let i=_.find(o=>o.id===t)?t:"default";return this._editor.dataset.theme=i,this._options.theme=i,this._themeSelect&&(this._themeSelect.value=i),this}setEnabled(t){return this._editor.contentEditable=String(t),this._root.classList.toggle("jotter--disabled",!t),this}on(t,e){return this._listeners[t]||(this._listeners[t]=[]),this._listeners[t].push(e),this}off(t,e){return this._listeners[t]&&(this._listeners[t]=this._listeners[t].filter(i=>i!==e)),this}destroy(){let t=this.getHTML();return this._hidePopup(),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=t,this._listeners={},t}};u.toolbar=g;u.actions={source:{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},sep:{type:"sep"},blockformat:{type:"blockformat"},fontfamily:{type:"fontfamily"},fontsize:{type:"fontsize"},theme:{type:"theme"},undo:{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},redo:{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},bold:{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},italic:{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},underline:{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},strike:{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},subscript:{cmd:"subscript",icon:"subscript",title:"Subscript"},superscript:{cmd:"superscript",icon:"superscript",title:"Superscript"},code:{custom:"code",icon:"code",title:"Inline Code"},copy:{cmd:"copy",icon:"content_copy",title:"Copy"},cut:{cmd:"cut",icon:"content_cut",title:"Cut"},paste:{cmd:"paste",icon:"content_paste",title:"Paste"},clearFormat:{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},alignLeft:{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},alignCenter:{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},alignRight:{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},bullets:{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},numbered:{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},link:{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},unlink:{cmd:"unlink",icon:"link_off",title:"Remove Link"},foreColor:{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},hiliteColor:{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},image:{type:"popup",id:"image",icon:"image",title:"Insert Image"},video:{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},table:{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},embed:{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},symbol:{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},specialChar:{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},lorem:{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"}};u.presets={minimal:[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold"},{cmd:"italic",icon:"format_italic",title:"Italic"},{cmd:"underline",icon:"format_underlined",title:"Underline"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"}],writing:[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo"},{cmd:"redo",icon:"redo",title:"Redo"},{type:"sep"},{type:"blockformat"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold"},{cmd:"italic",icon:"format_italic",title:"Italic"},{cmd:"underline",icon:"format_underlined",title:"Underline"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Ordered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"}]};var E=u;export{u as JotterJS,E as default};
4
+ `).map(a=>a.trim()).filter(a=>a.length>0).map(a=>{let l=a.match(/^<\/(\w+)/),r=a.match(/^<(\w+)/),d=a.endsWith("/>"),c=r?r[1].toLowerCase():null,p=l?l[1].toLowerCase():null;p&&!n.has(p)&&(e=Math.max(0,e-1));let u=o.repeat(e)+a;return c&&!d&&!i.has(c)&&!p&&!n.has(c)&&e++,u}).join(`
5
+ `)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(o=>o.remove()),e.querySelectorAll("*").forEach(o=>{Array.from(o.attributes).forEach(i=>{(i.name.startsWith("on")||["href","src","action","formaction","data"].includes(i.name)&&/^\s*javascript:/i.test(i.value))&&o.removeAttribute(i.name)})}),e.body.innerHTML}_toggleInlineCode(){let t=window.getSelection();if(!t||t.rangeCount===0)return;let e=t.getRangeAt(0),o=e.commonAncestorContainer;o.nodeType===3&&(o=o.parentNode);let i=o.closest?o.closest("code"):null;if(i){let n=i.parentNode;for(;i.firstChild;)n.insertBefore(i.firstChild,i);n.removeChild(i)}else if(e.collapsed){let n=document.createElement("code");n.innerHTML="&#8203;",e.insertNode(n);let a=document.createRange();a.setStart(n,0),a.setEnd(n,n.childNodes.length),t.removeAllRanges(),t.addRange(a)}else{let n=document.createElement("code");try{e.surroundContents(n)}catch{let l=e.extractContents();n.appendChild(l),e.insertNode(n)}}}_applyFontSize(t){let e=window.getSelection();if(!e||e.rangeCount===0)return;let o=e.getRangeAt(0);if(o.collapsed)return;let i=document.createElement("span");i.style.fontSize=t+"px";try{o.surroundContents(i)}catch{let a=o.extractContents();i.appendChild(a),o.insertNode(i)}}async _pasteFromClipboard(){try{if(navigator.clipboard&&navigator.clipboard.readText){let t=await navigator.clipboard.readText();document.execCommand("insertText",!1,t)}else document.execCommand("paste")}catch{}}_updateToolbarState(){this._toolbarEl.querySelectorAll(".jotter-btn[data-cmd]").forEach(e=>{try{e.classList.toggle("jotter-btn--active",document.queryCommandState(e.dataset.cmd))}catch{}});let t=this._toolbarEl.querySelector('[data-id="blockformat"]');if(t){let e=document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g,""),o=f.find(i=>i.tag===e);o&&(t.value=o.tag)}}_updateStatus(){let t=this._editor.innerText||"",e=t.trim()===""?0:t.trim().split(/\s+/).length,o=t.replace(/\n/g,"").length;this._wordCountEl.textContent=`${e} word${e!==1?"s":""}`,this._charCountEl.textContent=`${o} char${o!==1?"s":""}`}_saveRange(){let t=window.getSelection();return t&&t.rangeCount>0?t.getRangeAt(0).cloneRange():null}_restoreRange(t){if(!t)return;let e=window.getSelection();e.removeAllRanges(),e.addRange(t)}_emit(t,e){(this._listeners[t]||[]).forEach(o=>o(e))}getHTML(){return this._sourceMode?this._source.value:this._editor.innerHTML}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(t){return this._editor.focus(),document.execCommand("insertHTML",!1,this._sanitize(t)),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}insertText(t){return this._editor.focus(),document.execCommand("insertText",!1,t),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()),this}setHTML(t){return this._editor.innerHTML=this._sanitize(t),this._updateStatus(),this}setTheme(t){let o=b.find(i=>i.id===t)?t:"default";return this._editor.dataset.theme=o,this._options.theme=o,this._themeSelect&&(this._themeSelect.value=o),this}setEnabled(t){return this._editor.contentEditable=String(t),this._root.classList.toggle("jotter--disabled",!t),this}on(t,e){return this._listeners[t]||(this._listeners[t]=[]),this._listeners[t].push(e),this}off(t,e){return this._listeners[t]&&(this._listeners[t]=this._listeners[t].filter(o=>o!==e)),this}destroy(){let t=this.getHTML();return this._hidePopup(),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=t,this._listeners={},t}};h.toolbar=m.full;h.actions=_;h.presets=m;var x=h;export{h as JotterJS,x as default};
package/package.json CHANGED
@@ -1,45 +1,45 @@
1
- {
2
- "name": "jotterjs",
3
- "version": "0.1.2",
4
- "description": "A lightweight, vanilla JS rich-text editor component. No dependencies.",
5
- "main": "dist/jotter.min.js",
6
- "module": "dist/jotter.js",
7
- "exports": {
8
- ".": {
9
- "import": "./dist/jotter.js",
10
- "default": "./dist/jotter.min.js"
11
- },
12
- "./dist/jotter.min.css": "./dist/jotter.min.css"
13
- },
14
- "scripts": {
15
- "start": "node scripts/serve.js",
16
- "build": "node scripts/build.js",
17
- "dev": "node scripts/serve.js",
18
- "prepublishOnly": "npm run build"
19
- },
20
- "files": [
21
- "dist"
22
- ],
23
- "keywords": [
24
- "rich-text",
25
- "editor",
26
- "wysiwyg",
27
- "contenteditable",
28
- "vanilla-js",
29
- "html-editor",
30
- "text-editor"
31
- ],
32
- "repository": {
33
- "type": "git",
34
- "url": "https://github.com/xavier-follet/JotterJS.git"
35
- },
36
- "homepage": "https://github.com/xavier-follet/JotterJS",
37
- "bugs": {
38
- "url": "https://github.com/xavier-follet/JotterJS/issues"
39
- },
40
- "author": "Xavier Follet",
41
- "license": "MIT",
42
- "devDependencies": {
43
- "esbuild": "^0.20.0"
44
- }
45
- }
1
+ {
2
+ "name": "jotterjs",
3
+ "version": "0.2.0",
4
+ "description": "A lightweight, vanilla JS rich-text editor component. No dependencies.",
5
+ "main": "dist/jotter.min.js",
6
+ "module": "dist/jotter.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/jotter.js",
10
+ "default": "./dist/jotter.min.js"
11
+ },
12
+ "./dist/jotter.min.css": "./dist/jotter.min.css"
13
+ },
14
+ "scripts": {
15
+ "start": "node scripts/serve.js",
16
+ "build": "node scripts/build.js",
17
+ "dev": "node scripts/serve.js",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "keywords": [
24
+ "rich-text",
25
+ "editor",
26
+ "wysiwyg",
27
+ "contenteditable",
28
+ "vanilla-js",
29
+ "html-editor",
30
+ "text-editor"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/xavier-follet/JotterJS.git"
35
+ },
36
+ "homepage": "https://github.com/xavier-follet/JotterJS",
37
+ "bugs": {
38
+ "url": "https://github.com/xavier-follet/JotterJS/issues"
39
+ },
40
+ "author": "Xavier Follet",
41
+ "license": "MIT",
42
+ "devDependencies": {
43
+ "esbuild": "^0.20.0"
44
+ }
45
+ }