jotterjs 0.6.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 +28 -0
- package/dist/jotter.iife.min.js +3 -3
- package/dist/jotter.js +216 -11
- package/dist/jotter.min.css +1 -1
- package/dist/jotter.min.js +3 -3
- package/package.json +1 -1
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
|
package/dist/jotter.iife.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var JotterJS=(()=>{var v=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var M=(h,e)=>{for(var t in e)v(h,t,{get:e[t],enumerable:!0})},w=(h,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of L(e))!T.call(h,o)&&o!==t&&v(h,o,{get:()=>e[o],enumerable:!(i=x(e,o))||i.enumerable});return h};var j=h=>w(v({},"__esModule",{value:!0}),h);var D={};M(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 N=_.full,R={image:"onRequestImage",link:"onRequestLink",video:"onRequestVideo",embed:"onRequestEmbed"},k=[{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"}],A=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],I=[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"],H=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],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>"}],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._onResizeMove=this._onResizeMove.bind(this),this._onResizeEnd=this._onResizeEnd.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||N).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",k.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),A.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),I.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",E.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 p=a.value;s.style.background=p,e.cmd==="foreColor"?this._lastForeColor=p:this._lastHiliteColor=p,this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand(e.cmd,!1,p)),this._returnFocus(i,u)}),this._bindActivation(i,p=>{u=p,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=(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<o;c++)for(let l=0;l<i;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),s.appendChild(d)}let p={ArrowRight:[0,1],ArrowLeft:[0,-1],ArrowDown:[1,0],ArrowUp:[-1,0]};return s.addEventListener("keydown",c=>{let l=a.indexOf(document.activeElement)===-1?null:document.activeElement;if(!l)return;let d=+l.dataset.r,C=+l.dataset.c;if(c.key==="Enter"||c.key===" "||c.key==="Spacebar"){c.preventDefault(),this._insertTable(d+1,C+1);return}let b=p[c.key];if(!b)return;c.preventDefault();let S=Math.min(o-1,Math.max(0,d+b[0])),y=Math.min(i-1,Math.max(0,C+b[1])),g=a[S*i+y];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(([p,c])=>{let l=document.createElement("option");l.value=c,l.textContent=p,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 p=o.value.trim();if(!p)return;let c=s.value.trim()||i||p,l=r.value.trim(),d=u.value;this._commitPopup(()=>{t?(t.href=p,d?t.target=d:t.removeAttribute("target"),l?t.title=l:t.removeAttribute("title"),t.textContent=c):document.execCommand("insertHTML",!1,this._linkHTML({href:p,text:c,title:l,target:d}))})})),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(H)),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")),F.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,""").replace(/</g,"<").replace(/>/g,">")}_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=R[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=>{let i=t.target.closest(".jotter-resize-handle");!i||!this._selectedMedia||(t.preventDefault(),this._beginResize(i.dataset.handle,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.setStartBefore(e),t.collapse(!0);let i=window.getSelection();i.removeAllRanges(),i.addRange(t),this._updateToolbarState()}_deselectMedia(){this._resizeState&&this._abortResize(),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())}_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._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._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," "))}),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._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,`
|
|
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,""").replace(/</g,"<").replace(/>/g,">")}_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," "))}),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+)/),
|
|
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="​",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=
|
|
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="​",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
|
@@ -360,8 +360,12 @@ var JotterJS = class {
|
|
|
360
360
|
this._lastHiliteColor = "#c8a96e";
|
|
361
361
|
this._selectedMedia = null;
|
|
362
362
|
this._resizeState = null;
|
|
363
|
+
this._moveState = null;
|
|
364
|
+
this._suppressAutoEmit = false;
|
|
363
365
|
this._onResizeMove = this._onResizeMove.bind(this);
|
|
364
366
|
this._onResizeEnd = this._onResizeEnd.bind(this);
|
|
367
|
+
this._onMoveMove = this._onMoveMove.bind(this);
|
|
368
|
+
this._onMoveUp = this._onMoveUp.bind(this);
|
|
365
369
|
this._init();
|
|
366
370
|
}
|
|
367
371
|
// ─── Init ─────────────────────────────────────────────────────────────────
|
|
@@ -1330,11 +1334,14 @@ var JotterJS = class {
|
|
|
1330
1334
|
frame.appendChild(h);
|
|
1331
1335
|
});
|
|
1332
1336
|
frame.addEventListener("mousedown", (e) => {
|
|
1333
|
-
|
|
1334
|
-
if (!handle || !this._selectedMedia)
|
|
1337
|
+
if (!this._selectedMedia)
|
|
1335
1338
|
return;
|
|
1336
1339
|
e.preventDefault();
|
|
1337
|
-
|
|
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);
|
|
1338
1345
|
});
|
|
1339
1346
|
return frame;
|
|
1340
1347
|
}
|
|
@@ -1360,7 +1367,14 @@ var JotterJS = class {
|
|
|
1360
1367
|
return wrap;
|
|
1361
1368
|
return null;
|
|
1362
1369
|
}
|
|
1363
|
-
/**
|
|
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
|
+
*/
|
|
1364
1378
|
_selectMedia(el) {
|
|
1365
1379
|
if (this._selectedMedia === el)
|
|
1366
1380
|
return;
|
|
@@ -1370,17 +1384,18 @@ var JotterJS = class {
|
|
|
1370
1384
|
this._mediaFrame.classList.add("jotter-media-frame--visible");
|
|
1371
1385
|
this._editor.focus({ preventScroll: true });
|
|
1372
1386
|
const range = document.createRange();
|
|
1373
|
-
range.
|
|
1374
|
-
range.collapse(true);
|
|
1387
|
+
range.selectNode(el);
|
|
1375
1388
|
const sel = window.getSelection();
|
|
1376
1389
|
sel.removeAllRanges();
|
|
1377
1390
|
sel.addRange(range);
|
|
1378
1391
|
this._updateToolbarState();
|
|
1379
1392
|
}
|
|
1380
|
-
/** Clears the current media selection (and aborts any in-flight resize). */
|
|
1393
|
+
/** Clears the current media selection (and aborts any in-flight resize or move). */
|
|
1381
1394
|
_deselectMedia() {
|
|
1382
1395
|
if (this._resizeState)
|
|
1383
1396
|
this._abortResize();
|
|
1397
|
+
if (this._moveState)
|
|
1398
|
+
this._abortMove();
|
|
1384
1399
|
if (!this._selectedMedia)
|
|
1385
1400
|
return;
|
|
1386
1401
|
this._selectedMedia = null;
|
|
@@ -1479,6 +1494,165 @@ var JotterJS = class {
|
|
|
1479
1494
|
this._resizeState = null;
|
|
1480
1495
|
this._positionMediaFrame();
|
|
1481
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
|
+
}
|
|
1482
1656
|
// ─── Events ───────────────────────────────────────────────────────────────
|
|
1483
1657
|
_bindEvents() {
|
|
1484
1658
|
this._editor.addEventListener("input", () => {
|
|
@@ -1514,15 +1688,41 @@ var JotterJS = class {
|
|
|
1514
1688
|
if (this._selectedMedia)
|
|
1515
1689
|
this._positionMediaFrame();
|
|
1516
1690
|
this._updateStatus();
|
|
1517
|
-
this.
|
|
1691
|
+
if (!this._suppressAutoEmit)
|
|
1692
|
+
this._emitChange();
|
|
1518
1693
|
});
|
|
1519
1694
|
this._editor.addEventListener("keyup", () => this._updateToolbarState());
|
|
1520
1695
|
this._editor.addEventListener("mouseup", () => this._updateToolbarState());
|
|
1521
1696
|
this._editor.addEventListener("mousedown", (e) => {
|
|
1522
1697
|
const media = this._mediaElementFromTarget(e.target);
|
|
1523
|
-
if (media)
|
|
1524
|
-
|
|
1525
|
-
|
|
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));
|
|
1526
1726
|
}
|
|
1527
1727
|
});
|
|
1528
1728
|
this._onWindowResize = () => {
|
|
@@ -1586,6 +1786,10 @@ var JotterJS = class {
|
|
|
1586
1786
|
this._abortResize();
|
|
1587
1787
|
return;
|
|
1588
1788
|
}
|
|
1789
|
+
if (this._moveState && this._moveState.dragging) {
|
|
1790
|
+
this._abortMove();
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1589
1793
|
if (this._selectedMedia) {
|
|
1590
1794
|
this._deselectMedia();
|
|
1591
1795
|
return;
|
|
@@ -1792,6 +1996,7 @@ var JotterJS = class {
|
|
|
1792
1996
|
_richHTML() {
|
|
1793
1997
|
const clone = this._editor.cloneNode(true);
|
|
1794
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"));
|
|
1795
2000
|
return clone.innerHTML;
|
|
1796
2001
|
}
|
|
1797
2002
|
_makeMarker(id) {
|
package/dist/jotter.min.css
CHANGED
|
@@ -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{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}.jotter-editor .jotter-embed-wrap{position:relative;display:block;width:100%;margin:.75em 0}.jotter-media-frame{position:absolute;display:none;box-sizing:border-box;border:2px solid var(--jotter-accent);border-radius:2px;pointer-events:none;z-index:5}.jotter-media-frame--visible{display:block}.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-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}
|
package/dist/jotter.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var _={source:{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},sep:{type:"sep"},blockformat:{type:"blockformat"},fontfamily:{type:"fontfamily"},fontsize:{type:"fontsize"},theme:{type:"theme"},undo:{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},redo:{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},bold:{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},italic:{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},underline:{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},strike:{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},subscript:{cmd:"subscript",icon:"subscript",title:"Subscript"},superscript:{cmd:"superscript",icon:"superscript",title:"Superscript"},code:{custom:"code",icon:"code",title:"Inline Code"},copy:{cmd:"copy",icon:"content_copy",title:"Copy"},cut:{cmd:"cut",icon:"content_cut",title:"Cut"},paste:{cmd:"paste",icon:"content_paste",title:"Paste"},clearFormat:{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},alignLeft:{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},alignCenter:{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},alignRight:{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},bullets:{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},numbered:{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},link:{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},unlink:{cmd:"unlink",icon:"link_off",title:"Remove Link"},foreColor:{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},hiliteColor:{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},image:{type:"popup",id:"image",icon:"image",title:"Insert Image"},video:{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},table:{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},embed:{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},symbol:{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},specialChar:{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},lorem:{type:"popup",id:"lorem",icon:"history_edu",title:"Insert Lorem Ipsum"}};Object.values(_).forEach(Object.freeze);Object.freeze(_);var n=_,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 S=m.full,y={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"}],x=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],L=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],T=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],M=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],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>"}],h=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._onResizeMove=this._onResizeMove.bind(this),this._onResizeEnd=this._onResizeEnd.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||S).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),x.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),L.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 p=a.value;s.style.background=p,e.cmd==="foreColor"?this._lastForeColor=p:this._lastHiliteColor=p,this._restoreSavedBookmark(),this._applyEdit(()=>document.execCommand(e.cmd,!1,p)),this._returnFocus(i,u)}),this._bindActivation(i,p=>{u=p,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=(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<o;c++)for(let l=0;l<i;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),s.appendChild(d)}let p={ArrowRight:[0,1],ArrowLeft:[0,-1],ArrowDown:[1,0],ArrowUp:[-1,0]};return s.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 k=Math.min(o-1,Math.max(0,d+f[0])),E=Math.min(i-1,Math.max(0,g+f[1])),b=a[k*i+E];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(([p,c])=>{let l=document.createElement("option");l.value=c,l.textContent=p,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 p=o.value.trim();if(!p)return;let c=s.value.trim()||i||p,l=r.value.trim(),d=u.value;this._commitPopup(()=>{t?(t.href=p,d?t.target=d:t.removeAttribute("target"),l?t.title=l:t.removeAttribute("title"),t.textContent=c):document.execCommand("insertHTML",!1,this._linkHTML({href:p,text:c,title:l,target:d}))})})),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(T)),e}_popupSpecialChar(){let e=document.createElement("div");return e.className="jotter-popup-inner",e.appendChild(this._popupTitle("Special Characters")),e.appendChild(this._charGrid(M)),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,""").replace(/</g,"<").replace(/>/g,">")}_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=y[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=>{let i=t.target.closest(".jotter-resize-handle");!i||!this._selectedMedia||(t.preventDefault(),this._beginResize(i.dataset.handle,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.setStartBefore(e),t.collapse(!0);let i=window.getSelection();i.removeAllRanges(),i.addRange(t),this._updateToolbarState()}_deselectMedia(){this._resizeState&&this._abortResize(),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())}_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._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._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," "))}),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._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,`
|
|
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,""").replace(/</g,"<").replace(/>/g,">")}_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," "))}),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+)/),
|
|
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="​",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.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}};
|
|
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="​",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};
|