jotterjs 0.5.0 → 0.7.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
@@ -12,6 +12,7 @@ A lightweight, vanilla JS rich-text editor component built on `contenteditable`.
12
12
  - Pre-built toolbar presets (`minimal`, `writing`, `full`) and fully custom toolbar support
13
13
  - Custom toolbar buttons with `onClick` callbacks
14
14
  - Replaceable insert dialogs — swap the built-in image/link/video/embed popups for your own UI
15
+ - Click to select an image, video, or embed block: resize by dragging its handles, drag it to reposition, or copy/cut/paste it like any other content
15
16
  - Selection bookmarks that survive async host UI and DOM mutation
16
17
  - Event system (`change`, `focus`, `blur`)
17
18
  - Keyboard-operable toolbar — every control activates with Enter and Space
@@ -261,6 +262,33 @@ Two known gaps: `Tab` inside the editing area inserts indentation rather than
261
262
  moving focus, so reach the toolbar by tabbing in from before the editor; and the
262
263
  symbol and special-character grids are one tab stop per character.
263
264
 
265
+ ## Media blocks
266
+
267
+ Images, YouTube embeds, and generic embeds behave as atomic, direct-manipulation
268
+ blocks rather than plain inline content:
269
+
270
+ - **Select** — click one to show a selection frame with resize handles. Video
271
+ and embed content is never interactive while editing (clicking it always
272
+ selects the block instead of playing/activating it), so nothing behind it
273
+ can hijack the click.
274
+ - **Resize** — drag any handle to change its width; height always follows on
275
+ its own (natural image aspect ratio, the video's 16:9 box, or an embed's
276
+ normal reflow), so there's nothing to distort.
277
+ - **Move** — once selected, drag the block itself to reposition it elsewhere
278
+ in the document. A plain click that doesn't move the pointer just keeps it
279
+ selected, exactly as before.
280
+ - **Delete** — Backspace or Delete removes the selected block.
281
+ - **Copy / Cut / Paste** — work like they do anywhere else, including across
282
+ browser tabs and other JotterJS instances: copying or cutting a selected
283
+ block puts clean HTML on the system clipboard (no internal bookkeeping like
284
+ the click-shield leaks out), and pasting sanitizes and re-inserts it, so a
285
+ pasted video/embed block is fully functional again.
286
+
287
+ All of the above go through `document.execCommand`, so they participate in the
288
+ same native undo/redo stack as typing and toolbar commands (Ctrl+Z / Ctrl+Y,
289
+ or the Undo/Redo buttons). A move is internally a delete-then-insert, so
290
+ unwinding one takes two undo steps rather than one.
291
+
264
292
  ## Development
265
293
 
266
294
  ```bash
@@ -1,5 +1,5 @@
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,`
1
+ var JotterJS=(()=>{var v=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var T=(p,e)=>{for(var t in e)v(p,t,{get:e[t],enumerable:!0})},w=(p,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of x(e))!L.call(p,o)&&o!==t&&v(p,o,{get:()=>e[o],enumerable:!(i=M(e,o))||i.enumerable});return p};var j=p=>w(v({},"__esModule",{value:!0}),p);var D={};T(D,{JotterJS:()=>m,default:()=>z});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 R=_.full,I={image:"onRequestImage",link:"onRequestLink",video:"onRequestVideo",embed:"onRequestEmbed"},E=[{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"}],N=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],A=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],B=["\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"],F=["\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"}],H=[{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._selectedMedia=null,this._resizeState=null,this._moveState=null,this._suppressAutoEmit=!1,this._onResizeMove=this._onResizeMove.bind(this),this._onResizeEnd=this._onResizeEnd.bind(this),this._onMoveMove=this._onMoveMove.bind(this),this._onMoveUp=this._onMoveUp.bind(this),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");try{document.execCommand("enableObjectResizing",!1,!1)}catch{}try{document.execCommand("enableInlineTableEditing",!1,!1)}catch{}this._ensureMediaShields(),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._mediaFrame=this._buildMediaFrame(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._editorWrap.appendChild(this._mediaFrame),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||R).forEach(t=>{let i=this._buildAction(t);i&&e.appendChild(i)}),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}_bindActivation(e,t){e.addEventListener("mousedown",i=>{i.preventDefault(),t(!1)}),e.addEventListener("keydown",i=>{i.key!=="Enter"&&i.key!==" "&&i.key!=="Spacebar"||i.repeat||(i.preventDefault(),t(!0))})}_returnFocus(e,t){t&&this._isInternalTarget(document.activeElement)&&e.focus()}_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 i=document.createElement("span");i.className="material-icons",i.textContent=e.icon,t.appendChild(i)}return this._bindActivation(t,i=>{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 o=window.prompt(e.prompt);o&&document.execCommand(e.cmd,!1,o)}else document.execCommand(e.cmd,!1,null)}),this._updateToolbarState(),this._returnFocus(t,i)}),t}_bindSelectCaret(e){let t={viaKeyboard:!1};return e.addEventListener("mousedown",()=>{t.viaKeyboard=!1,this._saveBookmark()}),e.addEventListener("keydown",i=>{i.key==="Tab"||i.key==="Escape"||(t.viaKeyboard=!0,this._savedBookmark==null&&this._saveBookmark())}),t}_buildBlockFormatSelect(){let e=document.createElement("select");e.className="jotter-select",e.title="Block format",e.dataset.id="blockformat",E.forEach(({label:i,tag:o})=>{let s=document.createElement("option");s.value=o,s.textContent=i,e.appendChild(s)});let t=this._bindSelectCaret(e);return e.addEventListener("change",()=>{this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("formatBlock",!1,e.value)),this._returnFocus(e,t.viaKeyboard)}),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");t.value="",t.textContent="Font",e.appendChild(t),N.forEach(o=>{let s=document.createElement("option");s.value=o,s.textContent=o,s.style.fontFamily=o,e.appendChild(s)});let i=this._bindSelectCaret(e);return e.addEventListener("change",()=>{e.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("fontName",!1,e.value)),this._returnFocus(e,i.viaKeyboard))}),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");t.value="",t.textContent="Size",e.appendChild(t),A.forEach(o=>{let s=document.createElement("option");s.value=o,s.textContent=`${o}px`,e.appendChild(s)});let i=this._bindSelectCaret(e);return e.addEventListener("change",()=>{e.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>this._applyFontSize(e.value)),this._returnFocus(e,i.viaKeyboard))}),e}_buildThemeSelect(){let e=document.createElement("select");return e.className="jotter-select jotter-select--theme",e.title="Editor theme",e.dataset.id="theme",k.forEach(({id:t,label:i})=>{let o=document.createElement("option");o.value=t,o.textContent=i,e.appendChild(o)}),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 i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=e.cmd,i.title=e.title,i.setAttribute("aria-label",e.title);let o=document.createElement("span");o.className="material-icons",o.textContent=e.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let r=e.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=r,i.appendChild(s);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 h=a.value;s.style.background=h,e.cmd==="foreColor"?this._lastForeColor=h:this._lastHiliteColor=h,this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand(e.cmd,!1,h)),this._returnFocus(i,u)}),this._bindActivation(i,h=>{u=h,this._saveBookmark(),a.click()}),t.appendChild(i),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 i=document.createElement("span");return i.className="material-icons",i.textContent=e.icon,t.appendChild(i),this._bindActivation(t,o=>{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),o&&this._focusPopup()}),t}_focusPopup(){let e=this._popup.querySelector('input, textarea, select, button, [tabindex]:not([tabindex="-1"])');e&&e.focus()}_buildPopupContainer(){let e=document.createElement("div");return e.className="jotter-popup",e.setAttribute("role","dialog"),e}_showPopup(e,t,i){this._popup.innerHTML="",this._popup.appendChild(t),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=e.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_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 i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`,s.setAttribute("role","grid"),s.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=(d,l)=>{r.textContent=`${d+1} \xD7 ${l+1} table`,a.forEach(c=>{c.classList.toggle("jotter-table-cell--active",+c.dataset.r<=d&&+c.dataset.c<=l)})};for(let d=0;d<o;d++)for(let l=0;l<i;l++){let c=document.createElement("span");c.className="jotter-table-cell",c.dataset.r=d,c.dataset.c=l,c.setAttribute("role","gridcell"),c.setAttribute("aria-label",`${d+1} by ${l+1} table`),c.tabIndex=d===0&&l===0?0:-1,c.addEventListener("mouseenter",()=>u(d,l)),c.addEventListener("focus",()=>u(d,l)),c.addEventListener("click",()=>this._insertTable(d+1,l+1)),a.push(c),s.appendChild(c)}let h={ArrowRight:[0,1],ArrowLeft:[0,-1],ArrowDown:[1,0],ArrowUp:[-1,0]};return s.addEventListener("keydown",d=>{let l=a.indexOf(document.activeElement)===-1?null:document.activeElement;if(!l)return;let c=+l.dataset.r,C=+l.dataset.c;if(d.key==="Enter"||d.key===" "||d.key==="Spacebar"){d.preventDefault(),this._insertTable(c+1,C+1);return}let b=h[d.key];if(!b)return;d.preventDefault();let y=Math.min(o-1,Math.max(0,c+b[0])),S=Math.min(i-1,Math.max(0,C+b[1])),g=a[y*i+S];g!==l&&(l.tabIndex=-1,g.tabIndex=0,g.focus())}),e.appendChild(s),e.appendChild(r),e}_commitPopup(e){this._restoreSavedBookmark(),this._hidePopup(),this._applyEdit(e)}_insertTable(e,t){let i="<table><tbody>";for(let o=0;o<e;o++){i+="<tr>";for(let s=0;s<t;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",this._commitPopup(()=>document.execCommand("insertHTML",!1,i))}_popupLink(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert Link"));let t=this._anchorInSelection(),i=this._selectedText(),o=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 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(([h,d])=>{let l=document.createElement("option");l.value=d,l.textContent=h,u.appendChild(l)}),e.appendChild(a),e.appendChild(u),t?(o.value=t.getAttribute("href")||"",s.value=t.textContent||"",r.value=t.getAttribute("title")||"",u.value=t.getAttribute("target")||""):i&&(s.value=i),e.appendChild(this._makeSubmitBtn(t?"Update Link":"Insert Link",()=>{let h=o.value.trim();if(!h)return;let d=s.value.trim()||i||h,l=r.value.trim(),c=u.value;this._commitPopup(()=>{t?(t.href=h,c?t.target=c:t.removeAttribute("target"),l?t.title=l:t.removeAttribute("title"),t.textContent=d):document.execCommand("insertHTML",!1,this._linkHTML({href:h,text:d,title:l,target:c}))})})),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"),i=this._makeField(e,"Alt text","text","Descriptive text"),o=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:i.value.trim(),width:o.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 i=this._ytId(t.value.trim());if(!i){t.classList.add("jotter-input--error");return}t.classList.remove("jotter-input--error"),this._commitPopup(()=>document.execCommand("insertHTML",!1,this._videoHTML(i)))})),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 i=e.match(t);if(i)return i[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 i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,e.appendChild(t),e.appendChild(i),e.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&this._commitPopup(()=>document.execCommand("insertHTML",!1,this._embedHTML(o)))})),e}_popupSymbol(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Insert Symbol")),e.appendChild(this._charGrid(B)),e}_popupSpecialChar(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Special Characters")),e.appendChild(this._charGrid(F)),e}_charGrid(e){let t=document.createElement("div");return t.className="jotter-char-grid",e.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,this._bindActivation(o,()=>{this._commitPopup(()=>document.execCommand("insertText",!1,i))}),t.appendChild(o)}),t}_popupLorem(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Insert Lorem Ipsum")),H.forEach(t=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=t.label,this._bindActivation(i,()=>{this._commitPopup(()=>document.execCommand(t.isHTML?"insertHTML":"insertText",!1,t.text))}),e.appendChild(i)}),e}_popupTitle(e){let t=document.createElement("div");return t.className="jotter-popup-title",t.textContent=e,t}_makeField(e,t,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=t;let r=document.createElement("input");return r.type=i,r.className="jotter-popup-input",r.placeholder=o,e.appendChild(s),e.appendChild(r),r}_makeSubmitBtn(e,t){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=e,i.addEventListener("click",t),i}_esc(e){return String(e).replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_imageHTML({src:e,alt:t,width:i}){let o=i?`max-width:${i}`:"max-width:100%";return`<img src="${this._esc(e)}" alt="${this._esc(t||"")}" style="${this._esc(o)}">`}_linkHTML({href:e,text:t,title:i,target:o}){let s=`href="${this._esc(e)}"`;return o&&(s+=` target="${this._esc(o)}"`),i&&(s+=` title="${this._esc(i)}"`),`<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`<div class="jotter-embed-wrap">${e}</div><p><br></p>`}_resolverFor(e){let t=I[e],i=t?this._options[t]:null;return typeof i=="function"?i:null}async _runResolver(e,t){let i=this._resolverContext(e);this.beginExternalUI();let o=null;try{o=await t(i)}catch{o=null}if(this._destroyed)return;let s=o==null||o===!1?null:this._resolvedHTML(e,o,i);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 i=this._selectedText();return{href:"",text:i,title:"",target:"",selection:i,isEdit:!1}}let t=window.getSelection();if(t){let i=document.createRange();i.selectNode(e),t.removeAllRanges(),t.addRange(i)}return{href:e.getAttribute("href")||"",text:e.textContent||"",title:e.getAttribute("title")||"",target:e.getAttribute("target")||"",selection:e.textContent||"",isEdit:!0}}_resolvedHTML(e,t,i){switch(e){case"image":{let o=typeof t=="string"?{src:t}:t,s=String(o.src||o.url||"").trim();return s?this._imageHTML({...o,src:s}):null}case"link":{let o=typeof t=="string"?{href:t}:t,s=String(o.href||o.url||"").trim();if(!s)return null;let r=String(o.text!=null?o.text:i.text||"").trim();return this._linkHTML({...o,href:s,text:r})}case"video":{let o=typeof t=="string"?{url:t}:t,s=o.id?String(o.id).trim():this._ytId(String(o.url||"").trim());return s?this._videoHTML(s):null}case"embed":{let o=String(typeof t=="string"?t:t.html||"").trim();return o?this._embedHTML(o):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}_buildMediaFrame(){let e=document.createElement("div");return e.className="jotter-media-frame",["nw","ne","sw","se","w","e"].forEach(t=>{let i=document.createElement("div");i.className="jotter-resize-handle jotter-resize-handle--"+t,i.dataset.handle=t,i.tabIndex=-1,e.appendChild(i)}),e.addEventListener("mousedown",t=>{if(!this._selectedMedia)return;t.preventDefault();let i=t.target.closest(".jotter-resize-handle");i?this._beginResize(i.dataset.handle,t):this._beginPotentialMove(this._selectedMedia,t)}),e}_ensureMediaShields(){this._editor.querySelectorAll(".jotter-video-wrap, .jotter-embed-wrap").forEach(e=>{if(e.querySelector(":scope > .jotter-media-shield"))return;let t=document.createElement("div");t.className="jotter-media-shield",e.appendChild(t)})}_mediaElementFromTarget(e){if(!this._editor.contains(e))return null;let t=e.closest("img");if(t&&this._editor.contains(t))return t;let i=e.closest(".jotter-video-wrap, .jotter-embed-wrap");return i&&this._editor.contains(i)?i:null}_selectMedia(e){if(this._selectedMedia===e)return;this._deselectMedia(),this._selectedMedia=e,this._positionMediaFrame(),this._mediaFrame.classList.add("jotter-media-frame--visible"),this._editor.focus({preventScroll:!0});let t=document.createRange();t.selectNode(e);let i=window.getSelection();i.removeAllRanges(),i.addRange(t),this._updateToolbarState()}_deselectMedia(){this._resizeState&&this._abortResize(),this._moveState&&this._abortMove(),this._selectedMedia&&(this._selectedMedia=null,this._mediaFrame.classList.remove("jotter-media-frame--visible"))}_positionMediaFrame(){let e=this._selectedMedia;if(!e)return;let t=this._editorWrap.getBoundingClientRect(),i=e.getBoundingClientRect();this._mediaFrame.style.top=i.top-t.top+this._editorWrap.scrollTop+"px",this._mediaFrame.style.left=i.left-t.left+this._editorWrap.scrollLeft+"px",this._mediaFrame.style.width=i.width+"px",this._mediaFrame.style.height=i.height+"px"}_deleteSelectedMedia(){let e=this._selectedMedia;if(!e)return;this._deselectMedia();let t=document.createRange();t.selectNode(e);let i=window.getSelection();i.removeAllRanges(),i.addRange(t),this._applyEdit(()=>document.execCommand("delete",!1,null)),this._updateToolbarState()}_beginResize(e,t){let i=this._selectedMedia,o=i.getBoundingClientRect(),s=e.includes("w")?-1:1;this._resizeState={el:i,sign:s,startX:t.clientX,startWidth:o.width,startInlineWidth:i.style.width,before:this._richHTML()},document.addEventListener("mousemove",this._onResizeMove),document.addEventListener("mouseup",this._onResizeEnd,{once:!0})}_onResizeMove(e){let t=this._resizeState;if(!t)return;let i=(e.clientX-t.startX)*t.sign,o=40,s=Math.max(o,this._editor.clientWidth),r=Math.max(o,Math.min(s,Math.round(t.startWidth+i)));t.el.style.width=r+"px",t.el.style.removeProperty("max-width"),this._positionMediaFrame()}_onResizeEnd(){document.removeEventListener("mousemove",this._onResizeMove);let e=this._resizeState;this._resizeState=null,e&&(this._updateStatus(),this._richHTML()!==e.before&&this._emitChange())}_abortResize(){let e=this._resizeState;e&&(document.removeEventListener("mousemove",this._onResizeMove),document.removeEventListener("mouseup",this._onResizeEnd),e.el.style.width=e.startInlineWidth,this._resizeState=null,this._positionMediaFrame())}_beginPotentialMove(e,t){this._moveState={el:e,startX:t.clientX,startY:t.clientY,dragging:!1},document.addEventListener("mousemove",this._onMoveMove),document.addEventListener("mouseup",this._onMoveUp,{once:!0})}_onMoveMove(e){let t=this._moveState;if(t){if(!t.dragging){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<5)return;t.dragging=!0,t.before=this._richHTML(),this._mediaFrame.classList.add("jotter-media-frame--dragging"),document.body.style.cursor="grabbing"}t.dropRange=this._rangeFromPoint(e.clientX,e.clientY),this._showDropIndicator(t.dropRange)}}_onMoveUp(){document.removeEventListener("mousemove",this._onMoveMove);let e=this._moveState;this._moveState=null,this._mediaFrame.classList.remove("jotter-media-frame--dragging"),document.body.style.cursor="",this._hideDropIndicator(),e&&e.dragging&&this._commitMove(e.el,e.dropRange,e.before)}_abortMove(){this._moveState&&(document.removeEventListener("mousemove",this._onMoveMove),document.removeEventListener("mouseup",this._onMoveUp),this._moveState=null,this._mediaFrame.classList.remove("jotter-media-frame--dragging"),document.body.style.cursor="",this._hideDropIndicator())}_rangeFromPoint(e,t){let i=null;if(document.caretRangeFromPoint)i=document.caretRangeFromPoint(e,t);else if(document.caretPositionFromPoint){let s=document.caretPositionFromPoint(e,t);s&&(i=document.createRange(),i.setStart(s.offsetNode,s.offset),i.collapse(!0))}if(!i||!this._editor.contains(i.startContainer))return null;let o=this._moveState;return o&&o.el.contains(i.startContainer)?null:i}_buildDropIndicator(){let e=document.createElement("div");return e.className="jotter-drop-indicator",e}_showDropIndicator(e){this._dropIndicator||(this._dropIndicator=this._buildDropIndicator(),this._editorWrap.appendChild(this._dropIndicator));let t=e&&e.getClientRects()[0];if(!t){this._dropIndicator.style.display="none";return}let i=this._editorWrap.getBoundingClientRect();this._dropIndicator.style.display="block",this._dropIndicator.style.top=t.top-i.top+this._editorWrap.scrollTop+"px",this._dropIndicator.style.left=t.left-i.left+this._editorWrap.scrollLeft+"px",this._dropIndicator.style.height=(t.height||20)+"px"}_hideDropIndicator(){this._dropIndicator&&(this._dropIndicator.style.display="none")}_commitMove(e,t,i){if(!t||!t.startContainer.isConnected)return;let o="jmv"+ ++this._bmSeq,s=e.cloneNode(!0);s.querySelectorAll(".jotter-media-shield").forEach(h=>h.remove()),s.setAttribute("data-jotter-move",o),this._suppressAutoEmit=!0;let r=window.getSelection(),a=document.createRange();a.selectNode(e),r.removeAllRanges(),r.addRange(a),document.execCommand("delete",!1,null),r.removeAllRanges(),r.addRange(t),document.execCommand("insertHTML",!1,s.outerHTML);let u=this._editor.querySelector(`[data-jotter-move="${o}"]`);u&&u.removeAttribute("data-jotter-move"),this._suppressAutoEmit=!1,this._updateStatus(),u?this._selectMedia(u):this._deselectMedia(),this._richHTML()!==i&&this._emitChange()}_writeMediaToClipboard(e,t){let i=t.cloneNode(!0);i.querySelectorAll(".jotter-media-shield").forEach(o=>o.remove()),e.clipboardData.setData("text/html",i.outerHTML),e.clipboardData.setData("text/plain",t.tagName==="IMG"?t.alt||"":t.textContent||"")}_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(),i=null;if(t&&t.rangeCount){let s=t.getRangeAt(0);s.startContainer===e&&(i=s.startOffset)}let o=document.createElement("p");if(e.replaceWith(o),o.appendChild(e),i!==null){let s=document.createRange();s.setStart(e,i),s.collapse(!0),t.removeAllRanges(),t.addRange(s)}}}),this._ensureMediaShields(),this._selectedMedia&&this._positionMediaFrame(),this._updateStatus(),this._suppressAutoEmit||this._emitChange()}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("mousedown",e=>{let t=this._mediaElementFromTarget(e.target);t&&(e.preventDefault(),this._selectMedia(t),this._beginPotentialMove(t,e))}),this._editor.addEventListener("copy",e=>{this._selectedMedia&&(e.preventDefault(),this._writeMediaToClipboard(e,this._selectedMedia))}),this._editor.addEventListener("cut",e=>{this._selectedMedia&&(e.preventDefault(),this._writeMediaToClipboard(e,this._selectedMedia),this._deleteSelectedMedia())}),this._editor.addEventListener("paste",e=>{e.preventDefault();let t=(e.clipboardData||window.clipboardData).getData("text/html");if(t)this._applyEdit(()=>document.execCommand("insertHTML",!1,this._sanitize(t)));else{let i=(e.clipboardData||window.clipboardData).getData("text/plain");i&&this._applyEdit(()=>document.execCommand("insertText",!1,i))}}),this._onWindowResize=()=>{this._selectedMedia&&this._positionMediaFrame()},window.addEventListener("resize",this._onWindowResize),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._deselectMedia(),!(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=>{if((e.key==="Backspace"||e.key==="Delete")&&this._selectedMedia){e.preventDefault(),this._deleteSelectedMedia();return}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(),this._selectedMedia&&!this._mediaFrame.contains(e.target)&&e.target!==this._selectedMedia&&!this._selectedMedia.contains(e.target)&&this._deselectMedia()},document.addEventListener("mousedown",this._onDocMouseDown),this._onDocKeyDown=e=>{if(e.key==="Escape"){if(this._resizeState){this._abortResize();return}if(this._moveState&&this._moveState.dragging){this._abortMove();return}if(this._selectedMedia){this._deselectMedia();return}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._deselectMedia(),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._ensureMediaShields(),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,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return e.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+)/),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);})();
4
+ `).map(r=>r.trim()).filter(r=>r.length>0).map(r=>{let a=r.match(/^<\/(\w+)/),u=r.match(/^<(\w+)/),h=r.endsWith("/>"),d=u?u[1].toLowerCase():null,l=a?a[1].toLowerCase():null;l&&!s.has(l)&&(t=Math.max(0,t-1));let c=i.repeat(t)+r;return d&&!h&&!o.has(d)&&!l&&!s.has(d)&&t++,c}).join(`
5
+ `)}_sanitize(e){let t=new DOMParser().parseFromString(e,"text/html");return t.querySelectorAll("script").forEach(i=>i.remove()),t.querySelectorAll("[data-jotter-bookmark], .jotter-media-shield").forEach(i=>i.remove()),t.querySelectorAll("*").forEach(i=>{Array.from(i.attributes).forEach(o=>{(o.name.startsWith("on")||["href","src","action","formaction","data"].includes(o.name)&&/^\s*javascript:/i.test(o.value))&&i.removeAttribute(o.name)})}),t.body.innerHTML}_toggleInlineCode(){let e=window.getSelection();if(!e||e.rangeCount===0)return;let t=e.getRangeAt(0),i=t.commonAncestorContainer;i.nodeType===3&&(i=i.parentNode);let o=i.closest?i.closest("code"):null;if(o){let s=o.parentNode;for(;o.firstChild;)s.insertBefore(o.firstChild,o);s.removeChild(o)}else if(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 i=t.getRangeAt(0);if(i.collapsed)return;let o=document.createElement("span");o.style.fontSize=e+"px";try{i.surroundContents(o)}catch{let r=i.extractContents();o.appendChild(r),i.insertNode(o)}}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,""),i=E.find(o=>o.tag===t);i&&(e.value=i.tag)}}_updateStatus(){let e=this._editor.innerText||"",t=e.trim()===""?0:e.trim().split(/\s+/).length,i=e.replace(/\n/g,"").length;this._wordCountEl.textContent=`${t} word${t!==1?"s":""}`,this._charCountEl.textContent=`${i} char${i!==1?"s":""}`}_richHTML(){let e=this._editor.cloneNode(!0);return e.querySelectorAll("[data-jotter-bookmark], .jotter-media-shield").forEach(t=>t.remove()),e.querySelectorAll("[data-jotter-move]").forEach(t=>t.removeAttribute("data-jotter-move")),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:i,end:o}=t,s=this._editor.contains(i)&&(!o||this._editor.contains(o)),r=null;return s&&(r=document.createRange(),r.setStartAfter(i),o?r.setEndBefore(o):r.collapse(!0)),i.parentNode&&i.remove(),o&&o.parentNode&&o.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(i=>i(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,i=this._richHTML();e(),this._updateStatus(),this._changeCount===t&&this._richHTML()!==i&&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._deselectMedia(),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 i="jbm"+ ++this._bmSeq;if(this._editor.childNodes.length===0)return this._bookmarks.set(i,{atStart:!0}),i;let o=this._makeMarker(i),s=t.collapsed?null:this._makeMarker(i);if(s){let u=t.cloneRange();u.collapse(!1),u.insertNode(s)}let r=t.cloneRange();r.collapse(!0),r.insertNode(o),this._bookmarks.set(i,{start:o,end:s});let a=document.createRange();return a.setStartAfter(o),s?a.setEndBefore(s):a.collapse(!0),e.removeAllRanges(),e.addRange(a),i}restoreSelection(e){let t=this._takeRange(e);if(!t)return this;this._sourceMode||this._editor.focus();let i=window.getSelection();return i.removeAllRanges(),i.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._deselectMedia(),this._dropBookmarks(),this._editor.innerHTML=this._sanitize(e),this._ensureMediaShields(),this._updateStatus(),this}setTheme(e){let i=k.find(o=>o.id===e)?e:"default";return this._editor.dataset.theme=i,this._options.theme=i,this._themeSelect&&(this._themeSelect.value=i),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(i=>i!==t)),this}destroy(){let e=this.getHTML();return this._destroyed=!0,this._deselectMedia(),this._hidePopup(),this._dropBookmarks(),this._externalBookmark=null,this._externalDepth=0,document.removeEventListener("mousedown",this._onDocMouseDown),document.removeEventListener("keydown",this._onDocKeyDown),window.removeEventListener("resize",this._onWindowResize),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 z=m;return j(D);})();
package/dist/jotter.js CHANGED
@@ -358,6 +358,14 @@ var JotterJS = class {
358
358
  this._destroyed = false;
359
359
  this._lastForeColor = "#e8e4d8";
360
360
  this._lastHiliteColor = "#c8a96e";
361
+ this._selectedMedia = null;
362
+ this._resizeState = null;
363
+ this._moveState = null;
364
+ this._suppressAutoEmit = false;
365
+ this._onResizeMove = this._onResizeMove.bind(this);
366
+ this._onResizeEnd = this._onResizeEnd.bind(this);
367
+ this._onMoveMove = this._onMoveMove.bind(this);
368
+ this._onMoveUp = this._onMoveUp.bind(this);
361
369
  this._init();
362
370
  }
363
371
  // ─── Init ─────────────────────────────────────────────────────────────────
@@ -381,6 +389,15 @@ var JotterJS = class {
381
389
  this._editor.innerHTML = this._sanitize(initialHTML);
382
390
  this._editor.spellcheck = true;
383
391
  document.execCommand("defaultParagraphSeparator", false, "p");
392
+ try {
393
+ document.execCommand("enableObjectResizing", false, false);
394
+ } catch (_) {
395
+ }
396
+ try {
397
+ document.execCommand("enableInlineTableEditing", false, false);
398
+ } catch (_) {
399
+ }
400
+ this._ensureMediaShields();
384
401
  this._source = document.createElement("textarea");
385
402
  this._source.className = "jotter-source";
386
403
  this._source.setAttribute("aria-label", "HTML source");
@@ -388,8 +405,10 @@ var JotterJS = class {
388
405
  this._source.style.minHeight = this._options.height;
389
406
  this._sourceMode = false;
390
407
  this._statusBar = this._buildStatusBar();
408
+ this._mediaFrame = this._buildMediaFrame();
391
409
  this._editorWrap.appendChild(this._editor);
392
410
  this._editorWrap.appendChild(this._source);
411
+ this._editorWrap.appendChild(this._mediaFrame);
393
412
  this._root.appendChild(this._toolbar);
394
413
  this._root.appendChild(this._editorWrap);
395
414
  this._root.appendChild(this._statusBar);
@@ -1138,9 +1157,14 @@ var JotterJS = class {
1138
1157
  return null;
1139
1158
  return `<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${id}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;
1140
1159
  }
1141
- /** Embeds are inserted verbatim — the host, not the end user, supplies them. */
1160
+ /**
1161
+ * The embed markup itself is inserted verbatim (the host, not the end user,
1162
+ * supplies it, so it is never sanitized) inside a .jotter-embed-wrap, added
1163
+ * purely so the block is selectable and can carry a click shield — same
1164
+ * treatment as the video wrap.
1165
+ */
1142
1166
  _embedHTML(html) {
1143
- return html + "<p><br></p>";
1167
+ return `<div class="jotter-embed-wrap">${html}</div><p><br></p>`;
1144
1168
  }
1145
1169
  // ─── Resolver hooks ───────────────────────────────────────────────────────
1146
1170
  // An onRequest* option replaces the built-in popup for that insertable. The
@@ -1282,6 +1306,353 @@ var JotterJS = class {
1282
1306
  bar.appendChild(modeEl);
1283
1307
  return bar;
1284
1308
  }
1309
+ // ─── Media selection ──────────────────────────────────────────────────────
1310
+ // Images, YouTube embeds and generic embeds are atomic blocks: clicking one
1311
+ // shows a floating frame (positioned over it, not part of the saved HTML)
1312
+ // with resize handles, and Backspace/Delete removes the whole element.
1313
+ //
1314
+ // The frame lives in _editorWrap, a sibling of _editor/_source, rather than
1315
+ // inside the editable content or on document.body like the popup — being a
1316
+ // normal descendant of the scrolling wrap means it scrolls with the content
1317
+ // for free, with no scroll listener needed.
1318
+ //
1319
+ // Video/embed content sits behind a permanent "shield" div so it can never
1320
+ // be interacted with while editing (no accidental playback, no swallowed
1321
+ // clicks) — every click on the block always just selects it. The shield is
1322
+ // injected live-DOM only and stripped out of getHTML()/_sanitize(), exactly
1323
+ // like the selection bookmarks above are, so published HTML keeps a normal,
1324
+ // interactive iframe.
1325
+ /** Builds the reusable floating selection frame with its 6 resize handles. */
1326
+ _buildMediaFrame() {
1327
+ const frame = document.createElement("div");
1328
+ frame.className = "jotter-media-frame";
1329
+ ["nw", "ne", "sw", "se", "w", "e"].forEach((pos) => {
1330
+ const h = document.createElement("div");
1331
+ h.className = "jotter-resize-handle jotter-resize-handle--" + pos;
1332
+ h.dataset.handle = pos;
1333
+ h.tabIndex = -1;
1334
+ frame.appendChild(h);
1335
+ });
1336
+ frame.addEventListener("mousedown", (e) => {
1337
+ if (!this._selectedMedia)
1338
+ return;
1339
+ e.preventDefault();
1340
+ const handle = e.target.closest(".jotter-resize-handle");
1341
+ if (handle)
1342
+ this._beginResize(handle.dataset.handle, e);
1343
+ else
1344
+ this._beginPotentialMove(this._selectedMedia, e);
1345
+ });
1346
+ return frame;
1347
+ }
1348
+ /** Adds a click shield to every video/embed block that doesn't already have one. */
1349
+ _ensureMediaShields() {
1350
+ this._editor.querySelectorAll(".jotter-video-wrap, .jotter-embed-wrap").forEach((wrap) => {
1351
+ if (wrap.querySelector(":scope > .jotter-media-shield"))
1352
+ return;
1353
+ const shield = document.createElement("div");
1354
+ shield.className = "jotter-media-shield";
1355
+ wrap.appendChild(shield);
1356
+ });
1357
+ }
1358
+ /** Nearest selectable media element (img / video-wrap / embed-wrap) under target, or null. */
1359
+ _mediaElementFromTarget(target) {
1360
+ if (!this._editor.contains(target))
1361
+ return null;
1362
+ const img = target.closest("img");
1363
+ if (img && this._editor.contains(img))
1364
+ return img;
1365
+ const wrap = target.closest(".jotter-video-wrap, .jotter-embed-wrap");
1366
+ if (wrap && this._editor.contains(wrap))
1367
+ return wrap;
1368
+ return null;
1369
+ }
1370
+ /**
1371
+ * Selects a media element: shows the frame over it and makes it the real DOM
1372
+ * selection (the whole node, not a collapsed caret beside it). A real
1373
+ * non-collapsed selection is what lets native Ctrl+C/Ctrl+X and the
1374
+ * toolbar's Copy/Cut buttons (both just execCommand('copy'/'cut')) work on
1375
+ * it for free — see the 'copy'/'cut' listeners in _bindEvents for how the
1376
+ * click-shield is kept out of what actually lands on the clipboard.
1377
+ */
1378
+ _selectMedia(el) {
1379
+ if (this._selectedMedia === el)
1380
+ return;
1381
+ this._deselectMedia();
1382
+ this._selectedMedia = el;
1383
+ this._positionMediaFrame();
1384
+ this._mediaFrame.classList.add("jotter-media-frame--visible");
1385
+ this._editor.focus({ preventScroll: true });
1386
+ const range = document.createRange();
1387
+ range.selectNode(el);
1388
+ const sel = window.getSelection();
1389
+ sel.removeAllRanges();
1390
+ sel.addRange(range);
1391
+ this._updateToolbarState();
1392
+ }
1393
+ /** Clears the current media selection (and aborts any in-flight resize or move). */
1394
+ _deselectMedia() {
1395
+ if (this._resizeState)
1396
+ this._abortResize();
1397
+ if (this._moveState)
1398
+ this._abortMove();
1399
+ if (!this._selectedMedia)
1400
+ return;
1401
+ this._selectedMedia = null;
1402
+ this._mediaFrame.classList.remove("jotter-media-frame--visible");
1403
+ }
1404
+ /** Repositions the floating frame to match the selected element's current box. */
1405
+ _positionMediaFrame() {
1406
+ const el = this._selectedMedia;
1407
+ if (!el)
1408
+ return;
1409
+ const wrapRect = this._editorWrap.getBoundingClientRect();
1410
+ const elRect = el.getBoundingClientRect();
1411
+ this._mediaFrame.style.top = elRect.top - wrapRect.top + this._editorWrap.scrollTop + "px";
1412
+ this._mediaFrame.style.left = elRect.left - wrapRect.left + this._editorWrap.scrollLeft + "px";
1413
+ this._mediaFrame.style.width = elRect.width + "px";
1414
+ this._mediaFrame.style.height = elRect.height + "px";
1415
+ }
1416
+ /**
1417
+ * Removes the selected element via execCommand('delete') rather than a plain
1418
+ * el.remove() — the browser's native undo stack (what Ctrl+Z / the Undo
1419
+ * button run via execCommand('undo')) only tracks edits made through
1420
+ * execCommand, so a raw DOM removal would be invisible to it. Selecting the
1421
+ * whole node first (rather than a collapsed caret before it) means 'delete'
1422
+ * removes exactly that node regardless of direction, and the browser
1423
+ * collapses the caret to where it used to be on its own.
1424
+ */
1425
+ _deleteSelectedMedia() {
1426
+ const el = this._selectedMedia;
1427
+ if (!el)
1428
+ return;
1429
+ this._deselectMedia();
1430
+ const range = document.createRange();
1431
+ range.selectNode(el);
1432
+ const sel = window.getSelection();
1433
+ sel.removeAllRanges();
1434
+ sel.addRange(range);
1435
+ this._applyEdit(() => document.execCommand("delete", false, null));
1436
+ this._updateToolbarState();
1437
+ }
1438
+ /**
1439
+ * Starts a width-only resize drag. Height is never set directly: an image's
1440
+ * natural aspect ratio, the video wrap's padding-bottom-percent-of-width box,
1441
+ * and an embed's normal reflow all keep it correct on their own once width
1442
+ * changes, so one code path covers all three media types.
1443
+ */
1444
+ _beginResize(handleType, e) {
1445
+ const el = this._selectedMedia;
1446
+ const rect = el.getBoundingClientRect();
1447
+ const sign = handleType.includes("w") ? -1 : 1;
1448
+ this._resizeState = {
1449
+ el,
1450
+ sign,
1451
+ startX: e.clientX,
1452
+ startWidth: rect.width,
1453
+ startInlineWidth: el.style.width,
1454
+ before: this._richHTML()
1455
+ };
1456
+ document.addEventListener("mousemove", this._onResizeMove);
1457
+ document.addEventListener("mouseup", this._onResizeEnd, { once: true });
1458
+ }
1459
+ _onResizeMove(e) {
1460
+ const s = this._resizeState;
1461
+ if (!s)
1462
+ return;
1463
+ const dx = (e.clientX - s.startX) * s.sign;
1464
+ const min = 40;
1465
+ const max = Math.max(min, this._editor.clientWidth);
1466
+ const width = Math.max(min, Math.min(max, Math.round(s.startWidth + dx)));
1467
+ s.el.style.width = width + "px";
1468
+ s.el.style.removeProperty("max-width");
1469
+ this._positionMediaFrame();
1470
+ }
1471
+ /**
1472
+ * A drag spans async mousemove/mouseup events, so it can't go through the
1473
+ * synchronous _applyEdit() helper — this manually replicates its contract
1474
+ * instead: emit 'change' exactly once, only if the width actually changed.
1475
+ */
1476
+ _onResizeEnd() {
1477
+ document.removeEventListener("mousemove", this._onResizeMove);
1478
+ const s = this._resizeState;
1479
+ this._resizeState = null;
1480
+ if (!s)
1481
+ return;
1482
+ this._updateStatus();
1483
+ if (this._richHTML() !== s.before)
1484
+ this._emitChange();
1485
+ }
1486
+ /** Escape mid-drag: revert to the pre-drag width and emit nothing. */
1487
+ _abortResize() {
1488
+ const s = this._resizeState;
1489
+ if (!s)
1490
+ return;
1491
+ document.removeEventListener("mousemove", this._onResizeMove);
1492
+ document.removeEventListener("mouseup", this._onResizeEnd);
1493
+ s.el.style.width = s.startInlineWidth;
1494
+ this._resizeState = null;
1495
+ this._positionMediaFrame();
1496
+ }
1497
+ /**
1498
+ * Arms a possible drag-to-reposition without committing to one: a plain
1499
+ * click (mouseup with no meaningful movement) must still work as "just
1500
+ * select it", so nothing visible happens until the pointer actually moves
1501
+ * past a small threshold — see _onMoveMove.
1502
+ */
1503
+ _beginPotentialMove(el, e) {
1504
+ this._moveState = { el, startX: e.clientX, startY: e.clientY, dragging: false };
1505
+ document.addEventListener("mousemove", this._onMoveMove);
1506
+ document.addEventListener("mouseup", this._onMoveUp, { once: true });
1507
+ }
1508
+ _onMoveMove(e) {
1509
+ const s = this._moveState;
1510
+ if (!s)
1511
+ return;
1512
+ if (!s.dragging) {
1513
+ if (Math.hypot(e.clientX - s.startX, e.clientY - s.startY) < 5)
1514
+ return;
1515
+ s.dragging = true;
1516
+ s.before = this._richHTML();
1517
+ this._mediaFrame.classList.add("jotter-media-frame--dragging");
1518
+ document.body.style.cursor = "grabbing";
1519
+ }
1520
+ s.dropRange = this._rangeFromPoint(e.clientX, e.clientY);
1521
+ this._showDropIndicator(s.dropRange);
1522
+ }
1523
+ _onMoveUp() {
1524
+ document.removeEventListener("mousemove", this._onMoveMove);
1525
+ const s = this._moveState;
1526
+ this._moveState = null;
1527
+ this._mediaFrame.classList.remove("jotter-media-frame--dragging");
1528
+ document.body.style.cursor = "";
1529
+ this._hideDropIndicator();
1530
+ if (s && s.dragging)
1531
+ this._commitMove(s.el, s.dropRange, s.before);
1532
+ }
1533
+ /** Escape mid-drag: drop nothing, leave the document exactly as it was. */
1534
+ _abortMove() {
1535
+ const s = this._moveState;
1536
+ if (!s)
1537
+ return;
1538
+ document.removeEventListener("mousemove", this._onMoveMove);
1539
+ document.removeEventListener("mouseup", this._onMoveUp);
1540
+ this._moveState = null;
1541
+ this._mediaFrame.classList.remove("jotter-media-frame--dragging");
1542
+ document.body.style.cursor = "";
1543
+ this._hideDropIndicator();
1544
+ }
1545
+ /**
1546
+ * Hit-tests viewport coordinates to a caret position, rejecting drops
1547
+ * outside the editor and drops inside the very node being dragged (moving
1548
+ * something into itself makes no sense and would detach it from the doc).
1549
+ */
1550
+ _rangeFromPoint(x, y) {
1551
+ let range = null;
1552
+ if (document.caretRangeFromPoint) {
1553
+ range = document.caretRangeFromPoint(x, y);
1554
+ } else if (document.caretPositionFromPoint) {
1555
+ const pos = document.caretPositionFromPoint(x, y);
1556
+ if (pos) {
1557
+ range = document.createRange();
1558
+ range.setStart(pos.offsetNode, pos.offset);
1559
+ range.collapse(true);
1560
+ }
1561
+ }
1562
+ if (!range || !this._editor.contains(range.startContainer))
1563
+ return null;
1564
+ const s = this._moveState;
1565
+ if (s && s.el.contains(range.startContainer))
1566
+ return null;
1567
+ return range;
1568
+ }
1569
+ _buildDropIndicator() {
1570
+ const el = document.createElement("div");
1571
+ el.className = "jotter-drop-indicator";
1572
+ return el;
1573
+ }
1574
+ /**
1575
+ * Shows a vertical text-caret-style indicator at the exact drop point, or
1576
+ * hides it when the drop target is invalid. A full-width horizontal line
1577
+ * would suggest a new block boundary; the drop actually lands at whatever
1578
+ * precise character position was hit-tested (mid-line included), so the
1579
+ * indicator has to match that — same idea as where a text cursor blinks.
1580
+ */
1581
+ _showDropIndicator(range) {
1582
+ if (!this._dropIndicator) {
1583
+ this._dropIndicator = this._buildDropIndicator();
1584
+ this._editorWrap.appendChild(this._dropIndicator);
1585
+ }
1586
+ const rect = range && range.getClientRects()[0];
1587
+ if (!rect) {
1588
+ this._dropIndicator.style.display = "none";
1589
+ return;
1590
+ }
1591
+ const wrapRect = this._editorWrap.getBoundingClientRect();
1592
+ this._dropIndicator.style.display = "block";
1593
+ this._dropIndicator.style.top = rect.top - wrapRect.top + this._editorWrap.scrollTop + "px";
1594
+ this._dropIndicator.style.left = rect.left - wrapRect.left + this._editorWrap.scrollLeft + "px";
1595
+ this._dropIndicator.style.height = (rect.height || 20) + "px";
1596
+ }
1597
+ _hideDropIndicator() {
1598
+ if (this._dropIndicator)
1599
+ this._dropIndicator.style.display = "none";
1600
+ }
1601
+ /**
1602
+ * Moves el to dropRange via execCommand('delete') + execCommand('insertHTML')
1603
+ * rather than a plain DOM insertBefore, for the same reason _deleteSelectedMedia
1604
+ * avoids el.remove(): only execCommand-driven edits land on the native undo
1605
+ * stack. That does mean a move takes two Ctrl+Z presses to fully unwind
1606
+ * (one per command) rather than one — an accepted trade-off, since the
1607
+ * alternative (a raw DOM move) would not be undoable at all.
1608
+ *
1609
+ * The two execCommands' own 'input' events would each independently call
1610
+ * _emitChange() with intermediate — and, for the insert half, momentarily
1611
+ * tagged — HTML; _suppressAutoEmit holds that back so hosts only ever see
1612
+ * the clean start and end states, as exactly one 'change'.
1613
+ */
1614
+ _commitMove(el, dropRange, before) {
1615
+ if (!dropRange || !dropRange.startContainer.isConnected)
1616
+ return;
1617
+ const marker = "jmv" + ++this._bmSeq;
1618
+ const clone = el.cloneNode(true);
1619
+ clone.querySelectorAll(".jotter-media-shield").forEach((s) => s.remove());
1620
+ clone.setAttribute("data-jotter-move", marker);
1621
+ this._suppressAutoEmit = true;
1622
+ const sel = window.getSelection();
1623
+ const delRange = document.createRange();
1624
+ delRange.selectNode(el);
1625
+ sel.removeAllRanges();
1626
+ sel.addRange(delRange);
1627
+ document.execCommand("delete", false, null);
1628
+ sel.removeAllRanges();
1629
+ sel.addRange(dropRange);
1630
+ document.execCommand("insertHTML", false, clone.outerHTML);
1631
+ const inserted = this._editor.querySelector(`[data-jotter-move="${marker}"]`);
1632
+ if (inserted)
1633
+ inserted.removeAttribute("data-jotter-move");
1634
+ this._suppressAutoEmit = false;
1635
+ this._updateStatus();
1636
+ if (inserted)
1637
+ this._selectMedia(inserted);
1638
+ else
1639
+ this._deselectMedia();
1640
+ if (this._richHTML() !== before)
1641
+ this._emitChange();
1642
+ }
1643
+ /**
1644
+ * Writes a media element to the clipboard with its click-shield stripped —
1645
+ * the shield is edit-time-only bookkeeping (see _ensureMediaShields) and
1646
+ * must never leak into copied/cut content any more than it leaks into
1647
+ * getHTML(). Used by both the 'copy'/'cut' listeners below and (via
1648
+ * execCommand('copy'/'cut')) the toolbar's Copy/Cut buttons.
1649
+ */
1650
+ _writeMediaToClipboard(e, el) {
1651
+ const clone = el.cloneNode(true);
1652
+ clone.querySelectorAll(".jotter-media-shield").forEach((s) => s.remove());
1653
+ e.clipboardData.setData("text/html", clone.outerHTML);
1654
+ e.clipboardData.setData("text/plain", el.tagName === "IMG" ? el.alt || "" : el.textContent || "");
1655
+ }
1285
1656
  // ─── Events ───────────────────────────────────────────────────────────────
1286
1657
  _bindEvents() {
1287
1658
  this._editor.addEventListener("input", () => {
@@ -1313,11 +1684,52 @@ var JotterJS = class {
1313
1684
  }
1314
1685
  }
1315
1686
  });
1687
+ this._ensureMediaShields();
1688
+ if (this._selectedMedia)
1689
+ this._positionMediaFrame();
1316
1690
  this._updateStatus();
1317
- this._emitChange();
1691
+ if (!this._suppressAutoEmit)
1692
+ this._emitChange();
1318
1693
  });
1319
1694
  this._editor.addEventListener("keyup", () => this._updateToolbarState());
1320
1695
  this._editor.addEventListener("mouseup", () => this._updateToolbarState());
1696
+ this._editor.addEventListener("mousedown", (e) => {
1697
+ const media = this._mediaElementFromTarget(e.target);
1698
+ if (!media)
1699
+ return;
1700
+ e.preventDefault();
1701
+ this._selectMedia(media);
1702
+ this._beginPotentialMove(media, e);
1703
+ });
1704
+ this._editor.addEventListener("copy", (e) => {
1705
+ if (!this._selectedMedia)
1706
+ return;
1707
+ e.preventDefault();
1708
+ this._writeMediaToClipboard(e, this._selectedMedia);
1709
+ });
1710
+ this._editor.addEventListener("cut", (e) => {
1711
+ if (!this._selectedMedia)
1712
+ return;
1713
+ e.preventDefault();
1714
+ this._writeMediaToClipboard(e, this._selectedMedia);
1715
+ this._deleteSelectedMedia();
1716
+ });
1717
+ this._editor.addEventListener("paste", (e) => {
1718
+ e.preventDefault();
1719
+ const html = (e.clipboardData || window.clipboardData).getData("text/html");
1720
+ if (html) {
1721
+ this._applyEdit(() => document.execCommand("insertHTML", false, this._sanitize(html)));
1722
+ } else {
1723
+ const text = (e.clipboardData || window.clipboardData).getData("text/plain");
1724
+ if (text)
1725
+ this._applyEdit(() => document.execCommand("insertText", false, text));
1726
+ }
1727
+ });
1728
+ this._onWindowResize = () => {
1729
+ if (this._selectedMedia)
1730
+ this._positionMediaFrame();
1731
+ };
1732
+ window.addEventListener("resize", this._onWindowResize);
1321
1733
  this._editor.addEventListener("focusin", (e) => {
1322
1734
  this._root.classList.add("jotter--focused");
1323
1735
  if (this._externalDepth > 0)
@@ -1329,6 +1741,7 @@ var JotterJS = class {
1329
1741
  this._options.onFocus();
1330
1742
  });
1331
1743
  this._editor.addEventListener("focusout", (e) => {
1744
+ this._deselectMedia();
1332
1745
  if (this._externalDepth > 0)
1333
1746
  return;
1334
1747
  if (this._isInternalTarget(e.relatedTarget))
@@ -1347,6 +1760,11 @@ var JotterJS = class {
1347
1760
  }, 0);
1348
1761
  });
1349
1762
  this._editor.addEventListener("keydown", (e) => {
1763
+ if ((e.key === "Backspace" || e.key === "Delete") && this._selectedMedia) {
1764
+ e.preventDefault();
1765
+ this._deleteSelectedMedia();
1766
+ return;
1767
+ }
1350
1768
  if (e.key === "Tab") {
1351
1769
  e.preventDefault();
1352
1770
  document.execCommand("insertHTML", false, "&nbsp;&nbsp;&nbsp;&nbsp;");
@@ -1356,10 +1774,27 @@ var JotterJS = class {
1356
1774
  if (this._popupVisible() && !this._popup.contains(e.target) && !this._toolbarEl.contains(e.target)) {
1357
1775
  this._hidePopup();
1358
1776
  }
1777
+ if (this._selectedMedia && !this._mediaFrame.contains(e.target) && e.target !== this._selectedMedia && !this._selectedMedia.contains(e.target)) {
1778
+ this._deselectMedia();
1779
+ }
1359
1780
  };
1360
1781
  document.addEventListener("mousedown", this._onDocMouseDown);
1361
1782
  this._onDocKeyDown = (e) => {
1362
- if (e.key === "Escape" && this._popupVisible()) {
1783
+ if (e.key !== "Escape")
1784
+ return;
1785
+ if (this._resizeState) {
1786
+ this._abortResize();
1787
+ return;
1788
+ }
1789
+ if (this._moveState && this._moveState.dragging) {
1790
+ this._abortMove();
1791
+ return;
1792
+ }
1793
+ if (this._selectedMedia) {
1794
+ this._deselectMedia();
1795
+ return;
1796
+ }
1797
+ if (this._popupVisible()) {
1363
1798
  this._restoreSavedBookmark();
1364
1799
  this._hidePopup();
1365
1800
  }
@@ -1377,6 +1812,7 @@ var JotterJS = class {
1377
1812
  * Leaving source mode: sanitizes textarea value back into innerHTML.
1378
1813
  */
1379
1814
  _toggleSourceMode() {
1815
+ this._deselectMedia();
1380
1816
  this._sourceMode = !this._sourceMode;
1381
1817
  if (this._sourceMode) {
1382
1818
  this._dropBookmarks();
@@ -1385,6 +1821,7 @@ var JotterJS = class {
1385
1821
  this._source.style.display = "block";
1386
1822
  } else {
1387
1823
  this._editor.innerHTML = this._sanitize(this._source.value);
1824
+ this._ensureMediaShields();
1388
1825
  this._source.style.display = "none";
1389
1826
  this._editor.style.display = "";
1390
1827
  this._updateStatus();
@@ -1427,7 +1864,7 @@ var JotterJS = class {
1427
1864
  _sanitize(html) {
1428
1865
  const doc = new DOMParser().parseFromString(html, "text/html");
1429
1866
  doc.querySelectorAll("script").forEach((el) => el.remove());
1430
- doc.querySelectorAll("[data-jotter-bookmark]").forEach((el) => el.remove());
1867
+ doc.querySelectorAll("[data-jotter-bookmark], .jotter-media-shield").forEach((el) => el.remove());
1431
1868
  doc.querySelectorAll("*").forEach((el) => {
1432
1869
  Array.from(el.attributes).forEach((attr) => {
1433
1870
  if (attr.name.startsWith("on")) {
@@ -1551,12 +1988,15 @@ var JotterJS = class {
1551
1988
  // indicator or a host re-render. Empty marker <span>s move with the DOM
1552
1989
  // instead, so they still point at the right spot afterwards. They are stripped
1553
1990
  // from getHTML() and from _sanitize(), so hosts never see them.
1554
- /** Editable area's HTML, minus any live selection markers. */
1991
+ /**
1992
+ * Editable area's HTML, minus any live selection markers and media click-
1993
+ * shields. Always clones (no bookmark-free fast path): shields can be
1994
+ * present independently of bookmarks, so both must be stripped every time.
1995
+ */
1555
1996
  _richHTML() {
1556
- if (this._bookmarks.size === 0)
1557
- return this._editor.innerHTML;
1558
1997
  const clone = this._editor.cloneNode(true);
1559
- clone.querySelectorAll("[data-jotter-bookmark]").forEach((el) => el.remove());
1998
+ clone.querySelectorAll("[data-jotter-bookmark], .jotter-media-shield").forEach((el) => el.remove());
1999
+ clone.querySelectorAll("[data-jotter-move]").forEach((el) => el.removeAttribute("data-jotter-move"));
1560
2000
  return clone.innerHTML;
1561
2001
  }
1562
2002
  _makeMarker(id) {
@@ -1663,6 +2103,7 @@ var JotterJS = class {
1663
2103
  return this;
1664
2104
  }
1665
2105
  clear() {
2106
+ this._deselectMedia();
1666
2107
  this._dropBookmarks();
1667
2108
  this._editor.innerHTML = "";
1668
2109
  this._updateStatus();
@@ -1787,8 +2228,10 @@ var JotterJS = class {
1787
2228
  return this;
1788
2229
  }
1789
2230
  setHTML(html) {
2231
+ this._deselectMedia();
1790
2232
  this._dropBookmarks();
1791
2233
  this._editor.innerHTML = this._sanitize(html);
2234
+ this._ensureMediaShields();
1792
2235
  this._updateStatus();
1793
2236
  return this;
1794
2237
  }
@@ -1821,12 +2264,14 @@ var JotterJS = class {
1821
2264
  destroy() {
1822
2265
  const html = this.getHTML();
1823
2266
  this._destroyed = true;
2267
+ this._deselectMedia();
1824
2268
  this._hidePopup();
1825
2269
  this._dropBookmarks();
1826
2270
  this._externalBookmark = null;
1827
2271
  this._externalDepth = 0;
1828
2272
  document.removeEventListener("mousedown", this._onDocMouseDown);
1829
2273
  document.removeEventListener("keydown", this._onDocKeyDown);
2274
+ window.removeEventListener("resize", this._onWindowResize);
1830
2275
  if (this._popup.parentNode)
1831
2276
  this._popup.parentNode.removeChild(this._popup);
1832
2277
  this._target.classList.remove("jotter-host");
@@ -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}.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
+ @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{position:relative;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;z-index:1;pointer-events:none}.jotter-editor .jotter-embed-wrap{position:relative;display:block;width:100%;margin:.75em 0}.jotter-editor .jotter-embed-wrap>:not(.jotter-media-shield){pointer-events:none}.jotter-media-frame{position:absolute;display:none;box-sizing:border-box;border:2px solid var(--jotter-accent);border-radius:2px;pointer-events:auto;cursor:move;z-index:5}.jotter-media-frame--visible{display:block}.jotter-media-frame--dragging{border-style:dashed;opacity:.7}.jotter-resize-handle{position:absolute;width:10px;height:10px;background:var(--jotter-bg);border:2px solid var(--jotter-accent);border-radius:50%;pointer-events:auto;transform:translate(-50%,-50%)}.jotter-resize-handle--nw{top:0;left:0;cursor:nwse-resize}.jotter-resize-handle--ne{top:0;left:100%;cursor:nesw-resize}.jotter-resize-handle--sw{top:100%;left:0;cursor:nesw-resize}.jotter-resize-handle--se{top:100%;left:100%;cursor:nwse-resize}.jotter-resize-handle--w{top:50%;left:0;cursor:ew-resize}.jotter-resize-handle--e{top:50%;left:100%;cursor:ew-resize}.jotter--source-mode .jotter-media-frame{display:none!important}.jotter-media-shield{position:absolute;inset:0;z-index:2;background:transparent;cursor:pointer}.jotter-drop-indicator{position:absolute;display:none;width:2px;background:var(--jotter-accent);border-radius:1px;pointer-events:none;z-index:6}.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 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,`
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=_,m={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(m).forEach(Object.freeze);Object.freeze(m);var y=m.full,S={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"}],M=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],x=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],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"],T=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],C=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],w=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],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._selectedMedia=null,this._resizeState=null,this._moveState=null,this._suppressAutoEmit=!1,this._onResizeMove=this._onResizeMove.bind(this),this._onResizeEnd=this._onResizeEnd.bind(this),this._onMoveMove=this._onMoveMove.bind(this),this._onMoveUp=this._onMoveUp.bind(this),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");try{document.execCommand("enableObjectResizing",!1,!1)}catch{}try{document.execCommand("enableInlineTableEditing",!1,!1)}catch{}this._ensureMediaShields(),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._mediaFrame=this._buildMediaFrame(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._editorWrap.appendChild(this._mediaFrame),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||y).forEach(t=>{let i=this._buildAction(t);i&&e.appendChild(i)}),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}_bindActivation(e,t){e.addEventListener("mousedown",i=>{i.preventDefault(),t(!1)}),e.addEventListener("keydown",i=>{i.key!=="Enter"&&i.key!==" "&&i.key!=="Spacebar"||i.repeat||(i.preventDefault(),t(!0))})}_returnFocus(e,t){t&&this._isInternalTarget(document.activeElement)&&e.focus()}_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 i=document.createElement("span");i.className="material-icons",i.textContent=e.icon,t.appendChild(i)}return this._bindActivation(t,i=>{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 o=window.prompt(e.prompt);o&&document.execCommand(e.cmd,!1,o)}else document.execCommand(e.cmd,!1,null)}),this._updateToolbarState(),this._returnFocus(t,i)}),t}_bindSelectCaret(e){let t={viaKeyboard:!1};return e.addEventListener("mousedown",()=>{t.viaKeyboard=!1,this._saveBookmark()}),e.addEventListener("keydown",i=>{i.key==="Tab"||i.key==="Escape"||(t.viaKeyboard=!0,this._savedBookmark==null&&this._saveBookmark())}),t}_buildBlockFormatSelect(){let e=document.createElement("select");e.className="jotter-select",e.title="Block format",e.dataset.id="blockformat",v.forEach(({label:i,tag:o})=>{let s=document.createElement("option");s.value=o,s.textContent=i,e.appendChild(s)});let t=this._bindSelectCaret(e);return e.addEventListener("change",()=>{this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("formatBlock",!1,e.value)),this._returnFocus(e,t.viaKeyboard)}),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");t.value="",t.textContent="Font",e.appendChild(t),M.forEach(o=>{let s=document.createElement("option");s.value=o,s.textContent=o,s.style.fontFamily=o,e.appendChild(s)});let i=this._bindSelectCaret(e);return e.addEventListener("change",()=>{e.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand("fontName",!1,e.value)),this._returnFocus(e,i.viaKeyboard))}),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");t.value="",t.textContent="Size",e.appendChild(t),x.forEach(o=>{let s=document.createElement("option");s.value=o,s.textContent=`${o}px`,e.appendChild(s)});let i=this._bindSelectCaret(e);return e.addEventListener("change",()=>{e.value&&(this._restoreSavedBookmark(),this._applyEdit(()=>this._applyFontSize(e.value)),this._returnFocus(e,i.viaKeyboard))}),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:i})=>{let o=document.createElement("option");o.value=t,o.textContent=i,e.appendChild(o)}),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 i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=e.cmd,i.title=e.title,i.setAttribute("aria-label",e.title);let o=document.createElement("span");o.className="material-icons",o.textContent=e.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let r=e.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=r,i.appendChild(s);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 h=a.value;s.style.background=h,e.cmd==="foreColor"?this._lastForeColor=h:this._lastHiliteColor=h,this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand(e.cmd,!1,h)),this._returnFocus(i,u)}),this._bindActivation(i,h=>{u=h,this._saveBookmark(),a.click()}),t.appendChild(i),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 i=document.createElement("span");return i.className="material-icons",i.textContent=e.icon,t.appendChild(i),this._bindActivation(t,o=>{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),o&&this._focusPopup()}),t}_focusPopup(){let e=this._popup.querySelector('input, textarea, select, button, [tabindex]:not([tabindex="-1"])');e&&e.focus()}_buildPopupContainer(){let e=document.createElement("div");return e.className="jotter-popup",e.setAttribute("role","dialog"),e}_showPopup(e,t,i){this._popup.innerHTML="",this._popup.appendChild(t),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=e.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_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 i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`,s.setAttribute("role","grid"),s.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=(d,l)=>{r.textContent=`${d+1} \xD7 ${l+1} table`,a.forEach(c=>{c.classList.toggle("jotter-table-cell--active",+c.dataset.r<=d&&+c.dataset.c<=l)})};for(let d=0;d<o;d++)for(let l=0;l<i;l++){let c=document.createElement("span");c.className="jotter-table-cell",c.dataset.r=d,c.dataset.c=l,c.setAttribute("role","gridcell"),c.setAttribute("aria-label",`${d+1} by ${l+1} table`),c.tabIndex=d===0&&l===0?0:-1,c.addEventListener("mouseenter",()=>u(d,l)),c.addEventListener("focus",()=>u(d,l)),c.addEventListener("click",()=>this._insertTable(d+1,l+1)),a.push(c),s.appendChild(c)}let h={ArrowRight:[0,1],ArrowLeft:[0,-1],ArrowDown:[1,0],ArrowUp:[-1,0]};return s.addEventListener("keydown",d=>{let l=a.indexOf(document.activeElement)===-1?null:document.activeElement;if(!l)return;let c=+l.dataset.r,g=+l.dataset.c;if(d.key==="Enter"||d.key===" "||d.key==="Spacebar"){d.preventDefault(),this._insertTable(c+1,g+1);return}let f=h[d.key];if(!f)return;d.preventDefault();let E=Math.min(o-1,Math.max(0,c+f[0])),k=Math.min(i-1,Math.max(0,g+f[1])),b=a[E*i+k];b!==l&&(l.tabIndex=-1,b.tabIndex=0,b.focus())}),e.appendChild(s),e.appendChild(r),e}_commitPopup(e){this._restoreSavedBookmark(),this._hidePopup(),this._applyEdit(e)}_insertTable(e,t){let i="<table><tbody>";for(let o=0;o<e;o++){i+="<tr>";for(let s=0;s<t;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",this._commitPopup(()=>document.execCommand("insertHTML",!1,i))}_popupLink(){let e=document.createElement("div");e.className="jotter-popup-inner jotter-popup-form",e.appendChild(this._popupTitle("Insert Link"));let t=this._anchorInSelection(),i=this._selectedText(),o=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 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(([h,d])=>{let l=document.createElement("option");l.value=d,l.textContent=h,u.appendChild(l)}),e.appendChild(a),e.appendChild(u),t?(o.value=t.getAttribute("href")||"",s.value=t.textContent||"",r.value=t.getAttribute("title")||"",u.value=t.getAttribute("target")||""):i&&(s.value=i),e.appendChild(this._makeSubmitBtn(t?"Update Link":"Insert Link",()=>{let h=o.value.trim();if(!h)return;let d=s.value.trim()||i||h,l=r.value.trim(),c=u.value;this._commitPopup(()=>{t?(t.href=h,c?t.target=c:t.removeAttribute("target"),l?t.title=l:t.removeAttribute("title"),t.textContent=d):document.execCommand("insertHTML",!1,this._linkHTML({href:h,text:d,title:l,target:c}))})})),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"),i=this._makeField(e,"Alt text","text","Descriptive text"),o=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:i.value.trim(),width:o.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 i=this._ytId(t.value.trim());if(!i){t.classList.add("jotter-input--error");return}t.classList.remove("jotter-input--error"),this._commitPopup(()=>document.execCommand("insertHTML",!1,this._videoHTML(i)))})),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 i=e.match(t);if(i)return i[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 i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,e.appendChild(t),e.appendChild(i),e.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&this._commitPopup(()=>document.execCommand("insertHTML",!1,this._embedHTML(o)))})),e}_popupSymbol(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Insert Symbol")),e.appendChild(this._charGrid(L)),e}_popupSpecialChar(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Special Characters")),e.appendChild(this._charGrid(T)),e}_charGrid(e){let t=document.createElement("div");return t.className="jotter-char-grid",e.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,this._bindActivation(o,()=>{this._commitPopup(()=>document.execCommand("insertText",!1,i))}),t.appendChild(o)}),t}_popupLorem(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Insert Lorem Ipsum")),w.forEach(t=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=t.label,this._bindActivation(i,()=>{this._commitPopup(()=>document.execCommand(t.isHTML?"insertHTML":"insertText",!1,t.text))}),e.appendChild(i)}),e}_popupTitle(e){let t=document.createElement("div");return t.className="jotter-popup-title",t.textContent=e,t}_makeField(e,t,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=t;let r=document.createElement("input");return r.type=i,r.className="jotter-popup-input",r.placeholder=o,e.appendChild(s),e.appendChild(r),r}_makeSubmitBtn(e,t){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=e,i.addEventListener("click",t),i}_esc(e){return String(e).replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}_imageHTML({src:e,alt:t,width:i}){let o=i?`max-width:${i}`:"max-width:100%";return`<img src="${this._esc(e)}" alt="${this._esc(t||"")}" style="${this._esc(o)}">`}_linkHTML({href:e,text:t,title:i,target:o}){let s=`href="${this._esc(e)}"`;return o&&(s+=` target="${this._esc(o)}"`),i&&(s+=` title="${this._esc(i)}"`),`<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`<div class="jotter-embed-wrap">${e}</div><p><br></p>`}_resolverFor(e){let t=S[e],i=t?this._options[t]:null;return typeof i=="function"?i:null}async _runResolver(e,t){let i=this._resolverContext(e);this.beginExternalUI();let o=null;try{o=await t(i)}catch{o=null}if(this._destroyed)return;let s=o==null||o===!1?null:this._resolvedHTML(e,o,i);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 i=this._selectedText();return{href:"",text:i,title:"",target:"",selection:i,isEdit:!1}}let t=window.getSelection();if(t){let i=document.createRange();i.selectNode(e),t.removeAllRanges(),t.addRange(i)}return{href:e.getAttribute("href")||"",text:e.textContent||"",title:e.getAttribute("title")||"",target:e.getAttribute("target")||"",selection:e.textContent||"",isEdit:!0}}_resolvedHTML(e,t,i){switch(e){case"image":{let o=typeof t=="string"?{src:t}:t,s=String(o.src||o.url||"").trim();return s?this._imageHTML({...o,src:s}):null}case"link":{let o=typeof t=="string"?{href:t}:t,s=String(o.href||o.url||"").trim();if(!s)return null;let r=String(o.text!=null?o.text:i.text||"").trim();return this._linkHTML({...o,href:s,text:r})}case"video":{let o=typeof t=="string"?{url:t}:t,s=o.id?String(o.id).trim():this._ytId(String(o.url||"").trim());return s?this._videoHTML(s):null}case"embed":{let o=String(typeof t=="string"?t:t.html||"").trim();return o?this._embedHTML(o):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}_buildMediaFrame(){let e=document.createElement("div");return e.className="jotter-media-frame",["nw","ne","sw","se","w","e"].forEach(t=>{let i=document.createElement("div");i.className="jotter-resize-handle jotter-resize-handle--"+t,i.dataset.handle=t,i.tabIndex=-1,e.appendChild(i)}),e.addEventListener("mousedown",t=>{if(!this._selectedMedia)return;t.preventDefault();let i=t.target.closest(".jotter-resize-handle");i?this._beginResize(i.dataset.handle,t):this._beginPotentialMove(this._selectedMedia,t)}),e}_ensureMediaShields(){this._editor.querySelectorAll(".jotter-video-wrap, .jotter-embed-wrap").forEach(e=>{if(e.querySelector(":scope > .jotter-media-shield"))return;let t=document.createElement("div");t.className="jotter-media-shield",e.appendChild(t)})}_mediaElementFromTarget(e){if(!this._editor.contains(e))return null;let t=e.closest("img");if(t&&this._editor.contains(t))return t;let i=e.closest(".jotter-video-wrap, .jotter-embed-wrap");return i&&this._editor.contains(i)?i:null}_selectMedia(e){if(this._selectedMedia===e)return;this._deselectMedia(),this._selectedMedia=e,this._positionMediaFrame(),this._mediaFrame.classList.add("jotter-media-frame--visible"),this._editor.focus({preventScroll:!0});let t=document.createRange();t.selectNode(e);let i=window.getSelection();i.removeAllRanges(),i.addRange(t),this._updateToolbarState()}_deselectMedia(){this._resizeState&&this._abortResize(),this._moveState&&this._abortMove(),this._selectedMedia&&(this._selectedMedia=null,this._mediaFrame.classList.remove("jotter-media-frame--visible"))}_positionMediaFrame(){let e=this._selectedMedia;if(!e)return;let t=this._editorWrap.getBoundingClientRect(),i=e.getBoundingClientRect();this._mediaFrame.style.top=i.top-t.top+this._editorWrap.scrollTop+"px",this._mediaFrame.style.left=i.left-t.left+this._editorWrap.scrollLeft+"px",this._mediaFrame.style.width=i.width+"px",this._mediaFrame.style.height=i.height+"px"}_deleteSelectedMedia(){let e=this._selectedMedia;if(!e)return;this._deselectMedia();let t=document.createRange();t.selectNode(e);let i=window.getSelection();i.removeAllRanges(),i.addRange(t),this._applyEdit(()=>document.execCommand("delete",!1,null)),this._updateToolbarState()}_beginResize(e,t){let i=this._selectedMedia,o=i.getBoundingClientRect(),s=e.includes("w")?-1:1;this._resizeState={el:i,sign:s,startX:t.clientX,startWidth:o.width,startInlineWidth:i.style.width,before:this._richHTML()},document.addEventListener("mousemove",this._onResizeMove),document.addEventListener("mouseup",this._onResizeEnd,{once:!0})}_onResizeMove(e){let t=this._resizeState;if(!t)return;let i=(e.clientX-t.startX)*t.sign,o=40,s=Math.max(o,this._editor.clientWidth),r=Math.max(o,Math.min(s,Math.round(t.startWidth+i)));t.el.style.width=r+"px",t.el.style.removeProperty("max-width"),this._positionMediaFrame()}_onResizeEnd(){document.removeEventListener("mousemove",this._onResizeMove);let e=this._resizeState;this._resizeState=null,e&&(this._updateStatus(),this._richHTML()!==e.before&&this._emitChange())}_abortResize(){let e=this._resizeState;e&&(document.removeEventListener("mousemove",this._onResizeMove),document.removeEventListener("mouseup",this._onResizeEnd),e.el.style.width=e.startInlineWidth,this._resizeState=null,this._positionMediaFrame())}_beginPotentialMove(e,t){this._moveState={el:e,startX:t.clientX,startY:t.clientY,dragging:!1},document.addEventListener("mousemove",this._onMoveMove),document.addEventListener("mouseup",this._onMoveUp,{once:!0})}_onMoveMove(e){let t=this._moveState;if(t){if(!t.dragging){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<5)return;t.dragging=!0,t.before=this._richHTML(),this._mediaFrame.classList.add("jotter-media-frame--dragging"),document.body.style.cursor="grabbing"}t.dropRange=this._rangeFromPoint(e.clientX,e.clientY),this._showDropIndicator(t.dropRange)}}_onMoveUp(){document.removeEventListener("mousemove",this._onMoveMove);let e=this._moveState;this._moveState=null,this._mediaFrame.classList.remove("jotter-media-frame--dragging"),document.body.style.cursor="",this._hideDropIndicator(),e&&e.dragging&&this._commitMove(e.el,e.dropRange,e.before)}_abortMove(){this._moveState&&(document.removeEventListener("mousemove",this._onMoveMove),document.removeEventListener("mouseup",this._onMoveUp),this._moveState=null,this._mediaFrame.classList.remove("jotter-media-frame--dragging"),document.body.style.cursor="",this._hideDropIndicator())}_rangeFromPoint(e,t){let i=null;if(document.caretRangeFromPoint)i=document.caretRangeFromPoint(e,t);else if(document.caretPositionFromPoint){let s=document.caretPositionFromPoint(e,t);s&&(i=document.createRange(),i.setStart(s.offsetNode,s.offset),i.collapse(!0))}if(!i||!this._editor.contains(i.startContainer))return null;let o=this._moveState;return o&&o.el.contains(i.startContainer)?null:i}_buildDropIndicator(){let e=document.createElement("div");return e.className="jotter-drop-indicator",e}_showDropIndicator(e){this._dropIndicator||(this._dropIndicator=this._buildDropIndicator(),this._editorWrap.appendChild(this._dropIndicator));let t=e&&e.getClientRects()[0];if(!t){this._dropIndicator.style.display="none";return}let i=this._editorWrap.getBoundingClientRect();this._dropIndicator.style.display="block",this._dropIndicator.style.top=t.top-i.top+this._editorWrap.scrollTop+"px",this._dropIndicator.style.left=t.left-i.left+this._editorWrap.scrollLeft+"px",this._dropIndicator.style.height=(t.height||20)+"px"}_hideDropIndicator(){this._dropIndicator&&(this._dropIndicator.style.display="none")}_commitMove(e,t,i){if(!t||!t.startContainer.isConnected)return;let o="jmv"+ ++this._bmSeq,s=e.cloneNode(!0);s.querySelectorAll(".jotter-media-shield").forEach(h=>h.remove()),s.setAttribute("data-jotter-move",o),this._suppressAutoEmit=!0;let r=window.getSelection(),a=document.createRange();a.selectNode(e),r.removeAllRanges(),r.addRange(a),document.execCommand("delete",!1,null),r.removeAllRanges(),r.addRange(t),document.execCommand("insertHTML",!1,s.outerHTML);let u=this._editor.querySelector(`[data-jotter-move="${o}"]`);u&&u.removeAttribute("data-jotter-move"),this._suppressAutoEmit=!1,this._updateStatus(),u?this._selectMedia(u):this._deselectMedia(),this._richHTML()!==i&&this._emitChange()}_writeMediaToClipboard(e,t){let i=t.cloneNode(!0);i.querySelectorAll(".jotter-media-shield").forEach(o=>o.remove()),e.clipboardData.setData("text/html",i.outerHTML),e.clipboardData.setData("text/plain",t.tagName==="IMG"?t.alt||"":t.textContent||"")}_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(),i=null;if(t&&t.rangeCount){let s=t.getRangeAt(0);s.startContainer===e&&(i=s.startOffset)}let o=document.createElement("p");if(e.replaceWith(o),o.appendChild(e),i!==null){let s=document.createRange();s.setStart(e,i),s.collapse(!0),t.removeAllRanges(),t.addRange(s)}}}),this._ensureMediaShields(),this._selectedMedia&&this._positionMediaFrame(),this._updateStatus(),this._suppressAutoEmit||this._emitChange()}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("mousedown",e=>{let t=this._mediaElementFromTarget(e.target);t&&(e.preventDefault(),this._selectMedia(t),this._beginPotentialMove(t,e))}),this._editor.addEventListener("copy",e=>{this._selectedMedia&&(e.preventDefault(),this._writeMediaToClipboard(e,this._selectedMedia))}),this._editor.addEventListener("cut",e=>{this._selectedMedia&&(e.preventDefault(),this._writeMediaToClipboard(e,this._selectedMedia),this._deleteSelectedMedia())}),this._editor.addEventListener("paste",e=>{e.preventDefault();let t=(e.clipboardData||window.clipboardData).getData("text/html");if(t)this._applyEdit(()=>document.execCommand("insertHTML",!1,this._sanitize(t)));else{let i=(e.clipboardData||window.clipboardData).getData("text/plain");i&&this._applyEdit(()=>document.execCommand("insertText",!1,i))}}),this._onWindowResize=()=>{this._selectedMedia&&this._positionMediaFrame()},window.addEventListener("resize",this._onWindowResize),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._deselectMedia(),!(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=>{if((e.key==="Backspace"||e.key==="Delete")&&this._selectedMedia){e.preventDefault(),this._deleteSelectedMedia();return}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(),this._selectedMedia&&!this._mediaFrame.contains(e.target)&&e.target!==this._selectedMedia&&!this._selectedMedia.contains(e.target)&&this._deselectMedia()},document.addEventListener("mousedown",this._onDocMouseDown),this._onDocKeyDown=e=>{if(e.key==="Escape"){if(this._resizeState){this._abortResize();return}if(this._moveState&&this._moveState.dragging){this._abortMove();return}if(this._selectedMedia){this._deselectMedia();return}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._deselectMedia(),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._ensureMediaShields(),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,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return e.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+)/),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};
4
+ `).map(r=>r.trim()).filter(r=>r.length>0).map(r=>{let a=r.match(/^<\/(\w+)/),u=r.match(/^<(\w+)/),h=r.endsWith("/>"),d=u?u[1].toLowerCase():null,l=a?a[1].toLowerCase():null;l&&!s.has(l)&&(t=Math.max(0,t-1));let c=i.repeat(t)+r;return d&&!h&&!o.has(d)&&!l&&!s.has(d)&&t++,c}).join(`
5
+ `)}_sanitize(e){let t=new DOMParser().parseFromString(e,"text/html");return t.querySelectorAll("script").forEach(i=>i.remove()),t.querySelectorAll("[data-jotter-bookmark], .jotter-media-shield").forEach(i=>i.remove()),t.querySelectorAll("*").forEach(i=>{Array.from(i.attributes).forEach(o=>{(o.name.startsWith("on")||["href","src","action","formaction","data"].includes(o.name)&&/^\s*javascript:/i.test(o.value))&&i.removeAttribute(o.name)})}),t.body.innerHTML}_toggleInlineCode(){let e=window.getSelection();if(!e||e.rangeCount===0)return;let t=e.getRangeAt(0),i=t.commonAncestorContainer;i.nodeType===3&&(i=i.parentNode);let o=i.closest?i.closest("code"):null;if(o){let s=o.parentNode;for(;o.firstChild;)s.insertBefore(o.firstChild,o);s.removeChild(o)}else if(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 i=t.getRangeAt(0);if(i.collapsed)return;let o=document.createElement("span");o.style.fontSize=e+"px";try{i.surroundContents(o)}catch{let r=i.extractContents();o.appendChild(r),i.insertNode(o)}}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,""),i=v.find(o=>o.tag===t);i&&(e.value=i.tag)}}_updateStatus(){let e=this._editor.innerText||"",t=e.trim()===""?0:e.trim().split(/\s+/).length,i=e.replace(/\n/g,"").length;this._wordCountEl.textContent=`${t} word${t!==1?"s":""}`,this._charCountEl.textContent=`${i} char${i!==1?"s":""}`}_richHTML(){let e=this._editor.cloneNode(!0);return e.querySelectorAll("[data-jotter-bookmark], .jotter-media-shield").forEach(t=>t.remove()),e.querySelectorAll("[data-jotter-move]").forEach(t=>t.removeAttribute("data-jotter-move")),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:i,end:o}=t,s=this._editor.contains(i)&&(!o||this._editor.contains(o)),r=null;return s&&(r=document.createRange(),r.setStartAfter(i),o?r.setEndBefore(o):r.collapse(!0)),i.parentNode&&i.remove(),o&&o.parentNode&&o.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(i=>i(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,i=this._richHTML();e(),this._updateStatus(),this._changeCount===t&&this._richHTML()!==i&&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._deselectMedia(),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 i="jbm"+ ++this._bmSeq;if(this._editor.childNodes.length===0)return this._bookmarks.set(i,{atStart:!0}),i;let o=this._makeMarker(i),s=t.collapsed?null:this._makeMarker(i);if(s){let u=t.cloneRange();u.collapse(!1),u.insertNode(s)}let r=t.cloneRange();r.collapse(!0),r.insertNode(o),this._bookmarks.set(i,{start:o,end:s});let a=document.createRange();return a.setStartAfter(o),s?a.setEndBefore(s):a.collapse(!0),e.removeAllRanges(),e.addRange(a),i}restoreSelection(e){let t=this._takeRange(e);if(!t)return this;this._sourceMode||this._editor.focus();let i=window.getSelection();return i.removeAllRanges(),i.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._deselectMedia(),this._dropBookmarks(),this._editor.innerHTML=this._sanitize(e),this._ensureMediaShields(),this._updateStatus(),this}setTheme(e){let i=C.find(o=>o.id===e)?e:"default";return this._editor.dataset.theme=i,this._options.theme=i,this._themeSelect&&(this._themeSelect.value=i),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(i=>i!==t)),this}destroy(){let e=this.getHTML();return this._destroyed=!0,this._deselectMedia(),this._hidePopup(),this._dropBookmarks(),this._externalBookmark=null,this._externalDepth=0,document.removeEventListener("mousedown",this._onDocMouseDown),document.removeEventListener("keydown",this._onDocKeyDown),window.removeEventListener("resize",this._onWindowResize),this._popup.parentNode&&this._popup.parentNode.removeChild(this._popup),this._target.classList.remove("jotter-host"),this._target.innerHTML=e,this._listeners={},e}};p.toolbar=m.full;p.actions=_;p.presets=m;var R=p;export{p as JotterJS,R as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jotterjs",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "A lightweight, vanilla JS rich-text editor component. No dependencies.",
5
5
  "main": "dist/jotter.min.js",
6
6
  "module": "dist/jotter.js",