jotterjs 0.1.0 → 0.1.1
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/dist/jotter.iife.min.js +1 -1
- package/dist/jotter.js +27 -0
- package/dist/jotter.min.js +1 -1
- package/package.json +1 -1
package/dist/jotter.iife.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var JotterJS=(()=>{var _=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var y=(p,t)=>{for(var e in t)_(p,e,{get:t[e],enumerable:!0})},E=(p,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of L(t))!v.call(p,o)&&o!==e&&_(p,o,{get:()=>t[o],enumerable:!(i=C(t,o))||i.enumerable});return p};var T=p=>E(_({},"__esModule",{value:!0}),p);var H={};y(H,{JotterJS:()=>u,default:()=>j});var b=[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},{type:"sep"},{cmd:"copy",icon:"content_copy",title:"Copy"},{cmd:"cut",icon:"content_cut",title:"Cut"},{cmd:"paste",icon:"content_paste",title:"Paste"},{type:"sep"},{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},{type:"sep"},{type:"blockformat"},{type:"fontfamily"},{type:"fontsize"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{cmd:"subscript",icon:"subscript",title:"Subscript"},{cmd:"superscript",icon:"superscript",title:"Superscript"},{custom:"code",icon:"code",title:"Inline Code"},{type:"sep"},{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},{type:"sep"},{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"},{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"},{type:"sep"},{type:"theme"},{type:"sep"},{type:"theme"}],g=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],S=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],x=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],k=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],M=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],f=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],w=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],u=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||b).forEach(e=>{let i=this._buildAction(e);i&&t.appendChild(i)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,e.appendChild(i)}return e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let o=window.prompt(t.prompt);o&&document.execCommand(t.cmd,!1,o)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",g.forEach(({label:e,tag:i})=>{let o=document.createElement("option");o.value=i,o.textContent=e,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),S.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=i,o.style.fontFamily=i,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),x.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=`${i}px`,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",f.forEach(({id:e,label:i})=>{let o=document.createElement("option");o.value=e,o.textContent=i,t.appendChild(o)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=t.cmd,i.title=t.title,i.setAttribute("aria-label",t.title);let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let n=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=n,i.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=n,a.tabIndex=-1,a.addEventListener("change",()=>{let r=a.value;s.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),i.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),a.click()}),e.appendChild(i),e.appendChild(a),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let i=document.createElement("span");return i.className="material-icons",i.textContent=t.icon,e.appendChild(i),e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,i){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=t.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`;let n=document.createElement("div");n.className="jotter-popup-hint",n.textContent="Hover to select size";let a=[];for(let r=0;r<o;r++)for(let c=0;c<i;c++){let l=document.createElement("span");l.className="jotter-table-cell",l.dataset.r=r,l.dataset.c=c,l.addEventListener("mouseenter",()=>{n.textContent=`${r+1} \xD7 ${c+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=r&&+d.dataset.c<=c)})}),l.addEventListener("click",()=>{this._insertTable(r+1,c+1),this._hidePopup()}),a.push(l),s.appendChild(l)}return t.appendChild(s),t.appendChild(n),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let i="<table><tbody>";for(let o=0;o<t;o++){i+="<tr>";for(let s=0;s<e;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,i),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,i="";if(this._savedRange){let c=window.getSelection();if(c&&c.rangeCount){i=c.toString();let l=c.anchorNode;for(;l&&l!==this._editor;){if(l.nodeName==="A"){e=l;break}l=l.parentNode}}}let o=this._makeField(t,"URL","url","https://"),s=this._makeField(t,"Link text (leave blank to keep selection)","text",""),n=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([c,l])=>{let d=document.createElement("option");d.value=l,d.textContent=c,r.appendChild(d)}),t.appendChild(a),t.appendChild(r),e?(o.value=e.getAttribute("href")||"",s.value=e.textContent||"",n.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):i&&(s.value=i),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let c=o.value.trim();if(!c)return;let l=s.value.trim()||i||c,d=n.value.trim(),h=r.value,m=`href="${this._esc(c)}"`;h&&(m+=` target="${this._esc(h)}"`),d&&(m+=` title="${this._esc(d)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=c,h?e.target=h:e.removeAttribute("target"),d?e.title=d:e.removeAttribute("title"),e.textContent=l):document.execCommand("insertHTML",!1,`<a ${m}>${this._esc(l)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),i=this._makeField(t,"Alt text","text","Descriptive text"),o=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=e.value.trim();if(!s)return;let n=i.value.trim(),a=o.value.trim(),r=a?`max-width:${a}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(s)}" alt="${this._esc(n)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let i=this._ytId(e.value.trim());if(!i){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let o=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${i}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let i=t.match(e);if(i)return i[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,t.appendChild(e),t.appendChild(i),t.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(k)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(M)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,o.addEventListener("mousedown",s=>{s.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(o)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),w.forEach(e=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=e.label,i.addEventListener("mousedown",o=>{o.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(i)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=e;let n=document.createElement("input");return n.type=i,n.className="jotter-popup-input",n.placeholder=o,t.appendChild(s),t.appendChild(n),n}_makeSubmitBtn(t,e){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=t,i.addEventListener("click",e),i}_esc(t){return t.replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," "))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
|
|
1
|
+
var JotterJS=(()=>{var _=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var v=(p,t)=>{for(var e in t)_(p,e,{get:t[e],enumerable:!0})},E=(p,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of L(t))!y.call(p,o)&&o!==e&&_(p,o,{get:()=>t[o],enumerable:!(i=C(t,o))||i.enumerable});return p};var T=p=>E(_({},"__esModule",{value:!0}),p);var H={};v(H,{JotterJS:()=>u,default:()=>j});var b=[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},{type:"sep"},{cmd:"copy",icon:"content_copy",title:"Copy"},{cmd:"cut",icon:"content_cut",title:"Cut"},{cmd:"paste",icon:"content_paste",title:"Paste"},{type:"sep"},{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},{type:"sep"},{type:"blockformat"},{type:"fontfamily"},{type:"fontsize"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{cmd:"subscript",icon:"subscript",title:"Subscript"},{cmd:"superscript",icon:"superscript",title:"Superscript"},{custom:"code",icon:"code",title:"Inline Code"},{type:"sep"},{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},{type:"sep"},{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"},{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"},{type:"sep"},{type:"theme"},{type:"sep"},{type:"theme"}],g=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],S=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],x=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],k=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],M=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],f=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],w=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],u=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||b).forEach(e=>{let i=this._buildAction(e);i&&t.appendChild(i)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,e.appendChild(i)}return e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let o=window.prompt(t.prompt);o&&document.execCommand(t.cmd,!1,o)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",g.forEach(({label:e,tag:i})=>{let o=document.createElement("option");o.value=i,o.textContent=e,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),S.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=i,o.style.fontFamily=i,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),x.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=`${i}px`,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",f.forEach(({id:e,label:i})=>{let o=document.createElement("option");o.value=e,o.textContent=i,t.appendChild(o)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=t.cmd,i.title=t.title,i.setAttribute("aria-label",t.title);let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let n=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=n,i.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=n,a.tabIndex=-1,a.addEventListener("change",()=>{let r=a.value;s.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),i.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),a.click()}),e.appendChild(i),e.appendChild(a),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let i=document.createElement("span");return i.className="material-icons",i.textContent=t.icon,e.appendChild(i),e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,i){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=t.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`;let n=document.createElement("div");n.className="jotter-popup-hint",n.textContent="Hover to select size";let a=[];for(let r=0;r<o;r++)for(let c=0;c<i;c++){let l=document.createElement("span");l.className="jotter-table-cell",l.dataset.r=r,l.dataset.c=c,l.addEventListener("mouseenter",()=>{n.textContent=`${r+1} \xD7 ${c+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=r&&+d.dataset.c<=c)})}),l.addEventListener("click",()=>{this._insertTable(r+1,c+1),this._hidePopup()}),a.push(l),s.appendChild(l)}return t.appendChild(s),t.appendChild(n),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let i="<table><tbody>";for(let o=0;o<t;o++){i+="<tr>";for(let s=0;s<e;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,i),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,i="";if(this._savedRange){let c=window.getSelection();if(c&&c.rangeCount){i=c.toString();let l=c.anchorNode;for(;l&&l!==this._editor;){if(l.nodeName==="A"){e=l;break}l=l.parentNode}}}let o=this._makeField(t,"URL","url","https://"),s=this._makeField(t,"Link text (leave blank to keep selection)","text",""),n=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([c,l])=>{let d=document.createElement("option");d.value=l,d.textContent=c,r.appendChild(d)}),t.appendChild(a),t.appendChild(r),e?(o.value=e.getAttribute("href")||"",s.value=e.textContent||"",n.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):i&&(s.value=i),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let c=o.value.trim();if(!c)return;let l=s.value.trim()||i||c,d=n.value.trim(),h=r.value,m=`href="${this._esc(c)}"`;h&&(m+=` target="${this._esc(h)}"`),d&&(m+=` title="${this._esc(d)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=c,h?e.target=h:e.removeAttribute("target"),d?e.title=d:e.removeAttribute("title"),e.textContent=l):document.execCommand("insertHTML",!1,`<a ${m}>${this._esc(l)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),i=this._makeField(t,"Alt text","text","Descriptive text"),o=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=e.value.trim();if(!s)return;let n=i.value.trim(),a=o.value.trim(),r=a?`max-width:${a}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(s)}" alt="${this._esc(n)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let i=this._ytId(e.value.trim());if(!i){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let o=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${i}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let i=t.match(e);if(i)return i[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,t.appendChild(e),t.appendChild(i),t.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(k)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(M)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,o.addEventListener("mousedown",s=>{s.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(o)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),w.forEach(e=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=e.label,i.addEventListener("mousedown",o=>{o.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(i)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=e;let n=document.createElement("input");return n.type=i,n.className="jotter-popup-input",n.placeholder=o,t.appendChild(s),t.appendChild(n),n}_makeSubmitBtn(t,e){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=t,i.addEventListener("click",e),i}_esc(t){return t.replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),i=null;if(e&&e.rangeCount){let s=e.getRangeAt(0);s.startContainer===t&&(i=s.startOffset)}let o=document.createElement("p");if(t.replaceWith(o),o.appendChild(t),i!==null){let s=document.createRange();s.setStart(t,i),s.collapse(!0),e.removeAllRanges(),e.addRange(s)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," "))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
|
|
2
2
|
$1
|
|
3
3
|
`).split(`
|
|
4
4
|
`).map(n=>n.trim()).filter(n=>n.length>0).map(n=>{let a=n.match(/^<\/(\w+)/),r=n.match(/^<(\w+)/),c=n.endsWith("/>"),l=r?r[1].toLowerCase():null,d=a?a[1].toLowerCase():null;d&&!s.has(d)&&(e=Math.max(0,e-1));let h=i.repeat(e)+n;return l&&!c&&!o.has(l)&&!d&&!s.has(l)&&e++,h}).join(`
|
package/dist/jotter.js
CHANGED
|
@@ -290,6 +290,7 @@ var JotterJS = class {
|
|
|
290
290
|
this._editor.style.minHeight = this._options.height;
|
|
291
291
|
this._editor.innerHTML = this._sanitize(initialHTML);
|
|
292
292
|
this._editor.spellcheck = true;
|
|
293
|
+
document.execCommand("defaultParagraphSeparator", false, "p");
|
|
293
294
|
this._source = document.createElement("textarea");
|
|
294
295
|
this._source.className = "jotter-source";
|
|
295
296
|
this._source.setAttribute("aria-label", "HTML source");
|
|
@@ -981,6 +982,32 @@ var JotterJS = class {
|
|
|
981
982
|
// ─── Events ───────────────────────────────────────────────────────────────
|
|
982
983
|
_bindEvents() {
|
|
983
984
|
this._editor.addEventListener("input", () => {
|
|
985
|
+
this._editor.querySelectorAll(":scope > div").forEach((d) => {
|
|
986
|
+
const p = document.createElement("p");
|
|
987
|
+
p.innerHTML = d.innerHTML;
|
|
988
|
+
d.replaceWith(p);
|
|
989
|
+
});
|
|
990
|
+
Array.from(this._editor.childNodes).forEach((node) => {
|
|
991
|
+
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== "") {
|
|
992
|
+
const sel = window.getSelection();
|
|
993
|
+
let caretOffset = null;
|
|
994
|
+
if (sel && sel.rangeCount) {
|
|
995
|
+
const r = sel.getRangeAt(0);
|
|
996
|
+
if (r.startContainer === node)
|
|
997
|
+
caretOffset = r.startOffset;
|
|
998
|
+
}
|
|
999
|
+
const p = document.createElement("p");
|
|
1000
|
+
node.replaceWith(p);
|
|
1001
|
+
p.appendChild(node);
|
|
1002
|
+
if (caretOffset !== null) {
|
|
1003
|
+
const r = document.createRange();
|
|
1004
|
+
r.setStart(node, caretOffset);
|
|
1005
|
+
r.collapse(true);
|
|
1006
|
+
sel.removeAllRanges();
|
|
1007
|
+
sel.addRange(r);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
984
1011
|
this._updateStatus();
|
|
985
1012
|
this._emit("change", this.getHTML());
|
|
986
1013
|
if (this._options.onChange)
|
package/dist/jotter.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var g=[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},{type:"sep"},{cmd:"copy",icon:"content_copy",title:"Copy"},{cmd:"cut",icon:"content_cut",title:"Cut"},{cmd:"paste",icon:"content_paste",title:"Paste"},{type:"sep"},{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},{type:"sep"},{type:"blockformat"},{type:"fontfamily"},{type:"fontsize"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{cmd:"subscript",icon:"subscript",title:"Subscript"},{cmd:"superscript",icon:"superscript",title:"Superscript"},{custom:"code",icon:"code",title:"Inline Code"},{type:"sep"},{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},{type:"sep"},{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"},{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"},{type:"sep"},{type:"theme"},{type:"sep"},{type:"theme"}],m=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],f=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],b=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],C=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],L=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],_=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],v=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],u=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||g).forEach(e=>{let i=this._buildAction(e);i&&t.appendChild(i)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,e.appendChild(i)}return e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let o=window.prompt(t.prompt);o&&document.execCommand(t.cmd,!1,o)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",m.forEach(({label:e,tag:i})=>{let o=document.createElement("option");o.value=i,o.textContent=e,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),f.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=i,o.style.fontFamily=i,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),b.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=`${i}px`,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",_.forEach(({id:e,label:i})=>{let o=document.createElement("option");o.value=e,o.textContent=i,t.appendChild(o)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=t.cmd,i.title=t.title,i.setAttribute("aria-label",t.title);let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let n=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=n,i.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=n,a.tabIndex=-1,a.addEventListener("change",()=>{let r=a.value;s.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),i.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),a.click()}),e.appendChild(i),e.appendChild(a),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let i=document.createElement("span");return i.className="material-icons",i.textContent=t.icon,e.appendChild(i),e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,i){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=t.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`;let n=document.createElement("div");n.className="jotter-popup-hint",n.textContent="Hover to select size";let a=[];for(let r=0;r<o;r++)for(let c=0;c<i;c++){let l=document.createElement("span");l.className="jotter-table-cell",l.dataset.r=r,l.dataset.c=c,l.addEventListener("mouseenter",()=>{n.textContent=`${r+1} \xD7 ${c+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=r&&+d.dataset.c<=c)})}),l.addEventListener("click",()=>{this._insertTable(r+1,c+1),this._hidePopup()}),a.push(l),s.appendChild(l)}return t.appendChild(s),t.appendChild(n),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let i="<table><tbody>";for(let o=0;o<t;o++){i+="<tr>";for(let s=0;s<e;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,i),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,i="";if(this._savedRange){let c=window.getSelection();if(c&&c.rangeCount){i=c.toString();let l=c.anchorNode;for(;l&&l!==this._editor;){if(l.nodeName==="A"){e=l;break}l=l.parentNode}}}let o=this._makeField(t,"URL","url","https://"),s=this._makeField(t,"Link text (leave blank to keep selection)","text",""),n=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([c,l])=>{let d=document.createElement("option");d.value=l,d.textContent=c,r.appendChild(d)}),t.appendChild(a),t.appendChild(r),e?(o.value=e.getAttribute("href")||"",s.value=e.textContent||"",n.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):i&&(s.value=i),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let c=o.value.trim();if(!c)return;let l=s.value.trim()||i||c,d=n.value.trim(),p=r.value,h=`href="${this._esc(c)}"`;p&&(h+=` target="${this._esc(p)}"`),d&&(h+=` title="${this._esc(d)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=c,p?e.target=p:e.removeAttribute("target"),d?e.title=d:e.removeAttribute("title"),e.textContent=l):document.execCommand("insertHTML",!1,`<a ${h}>${this._esc(l)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),i=this._makeField(t,"Alt text","text","Descriptive text"),o=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=e.value.trim();if(!s)return;let n=i.value.trim(),a=o.value.trim(),r=a?`max-width:${a}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(s)}" alt="${this._esc(n)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let i=this._ytId(e.value.trim());if(!i){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let o=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${i}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let i=t.match(e);if(i)return i[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,t.appendChild(e),t.appendChild(i),t.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(C)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(L)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,o.addEventListener("mousedown",s=>{s.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(o)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),v.forEach(e=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=e.label,i.addEventListener("mousedown",o=>{o.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(i)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=e;let n=document.createElement("input");return n.type=i,n.className="jotter-popup-input",n.placeholder=o,t.appendChild(s),t.appendChild(n),n}_makeSubmitBtn(t,e){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=t,i.addEventListener("click",e),i}_esc(t){return t.replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," "))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
|
|
1
|
+
var g=[{custom:"toggleSource",label:"Source",title:"Edit HTML Source"},{type:"sep"},{cmd:"undo",icon:"undo",title:"Undo (Ctrl+Z)"},{cmd:"redo",icon:"redo",title:"Redo (Ctrl+Y)"},{type:"sep"},{cmd:"copy",icon:"content_copy",title:"Copy"},{cmd:"cut",icon:"content_cut",title:"Cut"},{cmd:"paste",icon:"content_paste",title:"Paste"},{type:"sep"},{cmd:"removeFormat",icon:"format_clear",title:"Clear Formatting"},{type:"sep"},{type:"blockformat"},{type:"fontfamily"},{type:"fontsize"},{type:"sep"},{cmd:"bold",icon:"format_bold",title:"Bold (Ctrl+B)"},{cmd:"italic",icon:"format_italic",title:"Italic (Ctrl+I)"},{cmd:"underline",icon:"format_underlined",title:"Underline (Ctrl+U)"},{cmd:"strikeThrough",icon:"strikethrough_s",title:"Strikethrough"},{cmd:"subscript",icon:"subscript",title:"Subscript"},{cmd:"superscript",icon:"superscript",title:"Superscript"},{custom:"code",icon:"code",title:"Inline Code"},{type:"sep"},{type:"color",cmd:"foreColor",icon:"format_color_text",title:"Text Color"},{type:"color",cmd:"hiliteColor",icon:"format_color_fill",title:"Background Color"},{type:"sep"},{cmd:"justifyLeft",icon:"format_align_left",title:"Align Left"},{cmd:"justifyCenter",icon:"format_align_center",title:"Align Center"},{cmd:"justifyRight",icon:"format_align_right",title:"Align Right"},{type:"sep"},{cmd:"insertUnorderedList",icon:"format_list_bulleted",title:"Bullet List"},{cmd:"insertOrderedList",icon:"format_list_numbered",title:"Numbered List"},{type:"sep"},{type:"popup",id:"link",icon:"insert_link",title:"Insert Link"},{cmd:"unlink",icon:"link_off",title:"Remove Link"},{type:"sep"},{type:"popup",id:"image",icon:"image",title:"Insert Image"},{type:"popup",id:"video",icon:"smart_display",title:"Insert YouTube Video"},{type:"popup",id:"table",icon:"table_chart",title:"Insert Table"},{type:"popup",id:"embed",icon:"html",title:"Insert Embed"},{type:"popup",id:"symbol",icon:"emoji_symbols",title:"Insert Symbol"},{type:"popup",id:"specialchar",icon:"format_shapes",title:"Special Characters"},{type:"popup",id:"lorem",icon:"article",title:"Insert Lorem Ipsum"},{type:"sep"},{type:"theme"},{type:"sep"},{type:"theme"}],m=[{label:"Paragraph",tag:"p"},{label:"Heading 1",tag:"h1"},{label:"Heading 2",tag:"h2"},{label:"Heading 3",tag:"h3"},{label:"Heading 4",tag:"h4"},{label:"Pre / Code",tag:"pre"},{label:"Blockquote",tag:"blockquote"}],f=["Arial","Arial Black","Comic Sans MS","Courier New","Georgia","Impact","Lucida Console","Palatino Linotype","Tahoma","Times New Roman","Trebuchet MS","Verdana"],b=[8,9,10,11,12,14,16,18,20,24,28,32,36,48,72],C=["\u2190","\u2192","\u2191","\u2193","\u2194","\u2195","\u21D0","\u21D2","\u21D1","\u21D3","\u21D4","\u2022","\xB7","\u25E6","\u25CB","\u25CF","\u25A1","\u25A0","\u25C6","\u25C7","\u25B2","\u25BC","\u2605","\u2606","\u2660","\u2663","\u2665","\u2666","\u2713","\u2717","\u2715","\u2718","\u2248","\u2260","\u2261","\u2264","\u2265","\xF7","\xD7","\xB1","\u221E","\u221A","\u2211","\u220F","\u222B","\u2202","\u2206","\u2207","\u03C0","\u03A9","\u03BC","\u03B1","\u03B2","\u03B3","\xA9","\xAE","\u2122","\xA7","\xB6","\u2020","\u2021","\xB0","\u2032","\u2033","\u2030","\u201C","\u201D","\u2018","\u2019","\xAB","\xBB","\u2039","\u203A","\u2014","\u2013","\u2026","\xBF","\xA1","\u20AC","\xA3","\xA5","\xA2","\u20B9","\u20BD","\u20BF"],L=["\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6","\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE","\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE","\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6","\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE","\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE","\xFF","\u0152","\u0153","\u0160","\u0161","\u0178","\u017D","\u017E"],_=[{id:"default",label:"Default"},{id:"warm",label:"Warm"},{id:"ink",label:"Ink / Navy"},{id:"forest",label:"Forest"}],y=[{label:"Short \u2014 1 sentence",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit."},{label:"Medium \u2014 1 paragraph",text:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},{label:"Long \u2014 3 paragraphs",isHTML:!0,text:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>"}],u=class{constructor(t,e={}){if(this._target=typeof t=="string"?document.querySelector(t):t,!this._target)throw new Error("[JotterJS] Target element not found.");this._options=Object.assign({placeholder:"Start typing\u2026",height:"320px",theme:"default",onChange:null,onFocus:null,onBlur:null},e),this._listeners={},this._savedRange=null,this._lastForeColor="#e8e4d8",this._lastHiliteColor="#c8a96e",this._init()}_init(){let t=this._target.innerHTML||"";this._target.innerHTML="",this._target.classList.add("jotter-host"),this._root=document.createElement("div"),this._root.className="htmled",this._toolbar=this._buildToolbar(),this._editorWrap=document.createElement("div"),this._editorWrap.className="jotter-editor-wrap",this._editor=document.createElement("div"),this._editor.className="jotter-editor",this._editor.contentEditable="true",this._editor.setAttribute("data-placeholder",this._options.placeholder),this._editor.style.minHeight=this._options.height,this._editor.innerHTML=this._sanitize(t),this._editor.spellcheck=!0,document.execCommand("defaultParagraphSeparator",!1,"p"),this._source=document.createElement("textarea"),this._source.className="jotter-source",this._source.setAttribute("aria-label","HTML source"),this._source.setAttribute("spellcheck","false"),this._source.style.minHeight=this._options.height,this._sourceMode=!1,this._statusBar=this._buildStatusBar(),this._editorWrap.appendChild(this._editor),this._editorWrap.appendChild(this._source),this._root.appendChild(this._toolbar),this._root.appendChild(this._editorWrap),this._root.appendChild(this._statusBar),this._target.appendChild(this._root),this._popup=this._buildPopupContainer(),document.body.appendChild(this._popup),this._bindEvents(),this._updateToolbarState(),this._updateStatus(),this.setTheme(this._options.theme)}_buildToolbar(){let t=document.createElement("div");return t.className="jotter-toolbar",this._toolbarEl=t,(this._options.toolbar||g).forEach(e=>{let i=this._buildAction(e);i&&t.appendChild(i)}),t}_buildAction(t){switch(t.type){case"sep":return this._makeSep();case"blockformat":return this._buildBlockFormatSelect();case"fontfamily":return this._buildFontFamilySelect();case"fontsize":return this._buildFontSizeSelect();case"color":return this._buildColorBtn(t);case"popup":return this._buildPopupBtn(t);case"theme":return this._buildThemeSelect();default:return this._buildBtn(t)}}_makeSep(){let t=document.createElement("span");return t.className="jotter-sep",t}_buildBtn(t){let e=document.createElement("button");if(e.type="button",e.className="jotter-btn",t.cmd&&(e.dataset.cmd=t.cmd),t.custom&&(e.dataset.custom=t.custom),e.title=t.title,e.setAttribute("aria-label",t.title),t.label)e.classList.add("jotter-btn--text"),e.appendChild(document.createTextNode(t.label));else{let i=document.createElement("span");i.className="material-icons",i.textContent=t.icon,e.appendChild(i)}return e.addEventListener("mousedown",i=>{if(i.preventDefault(),this._editor.focus(),t.onClick)t.onClick(this);else if(t.custom==="toggleSource")this._toggleSourceMode();else if(t.custom==="code")this._toggleInlineCode();else if(t.cmd==="copy")document.execCommand("copy");else if(t.cmd==="cut")document.execCommand("cut");else if(t.cmd==="paste")this._pasteFromClipboard();else if(t.prompt){let o=window.prompt(t.prompt);o&&document.execCommand(t.cmd,!1,o)}else document.execCommand(t.cmd,!1,null);this._updateToolbarState(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e}_buildBlockFormatSelect(){let t=document.createElement("select");return t.className="jotter-select",t.title="Block format",t.dataset.id="blockformat",m.forEach(({label:e,tag:i})=>{let o=document.createElement("option");o.value=i,o.textContent=e,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{this._restoreRange(this._savedRange),document.execCommand("formatBlock",!1,t.value),this._editor.focus(),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t}_buildFontFamilySelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--font",t.title="Font family",t.dataset.id="fontfamily";let e=document.createElement("option");return e.value="",e.textContent="Font",t.appendChild(e),f.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=i,o.style.fontFamily=i,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),document.execCommand("fontName",!1,t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildFontSizeSelect(){let t=document.createElement("select");t.className="jotter-select jotter-select--size",t.title="Font size",t.dataset.id="fontsize";let e=document.createElement("option");return e.value="",e.textContent="Size",t.appendChild(e),b.forEach(i=>{let o=document.createElement("option");o.value=i,o.textContent=`${i}px`,t.appendChild(o)}),t.addEventListener("mousedown",()=>{this._savedRange=this._saveRange()}),t.addEventListener("change",()=>{t.value&&(this._restoreRange(this._savedRange),this._applyFontSize(t.value),this._editor.focus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))}),t}_buildThemeSelect(){let t=document.createElement("select");return t.className="jotter-select jotter-select--theme",t.title="Editor theme",t.dataset.id="theme",_.forEach(({id:e,label:i})=>{let o=document.createElement("option");o.value=e,o.textContent=i,t.appendChild(o)}),t.value=this._options.theme,t.addEventListener("change",()=>this.setTheme(t.value)),this._themeSelect=t,t}_buildColorBtn(t){let e=document.createElement("span");e.className="jotter-color-wrap";let i=document.createElement("button");i.type="button",i.className="jotter-btn jotter-color-btn",i.dataset.cmd=t.cmd,i.title=t.title,i.setAttribute("aria-label",t.title);let o=document.createElement("span");o.className="material-icons",o.textContent=t.icon,i.appendChild(o);let s=document.createElement("span");s.className="jotter-color-swatch";let n=t.cmd==="foreColor"?this._lastForeColor:this._lastHiliteColor;s.style.background=n,i.appendChild(s);let a=document.createElement("input");return a.type="color",a.className="jotter-color-input",a.value=n,a.tabIndex=-1,a.addEventListener("change",()=>{let r=a.value;s.style.background=r,t.cmd==="foreColor"?this._lastForeColor=r:this._lastHiliteColor=r,this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand(t.cmd,!1,r),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),i.addEventListener("mousedown",r=>{r.preventDefault(),this._savedRange=this._saveRange(),a.click()}),e.appendChild(i),e.appendChild(a),e}_buildPopupBtn(t){let e=document.createElement("button");e.type="button",e.className="jotter-btn",e.title=t.title,e.setAttribute("aria-label",t.title);let i=document.createElement("span");return i.className="material-icons",i.textContent=t.icon,e.appendChild(i),e.addEventListener("mousedown",o=>{if(o.preventDefault(),this._savedRange=this._saveRange(),this._popup.classList.contains("jotter-popup--visible")&&this._popup.dataset.popupId===t.id){this._hidePopup();return}this._showPopup(e,this._buildPopupContent(t.id),t.id)}),e}_buildPopupContainer(){let t=document.createElement("div");return t.className="jotter-popup",t.setAttribute("role","dialog"),t}_showPopup(t,e,i){this._popup.innerHTML="",this._popup.appendChild(e),this._popup.dataset.popupId=i,this._popup.classList.add("jotter-popup--visible");let o=t.getBoundingClientRect();this._popup.style.top=o.bottom+6+"px",this._popup.style.left=o.left+"px",this._popup.style.right="auto",requestAnimationFrame(()=>{let s=this._popup.getBoundingClientRect();s.right>window.innerWidth-8&&(this._popup.style.left=Math.max(8,o.left-(s.right-window.innerWidth+8))+"px")})}_hidePopup(){this._popup.classList.remove("jotter-popup--visible"),this._popup.dataset.popupId=""}_buildPopupContent(t){switch(t){case"link":return this._popupLink();case"table":return this._popupTable();case"image":return this._popupImage();case"video":return this._popupVideo();case"embed":return this._popupEmbed();case"symbol":return this._popupSymbol();case"specialchar":return this._popupSpecialChar();case"lorem":return this._popupLorem();default:{let e=document.createElement("div");return e.className="jotter-popup-inner",e.textContent="Unknown: "+t,e}}}_popupTable(){let t=document.createElement("div");t.className="jotter-popup-inner";let e=this._popupTitle("Insert Table");t.appendChild(e);let i=10,o=8,s=document.createElement("div");s.className="jotter-table-grid",s.style.gridTemplateColumns=`repeat(${i}, 1fr)`;let n=document.createElement("div");n.className="jotter-popup-hint",n.textContent="Hover to select size";let a=[];for(let r=0;r<o;r++)for(let c=0;c<i;c++){let l=document.createElement("span");l.className="jotter-table-cell",l.dataset.r=r,l.dataset.c=c,l.addEventListener("mouseenter",()=>{n.textContent=`${r+1} \xD7 ${c+1} table`,a.forEach(d=>{d.classList.toggle("jotter-table-cell--active",+d.dataset.r<=r&&+d.dataset.c<=c)})}),l.addEventListener("click",()=>{this._insertTable(r+1,c+1),this._hidePopup()}),a.push(l),s.appendChild(l)}return t.appendChild(s),t.appendChild(n),t}_insertTable(t,e){this._restoreRange(this._savedRange),this._editor.focus();let i="<table><tbody>";for(let o=0;o<t;o++){i+="<tr>";for(let s=0;s<e;s++)i+=o===0?"<th><br></th>":"<td><br></td>";i+="</tr>"}i+="</tbody></table><p><br></p>",document.execCommand("insertHTML",!1,i),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}_popupLink(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Link"));let e=null,i="";if(this._savedRange){let c=window.getSelection();if(c&&c.rangeCount){i=c.toString();let l=c.anchorNode;for(;l&&l!==this._editor;){if(l.nodeName==="A"){e=l;break}l=l.parentNode}}}let o=this._makeField(t,"URL","url","https://"),s=this._makeField(t,"Link text (leave blank to keep selection)","text",""),n=this._makeField(t,"Title / tooltip","text",""),a=document.createElement("label");a.className="jotter-popup-label",a.textContent="Open in";let r=document.createElement("select");return r.className="jotter-popup-select",[["(same window)",""],["New tab (_blank)","_blank"],["Parent frame (_parent)","_parent"],["Top frame (_top)","_top"]].forEach(([c,l])=>{let d=document.createElement("option");d.value=l,d.textContent=c,r.appendChild(d)}),t.appendChild(a),t.appendChild(r),e?(o.value=e.getAttribute("href")||"",s.value=e.textContent||"",n.value=e.getAttribute("title")||"",r.value=e.getAttribute("target")||""):i&&(s.value=i),t.appendChild(this._makeSubmitBtn(e?"Update Link":"Insert Link",()=>{let c=o.value.trim();if(!c)return;let l=s.value.trim()||i||c,d=n.value.trim(),p=r.value,h=`href="${this._esc(c)}"`;p&&(h+=` target="${this._esc(p)}"`),d&&(h+=` title="${this._esc(d)}"`),this._restoreRange(this._savedRange),this._editor.focus(),e?(e.href=c,p?e.target=p:e.removeAttribute("target"),d?e.title=d:e.removeAttribute("title"),e.textContent=l):document.execCommand("insertHTML",!1,`<a ${h}>${this._esc(l)}</a>`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupImage(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Image"));let e=this._makeField(t,"Image URL","text","https://example.com/image.jpg"),i=this._makeField(t,"Alt text","text","Descriptive text"),o=this._makeField(t,"Width (e.g. 400px or 50%)","text","");return t.appendChild(this._makeSubmitBtn("Insert Image",()=>{let s=e.value.trim();if(!s)return;let n=i.value.trim(),a=o.value.trim(),r=a?`max-width:${a}`:"max-width:100%";this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,`<img src="${this._esc(s)}" alt="${this._esc(n)}" style="${r}">`),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_popupVideo(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert YouTube Video"));let e=this._makeField(t,"YouTube URL","text","https://www.youtube.com/watch?v=...");return t.appendChild(this._makeSubmitBtn("Embed Video",()=>{let i=this._ytId(e.value.trim());if(!i){e.classList.add("jotter-input--error");return}e.classList.remove("jotter-input--error");let o=`<div class="jotter-video-wrap"><iframe src="https://www.youtube.com/embed/${i}" frameborder="0" allowfullscreen loading="lazy" title="YouTube video"></iframe></div><p><br></p>`;this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())})),t}_ytId(t){for(let e of[/[?&]v=([A-Za-z0-9_-]{11})/,/youtu\.be\/([A-Za-z0-9_-]{11})/,/embed\/([A-Za-z0-9_-]{11})/]){let i=t.match(e);if(i)return i[1]}return null}_popupEmbed(){let t=document.createElement("div");t.className="jotter-popup-inner jotter-popup-form",t.appendChild(this._popupTitle("Insert Embed"));let e=document.createElement("label");e.className="jotter-popup-label",e.textContent="Paste HTML / embed code";let i=document.createElement("textarea");return i.className="jotter-popup-textarea",i.placeholder='<iframe src="..." ...></iframe>',i.rows=4,t.appendChild(e),t.appendChild(i),t.appendChild(this._makeSubmitBtn("Insert",()=>{let o=i.value.trim();o&&(this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertHTML",!1,o+"<p><br></p>"),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML()))})),t}_popupSymbol(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Symbol")),t.appendChild(this._charGrid(C)),t}_popupSpecialChar(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Special Characters")),t.appendChild(this._charGrid(L)),t}_charGrid(t){let e=document.createElement("div");return e.className="jotter-char-grid",t.forEach(i=>{let o=document.createElement("button");o.type="button",o.className="jotter-char-btn",o.textContent=i,o.title=`U+${i.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,o.addEventListener("mousedown",s=>{s.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),document.execCommand("insertText",!1,i),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),e.appendChild(o)}),e}_popupLorem(){let t=document.createElement("div");return t.className="jotter-popup-inner",t.appendChild(this._popupTitle("Insert Lorem Ipsum")),y.forEach(e=>{let i=document.createElement("button");i.type="button",i.className="jotter-lorem-btn",i.textContent=e.label,i.addEventListener("mousedown",o=>{o.preventDefault(),this._restoreRange(this._savedRange),this._editor.focus(),e.isHTML?document.execCommand("insertHTML",!1,e.text):document.execCommand("insertText",!1,e.text),this._hidePopup(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),t.appendChild(i)}),t}_popupTitle(t){let e=document.createElement("div");return e.className="jotter-popup-title",e.textContent=t,e}_makeField(t,e,i,o){let s=document.createElement("label");s.className="jotter-popup-label",s.textContent=e;let n=document.createElement("input");return n.type=i,n.className="jotter-popup-input",n.placeholder=o,t.appendChild(s),t.appendChild(n),n}_makeSubmitBtn(t,e){let i=document.createElement("button");return i.type="button",i.className="jotter-popup-submit",i.textContent=t,i.addEventListener("click",e),i}_esc(t){return t.replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_buildStatusBar(){let t=document.createElement("div");t.className="jotter-status",this._wordCountEl=document.createElement("span"),this._wordCountEl.className="jotter-status-words",this._charCountEl=document.createElement("span"),this._charCountEl.className="jotter-status-chars";let e=document.createElement("span");return e.className="jotter-status-mode",e.textContent="HTML",t.appendChild(this._wordCountEl),t.appendChild(this._charCountEl),t.appendChild(e),t}_bindEvents(){this._editor.addEventListener("input",()=>{this._editor.querySelectorAll(":scope > div").forEach(t=>{let e=document.createElement("p");e.innerHTML=t.innerHTML,t.replaceWith(e)}),Array.from(this._editor.childNodes).forEach(t=>{if(t.nodeType===Node.TEXT_NODE&&t.textContent.trim()!==""){let e=window.getSelection(),i=null;if(e&&e.rangeCount){let s=e.getRangeAt(0);s.startContainer===t&&(i=s.startOffset)}let o=document.createElement("p");if(t.replaceWith(o),o.appendChild(t),i!==null){let s=document.createRange();s.setStart(t,i),s.collapse(!0),e.removeAllRanges(),e.addRange(s)}}}),this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())}),this._editor.addEventListener("keyup",()=>this._updateToolbarState()),this._editor.addEventListener("mouseup",()=>this._updateToolbarState()),this._editor.addEventListener("focus",()=>{this._root.classList.add("jotter--focused"),this._emit("focus"),this._options.onFocus&&this._options.onFocus()}),this._editor.addEventListener("blur",()=>{this._root.classList.remove("jotter--focused"),this._emit("blur"),this._options.onBlur&&this._options.onBlur()}),this._editor.addEventListener("keydown",t=>{t.key==="Tab"&&(t.preventDefault(),document.execCommand("insertHTML",!1," "))}),document.addEventListener("mousedown",t=>{this._popup.classList.contains("jotter-popup--visible")&&!this._popup.contains(t.target)&&!this._toolbarEl.contains(t.target)&&this._hidePopup()}),document.addEventListener("keydown",t=>{t.key==="Escape"&&this._popup.classList.contains("jotter-popup--visible")&&(this._hidePopup(),this._editor.focus())})}_toggleSourceMode(){this._sourceMode=!this._sourceMode,this._sourceMode?(this._source.value=this._prettyHTML(this._editor.innerHTML),this._editor.style.display="none",this._source.style.display="block"):(this._editor.innerHTML=this._sanitize(this._source.value),this._source.style.display="none",this._editor.style.display="",this._updateStatus(),this._emit("change",this.getHTML()),this._options.onChange&&this._options.onChange(this.getHTML())),this._root.classList.toggle("jotter--source-mode",this._sourceMode);let t=this._toolbarEl.querySelector('[data-custom="toggleSource"]');t&&t.classList.toggle("jotter-btn--active",this._sourceMode)}_prettyHTML(t){let e=0,i=" ",o=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),s=new Set(["a","abbr","acronym","b","bdo","big","br","button","cite","code","dfn","em","i","img","input","kbd","label","map","object","output","q","samp","select","small","span","strong","sub","sup","textarea","time","tt","u","var"]);return t.replace(/>\s+</g,"><").replace(/(<[^>]+>)/g,`
|
|
2
2
|
$1
|
|
3
3
|
`).split(`
|
|
4
4
|
`).map(n=>n.trim()).filter(n=>n.length>0).map(n=>{let a=n.match(/^<\/(\w+)/),r=n.match(/^<(\w+)/),c=n.endsWith("/>"),l=r?r[1].toLowerCase():null,d=a?a[1].toLowerCase():null;d&&!s.has(d)&&(e=Math.max(0,e-1));let p=i.repeat(e)+n;return l&&!c&&!o.has(l)&&!d&&!s.has(l)&&e++,p}).join(`
|