jotterjs 0.3.0 → 0.5.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
@@ -14,6 +14,7 @@ A lightweight, vanilla JS rich-text editor component built on `contenteditable`.
14
14
  - Replaceable insert dialogs — swap the built-in image/link/video/embed popups for your own UI
15
15
  - Selection bookmarks that survive async host UI and DOM mutation
16
16
  - Event system (`change`, `focus`, `blur`)
17
+ - Keyboard-operable toolbar — every control activates with Enter and Space
17
18
  - Simple chainable API
18
19
 
19
20
  ## Installation
@@ -238,6 +239,28 @@ open on top of it. Between `beginExternalUI()` and `endExternalUI()`, `blur` and
238
239
  `focus` are not emitted, so that chain never starts. The `onRequest*` hooks wrap
239
240
  this for you.
240
241
 
242
+ ## Keyboard
243
+
244
+ Every toolbar control is operable without a mouse. Buttons activate on **Enter**
245
+ and **Space**; the selects work as native selects do; the colour buttons open the
246
+ native colour picker. Popups take focus when they are opened from the keyboard,
247
+ and the table picker is a single tab stop — **arrows** size the table, **Enter**
248
+ inserts it. **Escape** closes any popup and hands the caret back to the editor.
249
+
250
+ Activation runs the same code for the pointer and the keyboard, and fires
251
+ exactly once either way. Focus behaves the way each input method expects: a
252
+ mouse click leaves the caret in the editor, while a key press keeps focus on the
253
+ control so you can keep working along the toolbar.
254
+
255
+ > **Upgrading from ≤ 0.4.0:** if your app bridged this gap by listening for
256
+ > Enter/Space on `.jotter-toolbar` and re-dispatching a synthetic `mousedown` at
257
+ > `.jotter-btn`, remove that bridge. The editor now handles the key itself, and
258
+ > a synthetic `mousedown` on top of it would apply the command twice.
259
+
260
+ Two known gaps: `Tab` inside the editing area inserts indentation rather than
261
+ moving focus, so reach the toolbar by tabbing in from before the editor; and the
262
+ symbol and special-character grids are one tab stop per character.
263
+
241
264
  ## Development
242
265
 
243
266
  ```bash
@@ -1,5 +1,5 @@
1
- var JotterJS=(()=>{var b=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var S=(p,e)=>{for(var t in e)b(p,t,{get:e[t],enumerable:!0})},y=(p,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of v(e))!E.call(p,i)&&i!==t&&b(p,i,{get:()=>e[i],enumerable:!(o=k(e,i))||o.enumerable});return p};var x=p=>y(b({},"__esModule",{value:!0}),p);var H={};S(H,{JotterJS:()=>m,default:()=>B});var f={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(f).forEach(Object.freeze);Object.freeze(f);var n=f,_={minimal:[n.source,n.sep,n.bold,n.italic,n.underline,n.sep,n.link,n.unlink],writing:[n.source,n.sep,n.undo,n.redo,n.sep,n.blockformat,n.sep,n.bold,n.italic,n.underline,n.strike,n.sep,n.bullets,n.numbered,n.sep,n.link,n.unlink,n.sep,n.image],full:[n.source,n.sep,n.undo,n.redo,n.sep,n.copy,n.cut,n.paste,n.sep,n.clearFormat,n.sep,n.blockformat,n.fontfamily,n.fontsize,n.sep,n.bold,n.italic,n.underline,n.strike,n.subscript,n.superscript,n.code,n.sep,n.foreColor,n.hiliteColor,n.sep,n.alignLeft,n.alignCenter,n.alignRight,n.sep,n.bullets,n.numbered,n.sep,n.link,n.unlink,n.sep,n.image,n.video,n.table,n.embed,n.symbol,n.specialChar,n.lorem,n.sep,n.theme]};Object.values(_).forEach(Object.freeze);Object.freeze(_);var L=_.full,T={image:"onRequestImage",link:"onRequestLink",video:"onRequestVideo",embed:"onRequestEmbed"},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"}],w=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],j=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],M=["\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"],N=["\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"],C=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],I=[{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>"}],m=class{constructor(e,t={}){if(this._target=typeof e=="string"?document.querySelector(e):e,!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,onRequestImage:null,onRequestLink:null,onRequestVideo:null,onRequestEmbed:null},t),this._listeners={},this._bookmarks=new Map,this._bmSeq=0,this._savedBookmark=null,this._externalBookmark=null,this._externalDepth=0,this._changeCount=0,this._destroyed=!1,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let e=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(e),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 e=document.createElement("div");return e.className="jotter-toolbar",this._toolbarEl=e,(this._options.toolbar||L).forEach(t=>{let o=this._buildAction(t);o&&e.appendChild(o)}),e}_buildAction(e){if(typeof e.onClick=="function")return this._buildBtn(e);switch(e.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(e);case"popup":return this._buildPopupBtn(e);case"theme":return this._buildThemeSelect();default:return this._buildBtn(e)}}_makeSep(){let e=document.createElement("span");return e.className="jotter-sep",e}_buildBtn(e){let t=document.createElement("button");if(t.type="button",t.className="jotter-btn",e.cmd&&(t.dataset.cmd=e.cmd),e.custom&&(t.dataset.custom=e.custom),t.title=e.title,t.setAttribute("aria-label",e.title),e.label)t.classList.add("jotter-btn--text"),t.appendChild(document.createTextNode(e.label));else{let o=document.createElement("span");o.className="material-icons",o.textContent=e.icon,t.appendChild(o)}return t.addEventListener("mousedown",o=>{o.preventDefault(),this._editor.focus(),this._applyEdit(()=>{if(e.onClick)e.onClick(this);else if(e.custom==="toggleSource")this._toggleSourceMode();else if(e.custom==="code")this._toggleInlineCode();else if(e.cmd==="copy")document.execCommand("copy");else if(e.cmd==="cut")document.execCommand("cut");else if(e.cmd==="paste")this._pasteFromClipboard();else if(e.prompt){let i=window.prompt(e.prompt);i&&document.execCommand(e.cmd,!1,i)}else document.execCommand(e.cmd,!1,null)}),this._updateToolbarState()}),t}_buildBlockFormatSelect(){let e=document.createElement("select");return e.className="jotter-select",e.title="Block format",e.dataset.id="blockformat",g.forEach(({label:t,tag:o})=>{let i=document.createElement("option");i.value=o,i.textContent=t,e.appendChild(i)}),e.addEventListener("mousedown",()=>this._saveBookmark()),e.addEventListener("change",()=>{this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("formatBlock",!1,e.value))}),e}_buildFontFamilySelect(){let e=document.createElement("select");e.className="jotter-select jotter-select--font",e.title="Font family",e.dataset.id="fontfamily";let t=document.createElement("option");return t.value="",t.textContent="Font",e.appendChild(t),w.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=o,i.style.fontFamily=o,e.appendChild(i)}),e.addEventListener("mousedown",()=>this._saveBookmark()),e.addEventListener("change",()=>{e.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("fontName",!1,e.value)))}),e}_buildFontSizeSelect(){let e=document.createElement("select");e.className="jotter-select jotter-select--size",e.title="Font size",e.dataset.id="fontsize";let t=document.createElement("option");return t.value="",t.textContent="Size",e.appendChild(t),j.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=`${o}px`,e.appendChild(i)}),e.addEventListener("mousedown",()=>this._saveBookmark()),e.addEventListener("change",()=>{e.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>this._applyFontSize(e.value)))}),e}_buildThemeSelect(){let e=document.createElement("select");return e.className="jotter-select jotter-select--theme",e.title="Editor theme",e.dataset.id="theme",C.forEach(({id:t,label:o})=>{let i=document.createElement("option");i.value=t,i.textContent=o,e.appendChild(i)}),e.value=this._options.theme,e.addEventListener("change",()=>this.setTheme(e.value)),this._themeSelect=e,e}_buildColorBtn(e){let t=document.createElement("span");t.className="jotter-color-wrap";let o=document.createElement("button");o.type="button",o.className="jotter-btn jotter-color-btn",o.dataset.cmd=e.cmd,o.title=e.title,o.setAttribute("aria-label",e.title);let i=document.createElement("span");i.className="material-icons",i.textContent=e.icon,o.appendChild(i);let s=document.createElement("span");s.className="jotter-color-swatch";let r=e.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=r,o.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=r,a.tabIndex=-1,a.addEventListener("change",()=>{let l=a.value;s.style.background=l,e.cmd==="foreColor"?this._lastForeColor=l:this._lastHiliteColor=l,this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand(e.cmd,!1,l))}),o.addEventListener("mousedown",l=>{l.preventDefault(),this._saveBookmark(),a.click()}),t.appendChild(o),t.appendChild(a),t}_buildPopupBtn(e){let t=document.createElement("button");t.type="button",t.className="jotter-btn",t.title=e.title,t.setAttribute("aria-label",e.title);let o=document.createElement("span");return o.className="material-icons",o.textContent=e.icon,t.appendChild(o),t.addEventListener("mousedown",i=>{i.preventDefault();let s=this._resolverFor(e.id);if(s){this._hidePopup(),this._runResolver(e.id,s);return}if(this._saveBookmark(),this._popupVisible()&&this._popup.dataset.popupId===e.id){this._hidePopup();return}this._showPopup(t,this._buildPopupContent(e.id),e.id)}),t}_buildPopupContainer(){let e=document.createElement("div");return e.className="jotter-popup",e.setAttribute("role","dialog"),e}_showPopup(e,t,o){this._popup.innerHTML="",this._popup.appendChild(t),this._popup.dataset.popupId=o,this._popup.classList.add("jotter-popup--visible");let i=e.getBoundingClientRect();this._popup.style.top=i.bottom+6+"px",this._popup.style.left=i.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,i.left-(s.right-window.innerWidth+8))+"px")})}_popupVisible(){return this._popup.classList.contains("jotter-popup--visible")}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId="",this._releaseSavedBookmark()}_buildPopupContent(e){switch(e){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 t=document.createElement("div");return t.className="jotter-popup-inner",t.textContent="Unknown: "+e,t}}}_popupTable(){let e=document.createElement("div");e.className="jotter-popup-inner";let t=this._popupTitle("Insert Table");e.appendChild(t);let o=10,i=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${o}, 1fr)`;let r=document.createElement("div");r.className="jotter-popup-hint",r.textContent="Hover to select size";let a=[];for(let l=0;l<i;l++)for(let d=0;d<o;d++){let c=document.createElement("span");c.className="jotter-table-cell",c.dataset.r=l,c.dataset.c=d,c.addEventListener("mouseenter",()=>{r.textContent=`${l+1} \xD7 ${d+1} table`,a.forEach(u=>{u.classList.toggle("jotter-table-cell--active",+u.dataset.r<=l&&+u.dataset.c<=d)})}),c.addEventListener("click",()=>this._insertTable(l+1,d+1)),a.push(c),s.appendChild(c)}return e.appendChild(s),e.appendChild(r),e}_commitPopup(e){this._restoreSavedBookmark(),this._hidePopup(),this._applyEdit(e)}_insertTable(e,t){let o="<table><tbody>";for(let i=0;i<e;i++){o+="<tr>";for(let s=0;s<t;s++)o+=i===0?"<th><br></th>":"<td><br></td>";o+="</tr>"}o+="</tbody></table><p><br></p>",this._commitPopup(()=>document.execCommand("insertHTML",!1,o))}_popupLink(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert Link"));let t=this._anchorInSelection(),o=this._selectedText(),i=this._makeField(e,"URL","url","https://"),s=this._makeField(e,"Link text (leave blank to keep selection)","text",""),r=this._makeField(e,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let l=document.createElement("select");return l.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([d,c])=>{let u=document.createElement("option");u.value=c,u.textContent=d,l.appendChild(u)}),e.appendChild(a),e.appendChild(l),t?(i.value=t.getAttribute("href")||"",s.value=t.textContent||"",r.value=t.getAttribute("title")||"",l.value=t.getAttribute("target")||""):o&&(s.value=o),e.appendChild(this._makeSubmitBtn(t?"Update Link":"Insert Link",()=>{let d=i.value.trim();if(!d)return;let c=s.value.trim()||o||d,u=r.value.trim(),h=l.value;this._commitPopup(()=>{t?(t.href=d,h?t.target=h:t.removeAttribute("target"),u?t.title=u:t.removeAttribute("title"),t.textContent=c):document.execCommand("insertHTML",!1,this._linkHTML({href:d,text:c,title:u,target:h}))})})),e}_popupImage(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert Image"));let t=this._makeField(e,"Image URL","text","https://example.com/image.jpg"),o=this._makeField(e,"Alt text","text","Descriptive text"),i=this._makeField(e,"Width (e.g. 400px or 50%)","text","");return e.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=t.value.trim();if(!s)return;let r=this._imageHTML({src:s,alt:o.value.trim(),width:i.value.trim()});this._commitPopup(()=>document.execCommand("insertHTML",!1,r))})),e}_popupVideo(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert YouTube Video"));let t=this._makeField(e,"YouTube URL","text","https://www.youtube.com/watch?v=...");return e.appendChild(this._makeSubmitBtn("Embed Video",()=>{let o=this._ytId(t.value.trim());if(!o){t.classList.add("jotter-input--error");return}t.classList.remove("jotter-input--error"),this._commitPopup(()=>document.execCommand("insertHTML",!1,this._videoHTML(o)))})),e}_ytId(e){for(let t of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let o=e.match(t);if(o)return o[1]}return null}_popupEmbed(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert Embed"));let t=document.createElement("label");t.className="jotter-popup-label",t.textContent="Paste HTML / embed code";let o=document.createElement("textarea");return o.className="jotter-popup-textarea",o.placeholder='<iframe src="..." ...></iframe>',o.rows=4,e.appendChild(t),e.appendChild(o),e.appendChild(this._makeSubmitBtn("Insert",()=>{let i=o.value.trim();i&&this._commitPopup(()=>document.execCommand("insertHTML",!1,this._embedHTML(i)))})),e}_popupSymbol(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Insert Symbol")),e.appendChild(this._charGrid(M)),e}_popupSpecialChar(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Special Characters")),e.appendChild(this._charGrid(N)),e}_charGrid(e){let t=document.createElement("div");return t.className="jotter-char-grid",e.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",s=>{s.preventDefault(),this._commitPopup(()=>document.execCommand("insertText",!1,o))}),t.appendChild(i)}),t}_popupLorem(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Insert Lorem Ipsum")),I.forEach(t=>{let o=document.createElement("button");o.type="button",o.className="jotter-lorem-btn",o.textContent=t.label,o.addEventListener("mousedown",i=>{i.preventDefault(),this._commitPopup(()=>document.execCommand(t.isHTML?"insertHTML":"insertText",!1,t.text))}),e.appendChild(o)}),e}_popupTitle(e){let t=document.createElement("div");return t.className="jotter-popup-title",t.textContent=e,t}_makeField(e,t,o,i){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=t;let r=document.createElement("input");return r.type=o,r.className="jotter-popup-input",r.placeholder=i,e.appendChild(s),e.appendChild(r),r}_makeSubmitBtn(e,t){let o=document.createElement("button");return o.type="button",o.className="jotter-popup-submit",o.textContent=e,o.addEventListener("click",t),o}_esc(e){return String(e).replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_imageHTML({src:e,alt:t,width:o}){let i=o?`max-width:${o}`:"max-width:100%";return`<img src="${this._esc(e)}" alt="${this._esc(t||"")}" style="${this._esc(i)}">`}_linkHTML({href:e,text:t,title:o,target:i}){let s=`href="${this._esc(e)}"`;return i&&(s+=` target="${this._esc(i)}"`),o&&(s+=` title="${this._esc(o)}"`),`<a ${s}>${this._esc(t||e)}</a>`}_videoHTML(e){return/^[A-Za-z0-9_-]{11}$/.test(e)?`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${e}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`:null}_embedHTML(e){return e+"<p><br></p>"}_resolverFor(e){let t=T[e],o=t?this._options[t]:null;return typeof o=="function"?o:null}async _runResolver(e,t){let o=this._resolverContext(e);this.beginExternalUI();let i=null;try{i=await t(o)}catch{i=null}if(this._destroyed)return;let s=i==null||i===!1?null:this._resolvedHTML(e,i,o);this.endExternalUI(),s&&(this._applyEdit(()=>document.execCommand("insertHTML",!1,s)),this._updateToolbarState())}_resolverContext(e){switch(e){case"image":return{src:"",alt:"",width:"",selection:this._selectedText()};case"video":return{url:"",selection:this._selectedText()};case"embed":return{html:"",selection:this._selectedText()};case"link":return this._linkContext();default:return{selection:this._selectedText()}}}_linkContext(){let e=this._anchorInSelection();if(!e){let o=this._selectedText();return{href:"",text:o,title:"",target:"",selection:o,isEdit:!1}}let t=window.getSelection();if(t){let o=document.createRange();o.selectNode(e),t.removeAllRanges(),t.addRange(o)}return{href:e.getAttribute("href")||"",text:e.textContent||"",title:e.getAttribute("title")||"",target:e.getAttribute("target")||"",selection:e.textContent||"",isEdit:!0}}_resolvedHTML(e,t,o){switch(e){case"image":{let i=typeof t=="string"?{src:t}:t,s=String(i.src||i.url||"").trim();return s?this._imageHTML({...i,src:s}):null}case"link":{let i=typeof t=="string"?{href:t}:t,s=String(i.href||i.url||"").trim();if(!s)return null;let r=String(i.text!=null?i.text:o.text||"").trim();return this._linkHTML({...i,href:s,text:r})}case"video":{let i=typeof t=="string"?{url:t}:t,s=i.id?String(i.id).trim():this._ytId(String(i.url||"").trim());return s?this._videoHTML(s):null}case"embed":{let i=String(typeof t=="string"?t:t.html||"").trim();return i?this._embedHTML(i):null}default:return null}}_selectedText(){let e=window.getSelection();return!e||!e.rangeCount?"":this._editor.contains(e.getRangeAt(0).commonAncestorContainer)?e.toString():""}_anchorInSelection(){let e=window.getSelection();if(!e||!e.rangeCount)return null;let t=e.anchorNode;if(!t||!this._editor.contains(t))return null;for(;t&&t!==this._editor;){if(t.nodeName==="A")return t;t=t.parentNode}return null}_buildStatusBar(){let e=document.createElement("div");e.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 t=document.createElement("span");return t.className="jotter-status-mode",t.textContent="HTML",e.appendChild(this._wordCountEl),e.appendChild(this._charCountEl),e.appendChild(t),e}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(e=>{if(e.attributes.length>0)return;let t=document.createElement("p");t.innerHTML=e.innerHTML,e.replaceWith(t)}),Array.from(this._editor.childNodes).forEach(e=>{if(e.nodeType===Node.TEXT_NODE&&e.textContent.trim()!==""){let t=window.getSelection(),o=null;if(t&&t.rangeCount){let s=t.getRangeAt(0);s.startContainer===e&&(o=s.startOffset)}let i=document.createElement("p");if(e.replaceWith(i),i.appendChild(e),o!==null){let s=document.createRange();s.setStart(e,o),s.collapse(!0),t.removeAllRanges(),t.addRange(s)}}}),this._updateStatus(),this._emitChange()}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focusin",e=>{this._root.classList.add("jotter--focused"),!(this._externalDepth>0)&&(this._isInternalTarget(e.relatedTarget)||(this._emit("focus"),this._options.onFocus&&this._options.onFocus()))}),this._editor.addEventListener("focusout",e=>{this._externalDepth>0||this._isInternalTarget(e.relatedTarget)||setTimeout(()=>{this._destroyed||this._externalDepth>0||this._isInternalTarget(document.activeElement)||this._popupVisible()||(this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur())},0)}),this._editor.addEventListener("keydown",e=>{e.key==="Tab"&&(e.preventDefault(),document.execCommand("insertHTML",!1,"&nbsp;&nbsp;&nbsp;&nbsp;"))}),this._onDocMouseDown=e=>{this._popupVisible()&&!this._popup.contains(e.target)&&!this._toolbarEl.contains(e.target)&&this._hidePopup()},document.addEventListener("mousedown",this._onDocMouseDown),this._onDocKeyDown=e=>{e.key==="Escape"&&this._popupVisible()&&(this._restoreSavedBookmark(),this._hidePopup())},document.addEventListener("keydown",this._onDocKeyDown)}_isInternalTarget(e){return!!e&&(this._root.contains(e)||this._popup.contains(e))}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._dropBookmarks(),this._source.value=this._prettyHTML(this._richHTML()),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._emitChange()),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let e=this._toolbarEl.querySelector('[data-custom="toggleSource"]');e&&e.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(e){let t=0,o=" ",i=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 e.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
1
+ var JotterJS=(()=>{var C=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var w=(m,t)=>{for(var e in t)C(m,e,{get:t[e],enumerable:!0})},M=(m,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of T(t))!L.call(m,i)&&i!==e&&C(m,i,{get:()=>t[i],enumerable:!(o=x(t,i))||o.enumerable});return m};var j=m=>M(C({},"__esModule",{value:!0}),m);var q={};w(q,{JotterJS:()=>h,default:()=>P});var f={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(f).forEach(Object.freeze);Object.freeze(f);var s=f,_={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 N=_.full,A={image:"onRequestImage",link:"onRequestLink",video:"onRequestVideo",embed:"onRequestEmbed"},v=[{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"}],I=["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],H=["\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"],R=["\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"],E=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],F=[{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,onRequestImage:null,onRequestLink:null,onRequestVideo:null,onRequestEmbed:null},e),this._listeners={},this._bookmarks=new Map,this._bmSeq=0,this._savedBookmark=null,this._externalBookmark=null,this._externalDepth=0,this._changeCount=0,this._destroyed=!1,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||N).forEach(e=>{let o=this._buildAction(e);o&&t.appendChild(o)}),t}_buildAction(t){if(typeof t.onClick=="function")return this._buildBtn(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}_bindActivation(t,e){t.addEventListener("mousedown",o=>{o.preventDefault(),e(!1)}),t.addEventListener("keydown",o=>{o.key!=="Enter"&&o.key!==" "&&o.key!=="Spacebar"||o.repeat||(o.preventDefault(),e(!0))})}_returnFocus(t,e){e&&this._isInternalTarget(document.activeElement)&&t.focus()}_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 this._bindActivation(e,o=>{this._editor.focus(),this._applyEdit(()=>{if(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._returnFocus(e,o)}),e}_bindSelectCaret(t){let e={viaKeyboard:!1};return t.addEventListener("mousedown",()=>{e.viaKeyboard=!1,this._saveBookmark()}),t.addEventListener("keydown",o=>{o.key==="Tab"||o.key==="Escape"||(e.viaKeyboard=!0,this._savedBookmark==null&&this._saveBookmark())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",v.forEach(({label:o,tag:i})=>{let n=document.createElement("option");n.value=i,n.textContent=o,t.appendChild(n)});let e=this._bindSelectCaret(t);return t.addEventListener("change",()=>{this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("formatBlock",!1,t.value)),this._returnFocus(t,e.viaKeyboard)}),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");e.value="",e.textContent="Font",t.appendChild(e),I.forEach(i=>{let n=document.createElement("option");n.value=i,n.textContent=i,n.style.fontFamily=i,t.appendChild(n)});let o=this._bindSelectCaret(t);return t.addEventListener("change",()=>{t.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("fontName",!1,t.value)),this._returnFocus(t,o.viaKeyboard))}),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");e.value="",e.textContent="Size",t.appendChild(e),B.forEach(i=>{let n=document.createElement("option");n.value=i,n.textContent=`${i}px`,t.appendChild(n)});let o=this._bindSelectCaret(t);return t.addEventListener("change",()=>{t.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>this._applyFontSize(t.value)),this._returnFocus(t,o.viaKeyboard))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",E.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 r=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;n.style.background=r,o.appendChild(n);let a=document.createElement("input");a.type="color",a.className="jotter-color-input",a.value=r,a.tabIndex=-1;let u=!1;return a.addEventListener("change",()=>{let p=a.value;n.style.background=p,t.cmd==="foreColor"?this._lastForeColor=p:this._lastHiliteColor=p,this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand(t.cmd,!1,p)),this._returnFocus(o,u)}),this._bindActivation(o,p=>{u=p,this._saveBookmark(),a.click()}),e.appendChild(o),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 o=document.createElement("span");return o.className="material-icons",o.textContent=t.icon,e.appendChild(o),this._bindActivation(e,i=>{let n=this._resolverFor(t.id);if(n){this._hidePopup(),this._runResolver(t.id,n);return}if(this._saveBookmark(),this._popupVisible()&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id),i&&this._focusPopup()}),e}_focusPopup(){let t=this._popup.querySelector('input, textarea, select, button, [tabindex]:not([tabindex="-1"])');t&&t.focus()}_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")})}_popupVisible(){return this._popup.classList.contains("jotter-popup--visible")}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId="",this._releaseSavedBookmark()}_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)`,n.setAttribute("role","grid"),n.setAttribute("aria-label","Table size");let r=document.createElement("div");r.className="jotter-popup-hint",r.textContent="Hover or arrow to select size";let a=[],u=(c,l)=>{r.textContent=`${c+1} \xD7 ${l+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=c&&+d.dataset.c<=l)})};for(let c=0;c<i;c++)for(let l=0;l<o;l++){let d=document.createElement("span");d.className="jotter-table-cell",d.dataset.r=c,d.dataset.c=l,d.setAttribute("role","gridcell"),d.setAttribute("aria-label",`${c+1} by ${l+1} table`),d.tabIndex=c===0&&l===0?0:-1,d.addEventListener("mouseenter",()=>u(c,l)),d.addEventListener("focus",()=>u(c,l)),d.addEventListener("click",()=>this._insertTable(c+1,l+1)),a.push(d),n.appendChild(d)}let p={ArrowRight:[0,1],ArrowLeft:[0,-1],ArrowDown:[1,0],ArrowUp:[-1,0]};return n.addEventListener("keydown",c=>{let l=a.indexOf(document.activeElement)===-1?null:document.activeElement;if(!l)return;let d=+l.dataset.r,k=+l.dataset.c;if(c.key==="Enter"||c.key===" "||c.key==="Spacebar"){c.preventDefault(),this._insertTable(d+1,k+1);return}let b=p[c.key];if(!b)return;c.preventDefault();let y=Math.min(i-1,Math.max(0,d+b[0])),S=Math.min(o-1,Math.max(0,k+b[1])),g=a[y*o+S];g!==l&&(l.tabIndex=-1,g.tabIndex=0,g.focus())}),t.appendChild(n),t.appendChild(r),t}_commitPopup(t){this._restoreSavedBookmark(),this._hidePopup(),this._applyEdit(t)}_insertTable(t,e){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>",this._commitPopup(()=>document.execCommand("insertHTML",!1,o))}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=this._anchorInSelection(),o=this._selectedText(),i=this._makeField(t,"URL","url","https://"),n=this._makeField(t,"Link text (leave blank to keep selection)","text",""),r=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let u=document.createElement("select");return u.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([p,c])=>{let l=document.createElement("option");l.value=c,l.textContent=p,u.appendChild(l)}),t.appendChild(a),t.appendChild(u),e?(i.value=e.getAttribute("href")||"",n.value=e.textContent||"",r.value=e.getAttribute("title")||"",u.value=e.getAttribute("target")||""):o&&(n.value=o),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let p=i.value.trim();if(!p)return;let c=n.value.trim()||o||p,l=r.value.trim(),d=u.value;this._commitPopup(()=>{e?(e.href=p,d?e.target=d:e.removeAttribute("target"),l?e.title=l:e.removeAttribute("title"),e.textContent=c):document.execCommand("insertHTML",!1,this._linkHTML({href:p,text:c,title:l,target:d}))})})),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 r=this._imageHTML({src:n,alt:o.value.trim(),width:i.value.trim()});this._commitPopup(()=>document.execCommand("insertHTML",!1,r))})),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"),this._commitPopup(()=>document.execCommand("insertHTML",!1,this._videoHTML(o)))})),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._commitPopup(()=>document.execCommand("insertHTML",!1,this._embedHTML(i)))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(H)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(R)),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")}`,this._bindActivation(i,()=>{this._commitPopup(()=>document.execCommand("insertText",!1,o))}),e.appendChild(i)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),F.forEach(e=>{let o=document.createElement("button");o.type="button",o.className="jotter-lorem-btn",o.textContent=e.label,this._bindActivation(o,()=>{this._commitPopup(()=>document.execCommand(e.isHTML?"insertHTML":"insertText",!1,e.text))}),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 r=document.createElement("input");return r.type=o,r.className="jotter-popup-input",r.placeholder=i,t.appendChild(n),t.appendChild(r),r}_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 String(t).replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_imageHTML({src:t,alt:e,width:o}){let i=o?`max-width:${o}`:"max-width:100%";return`<img src="${this._esc(t)}" alt="${this._esc(e||"")}" style="${this._esc(i)}">`}_linkHTML({href:t,text:e,title:o,target:i}){let n=`href="${this._esc(t)}"`;return i&&(n+=` target="${this._esc(i)}"`),o&&(n+=` title="${this._esc(o)}"`),`<a ${n}>${this._esc(e||t)}</a>`}_videoHTML(t){return/^[A-Za-z0-9_-]{11}$/.test(t)?`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${t}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`:null}_embedHTML(t){return t+"<p><br></p>"}_resolverFor(t){let e=A[t],o=e?this._options[e]:null;return typeof o=="function"?o:null}async _runResolver(t,e){let o=this._resolverContext(t);this.beginExternalUI();let i=null;try{i=await e(o)}catch{i=null}if(this._destroyed)return;let n=i==null||i===!1?null:this._resolvedHTML(t,i,o);this.endExternalUI(),n&&(this._applyEdit(()=>document.execCommand("insertHTML",!1,n)),this._updateToolbarState())}_resolverContext(t){switch(t){case"image":return{src:"",alt:"",width:"",selection:this._selectedText()};case"video":return{url:"",selection:this._selectedText()};case"embed":return{html:"",selection:this._selectedText()};case"link":return this._linkContext();default:return{selection:this._selectedText()}}}_linkContext(){let t=this._anchorInSelection();if(!t){let o=this._selectedText();return{href:"",text:o,title:"",target:"",selection:o,isEdit:!1}}let e=window.getSelection();if(e){let o=document.createRange();o.selectNode(t),e.removeAllRanges(),e.addRange(o)}return{href:t.getAttribute("href")||"",text:t.textContent||"",title:t.getAttribute("title")||"",target:t.getAttribute("target")||"",selection:t.textContent||"",isEdit:!0}}_resolvedHTML(t,e,o){switch(t){case"image":{let i=typeof e=="string"?{src:e}:e,n=String(i.src||i.url||"").trim();return n?this._imageHTML({...i,src:n}):null}case"link":{let i=typeof e=="string"?{href:e}:e,n=String(i.href||i.url||"").trim();if(!n)return null;let r=String(i.text!=null?i.text:o.text||"").trim();return this._linkHTML({...i,href:n,text:r})}case"video":{let i=typeof e=="string"?{url:e}:e,n=i.id?String(i.id).trim():this._ytId(String(i.url||"").trim());return n?this._videoHTML(n):null}case"embed":{let i=String(typeof e=="string"?e:e.html||"").trim();return i?this._embedHTML(i):null}default:return null}}_selectedText(){let t=window.getSelection();return!t||!t.rangeCount?"":this._editor.contains(t.getRangeAt(0).commonAncestorContainer)?t.toString():""}_anchorInSelection(){let t=window.getSelection();if(!t||!t.rangeCount)return null;let e=t.anchorNode;if(!e||!this._editor.contains(e))return null;for(;e&&e!==this._editor;){if(e.nodeName==="A")return e;e=e.parentNode}return null}_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._emitChange()}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focusin",t=>{this._root.classList.add("jotter--focused"),!(this._externalDepth>0)&&(this._isInternalTarget(t.relatedTarget)||(this._emit("focus"),this._options.onFocus&&this._options.onFocus()))}),this._editor.addEventListener("focusout",t=>{this._externalDepth>0||this._isInternalTarget(t.relatedTarget)||setTimeout(()=>{this._destroyed||this._externalDepth>0||this._isInternalTarget(document.activeElement)||this._popupVisible()||(this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur())},0)}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1,"&nbsp;&nbsp;&nbsp;&nbsp;"))}),this._onDocMouseDown=t=>{this._popupVisible()&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()},document.addEventListener("mousedown",this._onDocMouseDown),this._onDocKeyDown=t=>{t.key==="Escape"&&this._popupVisible()&&(this._restoreSavedBookmark(),this._hidePopup())},document.addEventListener("keydown",this._onDocKeyDown)}_isInternalTarget(t){return!!t&&(this._root.contains(t)||this._popup.contains(t))}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._dropBookmarks(),this._source.value=this._prettyHTML(this._richHTML()),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._emitChange()),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(r=>r.trim()).filter(r=>r.length>0).map(r=>{let a=r.match(/^<\/(\w+)/),l=r.match(/^<(\w+)/),d=r.endsWith("/>"),c=l?l[1].toLowerCase():null,u=a?a[1].toLowerCase():null;u&&!s.has(u)&&(t=Math.max(0,t-1));let h=o.repeat(t)+r;return c&&!d&&!i.has(c)&&!u&&!s.has(c)&&t++,h}).join(`
5
- `)}_sanitize(e){let t=new DOMParser().parseFromString(e,"text/html");return t.querySelectorAll("script").forEach(o=>o.remove()),t.querySelectorAll("[data-jotter-bookmark]").forEach(o=>o.remove()),t.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)})}),t.body.innerHTML}_toggleInlineCode(){let e=window.getSelection();if(!e||e.rangeCount===0)return;let t=e.getRangeAt(0),o=t.commonAncestorContainer;o.nodeType===3&&(o=o.parentNode);let i=o.closest?o.closest("code"):null;if(i){let s=i.parentNode;for(;i.firstChild;)s.insertBefore(i.firstChild,i);s.removeChild(i)}else if(t.collapsed){let s=document.createElement("code");s.innerHTML="&#8203;",t.insertNode(s);let r=document.createRange();r.setStart(s,0),r.setEnd(s,s.childNodes.length),e.removeAllRanges(),e.addRange(r)}else{let s=document.createElement("code");try{t.surroundContents(s)}catch{let a=t.extractContents();s.appendChild(a),t.insertNode(s)}}}_applyFontSize(e){let t=window.getSelection();if(!t||t.rangeCount===0)return;let o=t.getRangeAt(0);if(o.collapsed)return;let i=document.createElement("span");i.style.fontSize=e+"px";try{o.surroundContents(i)}catch{let r=o.extractContents();i.appendChild(r),o.insertNode(i)}}async _pasteFromClipboard(){try{if(navigator.clipboard&&navigator.clipboard.readText){let e=await navigator.clipboard.readText();document.execCommand("insertText",!1,e)}else document.execCommand("paste")}catch{}}_updateToolbarState(){this._toolbarEl.querySelectorAll(".jotter-btn[data-cmd]").forEach(t=>{try{t.classList.toggle("jotter-btn--active",document.queryCommandState(t.dataset.cmd))}catch{}});let e=this._toolbarEl.querySelector('[data-id="blockformat"]');if(e){let t=document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g,""),o=g.find(i=>i.tag===t);o&&(e.value=o.tag)}}_updateStatus(){let e=this._editor.innerText||"",t=e.trim()===""?0:e.trim().split(/\s+/).length,o=e.replace(/\n/g,"").length;this._wordCountEl.textContent=`${t} word${t!==1?"s":""}`,this._charCountEl.textContent=`${o} char${o!==1?"s":""}`}_richHTML(){if(this._bookmarks.size===0)return this._editor.innerHTML;let e=this._editor.cloneNode(!0);return e.querySelectorAll("[data-jotter-bookmark]").forEach(t=>t.remove()),e.innerHTML}_makeMarker(e){let t=document.createElement("span");return t.className="jotter-bookmark",t.dataset.jotterBookmark=e,t}_takeRange(e){let t=e!=null?this._bookmarks.get(e):null;if(!t)return null;if(this._bookmarks.delete(e),t.atStart){let a=document.createRange();return a.setStart(this._editor,0),a.collapse(!0),a}let{start:o,end:i}=t,s=this._editor.contains(o)&&(!i||this._editor.contains(i)),r=null;return s&&(r=document.createRange(),r.setStartAfter(o),i?r.setEndBefore(i):r.collapse(!0)),o.parentNode&&o.remove(),i&&i.parentNode&&i.remove(),r}_saveBookmark(){this._releaseSavedBookmark(),this._savedBookmark=this.saveSelection()}_restoreSavedBookmark(){this.restoreSelection(this._savedBookmark),this._savedBookmark=null,this._editor.focus()}_releaseSavedBookmark(){this.releaseSelection(this._savedBookmark),this._savedBookmark=null}_dropBookmarks(){Array.from(this._bookmarks.keys()).forEach(e=>this.releaseSelection(e)),this._savedBookmark=null}_emit(e,t){(this._listeners[e]||[]).forEach(o=>o(t))}_emitChange(){let e=this.getHTML();this._changeCount++,this._emit("change",e),this._options.onChange&&this._options.onChange(e)}_applyEdit(e){let t=this._changeCount,o=this._richHTML();e(),this._updateStatus(),this._changeCount===t&&this._richHTML()!==o&&this._emitChange()}getHTML(){return this._sourceMode?this._source.value:this._richHTML()}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._dropBookmarks(),this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(e,t={}){return t.at!=null&&this.restoreSelection(t.at),this._editor.focus(),this._applyEdit(()=>document.execCommand("insertHTML",!1,this._sanitize(e))),this}insertText(e,t={}){return t.at!=null&&this.restoreSelection(t.at),this._editor.focus(),this._applyEdit(()=>document.execCommand("insertText",!1,e)),this}saveSelection(){let e=window.getSelection();if(!e||e.rangeCount===0)return null;let t=e.getRangeAt(0);if(!this._editor.contains(t.startContainer)||!this._editor.contains(t.endContainer))return null;let o="jbm"+ ++this._bmSeq;if(this._editor.childNodes.length===0)return this._bookmarks.set(o,{atStart:!0}),o;let i=this._makeMarker(o),s=t.collapsed?null:this._makeMarker(o);if(s){let l=t.cloneRange();l.collapse(!1),l.insertNode(s)}let r=t.cloneRange();r.collapse(!0),r.insertNode(i),this._bookmarks.set(o,{start:i,end:s});let a=document.createRange();return a.setStartAfter(i),s?a.setEndBefore(s):a.collapse(!0),e.removeAllRanges(),e.addRange(a),o}restoreSelection(e){let t=this._takeRange(e);if(!t)return this;this._sourceMode||this._editor.focus();let o=window.getSelection();return o.removeAllRanges(),o.addRange(t),this}releaseSelection(e){return this._takeRange(e),this}beginExternalUI(){return this._externalDepth===0&&(this._externalBookmark=this.saveSelection()),this._externalDepth++,this}endExternalUI(e={}){if(this._externalDepth===0)return this;if(this._externalDepth===1){let t=this._externalBookmark;this._externalBookmark=null,e.restore===!1?this.releaseSelection(t):this.restoreSelection(t)}return this._externalDepth--,this}setHTML(e){return this._dropBookmarks(),this._editor.innerHTML=this._sanitize(e),this._updateStatus(),this}setTheme(e){let o=C.find(i=>i.id===e)?e:"default";return this._editor.dataset.theme=o,this._options.theme=o,this._themeSelect&&(this._themeSelect.value=o),this}setEnabled(e){return this._editor.contentEditable=String(e),this._root.classList.toggle("jotter--disabled",!e),this}on(e,t){return this._listeners[e]||(this._listeners[e]=[]),this._listeners[e].push(t),this}off(e,t){return this._listeners[e]&&(this._listeners[e]=this._listeners[e].filter(o=>o!==t)),this}destroy(){let e=this.getHTML();return this._destroyed=!0,this._hidePopup(),this._dropBookmarks(),this._externalBookmark=null,this._externalDepth=0,document.removeEventListener("mousedown",this._onDocMouseDown),document.removeEventListener("keydown",this._onDocKeyDown),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=e,this._listeners={},e}};m.toolbar=_.full;m.actions=f;m.presets=_;var B=m;return x(H);})();
4
+ `).map(r=>r.trim()).filter(r=>r.length>0).map(r=>{let a=r.match(/^<\/(\w+)/),u=r.match(/^<(\w+)/),p=r.endsWith("/>"),c=u?u[1].toLowerCase():null,l=a?a[1].toLowerCase():null;l&&!n.has(l)&&(e=Math.max(0,e-1));let d=o.repeat(e)+r;return c&&!p&&!i.has(c)&&!l&&!n.has(c)&&e++,d}).join(`
5
+ `)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(o=>o.remove()),e.querySelectorAll("[data-jotter-bookmark]").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 r=document.createRange();r.setStart(n,0),r.setEnd(n,n.childNodes.length),t.removeAllRanges(),t.addRange(r)}else{let n=document.createElement("code");try{e.surroundContents(n)}catch{let a=e.extractContents();n.appendChild(a),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 r=o.extractContents();i.appendChild(r),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=v.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":""}`}_richHTML(){if(this._bookmarks.size===0)return this._editor.innerHTML;let t=this._editor.cloneNode(!0);return t.querySelectorAll("[data-jotter-bookmark]").forEach(e=>e.remove()),t.innerHTML}_makeMarker(t){let e=document.createElement("span");return e.className="jotter-bookmark",e.dataset.jotterBookmark=t,e}_takeRange(t){let e=t!=null?this._bookmarks.get(t):null;if(!e)return null;if(this._bookmarks.delete(t),e.atStart){let a=document.createRange();return a.setStart(this._editor,0),a.collapse(!0),a}let{start:o,end:i}=e,n=this._editor.contains(o)&&(!i||this._editor.contains(i)),r=null;return n&&(r=document.createRange(),r.setStartAfter(o),i?r.setEndBefore(i):r.collapse(!0)),o.parentNode&&o.remove(),i&&i.parentNode&&i.remove(),r}_saveBookmark(){this._releaseSavedBookmark(),this._savedBookmark=this.saveSelection()}_restoreSavedBookmark(){this.restoreSelection(this._savedBookmark),this._savedBookmark=null,this._editor.focus()}_releaseSavedBookmark(){this.releaseSelection(this._savedBookmark),this._savedBookmark=null}_dropBookmarks(){Array.from(this._bookmarks.keys()).forEach(t=>this.releaseSelection(t)),this._savedBookmark=null}_emit(t,e){(this._listeners[t]||[]).forEach(o=>o(e))}_emitChange(){let t=this.getHTML();this._changeCount++,this._emit("change",t),this._options.onChange&&this._options.onChange(t)}_applyEdit(t){let e=this._changeCount,o=this._richHTML();t(),this._updateStatus(),this._changeCount===e&&this._richHTML()!==o&&this._emitChange()}getHTML(){return this._sourceMode?this._source.value:this._richHTML()}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._dropBookmarks(),this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(t,e={}){return e.at!=null&&this.restoreSelection(e.at),this._editor.focus(),this._applyEdit(()=>document.execCommand("insertHTML",!1,this._sanitize(t))),this}insertText(t,e={}){return e.at!=null&&this.restoreSelection(e.at),this._editor.focus(),this._applyEdit(()=>document.execCommand("insertText",!1,t)),this}saveSelection(){let t=window.getSelection();if(!t||t.rangeCount===0)return null;let e=t.getRangeAt(0);if(!this._editor.contains(e.startContainer)||!this._editor.contains(e.endContainer))return null;let o="jbm"+ ++this._bmSeq;if(this._editor.childNodes.length===0)return this._bookmarks.set(o,{atStart:!0}),o;let i=this._makeMarker(o),n=e.collapsed?null:this._makeMarker(o);if(n){let u=e.cloneRange();u.collapse(!1),u.insertNode(n)}let r=e.cloneRange();r.collapse(!0),r.insertNode(i),this._bookmarks.set(o,{start:i,end:n});let a=document.createRange();return a.setStartAfter(i),n?a.setEndBefore(n):a.collapse(!0),t.removeAllRanges(),t.addRange(a),o}restoreSelection(t){let e=this._takeRange(t);if(!e)return this;this._sourceMode||this._editor.focus();let o=window.getSelection();return o.removeAllRanges(),o.addRange(e),this}releaseSelection(t){return this._takeRange(t),this}beginExternalUI(){return this._externalDepth===0&&(this._externalBookmark=this.saveSelection()),this._externalDepth++,this}endExternalUI(t={}){if(this._externalDepth===0)return this;if(this._externalDepth===1){let e=this._externalBookmark;this._externalBookmark=null,t.restore===!1?this.releaseSelection(e):this.restoreSelection(e)}return this._externalDepth--,this}setHTML(t){return this._dropBookmarks(),this._editor.innerHTML=this._sanitize(t),this._updateStatus(),this}setTheme(t){let o=E.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._destroyed=!0,this._hidePopup(),this._dropBookmarks(),this._externalBookmark=null,this._externalDepth=0,document.removeEventListener("mousedown",this._onDocMouseDown),document.removeEventListener("keydown",this._onDocKeyDown),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=f;h.presets=_;var P=h;return j(q);})();
package/dist/jotter.js CHANGED
@@ -446,6 +446,55 @@ var JotterJS = class {
446
446
  s.className = "jotter-sep";
447
447
  return s;
448
448
  }
449
+ // ─── Activation ───────────────────────────────────────────────────────────
450
+ // Every toolbar control activates through _bindActivation, so the mouse and
451
+ // the keyboard always run the same body. Binding them separately is what let
452
+ // the keyboard rot: the pointer path grew a selection save/restore dance the
453
+ // key path never had, and a focused button did nothing at all.
454
+ /**
455
+ * Binds a control's activation to the pointer *and* the keyboard.
456
+ *
457
+ * The pointer path has to run on `mousedown` with the default prevented —
458
+ * that is what keeps the caret inside the contenteditable instead of letting
459
+ * focus jump to the button before the command runs. A focused button has no
460
+ * such problem, so the key path just calls the same `run()`.
461
+ *
462
+ * preventDefault() on the keydown does double duty: Space no longer scrolls
463
+ * the page, and the browser no longer synthesises the click it derives from a
464
+ * key press — so one activation stays one activation, here and for anything
465
+ * else listening further down. Auto-repeat is dropped for the same reason.
466
+ *
467
+ * @param {Element} el
468
+ * @param {Function} run Receives true when the activation came from the keyboard.
469
+ */
470
+ _bindActivation(el, run) {
471
+ el.addEventListener("mousedown", (e) => {
472
+ e.preventDefault();
473
+ run(false);
474
+ });
475
+ el.addEventListener("keydown", (e) => {
476
+ if (e.key !== "Enter" && e.key !== " " && e.key !== "Spacebar")
477
+ return;
478
+ if (e.repeat)
479
+ return;
480
+ e.preventDefault();
481
+ run(true);
482
+ });
483
+ }
484
+ /**
485
+ * Hands focus back to a control after a keyboard activation, so the user
486
+ * keeps their place on the toolbar instead of being dropped into the editor
487
+ * on every keystroke. The pointer path never moved focus off the editor in
488
+ * the first place, so it is left alone.
489
+ *
490
+ * Skipped when something outside the editor's own chrome has taken focus —
491
+ * a host modal opened from an `onClick`, say — since stealing it back would
492
+ * break that UI.
493
+ */
494
+ _returnFocus(el, viaKeyboard) {
495
+ if (viaKeyboard && this._isInternalTarget(document.activeElement))
496
+ el.focus();
497
+ }
449
498
  /**
450
499
  * General button builder. Priority order for click handling:
451
500
  * 1. action.onClick(editor) — external callback
@@ -472,8 +521,7 @@ var JotterJS = class {
472
521
  icon.textContent = action.icon;
473
522
  btn.appendChild(icon);
474
523
  }
475
- btn.addEventListener("mousedown", (e) => {
476
- e.preventDefault();
524
+ this._bindActivation(btn, (viaKeyboard) => {
477
525
  this._editor.focus();
478
526
  this._applyEdit(() => {
479
527
  if (action.onClick) {
@@ -497,9 +545,35 @@ var JotterJS = class {
497
545
  }
498
546
  });
499
547
  this._updateToolbarState();
548
+ this._returnFocus(btn, viaKeyboard);
500
549
  });
501
550
  return btn;
502
551
  }
552
+ /**
553
+ * Keeps the caret bookmarked for a select, whichever way the user drives it.
554
+ * By mouse the bookmark has to be taken on `mousedown`, before focus leaves
555
+ * the editable; by keyboard the value can change on the very first arrow
556
+ * press, so the keydown has to take one too.
557
+ *
558
+ * Returns a state object the `change` handler reads to decide where focus
559
+ * belongs afterwards: back on the select for a keyboard user still walking
560
+ * the options, in the editor for a mouse user who has finished picking.
561
+ */
562
+ _bindSelectCaret(sel) {
563
+ const state = { viaKeyboard: false };
564
+ sel.addEventListener("mousedown", () => {
565
+ state.viaKeyboard = false;
566
+ this._saveBookmark();
567
+ });
568
+ sel.addEventListener("keydown", (e) => {
569
+ if (e.key === "Tab" || e.key === "Escape")
570
+ return;
571
+ state.viaKeyboard = true;
572
+ if (this._savedBookmark == null)
573
+ this._saveBookmark();
574
+ });
575
+ return state;
576
+ }
503
577
  _buildBlockFormatSelect() {
504
578
  const sel = document.createElement("select");
505
579
  sel.className = "jotter-select";
@@ -511,10 +585,11 @@ var JotterJS = class {
511
585
  opt.textContent = label;
512
586
  sel.appendChild(opt);
513
587
  });
514
- sel.addEventListener("mousedown", () => this._saveBookmark());
588
+ const caret = this._bindSelectCaret(sel);
515
589
  sel.addEventListener("change", () => {
516
590
  this._restoreSavedBookmark();
517
591
  this._applyEdit(() => document.execCommand("formatBlock", false, sel.value));
592
+ this._returnFocus(sel, caret.viaKeyboard);
518
593
  });
519
594
  return sel;
520
595
  }
@@ -534,12 +609,13 @@ var JotterJS = class {
534
609
  opt.style.fontFamily = f;
535
610
  sel.appendChild(opt);
536
611
  });
537
- sel.addEventListener("mousedown", () => this._saveBookmark());
612
+ const caret = this._bindSelectCaret(sel);
538
613
  sel.addEventListener("change", () => {
539
614
  if (!sel.value)
540
615
  return;
541
616
  this._restoreSavedBookmark();
542
617
  this._applyEdit(() => document.execCommand("fontName", false, sel.value));
618
+ this._returnFocus(sel, caret.viaKeyboard);
543
619
  });
544
620
  return sel;
545
621
  }
@@ -558,12 +634,13 @@ var JotterJS = class {
558
634
  opt.textContent = `${s}px`;
559
635
  sel.appendChild(opt);
560
636
  });
561
- sel.addEventListener("mousedown", () => this._saveBookmark());
637
+ const caret = this._bindSelectCaret(sel);
562
638
  sel.addEventListener("change", () => {
563
639
  if (!sel.value)
564
640
  return;
565
641
  this._restoreSavedBookmark();
566
642
  this._applyEdit(() => this._applyFontSize(sel.value));
643
+ this._returnFocus(sel, caret.viaKeyboard);
567
644
  });
568
645
  return sel;
569
646
  }
@@ -606,6 +683,7 @@ var JotterJS = class {
606
683
  input.className = "jotter-color-input";
607
684
  input.value = defaultColor;
608
685
  input.tabIndex = -1;
686
+ let viaKeyboard = false;
609
687
  input.addEventListener("change", () => {
610
688
  const color = input.value;
611
689
  swatch.style.background = color;
@@ -615,9 +693,10 @@ var JotterJS = class {
615
693
  this._lastHiliteColor = color;
616
694
  this._restoreSavedBookmark();
617
695
  this._applyEdit(() => document.execCommand(action.cmd, false, color));
696
+ this._returnFocus(btn, viaKeyboard);
618
697
  });
619
- btn.addEventListener("mousedown", (e) => {
620
- e.preventDefault();
698
+ this._bindActivation(btn, (fromKeyboard) => {
699
+ viaKeyboard = fromKeyboard;
621
700
  this._saveBookmark();
622
701
  input.click();
623
702
  });
@@ -641,8 +720,7 @@ var JotterJS = class {
641
720
  icon.className = "material-icons";
642
721
  icon.textContent = action.icon;
643
722
  btn.appendChild(icon);
644
- btn.addEventListener("mousedown", (e) => {
645
- e.preventDefault();
723
+ this._bindActivation(btn, (viaKeyboard) => {
646
724
  const resolver = this._resolverFor(action.id);
647
725
  if (resolver) {
648
726
  this._hidePopup();
@@ -655,9 +733,24 @@ var JotterJS = class {
655
733
  return;
656
734
  }
657
735
  this._showPopup(btn, this._buildPopupContent(action.id), action.id);
736
+ if (viaKeyboard)
737
+ this._focusPopup();
658
738
  });
659
739
  return btn;
660
740
  }
741
+ /**
742
+ * Moves focus into a popup that was opened from the keyboard. The popup lives
743
+ * on document.body, a whole document away from the button that opened it, so
744
+ * without this the fields the user just asked for would be unreachable
745
+ * without tabbing through the rest of the page.
746
+ */
747
+ _focusPopup() {
748
+ const el = this._popup.querySelector(
749
+ 'input, textarea, select, button, [tabindex]:not([tabindex="-1"])'
750
+ );
751
+ if (el)
752
+ el.focus();
753
+ }
661
754
  _buildPopupContainer() {
662
755
  const el = document.createElement("div");
663
756
  el.className = "jotter-popup";
@@ -716,7 +809,12 @@ var JotterJS = class {
716
809
  }
717
810
  }
718
811
  }
719
- /** Hover-to-select grid (up to 8×10). Click inserts <table> with <th> header row. */
812
+ /**
813
+ * Size picker (up to 8×10) inserting a <table> with a <th> header row.
814
+ * Pointer: hover sizes it, click inserts. Keyboard: the grid is a single tab
815
+ * stop (roving tabindex), arrows size it and Enter/Space inserts — 80 tab
816
+ * stops would be worse than no keyboard support at all.
817
+ */
720
818
  _popupTable() {
721
819
  const wrap = document.createElement("div");
722
820
  wrap.className = "jotter-popup-inner";
@@ -726,30 +824,61 @@ var JotterJS = class {
726
824
  const grid = document.createElement("div");
727
825
  grid.className = "jotter-table-grid";
728
826
  grid.style.gridTemplateColumns = `repeat(${COLS}, 1fr)`;
827
+ grid.setAttribute("role", "grid");
828
+ grid.setAttribute("aria-label", "Table size");
729
829
  const hint = document.createElement("div");
730
830
  hint.className = "jotter-popup-hint";
731
- hint.textContent = "Hover to select size";
831
+ hint.textContent = "Hover or arrow to select size";
732
832
  const cells = [];
833
+ const preview = (r, c) => {
834
+ hint.textContent = `${r + 1} \xD7 ${c + 1} table`;
835
+ cells.forEach((cl) => {
836
+ cl.classList.toggle(
837
+ "jotter-table-cell--active",
838
+ +cl.dataset.r <= r && +cl.dataset.c <= c
839
+ );
840
+ });
841
+ };
733
842
  for (let r = 0; r < ROWS; r++) {
734
843
  for (let c = 0; c < COLS; c++) {
735
844
  const cell = document.createElement("span");
736
845
  cell.className = "jotter-table-cell";
737
846
  cell.dataset.r = r;
738
847
  cell.dataset.c = c;
739
- cell.addEventListener("mouseenter", () => {
740
- hint.textContent = `${r + 1} \xD7 ${c + 1} table`;
741
- cells.forEach((cl) => {
742
- cl.classList.toggle(
743
- "jotter-table-cell--active",
744
- +cl.dataset.r <= r && +cl.dataset.c <= c
745
- );
746
- });
747
- });
848
+ cell.setAttribute("role", "gridcell");
849
+ cell.setAttribute("aria-label", `${r + 1} by ${c + 1} table`);
850
+ cell.tabIndex = r === 0 && c === 0 ? 0 : -1;
851
+ cell.addEventListener("mouseenter", () => preview(r, c));
852
+ cell.addEventListener("focus", () => preview(r, c));
748
853
  cell.addEventListener("click", () => this._insertTable(r + 1, c + 1));
749
854
  cells.push(cell);
750
855
  grid.appendChild(cell);
751
856
  }
752
857
  }
858
+ const STEP = { ArrowRight: [0, 1], ArrowLeft: [0, -1], ArrowDown: [1, 0], ArrowUp: [-1, 0] };
859
+ grid.addEventListener("keydown", (e) => {
860
+ const cell = cells.indexOf(document.activeElement) === -1 ? null : document.activeElement;
861
+ if (!cell)
862
+ return;
863
+ const r = +cell.dataset.r, c = +cell.dataset.c;
864
+ if (e.key === "Enter" || e.key === " " || e.key === "Spacebar") {
865
+ e.preventDefault();
866
+ this._insertTable(r + 1, c + 1);
867
+ return;
868
+ }
869
+ const step = STEP[e.key];
870
+ if (!step)
871
+ return;
872
+ e.preventDefault();
873
+ const nr = Math.min(ROWS - 1, Math.max(0, r + step[0]));
874
+ const nc = Math.min(COLS - 1, Math.max(0, c + step[1]));
875
+ const next = cells[nr * COLS + nc];
876
+ if (next === cell)
877
+ return;
878
+ cell.tabIndex = -1;
879
+ next.tabIndex = 0;
880
+ next.focus();
881
+ });
753
882
  wrap.appendChild(grid);
754
883
  wrap.appendChild(hint);
755
884
  return wrap;
@@ -926,8 +1055,7 @@ var JotterJS = class {
926
1055
  btn.className = "jotter-char-btn";
927
1056
  btn.textContent = ch;
928
1057
  btn.title = `U+${ch.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`;
929
- btn.addEventListener("mousedown", (e) => {
930
- e.preventDefault();
1058
+ this._bindActivation(btn, () => {
931
1059
  this._commitPopup(() => document.execCommand("insertText", false, ch));
932
1060
  });
933
1061
  grid.appendChild(btn);
@@ -944,8 +1072,7 @@ var JotterJS = class {
944
1072
  btn.type = "button";
945
1073
  btn.className = "jotter-lorem-btn";
946
1074
  btn.textContent = v.label;
947
- btn.addEventListener("mousedown", (e) => {
948
- e.preventDefault();
1075
+ this._bindActivation(btn, () => {
949
1076
  this._commitPopup(() => document.execCommand(
950
1077
  v.isHTML ? "insertHTML" : "insertText",
951
1078
  false,
@@ -1 +1 @@
1
- @font-face{font-family:Material Icons;font-style:normal;font-weight:400;src:url("./fonts/material-symbols-rounded.woff2") format("woff2")}.material-icons{font-family:Material Icons;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:"liga";font-feature-settings:"liga";-webkit-font-smoothing:antialiased}:root{--jotter-bg: #ffffff;--jotter-surface: #f4f3f0;--jotter-surface2: #eceae5;--jotter-border: #d8d4cc;--jotter-border-focus: #7a6a4a;--jotter-text: #1e1c18;--jotter-text-muted: #9a9488;--jotter-accent: #7a6a4a;--jotter-btn-hover: rgba(122, 106, 74, .07);--jotter-btn-active-bg: rgba(122, 106, 74, .14);--jotter-radius: 4px;--jotter-font-ui: "DM Mono", "Fira Mono", "Consolas", monospace;--jotter-font-editor: Georgia, "Times New Roman", serif;--jotter-status-h: 32px;--jotter-transition: .14s ease}.jotter-host{display:block}.htmled{display:flex;flex-direction:column;border:1px solid var(--jotter-border);border-radius:var(--jotter-radius);background:var(--jotter-bg);color:var(--jotter-text);font-family:var(--jotter-font-ui);overflow:hidden;transition:border-color var(--jotter-transition),box-shadow var(--jotter-transition)}.jotter--focused{border-color:var(--jotter-border-focus);box-shadow:0 0 0 3px #7a6a4a1a}.jotter--disabled{opacity:.55;pointer-events:none}.jotter-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:2px;padding:6px 8px;background:var(--jotter-surface);border-bottom:1px solid var(--jotter-border)}.jotter-select{appearance:none;-webkit-appearance:none;background:transparent;border:1px solid var(--jotter-border);border-radius:var(--jotter-radius);color:var(--jotter-text);font-family:var(--jotter-font-ui);font-size:11px;letter-spacing:.04em;padding:4px 8px;height:28px;cursor:pointer;flex-shrink:0;transition:border-color var(--jotter-transition),background var(--jotter-transition)}.jotter-select{min-width:108px;text-transform:uppercase}.jotter-select--font{min-width:130px;text-transform:none}.jotter-select--size{min-width:68px;text-transform:none}.jotter-select--theme{min-width:90px;text-transform:none}.jotter-select:hover{border-color:var(--jotter-accent);background:var(--jotter-btn-hover)}.jotter-select:focus{outline:none;border-color:var(--jotter-accent)}.jotter-select option{background:var(--jotter-surface2);color:var(--jotter-text)}.jotter-btn{display:flex;align-items:center;justify-content:center;flex-direction:column;width:32px;height:30px;padding:0;border:none;border-radius:var(--jotter-radius);background:transparent;color:var(--jotter-text-muted);cursor:pointer;flex-shrink:0;transition:color var(--jotter-transition),background var(--jotter-transition);position:relative}.jotter-btn .material-icons{pointer-events:none;font-size:18px;line-height:1;user-select:none}.jotter-btn:hover{color:var(--jotter-accent);background:var(--jotter-btn-hover)}.jotter-btn--active{color:var(--jotter-accent)!important;background:var(--jotter-btn-active-bg)!important}.jotter-btn--text{width:auto;padding:0 10px;font-family:var(--jotter-font-ui);font-size:11px;letter-spacing:.08em;text-transform:uppercase;border:1px solid var(--jotter-border)}.jotter-sep{width:1px;height:18px;background:var(--jotter-border);margin:0 4px;flex-shrink:0}.jotter-color-wrap{display:inline-flex;align-items:center;flex-shrink:0}.jotter-color-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none}.jotter-color-btn{width:32px;height:34px;gap:0;padding-bottom:3px}.jotter-color-swatch{display:block;width:18px;height:3px;border-radius:1px;pointer-events:none;flex-shrink:0}.jotter-editor-wrap{flex:1;overflow-y:auto;background:var(--jotter-bg);scrollbar-width:thin;scrollbar-color:var(--jotter-border) transparent}.jotter-editor-wrap::-webkit-scrollbar{width:6px}.jotter-editor-wrap::-webkit-scrollbar-track{background:transparent}.jotter-editor-wrap::-webkit-scrollbar-thumb{background:var(--jotter-border);border-radius:3px}.jotter-editor{outline:none;padding:24px 28px;min-height:200px;font-family:var(--jotter-font-editor);font-size:16px;line-height:1.75;color:var(--jotter-text);caret-color:auto;word-break:break-word}.jotter-source{display:none;width:100%;min-height:200px;padding:24px 28px;background:var(--jotter-bg);color:#3a6030;font-family:var(--jotter-font-ui);font-size:13px;line-height:1.7;border:none;outline:none;resize:vertical;caret-color:var(--jotter-accent);tab-size:2;white-space:pre;overflow-wrap:normal;overflow-x:auto}.jotter--source-mode .jotter-btn:not([data-custom=toggleSource]),.jotter--source-mode .jotter-select,.jotter--source-mode .jotter-color-btn{opacity:.35;pointer-events:none}.jotter-editor:empty:before{content:attr(data-placeholder);color:var(--jotter-text-muted);pointer-events:none;font-style:italic}.jotter-editor .jotter-bookmark{display:none}.jotter-editor h1,.jotter-editor h2,.jotter-editor h3,.jotter-editor h4,.jotter-editor h5,.jotter-editor h6{text-transform:none;font-family:var(--ed-heading-font, inherit);letter-spacing:var(--ed-heading-tracking, normal)}.jotter-editor h1{font-size:2em;font-weight:700;margin:.6em 0 .3em;color:var(--ed-h1, inherit);border-bottom:var(--ed-h1-rule, none);padding-bottom:.25em}.jotter-editor h2{font-size:1.5em;font-weight:600;margin:.75em 0 .25em;color:var(--ed-h2, inherit)}.jotter-editor h3{font-size:1.2em;font-weight:600;margin:.75em 0 .25em;color:var(--ed-h3, inherit)}.jotter-editor h4{font-size:1em;font-weight:600;margin:.75em 0 .25em;color:var(--ed-h4, inherit)}.jotter-editor p{margin:0 0 .85em}.jotter-editor a{color:var(--ed-link, -webkit-link);text-decoration-color:var(--ed-link-deco, auto);text-underline-offset:3px}.jotter-editor a:hover{text-decoration-color:var(--ed-link, -webkit-link)}.jotter-editor strong,.jotter-editor b{color:var(--ed-strong, inherit);font-weight:700}.jotter-editor em,.jotter-editor i{color:var(--ed-em, inherit)}.jotter-editor blockquote{margin:1em 0;padding:8px 0 8px 20px;font-style:italic;border-left:3px solid var(--ed-quote-bar, #ccc);color:var(--ed-quote, inherit)}.jotter-editor pre{font-family:var(--ed-code-font, monospace);font-size:.875em;margin:.75em 0;padding:12px 16px;overflow-x:auto;white-space:pre-wrap;background:var(--ed-code-bg, transparent);color:var(--ed-code-text, inherit);border:1px solid var(--ed-code-border, transparent);border-radius:var(--jotter-radius)}.jotter-editor code{font-family:var(--ed-code-font, monospace);font-size:.875em;padding:1px 4px;background:var(--ed-code-bg, transparent);color:var(--ed-code-text, inherit);border:1px solid var(--ed-code-border, transparent);border-radius:2px}.jotter-editor ul,.jotter-editor ol{padding-left:24px;margin:.5em 0 .85em}.jotter-editor li{margin-bottom:.25em}.jotter-editor ul li::marker{color:var(--ed-marker, inherit)}.jotter-editor ol li::marker{color:var(--ed-marker, inherit)}.jotter-editor hr{border:none;border-top:1px solid var(--ed-hr, currentColor);opacity:.2;margin:1.5em 0}.jotter-editor sub,.jotter-editor sup{font-size:.75em}.jotter-editor ::selection{background:var(--ed-selection, highlight)}.jotter-editor table{border-collapse:collapse;width:100%;margin:.75em 0;font-size:.95em}.jotter-editor th,.jotter-editor td{border:1px solid var(--ed-table-border, #ccc);padding:7px 12px;text-align:left;vertical-align:top;min-width:40px}.jotter-editor th{background:var(--ed-th-bg, transparent);color:var(--ed-th-text, inherit);font-family:var(--ed-heading-font, inherit);font-size:.85em;font-weight:600;text-transform:none}.jotter-editor[data-theme=warm]{--ed-heading-font: "DM Mono", "Fira Mono", monospace;--ed-heading-tracking: -.01em;--ed-caret: #7a6a4a;--ed-selection: rgba(122, 106, 74, .18);--ed-h1: #7a6a4a;--ed-h2: #4a3e28;--ed-h3: #5a4e38;--ed-h4: #9a9488;--ed-h1-rule: 1px solid #d8d4cc;--ed-link: #7a6a4a;--ed-link-deco: rgba(122, 106, 74, .35);--ed-strong: #1a1612;--ed-em: #3a3028;--ed-quote: #7a7468;--ed-quote-bar: #7a6a4a;--ed-code-font: "DM Mono", "Fira Mono", monospace;--ed-code-bg: #eceae5;--ed-code-border: #d8d4cc;--ed-code-text: #3a6030;--ed-marker: #7a6a4a;--ed-hr: #d8d4cc;--ed-th-bg: #f4f3f0;--ed-th-text: #7a6a4a;--ed-table-border: #d8d4cc}.jotter-editor[data-theme=ink]{--ed-heading-font: "DM Mono", "Fira Mono", monospace;--ed-heading-tracking: -.01em;--ed-caret: #1a2a4a;--ed-selection: rgba(26, 42, 74, .14);--ed-h1: #0d1f3c;--ed-h2: #1a2a4a;--ed-h3: #243560;--ed-h4: #5a6888;--ed-h1-rule: 1px solid #b8c4d8;--ed-link: #1a4a8a;--ed-link-deco: rgba(26, 74, 138, .3);--ed-strong: #0a1428;--ed-em: #2a3a5a;--ed-quote: #4a5a78;--ed-quote-bar: #1a4a8a;--ed-code-font: "DM Mono", "Fira Mono", monospace;--ed-code-bg: #eef0f6;--ed-code-border: #c8cedd;--ed-code-text: #1a4a3a;--ed-marker: #1a4a8a;--ed-hr: #b8c4d8;--ed-th-bg: #eef0f6;--ed-th-text: #1a2a4a;--ed-table-border: #b8c4d8}.jotter-editor[data-theme=forest]{--ed-heading-font: "DM Mono", "Fira Mono", monospace;--ed-heading-tracking: -.01em;--ed-caret: #1a3a28;--ed-selection: rgba(26, 58, 40, .14);--ed-h1: #1a3a28;--ed-h2: #1e4430;--ed-h3: #2a5a40;--ed-h4: #5a7a68;--ed-h1-rule: 1px solid #b0ccc0;--ed-link: #1a6040;--ed-link-deco: rgba(26, 96, 64, .3);--ed-strong: #0e2018;--ed-em: #2a4038;--ed-quote: #3a5a48;--ed-quote-bar: #1a6040;--ed-code-font: "DM Mono", "Fira Mono", monospace;--ed-code-bg: #eef5f0;--ed-code-border: #c0d8c8;--ed-code-text: #1a5828;--ed-marker: #1a6040;--ed-hr: #b0ccc0;--ed-th-bg: #eef5f0;--ed-th-text: #1a3a28;--ed-table-border: #b0ccc0}.jotter-editor .jotter-video-wrap{position:relative;padding-bottom:56.25%;height:0;overflow:hidden;margin:.75em 0;border-radius:var(--jotter-radius);border:1px solid var(--jotter-border)}.jotter-editor .jotter-video-wrap iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:none}.jotter-status{display:flex;align-items:center;gap:16px;padding:0 14px;height:var(--jotter-status-h);min-height:var(--jotter-status-h);background:var(--jotter-surface);border-top:1px solid var(--jotter-border);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--jotter-text-muted)}.jotter-status-mode{margin-left:auto;color:var(--jotter-accent);font-weight:600}.jotter-popup{display:none;position:fixed;z-index:9999;background:#fff;border:1px solid #d8d4cc;border-radius:5px;box-shadow:0 8px 24px #0000001f,0 2px 6px #00000012;min-width:220px;max-width:360px}.jotter-popup--visible{display:block}.jotter-popup-inner{padding:14px}.jotter-popup-title{font-family:var(--jotter-font-ui);font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:#9a9488;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid #d8d4cc}.jotter-popup-hint{font-size:11px;color:#9a9488;margin-top:8px;text-align:center;letter-spacing:.04em}.jotter-popup-form{display:flex;flex-direction:column;gap:6px;min-width:280px}.jotter-popup-label{font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:#9a9488;margin-top:4px}.jotter-popup-input,.jotter-popup-select,.jotter-popup-textarea{width:100%;background:#f8f7f4;border:1px solid #d8d4cc;border-radius:3px;color:#1e1c18;font-family:DM Mono,Fira Mono,monospace;font-size:12px;padding:7px 9px;outline:none;transition:border-color .12s;resize:vertical}.jotter-popup-input:focus,.jotter-popup-select:focus,.jotter-popup-textarea:focus{border-color:#7a6a4a}.jotter-popup-select{appearance:auto;cursor:pointer;resize:none}.jotter-input--error{border-color:#b85050!important}.jotter-popup-submit{margin-top:6px;padding:7px 14px;background:#7a6a4a14;border:1px solid #7a6a4a;border-radius:3px;color:#7a6a4a;font-family:DM Mono,Fira Mono,monospace;font-size:11px;letter-spacing:.08em;text-transform:uppercase;cursor:pointer;transition:background .12s}.jotter-popup-submit:hover{background:#7a6a4a29}.jotter-table-grid{display:grid;gap:3px}.jotter-table-cell{width:18px;height:18px;border:1px solid #d8d4cc;border-radius:2px;cursor:pointer;transition:border-color 80ms,background 80ms}.jotter-table-cell--active{background:#7a6a4a24;border-color:#7a6a4a}.jotter-char-grid{display:flex;flex-wrap:wrap;gap:2px;max-width:300px;max-height:220px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#d8d4cc transparent}.jotter-char-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid #d8d4cc;border-radius:3px;background:transparent;color:#1e1c18;font-size:14px;cursor:pointer;transition:background 80ms,border-color 80ms,color 80ms;flex-shrink:0}.jotter-char-btn:hover{background:#7a6a4a1a;border-color:#7a6a4a;color:#7a6a4a}.jotter-lorem-btn{display:block;width:100%;padding:9px 12px;background:transparent;border:1px solid #d8d4cc;border-radius:3px;color:#1e1c18;font-family:DM Mono,Fira Mono,monospace;font-size:11px;letter-spacing:.04em;text-align:left;cursor:pointer;margin-bottom:4px;transition:background .1s,border-color .1s,color .1s}.jotter-lorem-btn:hover{background:#7a6a4a12;border-color:#7a6a4a;color:#7a6a4a}
1
+ @font-face{font-family:Material Icons;font-style:normal;font-weight:400;src:url("./fonts/material-symbols-rounded.woff2") format("woff2")}.material-icons{font-family:Material Icons;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:"liga";font-feature-settings:"liga";-webkit-font-smoothing:antialiased}:root{--jotter-bg: #ffffff;--jotter-surface: #f4f3f0;--jotter-surface2: #eceae5;--jotter-border: #d8d4cc;--jotter-border-focus: #7a6a4a;--jotter-text: #1e1c18;--jotter-text-muted: #9a9488;--jotter-accent: #7a6a4a;--jotter-btn-hover: rgba(122, 106, 74, .07);--jotter-btn-active-bg: rgba(122, 106, 74, .14);--jotter-radius: 4px;--jotter-font-ui: "DM Mono", "Fira Mono", "Consolas", monospace;--jotter-font-editor: Georgia, "Times New Roman", serif;--jotter-status-h: 32px;--jotter-transition: .14s ease}.jotter-host{display:block}.htmled{display:flex;flex-direction:column;border:1px solid var(--jotter-border);border-radius:var(--jotter-radius);background:var(--jotter-bg);color:var(--jotter-text);font-family:var(--jotter-font-ui);overflow:hidden;transition:border-color var(--jotter-transition),box-shadow var(--jotter-transition)}.jotter--focused{border-color:var(--jotter-border-focus);box-shadow:0 0 0 3px #7a6a4a1a}.jotter--disabled{opacity:.55;pointer-events:none}.jotter-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:2px;padding:6px 8px;background:var(--jotter-surface);border-bottom:1px solid var(--jotter-border)}.jotter-select{appearance:none;-webkit-appearance:none;background:transparent;border:1px solid var(--jotter-border);border-radius:var(--jotter-radius);color:var(--jotter-text);font-family:var(--jotter-font-ui);font-size:11px;letter-spacing:.04em;padding:4px 8px;height:28px;cursor:pointer;flex-shrink:0;transition:border-color var(--jotter-transition),background var(--jotter-transition)}.jotter-select{min-width:108px;text-transform:uppercase}.jotter-select--font{min-width:130px;text-transform:none}.jotter-select--size{min-width:68px;text-transform:none}.jotter-select--theme{min-width:90px;text-transform:none}.jotter-select:hover{border-color:var(--jotter-accent);background:var(--jotter-btn-hover)}.jotter-select:focus{outline:none;border-color:var(--jotter-accent)}.jotter-select option{background:var(--jotter-surface2);color:var(--jotter-text)}.jotter-btn{display:flex;align-items:center;justify-content:center;flex-direction:column;width:32px;height:30px;padding:0;border:none;border-radius:var(--jotter-radius);background:transparent;color:var(--jotter-text-muted);cursor:pointer;flex-shrink:0;transition:color var(--jotter-transition),background var(--jotter-transition);position:relative}.jotter-btn .material-icons{pointer-events:none;font-size:18px;line-height:1;user-select:none}.jotter-btn:hover{color:var(--jotter-accent);background:var(--jotter-btn-hover)}.jotter-btn--active{color:var(--jotter-accent)!important;background:var(--jotter-btn-active-bg)!important}.jotter-btn--text{width:auto;padding:0 10px;font-family:var(--jotter-font-ui);font-size:11px;letter-spacing:.08em;text-transform:uppercase;border:1px solid var(--jotter-border)}.jotter-sep{width:1px;height:18px;background:var(--jotter-border);margin:0 4px;flex-shrink:0}.jotter-color-wrap{display:inline-flex;align-items:center;flex-shrink:0}.jotter-color-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none}.jotter-color-btn{width:32px;height:34px;gap:0;padding-bottom:3px}.jotter-color-swatch{display:block;width:18px;height:3px;border-radius:1px;pointer-events:none;flex-shrink:0}.jotter-editor-wrap{flex:1;overflow-y:auto;background:var(--jotter-bg);scrollbar-width:thin;scrollbar-color:var(--jotter-border) transparent}.jotter-editor-wrap::-webkit-scrollbar{width:6px}.jotter-editor-wrap::-webkit-scrollbar-track{background:transparent}.jotter-editor-wrap::-webkit-scrollbar-thumb{background:var(--jotter-border);border-radius:3px}.jotter-editor{outline:none;padding:24px 28px;min-height:200px;font-family:var(--jotter-font-editor);font-size:16px;line-height:1.75;color:var(--jotter-text);caret-color:auto;word-break:break-word}.jotter-source{display:none;width:100%;min-height:200px;padding:24px 28px;background:var(--jotter-bg);color:#3a6030;font-family:var(--jotter-font-ui);font-size:13px;line-height:1.7;border:none;outline:none;resize:vertical;caret-color:var(--jotter-accent);tab-size:2;white-space:pre;overflow-wrap:normal;overflow-x:auto}.jotter--source-mode .jotter-btn:not([data-custom=toggleSource]),.jotter--source-mode .jotter-select,.jotter--source-mode .jotter-color-btn{opacity:.35;pointer-events:none}.jotter-editor:empty:before{content:attr(data-placeholder);color:var(--jotter-text-muted);pointer-events:none;font-style:italic}.jotter-editor .jotter-bookmark{display:none}.jotter-editor h1,.jotter-editor h2,.jotter-editor h3,.jotter-editor h4,.jotter-editor h5,.jotter-editor h6{text-transform:none;font-family:var(--ed-heading-font, inherit);letter-spacing:var(--ed-heading-tracking, normal)}.jotter-editor h1{font-size:2em;font-weight:700;margin:.6em 0 .3em;color:var(--ed-h1, inherit);border-bottom:var(--ed-h1-rule, none);padding-bottom:.25em}.jotter-editor h2{font-size:1.5em;font-weight:600;margin:.75em 0 .25em;color:var(--ed-h2, inherit)}.jotter-editor h3{font-size:1.2em;font-weight:600;margin:.75em 0 .25em;color:var(--ed-h3, inherit)}.jotter-editor h4{font-size:1em;font-weight:600;margin:.75em 0 .25em;color:var(--ed-h4, inherit)}.jotter-editor p{margin:0 0 .85em}.jotter-editor a{color:var(--ed-link, -webkit-link);text-decoration-color:var(--ed-link-deco, auto);text-underline-offset:3px}.jotter-editor a:hover{text-decoration-color:var(--ed-link, -webkit-link)}.jotter-editor strong,.jotter-editor b{color:var(--ed-strong, inherit);font-weight:700}.jotter-editor em,.jotter-editor i{color:var(--ed-em, inherit)}.jotter-editor blockquote{margin:1em 0;padding:8px 0 8px 20px;font-style:italic;border-left:3px solid var(--ed-quote-bar, #ccc);color:var(--ed-quote, inherit)}.jotter-editor pre{font-family:var(--ed-code-font, monospace);font-size:.875em;margin:.75em 0;padding:12px 16px;overflow-x:auto;white-space:pre-wrap;background:var(--ed-code-bg, transparent);color:var(--ed-code-text, inherit);border:1px solid var(--ed-code-border, transparent);border-radius:var(--jotter-radius)}.jotter-editor code{font-family:var(--ed-code-font, monospace);font-size:.875em;padding:1px 4px;background:var(--ed-code-bg, transparent);color:var(--ed-code-text, inherit);border:1px solid var(--ed-code-border, transparent);border-radius:2px}.jotter-editor ul,.jotter-editor ol{padding-left:24px;margin:.5em 0 .85em}.jotter-editor li{margin-bottom:.25em}.jotter-editor ul li::marker{color:var(--ed-marker, inherit)}.jotter-editor ol li::marker{color:var(--ed-marker, inherit)}.jotter-editor hr{border:none;border-top:1px solid var(--ed-hr, currentColor);opacity:.2;margin:1.5em 0}.jotter-editor sub,.jotter-editor sup{font-size:.75em}.jotter-editor ::selection{background:var(--ed-selection, highlight)}.jotter-editor table{border-collapse:collapse;width:100%;margin:.75em 0;font-size:.95em}.jotter-editor th,.jotter-editor td{border:1px solid var(--ed-table-border, #ccc);padding:7px 12px;text-align:left;vertical-align:top;min-width:40px}.jotter-editor th{background:var(--ed-th-bg, transparent);color:var(--ed-th-text, inherit);font-family:var(--ed-heading-font, inherit);font-size:.85em;font-weight:600;text-transform:none}.jotter-editor[data-theme=warm]{--ed-heading-font: "DM Mono", "Fira Mono", monospace;--ed-heading-tracking: -.01em;--ed-caret: #7a6a4a;--ed-selection: rgba(122, 106, 74, .18);--ed-h1: #7a6a4a;--ed-h2: #4a3e28;--ed-h3: #5a4e38;--ed-h4: #9a9488;--ed-h1-rule: 1px solid #d8d4cc;--ed-link: #7a6a4a;--ed-link-deco: rgba(122, 106, 74, .35);--ed-strong: #1a1612;--ed-em: #3a3028;--ed-quote: #7a7468;--ed-quote-bar: #7a6a4a;--ed-code-font: "DM Mono", "Fira Mono", monospace;--ed-code-bg: #eceae5;--ed-code-border: #d8d4cc;--ed-code-text: #3a6030;--ed-marker: #7a6a4a;--ed-hr: #d8d4cc;--ed-th-bg: #f4f3f0;--ed-th-text: #7a6a4a;--ed-table-border: #d8d4cc}.jotter-editor[data-theme=ink]{--ed-heading-font: "DM Mono", "Fira Mono", monospace;--ed-heading-tracking: -.01em;--ed-caret: #1a2a4a;--ed-selection: rgba(26, 42, 74, .14);--ed-h1: #0d1f3c;--ed-h2: #1a2a4a;--ed-h3: #243560;--ed-h4: #5a6888;--ed-h1-rule: 1px solid #b8c4d8;--ed-link: #1a4a8a;--ed-link-deco: rgba(26, 74, 138, .3);--ed-strong: #0a1428;--ed-em: #2a3a5a;--ed-quote: #4a5a78;--ed-quote-bar: #1a4a8a;--ed-code-font: "DM Mono", "Fira Mono", monospace;--ed-code-bg: #eef0f6;--ed-code-border: #c8cedd;--ed-code-text: #1a4a3a;--ed-marker: #1a4a8a;--ed-hr: #b8c4d8;--ed-th-bg: #eef0f6;--ed-th-text: #1a2a4a;--ed-table-border: #b8c4d8}.jotter-editor[data-theme=forest]{--ed-heading-font: "DM Mono", "Fira Mono", monospace;--ed-heading-tracking: -.01em;--ed-caret: #1a3a28;--ed-selection: rgba(26, 58, 40, .14);--ed-h1: #1a3a28;--ed-h2: #1e4430;--ed-h3: #2a5a40;--ed-h4: #5a7a68;--ed-h1-rule: 1px solid #b0ccc0;--ed-link: #1a6040;--ed-link-deco: rgba(26, 96, 64, .3);--ed-strong: #0e2018;--ed-em: #2a4038;--ed-quote: #3a5a48;--ed-quote-bar: #1a6040;--ed-code-font: "DM Mono", "Fira Mono", monospace;--ed-code-bg: #eef5f0;--ed-code-border: #c0d8c8;--ed-code-text: #1a5828;--ed-marker: #1a6040;--ed-hr: #b0ccc0;--ed-th-bg: #eef5f0;--ed-th-text: #1a3a28;--ed-table-border: #b0ccc0}.jotter-editor .jotter-video-wrap{position:relative;padding-bottom:56.25%;height:0;overflow:hidden;margin:.75em 0;border-radius:var(--jotter-radius);border:1px solid var(--jotter-border)}.jotter-editor .jotter-video-wrap iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:none}.jotter-status{display:flex;align-items:center;gap:16px;padding:0 14px;height:var(--jotter-status-h);min-height:var(--jotter-status-h);background:var(--jotter-surface);border-top:1px solid var(--jotter-border);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--jotter-text-muted)}.jotter-status-mode{margin-left:auto;color:var(--jotter-accent);font-weight:600}.jotter-popup{display:none;position:fixed;z-index:9999;background:#fff;border:1px solid #d8d4cc;border-radius:5px;box-shadow:0 8px 24px #0000001f,0 2px 6px #00000012;min-width:220px;max-width:360px}.jotter-popup--visible{display:block}.jotter-popup-inner{padding:14px}.jotter-popup-title{font-family:var(--jotter-font-ui);font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:#9a9488;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid #d8d4cc}.jotter-popup-hint{font-size:11px;color:#9a9488;margin-top:8px;text-align:center;letter-spacing:.04em}.jotter-popup-form{display:flex;flex-direction:column;gap:6px;min-width:280px}.jotter-popup-label{font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:#9a9488;margin-top:4px}.jotter-popup-input,.jotter-popup-select,.jotter-popup-textarea{width:100%;background:#f8f7f4;border:1px solid #d8d4cc;border-radius:3px;color:#1e1c18;font-family:DM Mono,Fira Mono,monospace;font-size:12px;padding:7px 9px;outline:none;transition:border-color .12s;resize:vertical}.jotter-popup-input:focus,.jotter-popup-select:focus,.jotter-popup-textarea:focus{border-color:#7a6a4a}.jotter-popup-select{appearance:auto;cursor:pointer;resize:none}.jotter-input--error{border-color:#b85050!important}.jotter-popup-submit{margin-top:6px;padding:7px 14px;background:#7a6a4a14;border:1px solid #7a6a4a;border-radius:3px;color:#7a6a4a;font-family:DM Mono,Fira Mono,monospace;font-size:11px;letter-spacing:.08em;text-transform:uppercase;cursor:pointer;transition:background .12s}.jotter-popup-submit:hover{background:#7a6a4a29}.jotter-table-grid{display:grid;gap:3px}.jotter-table-cell{width:18px;height:18px;border:1px solid #d8d4cc;border-radius:2px;cursor:pointer;transition:border-color 80ms,background 80ms}.jotter-table-cell--active{background:#7a6a4a24;border-color:#7a6a4a}.jotter-char-grid{display:flex;flex-wrap:wrap;gap:2px;max-width:300px;max-height:220px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#d8d4cc transparent}.jotter-char-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid #d8d4cc;border-radius:3px;background:transparent;color:#1e1c18;font-size:14px;cursor:pointer;transition:background 80ms,border-color 80ms,color 80ms;flex-shrink:0}.jotter-char-btn:hover{background:#7a6a4a1a;border-color:#7a6a4a;color:#7a6a4a}.jotter-lorem-btn{display:block;width:100%;padding:9px 12px;background:transparent;border:1px solid #d8d4cc;border-radius:3px;color:#1e1c18;font-family:DM Mono,Fira Mono,monospace;font-size:11px;letter-spacing:.04em;text-align:left;cursor:pointer;margin-bottom:4px;transition:background .1s,border-color .1s,color .1s}.jotter-lorem-btn:hover{background:#7a6a4a12;border-color:#7a6a4a;color:#7a6a4a}.jotter-btn:focus-visible,.jotter-select:focus-visible,.jotter-char-btn:focus-visible,.jotter-lorem-btn:focus-visible,.jotter-popup-submit:focus-visible,.jotter-table-cell:focus-visible{outline:2px solid var(--jotter-accent);outline-offset:1px}
@@ -1,5 +1,5 @@
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 n=_,h={minimal:[n.source,n.sep,n.bold,n.italic,n.underline,n.sep,n.link,n.unlink],writing:[n.source,n.sep,n.undo,n.redo,n.sep,n.blockformat,n.sep,n.bold,n.italic,n.underline,n.strike,n.sep,n.bullets,n.numbered,n.sep,n.link,n.unlink,n.sep,n.image],full:[n.source,n.sep,n.undo,n.redo,n.sep,n.copy,n.cut,n.paste,n.sep,n.clearFormat,n.sep,n.blockformat,n.fontfamily,n.fontsize,n.sep,n.bold,n.italic,n.underline,n.strike,n.subscript,n.superscript,n.code,n.sep,n.foreColor,n.hiliteColor,n.sep,n.alignLeft,n.alignCenter,n.alignRight,n.sep,n.bullets,n.numbered,n.sep,n.link,n.unlink,n.sep,n.image,n.video,n.table,n.embed,n.symbol,n.specialChar,n.lorem,n.sep,n.theme]};Object.values(h).forEach(Object.freeze);Object.freeze(h);var g=h.full,C={image:"onRequestImage",link:"onRequestLink",video:"onRequestVideo",embed:"onRequestEmbed"},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"}],k=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],v=[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"],S=["\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>"}],p=class{constructor(e,t={}){if(this._target=typeof e=="string"?document.querySelector(e):e,!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,onRequestImage:null,onRequestLink:null,onRequestVideo:null,onRequestEmbed:null},t),this._listeners={},this._bookmarks=new Map,this._bmSeq=0,this._savedBookmark=null,this._externalBookmark=null,this._externalDepth=0,this._changeCount=0,this._destroyed=!1,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let e=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(e),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 e=document.createElement("div");return e.className="jotter-toolbar",this._toolbarEl=e,(this._options.toolbar||g).forEach(t=>{let o=this._buildAction(t);o&&e.appendChild(o)}),e}_buildAction(e){if(typeof e.onClick=="function")return this._buildBtn(e);switch(e.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(e);case"popup":return this._buildPopupBtn(e);case"theme":return this._buildThemeSelect();default:return this._buildBtn(e)}}_makeSep(){let e=document.createElement("span");return e.className="jotter-sep",e}_buildBtn(e){let t=document.createElement("button");if(t.type="button",t.className="jotter-btn",e.cmd&&(t.dataset.cmd=e.cmd),e.custom&&(t.dataset.custom=e.custom),t.title=e.title,t.setAttribute("aria-label",e.title),e.label)t.classList.add("jotter-btn--text"),t.appendChild(document.createTextNode(e.label));else{let o=document.createElement("span");o.className="material-icons",o.textContent=e.icon,t.appendChild(o)}return t.addEventListener("mousedown",o=>{o.preventDefault(),this._editor.focus(),this._applyEdit(()=>{if(e.onClick)e.onClick(this);else if(e.custom==="toggleSource")this._toggleSourceMode();else if(e.custom==="code")this._toggleInlineCode();else if(e.cmd==="copy")document.execCommand("copy");else if(e.cmd==="cut")document.execCommand("cut");else if(e.cmd==="paste")this._pasteFromClipboard();else if(e.prompt){let i=window.prompt(e.prompt);i&&document.execCommand(e.cmd,!1,i)}else document.execCommand(e.cmd,!1,null)}),this._updateToolbarState()}),t}_buildBlockFormatSelect(){let e=document.createElement("select");return e.className="jotter-select",e.title="Block format",e.dataset.id="blockformat",f.forEach(({label:t,tag:o})=>{let i=document.createElement("option");i.value=o,i.textContent=t,e.appendChild(i)}),e.addEventListener("mousedown",()=>this._saveBookmark()),e.addEventListener("change",()=>{this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("formatBlock",!1,e.value))}),e}_buildFontFamilySelect(){let e=document.createElement("select");e.className="jotter-select jotter-select--font",e.title="Font family",e.dataset.id="fontfamily";let t=document.createElement("option");return t.value="",t.textContent="Font",e.appendChild(t),k.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=o,i.style.fontFamily=o,e.appendChild(i)}),e.addEventListener("mousedown",()=>this._saveBookmark()),e.addEventListener("change",()=>{e.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("fontName",!1,e.value)))}),e}_buildFontSizeSelect(){let e=document.createElement("select");e.className="jotter-select jotter-select--size",e.title="Font size",e.dataset.id="fontsize";let t=document.createElement("option");return t.value="",t.textContent="Size",e.appendChild(t),v.forEach(o=>{let i=document.createElement("option");i.value=o,i.textContent=`${o}px`,e.appendChild(i)}),e.addEventListener("mousedown",()=>this._saveBookmark()),e.addEventListener("change",()=>{e.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>this._applyFontSize(e.value)))}),e}_buildThemeSelect(){let e=document.createElement("select");return e.className="jotter-select jotter-select--theme",e.title="Editor theme",e.dataset.id="theme",b.forEach(({id:t,label:o})=>{let i=document.createElement("option");i.value=t,i.textContent=o,e.appendChild(i)}),e.value=this._options.theme,e.addEventListener("change",()=>this.setTheme(e.value)),this._themeSelect=e,e}_buildColorBtn(e){let t=document.createElement("span");t.className="jotter-color-wrap";let o=document.createElement("button");o.type="button",o.className="jotter-btn jotter-color-btn",o.dataset.cmd=e.cmd,o.title=e.title,o.setAttribute("aria-label",e.title);let i=document.createElement("span");i.className="material-icons",i.textContent=e.icon,o.appendChild(i);let s=document.createElement("span");s.className="jotter-color-swatch";let r=e.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=r,o.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=r,a.tabIndex=-1,a.addEventListener("change",()=>{let l=a.value;s.style.background=l,e.cmd==="foreColor"?this._lastForeColor=l:this._lastHiliteColor=l,this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand(e.cmd,!1,l))}),o.addEventListener("mousedown",l=>{l.preventDefault(),this._saveBookmark(),a.click()}),t.appendChild(o),t.appendChild(a),t}_buildPopupBtn(e){let t=document.createElement("button");t.type="button",t.className="jotter-btn",t.title=e.title,t.setAttribute("aria-label",e.title);let o=document.createElement("span");return o.className="material-icons",o.textContent=e.icon,t.appendChild(o),t.addEventListener("mousedown",i=>{i.preventDefault();let s=this._resolverFor(e.id);if(s){this._hidePopup(),this._runResolver(e.id,s);return}if(this._saveBookmark(),this._popupVisible()&&this._popup.dataset.popupId===e.id){this._hidePopup();return}this._showPopup(t,this._buildPopupContent(e.id),e.id)}),t}_buildPopupContainer(){let e=document.createElement("div");return e.className="jotter-popup",e.setAttribute("role","dialog"),e}_showPopup(e,t,o){this._popup.innerHTML="",this._popup.appendChild(t),this._popup.dataset.popupId=o,this._popup.classList.add("jotter-popup--visible");let i=e.getBoundingClientRect();this._popup.style.top=i.bottom+6+"px",this._popup.style.left=i.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,i.left-(s.right-window.innerWidth+8))+"px")})}_popupVisible(){return this._popup.classList.contains("jotter-popup--visible")}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId="",this._releaseSavedBookmark()}_buildPopupContent(e){switch(e){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 t=document.createElement("div");return t.className="jotter-popup-inner",t.textContent="Unknown: "+e,t}}}_popupTable(){let e=document.createElement("div");e.className="jotter-popup-inner";let t=this._popupTitle("Insert Table");e.appendChild(t);let o=10,i=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${o}, 1fr)`;let r=document.createElement("div");r.className="jotter-popup-hint",r.textContent="Hover to select size";let a=[];for(let l=0;l<i;l++)for(let d=0;d<o;d++){let c=document.createElement("span");c.className="jotter-table-cell",c.dataset.r=l,c.dataset.c=d,c.addEventListener("mouseenter",()=>{r.textContent=`${l+1} \xD7 ${d+1} table`,a.forEach(u=>{u.classList.toggle("jotter-table-cell--active",+u.dataset.r<=l&&+u.dataset.c<=d)})}),c.addEventListener("click",()=>this._insertTable(l+1,d+1)),a.push(c),s.appendChild(c)}return e.appendChild(s),e.appendChild(r),e}_commitPopup(e){this._restoreSavedBookmark(),this._hidePopup(),this._applyEdit(e)}_insertTable(e,t){let o="<table><tbody>";for(let i=0;i<e;i++){o+="<tr>";for(let s=0;s<t;s++)o+=i===0?"<th><br></th>":"<td><br></td>";o+="</tr>"}o+="</tbody></table><p><br></p>",this._commitPopup(()=>document.execCommand("insertHTML",!1,o))}_popupLink(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert Link"));let t=this._anchorInSelection(),o=this._selectedText(),i=this._makeField(e,"URL","url","https://"),s=this._makeField(e,"Link text (leave blank to keep selection)","text",""),r=this._makeField(e,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let l=document.createElement("select");return l.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([d,c])=>{let u=document.createElement("option");u.value=c,u.textContent=d,l.appendChild(u)}),e.appendChild(a),e.appendChild(l),t?(i.value=t.getAttribute("href")||"",s.value=t.textContent||"",r.value=t.getAttribute("title")||"",l.value=t.getAttribute("target")||""):o&&(s.value=o),e.appendChild(this._makeSubmitBtn(t?"Update Link":"Insert Link",()=>{let d=i.value.trim();if(!d)return;let c=s.value.trim()||o||d,u=r.value.trim(),m=l.value;this._commitPopup(()=>{t?(t.href=d,m?t.target=m:t.removeAttribute("target"),u?t.title=u:t.removeAttribute("title"),t.textContent=c):document.execCommand("insertHTML",!1,this._linkHTML({href:d,text:c,title:u,target:m}))})})),e}_popupImage(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert Image"));let t=this._makeField(e,"Image URL","text","https://example.com/image.jpg"),o=this._makeField(e,"Alt text","text","Descriptive text"),i=this._makeField(e,"Width (e.g. 400px or 50%)","text","");return e.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=t.value.trim();if(!s)return;let r=this._imageHTML({src:s,alt:o.value.trim(),width:i.value.trim()});this._commitPopup(()=>document.execCommand("insertHTML",!1,r))})),e}_popupVideo(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert YouTube Video"));let t=this._makeField(e,"YouTube URL","text","https://www.youtube.com/watch?v=...");return e.appendChild(this._makeSubmitBtn("Embed Video",()=>{let o=this._ytId(t.value.trim());if(!o){t.classList.add("jotter-input--error");return}t.classList.remove("jotter-input--error"),this._commitPopup(()=>document.execCommand("insertHTML",!1,this._videoHTML(o)))})),e}_ytId(e){for(let t of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let o=e.match(t);if(o)return o[1]}return null}_popupEmbed(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert Embed"));let t=document.createElement("label");t.className="jotter-popup-label",t.textContent="Paste HTML / embed code";let o=document.createElement("textarea");return o.className="jotter-popup-textarea",o.placeholder='<iframe src="..." ...></iframe>',o.rows=4,e.appendChild(t),e.appendChild(o),e.appendChild(this._makeSubmitBtn("Insert",()=>{let i=o.value.trim();i&&this._commitPopup(()=>document.execCommand("insertHTML",!1,this._embedHTML(i)))})),e}_popupSymbol(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Insert Symbol")),e.appendChild(this._charGrid(E)),e}_popupSpecialChar(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Special Characters")),e.appendChild(this._charGrid(S)),e}_charGrid(e){let t=document.createElement("div");return t.className="jotter-char-grid",e.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",s=>{s.preventDefault(),this._commitPopup(()=>document.execCommand("insertText",!1,o))}),t.appendChild(i)}),t}_popupLorem(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Insert Lorem Ipsum")),y.forEach(t=>{let o=document.createElement("button");o.type="button",o.className="jotter-lorem-btn",o.textContent=t.label,o.addEventListener("mousedown",i=>{i.preventDefault(),this._commitPopup(()=>document.execCommand(t.isHTML?"insertHTML":"insertText",!1,t.text))}),e.appendChild(o)}),e}_popupTitle(e){let t=document.createElement("div");return t.className="jotter-popup-title",t.textContent=e,t}_makeField(e,t,o,i){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=t;let r=document.createElement("input");return r.type=o,r.className="jotter-popup-input",r.placeholder=i,e.appendChild(s),e.appendChild(r),r}_makeSubmitBtn(e,t){let o=document.createElement("button");return o.type="button",o.className="jotter-popup-submit",o.textContent=e,o.addEventListener("click",t),o}_esc(e){return String(e).replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_imageHTML({src:e,alt:t,width:o}){let i=o?`max-width:${o}`:"max-width:100%";return`<img src="${this._esc(e)}" alt="${this._esc(t||"")}" style="${this._esc(i)}">`}_linkHTML({href:e,text:t,title:o,target:i}){let s=`href="${this._esc(e)}"`;return i&&(s+=` target="${this._esc(i)}"`),o&&(s+=` title="${this._esc(o)}"`),`<a ${s}>${this._esc(t||e)}</a>`}_videoHTML(e){return/^[A-Za-z0-9_-]{11}$/.test(e)?`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${e}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`:null}_embedHTML(e){return e+"<p><br></p>"}_resolverFor(e){let t=C[e],o=t?this._options[t]:null;return typeof o=="function"?o:null}async _runResolver(e,t){let o=this._resolverContext(e);this.beginExternalUI();let i=null;try{i=await t(o)}catch{i=null}if(this._destroyed)return;let s=i==null||i===!1?null:this._resolvedHTML(e,i,o);this.endExternalUI(),s&&(this._applyEdit(()=>document.execCommand("insertHTML",!1,s)),this._updateToolbarState())}_resolverContext(e){switch(e){case"image":return{src:"",alt:"",width:"",selection:this._selectedText()};case"video":return{url:"",selection:this._selectedText()};case"embed":return{html:"",selection:this._selectedText()};case"link":return this._linkContext();default:return{selection:this._selectedText()}}}_linkContext(){let e=this._anchorInSelection();if(!e){let o=this._selectedText();return{href:"",text:o,title:"",target:"",selection:o,isEdit:!1}}let t=window.getSelection();if(t){let o=document.createRange();o.selectNode(e),t.removeAllRanges(),t.addRange(o)}return{href:e.getAttribute("href")||"",text:e.textContent||"",title:e.getAttribute("title")||"",target:e.getAttribute("target")||"",selection:e.textContent||"",isEdit:!0}}_resolvedHTML(e,t,o){switch(e){case"image":{let i=typeof t=="string"?{src:t}:t,s=String(i.src||i.url||"").trim();return s?this._imageHTML({...i,src:s}):null}case"link":{let i=typeof t=="string"?{href:t}:t,s=String(i.href||i.url||"").trim();if(!s)return null;let r=String(i.text!=null?i.text:o.text||"").trim();return this._linkHTML({...i,href:s,text:r})}case"video":{let i=typeof t=="string"?{url:t}:t,s=i.id?String(i.id).trim():this._ytId(String(i.url||"").trim());return s?this._videoHTML(s):null}case"embed":{let i=String(typeof t=="string"?t:t.html||"").trim();return i?this._embedHTML(i):null}default:return null}}_selectedText(){let e=window.getSelection();return!e||!e.rangeCount?"":this._editor.contains(e.getRangeAt(0).commonAncestorContainer)?e.toString():""}_anchorInSelection(){let e=window.getSelection();if(!e||!e.rangeCount)return null;let t=e.anchorNode;if(!t||!this._editor.contains(t))return null;for(;t&&t!==this._editor;){if(t.nodeName==="A")return t;t=t.parentNode}return null}_buildStatusBar(){let e=document.createElement("div");e.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 t=document.createElement("span");return t.className="jotter-status-mode",t.textContent="HTML",e.appendChild(this._wordCountEl),e.appendChild(this._charCountEl),e.appendChild(t),e}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(e=>{if(e.attributes.length>0)return;let t=document.createElement("p");t.innerHTML=e.innerHTML,e.replaceWith(t)}),Array.from(this._editor.childNodes).forEach(e=>{if(e.nodeType===Node.TEXT_NODE&&e.textContent.trim()!==""){let t=window.getSelection(),o=null;if(t&&t.rangeCount){let s=t.getRangeAt(0);s.startContainer===e&&(o=s.startOffset)}let i=document.createElement("p");if(e.replaceWith(i),i.appendChild(e),o!==null){let s=document.createRange();s.setStart(e,o),s.collapse(!0),t.removeAllRanges(),t.addRange(s)}}}),this._updateStatus(),this._emitChange()}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focusin",e=>{this._root.classList.add("jotter--focused"),!(this._externalDepth>0)&&(this._isInternalTarget(e.relatedTarget)||(this._emit("focus"),this._options.onFocus&&this._options.onFocus()))}),this._editor.addEventListener("focusout",e=>{this._externalDepth>0||this._isInternalTarget(e.relatedTarget)||setTimeout(()=>{this._destroyed||this._externalDepth>0||this._isInternalTarget(document.activeElement)||this._popupVisible()||(this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur())},0)}),this._editor.addEventListener("keydown",e=>{e.key==="Tab"&&(e.preventDefault(),document.execCommand("insertHTML",!1,"&nbsp;&nbsp;&nbsp;&nbsp;"))}),this._onDocMouseDown=e=>{this._popupVisible()&&!this._popup.contains(e.target)&&!this._toolbarEl.contains(e.target)&&this._hidePopup()},document.addEventListener("mousedown",this._onDocMouseDown),this._onDocKeyDown=e=>{e.key==="Escape"&&this._popupVisible()&&(this._restoreSavedBookmark(),this._hidePopup())},document.addEventListener("keydown",this._onDocKeyDown)}_isInternalTarget(e){return!!e&&(this._root.contains(e)||this._popup.contains(e))}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._dropBookmarks(),this._source.value=this._prettyHTML(this._richHTML()),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._emitChange()),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let e=this._toolbarEl.querySelector('[data-custom="toggleSource"]');e&&e.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(e){let t=0,o=" ",i=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 e.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=_,h={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(h).forEach(Object.freeze);Object.freeze(h);var y=h.full,S={image:"onRequestImage",link:"onRequestLink",video:"onRequestVideo",embed:"onRequestEmbed"},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"}],x=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],T=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],L=["\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"],w=["\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"],k=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],M=[{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>"}],m=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,onRequestImage:null,onRequestLink:null,onRequestVideo:null,onRequestEmbed:null},e),this._listeners={},this._bookmarks=new Map,this._bmSeq=0,this._savedBookmark=null,this._externalBookmark=null,this._externalDepth=0,this._changeCount=0,this._destroyed=!1,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||y).forEach(e=>{let o=this._buildAction(e);o&&t.appendChild(o)}),t}_buildAction(t){if(typeof t.onClick=="function")return this._buildBtn(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}_bindActivation(t,e){t.addEventListener("mousedown",o=>{o.preventDefault(),e(!1)}),t.addEventListener("keydown",o=>{o.key!=="Enter"&&o.key!==" "&&o.key!=="Spacebar"||o.repeat||(o.preventDefault(),e(!0))})}_returnFocus(t,e){e&&this._isInternalTarget(document.activeElement)&&t.focus()}_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 this._bindActivation(e,o=>{this._editor.focus(),this._applyEdit(()=>{if(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._returnFocus(e,o)}),e}_bindSelectCaret(t){let e={viaKeyboard:!1};return t.addEventListener("mousedown",()=>{e.viaKeyboard=!1,this._saveBookmark()}),t.addEventListener("keydown",o=>{o.key==="Tab"||o.key==="Escape"||(e.viaKeyboard=!0,this._savedBookmark==null&&this._saveBookmark())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",C.forEach(({label:o,tag:i})=>{let n=document.createElement("option");n.value=i,n.textContent=o,t.appendChild(n)});let e=this._bindSelectCaret(t);return t.addEventListener("change",()=>{this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("formatBlock",!1,t.value)),this._returnFocus(t,e.viaKeyboard)}),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");e.value="",e.textContent="Font",t.appendChild(e),x.forEach(i=>{let n=document.createElement("option");n.value=i,n.textContent=i,n.style.fontFamily=i,t.appendChild(n)});let o=this._bindSelectCaret(t);return t.addEventListener("change",()=>{t.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("fontName",!1,t.value)),this._returnFocus(t,o.viaKeyboard))}),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");e.value="",e.textContent="Size",t.appendChild(e),T.forEach(i=>{let n=document.createElement("option");n.value=i,n.textContent=`${i}px`,t.appendChild(n)});let o=this._bindSelectCaret(t);return t.addEventListener("change",()=>{t.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>this._applyFontSize(t.value)),this._returnFocus(t,o.viaKeyboard))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",k.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 r=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;n.style.background=r,o.appendChild(n);let a=document.createElement("input");a.type="color",a.className="jotter-color-input",a.value=r,a.tabIndex=-1;let u=!1;return a.addEventListener("change",()=>{let p=a.value;n.style.background=p,t.cmd==="foreColor"?this._lastForeColor=p:this._lastHiliteColor=p,this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand(t.cmd,!1,p)),this._returnFocus(o,u)}),this._bindActivation(o,p=>{u=p,this._saveBookmark(),a.click()}),e.appendChild(o),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 o=document.createElement("span");return o.className="material-icons",o.textContent=t.icon,e.appendChild(o),this._bindActivation(e,i=>{let n=this._resolverFor(t.id);if(n){this._hidePopup(),this._runResolver(t.id,n);return}if(this._saveBookmark(),this._popupVisible()&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id),i&&this._focusPopup()}),e}_focusPopup(){let t=this._popup.querySelector('input, textarea, select, button, [tabindex]:not([tabindex="-1"])');t&&t.focus()}_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")})}_popupVisible(){return this._popup.classList.contains("jotter-popup--visible")}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId="",this._releaseSavedBookmark()}_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)`,n.setAttribute("role","grid"),n.setAttribute("aria-label","Table size");let r=document.createElement("div");r.className="jotter-popup-hint",r.textContent="Hover or arrow to select size";let a=[],u=(c,l)=>{r.textContent=`${c+1} \xD7 ${l+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=c&&+d.dataset.c<=l)})};for(let c=0;c<i;c++)for(let l=0;l<o;l++){let d=document.createElement("span");d.className="jotter-table-cell",d.dataset.r=c,d.dataset.c=l,d.setAttribute("role","gridcell"),d.setAttribute("aria-label",`${c+1} by ${l+1} table`),d.tabIndex=c===0&&l===0?0:-1,d.addEventListener("mouseenter",()=>u(c,l)),d.addEventListener("focus",()=>u(c,l)),d.addEventListener("click",()=>this._insertTable(c+1,l+1)),a.push(d),n.appendChild(d)}let p={ArrowRight:[0,1],ArrowLeft:[0,-1],ArrowDown:[1,0],ArrowUp:[-1,0]};return n.addEventListener("keydown",c=>{let l=a.indexOf(document.activeElement)===-1?null:document.activeElement;if(!l)return;let d=+l.dataset.r,g=+l.dataset.c;if(c.key==="Enter"||c.key===" "||c.key==="Spacebar"){c.preventDefault(),this._insertTable(d+1,g+1);return}let f=p[c.key];if(!f)return;c.preventDefault();let v=Math.min(i-1,Math.max(0,d+f[0])),E=Math.min(o-1,Math.max(0,g+f[1])),b=a[v*o+E];b!==l&&(l.tabIndex=-1,b.tabIndex=0,b.focus())}),t.appendChild(n),t.appendChild(r),t}_commitPopup(t){this._restoreSavedBookmark(),this._hidePopup(),this._applyEdit(t)}_insertTable(t,e){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>",this._commitPopup(()=>document.execCommand("insertHTML",!1,o))}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=this._anchorInSelection(),o=this._selectedText(),i=this._makeField(t,"URL","url","https://"),n=this._makeField(t,"Link text (leave blank to keep selection)","text",""),r=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let u=document.createElement("select");return u.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([p,c])=>{let l=document.createElement("option");l.value=c,l.textContent=p,u.appendChild(l)}),t.appendChild(a),t.appendChild(u),e?(i.value=e.getAttribute("href")||"",n.value=e.textContent||"",r.value=e.getAttribute("title")||"",u.value=e.getAttribute("target")||""):o&&(n.value=o),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let p=i.value.trim();if(!p)return;let c=n.value.trim()||o||p,l=r.value.trim(),d=u.value;this._commitPopup(()=>{e?(e.href=p,d?e.target=d:e.removeAttribute("target"),l?e.title=l:e.removeAttribute("title"),e.textContent=c):document.execCommand("insertHTML",!1,this._linkHTML({href:p,text:c,title:l,target:d}))})})),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 r=this._imageHTML({src:n,alt:o.value.trim(),width:i.value.trim()});this._commitPopup(()=>document.execCommand("insertHTML",!1,r))})),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"),this._commitPopup(()=>document.execCommand("insertHTML",!1,this._videoHTML(o)))})),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._commitPopup(()=>document.execCommand("insertHTML",!1,this._embedHTML(i)))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(L)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(w)),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")}`,this._bindActivation(i,()=>{this._commitPopup(()=>document.execCommand("insertText",!1,o))}),e.appendChild(i)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),M.forEach(e=>{let o=document.createElement("button");o.type="button",o.className="jotter-lorem-btn",o.textContent=e.label,this._bindActivation(o,()=>{this._commitPopup(()=>document.execCommand(e.isHTML?"insertHTML":"insertText",!1,e.text))}),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 r=document.createElement("input");return r.type=o,r.className="jotter-popup-input",r.placeholder=i,t.appendChild(n),t.appendChild(r),r}_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 String(t).replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_imageHTML({src:t,alt:e,width:o}){let i=o?`max-width:${o}`:"max-width:100%";return`<img src="${this._esc(t)}" alt="${this._esc(e||"")}" style="${this._esc(i)}">`}_linkHTML({href:t,text:e,title:o,target:i}){let n=`href="${this._esc(t)}"`;return i&&(n+=` target="${this._esc(i)}"`),o&&(n+=` title="${this._esc(o)}"`),`<a ${n}>${this._esc(e||t)}</a>`}_videoHTML(t){return/^[A-Za-z0-9_-]{11}$/.test(t)?`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${t}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`:null}_embedHTML(t){return t+"<p><br></p>"}_resolverFor(t){let e=S[t],o=e?this._options[e]:null;return typeof o=="function"?o:null}async _runResolver(t,e){let o=this._resolverContext(t);this.beginExternalUI();let i=null;try{i=await e(o)}catch{i=null}if(this._destroyed)return;let n=i==null||i===!1?null:this._resolvedHTML(t,i,o);this.endExternalUI(),n&&(this._applyEdit(()=>document.execCommand("insertHTML",!1,n)),this._updateToolbarState())}_resolverContext(t){switch(t){case"image":return{src:"",alt:"",width:"",selection:this._selectedText()};case"video":return{url:"",selection:this._selectedText()};case"embed":return{html:"",selection:this._selectedText()};case"link":return this._linkContext();default:return{selection:this._selectedText()}}}_linkContext(){let t=this._anchorInSelection();if(!t){let o=this._selectedText();return{href:"",text:o,title:"",target:"",selection:o,isEdit:!1}}let e=window.getSelection();if(e){let o=document.createRange();o.selectNode(t),e.removeAllRanges(),e.addRange(o)}return{href:t.getAttribute("href")||"",text:t.textContent||"",title:t.getAttribute("title")||"",target:t.getAttribute("target")||"",selection:t.textContent||"",isEdit:!0}}_resolvedHTML(t,e,o){switch(t){case"image":{let i=typeof e=="string"?{src:e}:e,n=String(i.src||i.url||"").trim();return n?this._imageHTML({...i,src:n}):null}case"link":{let i=typeof e=="string"?{href:e}:e,n=String(i.href||i.url||"").trim();if(!n)return null;let r=String(i.text!=null?i.text:o.text||"").trim();return this._linkHTML({...i,href:n,text:r})}case"video":{let i=typeof e=="string"?{url:e}:e,n=i.id?String(i.id).trim():this._ytId(String(i.url||"").trim());return n?this._videoHTML(n):null}case"embed":{let i=String(typeof e=="string"?e:e.html||"").trim();return i?this._embedHTML(i):null}default:return null}}_selectedText(){let t=window.getSelection();return!t||!t.rangeCount?"":this._editor.contains(t.getRangeAt(0).commonAncestorContainer)?t.toString():""}_anchorInSelection(){let t=window.getSelection();if(!t||!t.rangeCount)return null;let e=t.anchorNode;if(!e||!this._editor.contains(e))return null;for(;e&&e!==this._editor;){if(e.nodeName==="A")return e;e=e.parentNode}return null}_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._emitChange()}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focusin",t=>{this._root.classList.add("jotter--focused"),!(this._externalDepth>0)&&(this._isInternalTarget(t.relatedTarget)||(this._emit("focus"),this._options.onFocus&&this._options.onFocus()))}),this._editor.addEventListener("focusout",t=>{this._externalDepth>0||this._isInternalTarget(t.relatedTarget)||setTimeout(()=>{this._destroyed||this._externalDepth>0||this._isInternalTarget(document.activeElement)||this._popupVisible()||(this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur())},0)}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1,"&nbsp;&nbsp;&nbsp;&nbsp;"))}),this._onDocMouseDown=t=>{this._popupVisible()&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()},document.addEventListener("mousedown",this._onDocMouseDown),this._onDocKeyDown=t=>{t.key==="Escape"&&this._popupVisible()&&(this._restoreSavedBookmark(),this._hidePopup())},document.addEventListener("keydown",this._onDocKeyDown)}_isInternalTarget(t){return!!t&&(this._root.contains(t)||this._popup.contains(t))}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._dropBookmarks(),this._source.value=this._prettyHTML(this._richHTML()),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._emitChange()),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(r=>r.trim()).filter(r=>r.length>0).map(r=>{let a=r.match(/^<\/(\w+)/),l=r.match(/^<(\w+)/),d=r.endsWith("/>"),c=l?l[1].toLowerCase():null,u=a?a[1].toLowerCase():null;u&&!s.has(u)&&(t=Math.max(0,t-1));let m=o.repeat(t)+r;return c&&!d&&!i.has(c)&&!u&&!s.has(c)&&t++,m}).join(`
5
- `)}_sanitize(e){let t=new DOMParser().parseFromString(e,"text/html");return t.querySelectorAll("script").forEach(o=>o.remove()),t.querySelectorAll("[data-jotter-bookmark]").forEach(o=>o.remove()),t.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)})}),t.body.innerHTML}_toggleInlineCode(){let e=window.getSelection();if(!e||e.rangeCount===0)return;let t=e.getRangeAt(0),o=t.commonAncestorContainer;o.nodeType===3&&(o=o.parentNode);let i=o.closest?o.closest("code"):null;if(i){let s=i.parentNode;for(;i.firstChild;)s.insertBefore(i.firstChild,i);s.removeChild(i)}else if(t.collapsed){let s=document.createElement("code");s.innerHTML="&#8203;",t.insertNode(s);let r=document.createRange();r.setStart(s,0),r.setEnd(s,s.childNodes.length),e.removeAllRanges(),e.addRange(r)}else{let s=document.createElement("code");try{t.surroundContents(s)}catch{let a=t.extractContents();s.appendChild(a),t.insertNode(s)}}}_applyFontSize(e){let t=window.getSelection();if(!t||t.rangeCount===0)return;let o=t.getRangeAt(0);if(o.collapsed)return;let i=document.createElement("span");i.style.fontSize=e+"px";try{o.surroundContents(i)}catch{let r=o.extractContents();i.appendChild(r),o.insertNode(i)}}async _pasteFromClipboard(){try{if(navigator.clipboard&&navigator.clipboard.readText){let e=await navigator.clipboard.readText();document.execCommand("insertText",!1,e)}else document.execCommand("paste")}catch{}}_updateToolbarState(){this._toolbarEl.querySelectorAll(".jotter-btn[data-cmd]").forEach(t=>{try{t.classList.toggle("jotter-btn--active",document.queryCommandState(t.dataset.cmd))}catch{}});let e=this._toolbarEl.querySelector('[data-id="blockformat"]');if(e){let t=document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g,""),o=f.find(i=>i.tag===t);o&&(e.value=o.tag)}}_updateStatus(){let e=this._editor.innerText||"",t=e.trim()===""?0:e.trim().split(/\s+/).length,o=e.replace(/\n/g,"").length;this._wordCountEl.textContent=`${t} word${t!==1?"s":""}`,this._charCountEl.textContent=`${o} char${o!==1?"s":""}`}_richHTML(){if(this._bookmarks.size===0)return this._editor.innerHTML;let e=this._editor.cloneNode(!0);return e.querySelectorAll("[data-jotter-bookmark]").forEach(t=>t.remove()),e.innerHTML}_makeMarker(e){let t=document.createElement("span");return t.className="jotter-bookmark",t.dataset.jotterBookmark=e,t}_takeRange(e){let t=e!=null?this._bookmarks.get(e):null;if(!t)return null;if(this._bookmarks.delete(e),t.atStart){let a=document.createRange();return a.setStart(this._editor,0),a.collapse(!0),a}let{start:o,end:i}=t,s=this._editor.contains(o)&&(!i||this._editor.contains(i)),r=null;return s&&(r=document.createRange(),r.setStartAfter(o),i?r.setEndBefore(i):r.collapse(!0)),o.parentNode&&o.remove(),i&&i.parentNode&&i.remove(),r}_saveBookmark(){this._releaseSavedBookmark(),this._savedBookmark=this.saveSelection()}_restoreSavedBookmark(){this.restoreSelection(this._savedBookmark),this._savedBookmark=null,this._editor.focus()}_releaseSavedBookmark(){this.releaseSelection(this._savedBookmark),this._savedBookmark=null}_dropBookmarks(){Array.from(this._bookmarks.keys()).forEach(e=>this.releaseSelection(e)),this._savedBookmark=null}_emit(e,t){(this._listeners[e]||[]).forEach(o=>o(t))}_emitChange(){let e=this.getHTML();this._changeCount++,this._emit("change",e),this._options.onChange&&this._options.onChange(e)}_applyEdit(e){let t=this._changeCount,o=this._richHTML();e(),this._updateStatus(),this._changeCount===t&&this._richHTML()!==o&&this._emitChange()}getHTML(){return this._sourceMode?this._source.value:this._richHTML()}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._dropBookmarks(),this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(e,t={}){return t.at!=null&&this.restoreSelection(t.at),this._editor.focus(),this._applyEdit(()=>document.execCommand("insertHTML",!1,this._sanitize(e))),this}insertText(e,t={}){return t.at!=null&&this.restoreSelection(t.at),this._editor.focus(),this._applyEdit(()=>document.execCommand("insertText",!1,e)),this}saveSelection(){let e=window.getSelection();if(!e||e.rangeCount===0)return null;let t=e.getRangeAt(0);if(!this._editor.contains(t.startContainer)||!this._editor.contains(t.endContainer))return null;let o="jbm"+ ++this._bmSeq;if(this._editor.childNodes.length===0)return this._bookmarks.set(o,{atStart:!0}),o;let i=this._makeMarker(o),s=t.collapsed?null:this._makeMarker(o);if(s){let l=t.cloneRange();l.collapse(!1),l.insertNode(s)}let r=t.cloneRange();r.collapse(!0),r.insertNode(i),this._bookmarks.set(o,{start:i,end:s});let a=document.createRange();return a.setStartAfter(i),s?a.setEndBefore(s):a.collapse(!0),e.removeAllRanges(),e.addRange(a),o}restoreSelection(e){let t=this._takeRange(e);if(!t)return this;this._sourceMode||this._editor.focus();let o=window.getSelection();return o.removeAllRanges(),o.addRange(t),this}releaseSelection(e){return this._takeRange(e),this}beginExternalUI(){return this._externalDepth===0&&(this._externalBookmark=this.saveSelection()),this._externalDepth++,this}endExternalUI(e={}){if(this._externalDepth===0)return this;if(this._externalDepth===1){let t=this._externalBookmark;this._externalBookmark=null,e.restore===!1?this.releaseSelection(t):this.restoreSelection(t)}return this._externalDepth--,this}setHTML(e){return this._dropBookmarks(),this._editor.innerHTML=this._sanitize(e),this._updateStatus(),this}setTheme(e){let o=b.find(i=>i.id===e)?e:"default";return this._editor.dataset.theme=o,this._options.theme=o,this._themeSelect&&(this._themeSelect.value=o),this}setEnabled(e){return this._editor.contentEditable=String(e),this._root.classList.toggle("jotter--disabled",!e),this}on(e,t){return this._listeners[e]||(this._listeners[e]=[]),this._listeners[e].push(t),this}off(e,t){return this._listeners[e]&&(this._listeners[e]=this._listeners[e].filter(o=>o!==t)),this}destroy(){let e=this.getHTML();return this._destroyed=!0,this._hidePopup(),this._dropBookmarks(),this._externalBookmark=null,this._externalDepth=0,document.removeEventListener("mousedown",this._onDocMouseDown),document.removeEventListener("keydown",this._onDocKeyDown),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=e,this._listeners={},e}};p.toolbar=h.full;p.actions=_;p.presets=h;var L=p;export{p as JotterJS,L as default};
4
+ `).map(r=>r.trim()).filter(r=>r.length>0).map(r=>{let a=r.match(/^<\/(\w+)/),u=r.match(/^<(\w+)/),p=r.endsWith("/>"),c=u?u[1].toLowerCase():null,l=a?a[1].toLowerCase():null;l&&!n.has(l)&&(e=Math.max(0,e-1));let d=o.repeat(e)+r;return c&&!p&&!i.has(c)&&!l&&!n.has(c)&&e++,d}).join(`
5
+ `)}_sanitize(t){let e=new DOMParser().parseFromString(t,"text/html");return e.querySelectorAll("script").forEach(o=>o.remove()),e.querySelectorAll("[data-jotter-bookmark]").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 r=document.createRange();r.setStart(n,0),r.setEnd(n,n.childNodes.length),t.removeAllRanges(),t.addRange(r)}else{let n=document.createElement("code");try{e.surroundContents(n)}catch{let a=e.extractContents();n.appendChild(a),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 r=o.extractContents();i.appendChild(r),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":""}`}_richHTML(){if(this._bookmarks.size===0)return this._editor.innerHTML;let t=this._editor.cloneNode(!0);return t.querySelectorAll("[data-jotter-bookmark]").forEach(e=>e.remove()),t.innerHTML}_makeMarker(t){let e=document.createElement("span");return e.className="jotter-bookmark",e.dataset.jotterBookmark=t,e}_takeRange(t){let e=t!=null?this._bookmarks.get(t):null;if(!e)return null;if(this._bookmarks.delete(t),e.atStart){let a=document.createRange();return a.setStart(this._editor,0),a.collapse(!0),a}let{start:o,end:i}=e,n=this._editor.contains(o)&&(!i||this._editor.contains(i)),r=null;return n&&(r=document.createRange(),r.setStartAfter(o),i?r.setEndBefore(i):r.collapse(!0)),o.parentNode&&o.remove(),i&&i.parentNode&&i.remove(),r}_saveBookmark(){this._releaseSavedBookmark(),this._savedBookmark=this.saveSelection()}_restoreSavedBookmark(){this.restoreSelection(this._savedBookmark),this._savedBookmark=null,this._editor.focus()}_releaseSavedBookmark(){this.releaseSelection(this._savedBookmark),this._savedBookmark=null}_dropBookmarks(){Array.from(this._bookmarks.keys()).forEach(t=>this.releaseSelection(t)),this._savedBookmark=null}_emit(t,e){(this._listeners[t]||[]).forEach(o=>o(e))}_emitChange(){let t=this.getHTML();this._changeCount++,this._emit("change",t),this._options.onChange&&this._options.onChange(t)}_applyEdit(t){let e=this._changeCount,o=this._richHTML();t(),this._updateStatus(),this._changeCount===e&&this._richHTML()!==o&&this._emitChange()}getHTML(){return this._sourceMode?this._source.value:this._richHTML()}getText(){return this._editor.innerText}isSourceMode(){return this._sourceMode}toggleSource(){return this._toggleSourceMode(),this}clear(){return this._dropBookmarks(),this._editor.innerHTML="",this._updateStatus(),this}focus(){return this._editor.focus(),this}insertHTML(t,e={}){return e.at!=null&&this.restoreSelection(e.at),this._editor.focus(),this._applyEdit(()=>document.execCommand("insertHTML",!1,this._sanitize(t))),this}insertText(t,e={}){return e.at!=null&&this.restoreSelection(e.at),this._editor.focus(),this._applyEdit(()=>document.execCommand("insertText",!1,t)),this}saveSelection(){let t=window.getSelection();if(!t||t.rangeCount===0)return null;let e=t.getRangeAt(0);if(!this._editor.contains(e.startContainer)||!this._editor.contains(e.endContainer))return null;let o="jbm"+ ++this._bmSeq;if(this._editor.childNodes.length===0)return this._bookmarks.set(o,{atStart:!0}),o;let i=this._makeMarker(o),n=e.collapsed?null:this._makeMarker(o);if(n){let u=e.cloneRange();u.collapse(!1),u.insertNode(n)}let r=e.cloneRange();r.collapse(!0),r.insertNode(i),this._bookmarks.set(o,{start:i,end:n});let a=document.createRange();return a.setStartAfter(i),n?a.setEndBefore(n):a.collapse(!0),t.removeAllRanges(),t.addRange(a),o}restoreSelection(t){let e=this._takeRange(t);if(!e)return this;this._sourceMode||this._editor.focus();let o=window.getSelection();return o.removeAllRanges(),o.addRange(e),this}releaseSelection(t){return this._takeRange(t),this}beginExternalUI(){return this._externalDepth===0&&(this._externalBookmark=this.saveSelection()),this._externalDepth++,this}endExternalUI(t={}){if(this._externalDepth===0)return this;if(this._externalDepth===1){let e=this._externalBookmark;this._externalBookmark=null,t.restore===!1?this.releaseSelection(e):this.restoreSelection(e)}return this._externalDepth--,this}setHTML(t){return this._dropBookmarks(),this._editor.innerHTML=this._sanitize(t),this._updateStatus(),this}setTheme(t){let o=k.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._destroyed=!0,this._hidePopup(),this._dropBookmarks(),this._externalBookmark=null,this._externalDepth=0,document.removeEventListener("mousedown",this._onDocMouseDown),document.removeEventListener("keydown",this._onDocKeyDown),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=t,this._listeners={},t}};m.toolbar=h.full;m.actions=_;m.presets=h;var N=m;export{m as JotterJS,N as default};
package/package.json CHANGED
@@ -1,45 +1,45 @@
1
- {
2
- "name": "jotterjs",
3
- "version": "0.3.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
- }
1
+ {
2
+ "name": "jotterjs",
3
+ "version": "0.5.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
+ }