@lesjoursfr/edith 2.1.3 → 2.1.4

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/build/edith.js CHANGED
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("CodeMirror"),require("Popper")):"function"==typeof define&&define.amd?define(["CodeMirror","Popper"],e):"object"==typeof exports?exports.Edith=e(require("CodeMirror"),require("Popper")):t.Edith=e(t.CodeMirror,t.Popper)}(self,(function(t,e){return function(){"use strict";var n={704:function(e){e.exports=t},613:function(t){t.exports=e}},i={};function o(t){var e=i[t];if(void 0!==e)return e.exports;var s=i[t]={exports:{}};return n[t](s,s.exports,o),s.exports}o.d=function(t,e){for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var s={};return function(){o.r(s),o.d(s,{Edith:function(){return Q},EdithButton:function(){return B}});function t(t,n){return e(t)&&t.tagName===n.toUpperCase()}function e(t){return t.nodeType===Node.ELEMENT_NODE}function n(t,e){return t.getAttribute(e)}function i(t,e){return"string"==typeof e?t.tagName===e.toUpperCase():e.some((e=>t.tagName===e.toUpperCase()))}function r(t,e){return t.classList.contains(e)}function a(t,{innerHTML:e,textContent:n,attributes:i}={}){const o=document.createElement(t);if(i)for(const t in i)Object.hasOwnProperty.call(i,t)&&o.setAttribute(t,i[t]);return"string"==typeof e?o.innerHTML=e:"string"==typeof n&&(o.textContent=n),o}function l(t,e){return t.replaceWith(e),e}function c(t){const e=[...t.childNodes];return t.replaceWith(...e),e}function d(t){const e=document.createTextNode(t.textContent??"");return t.replaceWith(e),e}function h(t){return["AREA","BASE","BR","COL","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"].includes(t)}function u(t,e){for(const n of[...t.childNodes])e(n)&&n.remove()}function p(t,e){if(e(t))t.remove();else for(const n of[...t.childNodes])p(n,e)}function f(t){u(t,(t=>function(t){return t.nodeType===Node.TEXT_NODE}(t)&&(null===t.textContent||0===t.textContent.trim().length)))}function m(t){u(t,(t=>function(t){return t.nodeType===Node.COMMENT_NODE}(t)))}function b(t,e){for(const n of t.getAttributeNames())void 0===e[n]&&t.removeAttribute(n);for(const n of Object.keys(e))t.setAttribute(n,e[n])}function g(t){const e=t.getAttribute("style")||"";return(i(t,"b")&&e.match(/font-weight\s*:\s*(normal|400);/)||i(t,"i")&&e.match(/font-style\s*:\s*normal;/)||i(t,["u","s"])&&e.match(/text-decoration\s*:\s*none;/))&&(t=l(t,a("span",{attributes:{style:e},innerHTML:t.innerHTML}))),e.match(/font-weight\s*:\s*(bold|700|800|900);/)?t=l(t,a("b",{innerHTML:`<span style="${e.replace(/font-weight\s*:\s*(bold|700|800|900);/,"")}">${t.innerHTML}</span>`})):e.match(/font-style\s*:\s*italic;/)?t=l(t,a("i",{innerHTML:`<span style="${e.replace(/font-style\s*:\s*italic;/,"")}">${t.innerHTML}</span>`})):e.match(/text-decoration\s*:\s*underline;/)?t=l(t,a("u",{innerHTML:`<span style="${e.replace(/text-decoration\s*:\s*underline;/,"")}">${t.innerHTML}</span>`})):e.match(/text-decoration\s*:\s*line-through;/)&&(t=l(t,a("s",{innerHTML:`<span style="${e.replace(/text-decoration\s*:\s*line-through;/,"")}">${t.innerHTML}</span>`}))),t}function v(e,n){const i=e.childNodes;for(;i.length>0&&t(i[0],n);)i[0].remove();for(;i.length>0&&t(i[i.length-1],n);)i[i.length-1].remove()}function y(t,e){for(let n of[...t.children]){if(i(n,"span")&&r(n,"edith-nbsp")){b(n,{class:"edith-nbsp",contenteditable:"false"}),n.innerHTML="¶";continue}n.hasAttribute("style")&&(n=g(n)),e[n.tagName]&&(n=l(n,a("span",{attributes:{style:n.getAttribute("style")||""},innerHTML:n.innerHTML})));const t={...e};if(i(n,["b","i","q","u","s"])&&(t[n.tagName]=!0),y(n,t),i(n,"a")){const t={};n.hasAttribute("href")&&(t.href=n.getAttribute("href")),n.hasAttribute("target")&&(t.target=n.getAttribute("target")),b(n,t)}else if(i(n,["b","i","q","u","s","br","sup"]))b(n,{});else if(i(n,["style","meta","link"]))n.remove();else if(i(n,"p")){if(null===n.textContent||0===n.textContent.trim().length){n.remove();continue}b(n,{}),v(n,"br")}else c(n)}}function E(){const t=window.getSelection();return{sel:t,range:t.rangeCount?t.getRangeAt(0):void 0}}function w(t){const e=document.createRange(),n=window.getSelection();e.setStart(t,1),e.collapse(!0),n.removeAllRanges(),n.addRange(e)}function C(t){const e=document.createRange(),n=window.getSelection();e.setStartAfter(t),e.collapse(!0),n.removeAllRanges(),n.addRange(e)}function x(t){if(1===t.length&&e(t[0])&&h(t[0].tagName))return void C(t[0]);const n=document.createRange(),i=window.getSelection();n.setStartBefore(t[0]),n.setEndAfter(t[t.length-1]),i.removeAllRanges(),i.addRange(n)}function T(t){const{range:e}=E();return void 0!==e&&(t.contains(e.startContainer)&&t.contains(e.endContainer))}function k(t,e){const n=e.parentNode,i=t.cloneRange();i.setStart(n,0);const o=i.extractContents(),s=document.createTextNode("​");return o.append(s),n.prepend(o),w(s),s}function N(t,n){const i=n.parentNode,o=new Range;o.selectNodeContents(i),o.setEnd(t.startContainer,t.startOffset);const s=new Range;s.selectNodeContents(i),s.setStart(t.endContainer,t.endOffset);const r=o.extractContents(),a=s.extractContents();i.prepend(r),i.append(a);let l=e(t.commonAncestorContainer)?t.commonAncestorContainer:t.commonAncestorContainer.parentNode;for(;l.tagName!==n.tagName;)l=l.parentNode;const d=c(l);return x(d),d[0].parentNode}function M(t,n={}){const{sel:o,range:s}=E();if(void 0===s)return;if(s.collapsed){let e=o.anchorNode.parentNode;for(;!r(e,"edith-visual");){if(i(e,t))return k(s,e);e=e.parentNode}return function(t,e,n={}){const i=document.createElement(e);return"a"===e?i.textContent=n.textContent||"lien":i.innerHTML="​",t.insertNode(i),"a"===e&&i.insertAdjacentText("afterend"," "),w(i),i}(s,t,n)}let a=s.commonAncestorContainer;for(;!e(a)||!r(a,"edith-visual");){if(e(a)&&i(a,t))return N(s,a);a=a.parentNode}for(const e of[...a.getElementsByTagName(t)])if(o.containsNode(e,!0)){return x(c(e)),a.normalize(),a}const l=document.createElement(t);return l.appendChild(s.extractContents()),s.insertNode(l),u(a,(t=>e(t)&&!h(t.tagName)&&(null===t.textContent||0===t.textContent.length))),function(t){const e=document.createRange(),n=window.getSelection();e.selectNodeContents(t),e.collapse(!1),n.removeAllRanges(),n.addRange(e)}(l),l}function A(t,e){const n=document.createElement("div");return n.innerHTML=t,y(n,e),n.normalize(),f(n),n.innerHTML=n.innerHTML.replace(/\s*&nbsp;\s*/g," ").replace(/\s+/g," ").replace(/(<\/b>[\n\r\s]*<b>|<\/i>[\n\r\s]*<i>|<\/u>[\n\r\s]*<u>|<\/s>[\n\r\s]*<s>)/g," "),m(n),n}const L="edithEvents";let S=0;var H,O;function R(t){const[e,...n]=t.split(".");return{type:e,ns:n??null}}function D(t,e,n){void 0===t[L]&&(t[L]={});for(const i of e.split(" ")){const{type:e,ns:o}=R(i),s=(++S).toString(10);t.addEventListener(e,n),t[L][s]={type:e,ns:o,handler:n}}}function I(t,e,n){void 0===t[L]&&(t[L]={});for(const i of e.split(" ")){const{type:e,ns:o}=R(i);for(const[i,s]of Object.entries(t[L]))s.type!==e&&"*"!==e||null!==o&&!s.ns?.includes(o[0])||void 0!==n&&("function"!=typeof n||n!==s.handler)||(delete t[L][i],t.removeEventListener(s.type,s.handler))}}!function(t){t.modeChanged="edith-mode-changed",t.initialized="edith-initialized"}(H||(H={}));class V{buffer=[];constructor(){}push(t){this.buffer.push(t),this.buffer.length>20&&this.buffer.shift()}pop(){return 0===this.buffer.length?null:this.buffer.pop()}}function z(t,e,n={}){let i,o,s,r,a,l=0;const c=!!n.leading,d="maxWait"in n,h=d?Math.max(n.maxWait||0,e):void 0,u=!("trailing"in n)||!!n.trailing;function p(e){const n=i,r=o;return i=o=void 0,l=e,s=t.apply(r,n),s}function f(t,e){return setTimeout(t,e)}function m(t){const n=t-a;return void 0===a||n>=e||n<0||d&&t-l>=h}function b(){const t=Date.now();if(m(t))return function(t){if(r=void 0,u&&i)return p(t);return i=o=void 0,s}(t);r=f(b,function(t){const n=t-l,i=e-(t-a);return d?Math.min(i,h-n):i}(t))}return function(...t){const n=Date.now(),h=m(n);if(i=t,o=this,a=n,h){if(void 0===r)return function(t){return l=t,r=f(b,e),c?p(t):s}(a);if(d)return r=f(b,e),p(a)}return void 0===r&&(r=f(b,e)),s}}!function(t){t[t.Visual=1]="Visual",t[t.Code=2]="Code"}(O||(O={}));const P={height:80,resizable:!1,toolbar:[["style",["bold","italic","underline","strikethrough"]]],buttons:{},initialContent:""};var j=o(613);class B{el;ctx;icon;title;onclick;showOnCodeView;popperEl;popper;constructor(t,e){this.ctx=t,this.icon=e.icon,this.title=e.title,this.onclick=e.onclick,this.showOnCodeView=!0===e.showOnCodeView}click(t){t.preventDefault(),this.onclick(this.ctx,t)}showTooltip(){if(void 0!==this.popper)return;this.popperEl=a("div",{textContent:this.title,attributes:{class:"edith-tooltip"}});const t=a("div",{attributes:{class:"arrow","data-popper-arrow":""}});this.popperEl.append(t),this.ctx.toolbar.append(this.popperEl),this.popper=(0,j.createPopper)(this.el,this.popperEl,{placement:"bottom",modifiers:[{name:"arrow",options:{padding:5}},{name:"offset",options:{offset:[0,8]}}]})}hideTooltip(){void 0!==this.popper&&(this.popper.destroy(),this.popper=void 0,this.popperEl?.remove())}onEditorModeChange(t){t.detail.mode===O.Code?this.el.setAttribute("disabled","disabled"):this.el.removeAttribute("disabled")}render(){return this.el=a("button",{attributes:{class:`edith-btn ${this.icon}`,type:"button"}}),this.el.onclick=this.click.bind(this),this.el.onmouseenter=this.showTooltip.bind(this),this.el.onmouseleave=this.hideTooltip.bind(this),!0!==this.showOnCodeView&&this.ctx.on(H.modeChanged,this.onEditorModeChange.bind(this)),this.el}}const $=Object.freeze({bold:t=>new B(t,{icon:"fa-solid fa-bold",title:"Gras",onclick:t=>{t.editor.wrapInsideTag("b")}}),italic:t=>new B(t,{icon:"fa-solid fa-italic",title:"Italique",onclick:t=>{t.editor.wrapInsideTag("i")}}),underline:t=>new B(t,{icon:"fa-solid fa-underline",title:"Souligner",onclick:t=>{t.editor.wrapInsideTag("u")}}),strikethrough:t=>new B(t,{icon:"fa-solid fa-strikethrough",title:"Barrer",onclick:t=>{t.editor.wrapInsideTag("s")}}),subscript:t=>new B(t,{icon:"fa-solid fa-subscript",title:"Indice",onclick:t=>{t.editor.wrapInsideTag("sub")}}),superscript:t=>new B(t,{icon:"fa-solid fa-superscript",title:"Exposant",onclick:t=>{t.editor.wrapInsideTag("sup")}}),nbsp:t=>new B(t,{icon:"edith-btn-nbsp",title:"Ajouter une espace insécable",onclick:t=>{t.editor.replaceByHtml('<span class="edith-nbsp" contenteditable="false">¶</span>')}}),clear:t=>new B(t,{icon:"fa-solid fa-eraser",title:"Effacer la mise en forme",onclick:t=>{t.editor.clearStyle()}}),link:t=>new B(t,{icon:"fa-solid fa-link",title:"Lien",onclick:t=>{t.editor.insertLink()}}),codeview:t=>new B(t,{icon:"fa-solid fa-code",title:"Afficher le code HTML",onclick:t=>{t.editor.toggleCodeView()},showOnCodeView:!0})});var q,K=o(704);function W(t){const e=document.createElement("div");e.setAttribute("class","edith-modal-input");const n=document.createElement("label");n.textContent=t.label;const i=document.createElement("input");return i.setAttribute("name",t.name),i.setAttribute("type","text"),null!==t.initialState&&(i.value=t.initialState.toString()),e.append(n),e.append(i),e}function _(t){const e=document.createElement("div");e.setAttribute("class","edith-modal-checkbox");const n=document.createElement("label");n.textContent=t.label;const i=document.createElement("input");return i.setAttribute("name",t.name),i.setAttribute("type","checkbox"),t.initialState&&(i.checked=!0),n.prepend(i),e.append(n),e}function U(t,e,n=null){return{fieldType:q.input,label:t,name:e,initialState:n}}function F(t,e,n=!1){return{fieldType:q.checkbox,label:t,name:e,initialState:n}}!function(t){t[t.input=1]="input",t[t.checkbox=2]="checkbox"}(q||(q={}));class G{el;ctx;title;fields;callback;constructor(t,e){this.ctx=t,this.title=e.title,this.fields=e.fields||[],this.callback=e.callback}cancel(t){t.preventDefault(),this.callback(null),this.close()}submit(t){t.preventDefault();const e={};for(const t of this.el.querySelectorAll("input"))i="name",t.hasAttribute(i)&&(e[n(t,"name")]="checkbox"===n(t,"type")?t.checked:t.value);var i;this.callback(e),this.close()}close(){this.el.remove()}show(){this.el=a("div",{attributes:{class:"edith-modal"}});const t=a("div",{attributes:{class:"edith-modal-header"}}),e=a("span",{textContent:this.title,attributes:{class:"edith-modal-title"}});t.append(e);const n=a("div",{attributes:{class:"edith-modal-content"}});for(const t of this.fields)switch(t.fieldType){case q.input:n.append(W(t));break;case q.checkbox:n.append(_(t));break;default:throw new Error(`Unknown fieldType ${t.fieldType}`)}const i=a("div",{attributes:{class:"edith-modal-footer"}}),o=a("button",{textContent:"Annuler",attributes:{class:"edith-modal-cancel",type:"button"}});i.append(o);const s=a("button",{textContent:"Valider",attributes:{class:"edith-modal-submit",type:"button"}});return i.append(s),this.el.append(t),this.el.append(n),this.el.append(i),this.ctx.modals.append(this.el),o.onclick=this.cancel.bind(this),s.onclick=this.submit.bind(this),this.el}}class J{el;ctx;content;height;resizable;mode;visualEditor;codeEditor;codeMirror;history;throttledSnapshots;constructor(t,e){this.ctx=t,this.content=e.initialContent,this.height=e.height,this.resizable=e.resizable,this.mode=O.Visual,this.history=new V,this.throttledSnapshots=function(t,e,n={}){return z(t,e,{leading:!("leading"in n)||!!n.leading,trailing:!("trailing"in n)||!!n.trailing,maxWait:e})}((()=>this.takeSnapshot()),3e3,{leading:!1,trailing:!0}),this.content=this.content.replace(/&nbsp;/g,'<span class="edith-nbsp" contenteditable="false">¶</span>')}render(){this.el=a("div",{attributes:{class:"edith-editing-area",style:this.resizable?`min-height: ${this.height}px; resize: vertical`:`height: ${this.height}px`}}),this.visualEditor=a("div",{innerHTML:this.content,attributes:{class:"edith-visual",contenteditable:"true",style:this.resizable?`min-height: ${this.height-10}px`:`height: ${this.height-10}px`}}),this.el.append(this.visualEditor),this.codeEditor=a("div",{attributes:{class:"edith-code edith-hidden"}}),this.el.append(this.codeEditor);const t=this.onKeyEvent.bind(this);this.visualEditor.addEventListener("keydown",t),this.visualEditor.addEventListener("keyup",t);const e=this.onPasteEvent.bind(this);return this.visualEditor.addEventListener("paste",e),this.el}getVisualEditorElement(){return this.visualEditor}getCodeEditorElement(){return this.codeEditor}setContent(t){t=t.replace(/&nbsp;/g,'<span class="edith-nbsp" contenteditable="false">¶</span>'),this.mode===O.Visual?this.visualEditor.innerHTML=t:this.codeMirror.dispatch({changes:{from:0,to:this.codeMirror.state.doc.length,insert:t}})}getContent(){const t=this.mode===O.Visual?this.visualEditor.innerHTML:this.codeMirror.state.doc.toJSON().map((t=>t.trim())).join("\n");if("<p><br></p>"===t)return"";const n=a("div",{innerHTML:t});p(n,(t=>e(t)&&!h(t.tagName)&&(null===t.textContent||0===t.textContent.length)));for(const t of n.querySelectorAll("[style]"))t.removeAttribute("style");for(const t of n.querySelectorAll("span"))0===t.attributes.length&&c(t);return n.innerHTML.replace(/\u200B/gi,"").replace(/<\/p>\s*<p>/gi,"<br>").replace(/(<p>|<\/p>)/gi,"").replace(/<span[^>]+class="edith-nbsp"[^>]*>[^<]*<\/span>/gi,"&nbsp;").replace(/(?:<br\s?\/?>)+$/gi,"")}takeSnapshot(){this.history.push(this.visualEditor.innerHTML)}restoreSnapshot(){this.visualEditor.innerHTML=this.history.pop()??""}wrapInsideTag(t){T(this.visualEditor)&&(M(t),this.takeSnapshot())}replaceByHtml(t){T(this.visualEditor)&&(!function(t){const{sel:e,range:n}=E();if(void 0===n)return;const i=document.createDocumentFragment(),o=a("div",{innerHTML:t});i.append(...o.childNodes);const s=i.childNodes[i.childNodes.length-1];e.deleteFromDocument(),n.insertNode(i),C(s)}(t),this.takeSnapshot())}clearStyle(){!function(){const{sel:t,range:n}=E();if(void 0!==n&&e(n.commonAncestorContainer))for(const e of[...n.commonAncestorContainer.children])t.containsNode(e,!0)&&d(e)}(),this.takeSnapshot()}insertLink(){const{sel:t,range:n}=E();if(void 0===n)return;const i=new G(this.ctx,{title:"Insérer un lien",fields:[U("Texte à afficher","text",n.toString()),U("URL du lien","href"),F("Ouvrir dans une nouvelle fenêtre","openInNewTab",!0)],callback:i=>{null!==i&&(function(t){const e=window.getSelection();e.removeAllRanges(),void 0!==t.range&&e.addRange(t.range)}({sel:t,range:n}),function(t,n,i){const o=M("a",{textContent:t});if(void 0!==o)e(o)&&(o.setAttribute("href",n),!0===i&&o.setAttribute("target","_blank"))}(i.text,i.href,i.openInNewTab))}});i.show()}toggleCodeView(){if(this.mode===O.Visual){this.mode=O.Code,this.visualEditor.classList.add("edith-hidden"),this.codeEditor.classList.remove("edith-hidden");const t=document.createElement("div");this.codeEditor.append(t),this.codeMirror=new K.EditorView({doc:this.visualEditor.innerHTML,extensions:[K.basicSetup,K.EditorView.lineWrapping,(0,K.html)({matchClosingTags:!0,autoCloseTags:!0})],parent:t})}else this.mode=O.Visual,this.codeEditor.classList.add("edith-hidden"),this.visualEditor.classList.remove("edith-hidden"),this.visualEditor.innerHTML=this.codeMirror.state.doc.toJSON().map((t=>t.trim())).join("\n"),this.codeMirror.destroy(),this.codeMirror=void 0,this.codeEditor.innerHTML="";this.ctx.trigger(H.modeChanged,{mode:this.mode})}onKeyEvent(t){(t.metaKey||t.ctrlKey?this._processKeyEventWithMeta(t):this._processKeyEvent(t))&&(t.preventDefault(),t.stopPropagation())}_processKeyEvent(t){return 13===t.keyCode?("keydown"===t.type&&this.replaceByHtml("<br />"),!0):(this.throttledSnapshots(),!1)}_processKeyEventWithMeta(t){switch(t.keyCode){case 13:return"keydown"===t.type&&this.replaceByHtml("<br />"),!0;case 32:return"keydown"===t.type&&this.replaceByHtml('<span class="edith-nbsp" contenteditable="false">¶</span>'),!0;case 66:return"keydown"===t.type&&this.wrapInsideTag("b"),!0;case 73:return"keydown"===t.type&&this.wrapInsideTag("i"),!0;case 85:return"keydown"===t.type&&this.wrapInsideTag("u"),!0;case 83:return"keydown"===t.type&&this.wrapInsideTag("s"),!0;case 90:return"keydown"===t.type&&this.restoreSnapshot(),!0}return!1}onPasteEvent(t){t.preventDefault(),t.stopPropagation();const{sel:e,range:n}=E();if(void 0===n||null===t.clipboardData)return;const o=document.createDocumentFragment();if(t.clipboardData.types.includes("text/html")){let n=e.anchorNode;const s={B:!1,I:!1,U:!1,S:!1,Q:!1};for(;null!==n&&!r(n,"edith-visual");)i(n,["b","i","u","s","q"])&&(s[(n.tagName,"Q")]=!0),n=n.parentNode;let a=t.clipboardData.getData("text/html").replace(/[\r\n]+/g," ");/^<html>\s*<body>/.test(a)||(a="<html><body>"+a+"</body></html>");const l=A(a,s);o.append(...l.childNodes)}else{const e=t.clipboardData.getData("text/plain").split(/[\r\n]+/g);for(let t=0;t<e.length;t++)0!==t&&o.append(document.createElement("br")),o.append(document.createTextNode(e[t]))}e.deleteFromDocument(),n.insertNode(o)}destroy(){this.codeMirror?.destroy(),this.codeMirror=void 0,this.el.remove()}}class Q{element;toolbar;editor;modals;constructor(t,e){var n,i;this.element=t,n=this.element,"string"==typeof(i="edith")?n.classList.add(i):n.classList.add(...i),this.toolbar=a("div",{attributes:{class:"edith-toolbar"}}),this.element.append(this.toolbar);const o=e.buttons??P.buttons,s=e.toolbar??P.toolbar;for(const{0:t,1:e}of s){const n=document.createElement("div");n.setAttribute("id",t),n.setAttribute("class","edith-btn-group"),this.toolbar.append(n);for(const t of e){const e=o[t]??$[t];n.append(e(this).render())}}this.editor=new J(this,{initialContent:e.initialContent??P.initialContent,height:e.height??P.height,resizable:e.resizable??P.resizable}),this.element.append(this.editor.render()),this.modals=a("div",{attributes:{class:"edith-modals"}}),this.element.append(this.modals),this.element.edith=this,this.trigger(H.initialized)}on(t,e){!function(t,e,n){if(t instanceof NodeList)for(const i of t)D(i,e,n);else D(t,e,n)}(this.element,t,e)}off(t,e){!function(t,e,n){if(t instanceof NodeList)for(const i of t)I(i,e,n);else I(t,e,n)}(this.element,t,e)}trigger(t,e){!function(t,e,n){t.dispatchEvent(new CustomEvent(e,{detail:n}))}(this.element,t,e)}setContent(t){this.editor.setContent(t)}getContent(){return this.editor.getContent()}destroy(){var t,e;t=this.element,"string"==typeof(e="edith")?t.classList.remove(e):t.classList.remove(...e),this.modals.remove(),this.editor.destroy(),this.toolbar.remove(),this.element.remove()}}}(),s}()}));
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("CodeMirror"),require("Popper")):"function"==typeof define&&define.amd?define(["CodeMirror","Popper"],e):"object"==typeof exports?exports.Edith=e(require("CodeMirror"),require("Popper")):t.Edith=e(t.CodeMirror,t.Popper)}(self,(function(t,e){return function(){"use strict";var n={704:function(e){e.exports=t},613:function(t){t.exports=e}},i={};function o(t){var e=i[t];if(void 0!==e)return e.exports;var s=i[t]={exports:{}};return n[t](s,s.exports,o),s.exports}o.d=function(t,e){for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var s={};return function(){function t(t,e){return n(t)&&t.tagName===e.toUpperCase()}function e(t){return t.nodeType===Node.TEXT_NODE}function n(t){return t.nodeType===Node.ELEMENT_NODE}function i(t,e){return t.getAttribute(e)}function r(t,e){return"string"==typeof e?t.tagName===e.toUpperCase():e.some((e=>t.tagName===e.toUpperCase()))}function a(t,e){return t.classList.contains(e)}function l(t,{innerHTML:e,textContent:n,attributes:i}={}){const o=document.createElement(t);if(i)for(const t in i)Object.hasOwnProperty.call(i,t)&&o.setAttribute(t,i[t]);return"string"==typeof e?o.innerHTML=e:"string"==typeof n&&(o.textContent=n),o}function c(t,e){return t.replaceWith(e),e}function d(t){const e=[...t.childNodes];return t.replaceWith(...e),e}function h(t){const e=document.createTextNode(t.textContent??"");return t.replaceWith(e),e}function u(t){return["AREA","BASE","BR","COL","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"].includes(t)}function p(t,e){for(const n of[...t.childNodes])e(n)&&n.remove()}function f(t,e){if(e(t))t.remove();else for(const n of[...t.childNodes])f(n,e)}function m(t){p(t,(t=>function(t){return t.nodeType===Node.COMMENT_NODE}(t)))}function b(t,e){for(const n of t.getAttributeNames())void 0===e[n]&&t.removeAttribute(n);for(const n of Object.keys(e))t.setAttribute(n,e[n])}function g(t){const e=t.getAttribute("style")||"";return(r(t,"b")&&e.match(/font-weight\s*:\s*(normal|400);/)||r(t,"i")&&e.match(/font-style\s*:\s*normal;/)||r(t,["u","s"])&&e.match(/text-decoration\s*:\s*none;/))&&(t=c(t,l("span",{attributes:{style:e},innerHTML:t.innerHTML}))),e.match(/font-weight\s*:\s*(bold|700|800|900);/)?t=c(t,l("b",{innerHTML:`<span style="${e.replace(/font-weight\s*:\s*(bold|700|800|900);/,"")}">${t.innerHTML}</span>`})):e.match(/font-style\s*:\s*italic;/)?t=c(t,l("i",{innerHTML:`<span style="${e.replace(/font-style\s*:\s*italic;/,"")}">${t.innerHTML}</span>`})):e.match(/text-decoration\s*:\s*underline;/)?t=c(t,l("u",{innerHTML:`<span style="${e.replace(/text-decoration\s*:\s*underline;/,"")}">${t.innerHTML}</span>`})):e.match(/text-decoration\s*:\s*line-through;/)&&(t=c(t,l("s",{innerHTML:`<span style="${e.replace(/text-decoration\s*:\s*line-through;/,"")}">${t.innerHTML}</span>`}))),t}function v(e,n){const i=e.childNodes;for(;i.length>0&&t(i[0],n);)i[0].remove();for(;i.length>0&&t(i[i.length-1],n);)i[i.length-1].remove()}o.r(s),o.d(s,{Edith:function(){return J},EdithButton:function(){return z}});let y=0;function E(t){const[e,...n]=t.split(".");return{type:e,ns:n??null}}function w(t,e,n){void 0===t.ljbtEvents&&(t.ljbtEvents={});for(const i of e.split(" ")){const{type:e,ns:o}=E(i),s=(++y).toString(10);t.addEventListener(e,n),t.ljbtEvents[s]={type:e,ns:o,handler:n}}}function C(t,e,n){void 0===t.ljbtEvents&&(t.ljbtEvents={});for(const i of e.split(" ")){const{type:e,ns:o}=E(i);for(const[i,s]of Object.entries(t.ljbtEvents))s.type!==e&&"*"!==e||null!==o&&!s.ns?.includes(o[0])||void 0!==n&&("function"!=typeof n||n!==s.handler)||(delete t.ljbtEvents[i],t.removeEventListener(s.type,s.handler))}}function x(){const t=window.getSelection();return{sel:t,range:t.rangeCount?t.getRangeAt(0):void 0}}function T(t){const e=document.createRange(),n=window.getSelection();e.setStart(t,1),e.collapse(!0),n.removeAllRanges(),n.addRange(e)}function k(t){const e=document.createRange(),n=window.getSelection();e.setStartAfter(t),e.collapse(!0),n.removeAllRanges(),n.addRange(e)}function N(t){if(1===t.length&&n(t[0])&&u(t[0].tagName))return void k(t[0]);const e=document.createRange(),i=window.getSelection();e.setStartBefore(t[0]),e.setEndAfter(t[t.length-1]),i.removeAllRanges(),i.addRange(e)}function M(t){const{range:e}=x();return void 0!==e&&(t.contains(e.startContainer)&&t.contains(e.endContainer))}function A(t,e){const n=e.parentNode,i=t.cloneRange();i.setStart(n,0);const o=i.extractContents(),s=document.createTextNode("​");return o.append(s),n.prepend(o),T(s),s}function L(t,e){const i=e.parentNode,o=new Range;o.selectNodeContents(i),o.setEnd(t.startContainer,t.startOffset);const s=new Range;s.selectNodeContents(i),s.setStart(t.endContainer,t.endOffset);const r=o.extractContents(),a=s.extractContents();i.prepend(r),i.append(a);let l=n(t.commonAncestorContainer)?t.commonAncestorContainer:t.commonAncestorContainer.parentNode;for(;l.tagName!==e.tagName;)l=l.parentNode;const c=d(l);return N(c),c[0].parentNode}function S(t,e={}){const{sel:i,range:o}=x();if(void 0===o)return;if(o.collapsed){let n=i.anchorNode.parentNode;for(;!a(n,"edith-visual");){if(r(n,t))return A(o,n);n=n.parentNode}return function(t,e,n={}){const i=document.createElement(e);return"a"===e?i.textContent=n.textContent||"lien":i.innerHTML="​",t.insertNode(i),"a"===e&&i.insertAdjacentText("afterend"," "),T(i),i}(o,t,e)}let s=o.commonAncestorContainer;for(;!n(s)||!a(s,"edith-visual");){if(n(s)&&r(s,t))return L(o,s);s=s.parentNode}for(const e of[...s.getElementsByTagName(t)])if(i.containsNode(e,!0)){return N(d(e)),s.normalize(),s}const l=document.createElement(t);return l.appendChild(o.extractContents()),o.insertNode(l),p(s,(t=>n(t)&&!u(t.tagName)&&(null===t.textContent||0===t.textContent.length))),function(t){const e=document.createRange(),n=window.getSelection();e.selectNodeContents(t),e.collapse(!1),n.removeAllRanges(),n.addRange(e)}(l),l}function H(t,e){for(let n of[...t.children]){if(r(n,"span")&&a(n,"edith-nbsp")){b(n,{class:"edith-nbsp",contenteditable:"false"}),n.innerHTML="¶";continue}n.hasAttribute("style")&&(n=g(n)),e[n.tagName]&&(n=c(n,l("span",{attributes:{style:n.getAttribute("style")||""},innerHTML:n.innerHTML})));const t={...e};if(r(n,["b","i","q","u","s"])&&(t[n.tagName]=!0),H(n,t),r(n,"a")){const t={};n.hasAttribute("href")&&(t.href=n.getAttribute("href")),n.hasAttribute("target")&&(t.target=n.getAttribute("target")),b(n,t)}else if(r(n,["b","i","q","u","s","br","sup"]))b(n,{});else if(r(n,["style","meta","link"]))n.remove();else if(r(n,"p")){if(null===n.textContent||0===n.textContent.trim().length){n.remove();continue}b(n,{}),v(n,"br")}else d(n)}}function O(t,n){const i=document.createElement("div");return i.innerHTML=t,H(i,n),i.normalize(),p(i,(t=>e(t)&&(null===t.textContent||0===t.textContent.trim().length))),i.innerHTML=i.innerHTML.replace(/\s*&nbsp;\s*/g," ").replace(/\s+/g," ").replace(/(<\/b>[\n\r\s]*<b>|<\/i>[\n\r\s]*<i>|<\/u>[\n\r\s]*<u>|<\/s>[\n\r\s]*<s>)/g," "),m(i),i}var R,j;!function(t){t.modeChanged="edith-mode-changed",t.initialized="edith-initialized"}(R||(R={}));class D{buffer=[];constructor(){}push(t){this.buffer.push(t),this.buffer.length>20&&this.buffer.shift()}pop(){return 0===this.buffer.length?null:this.buffer.pop()}}!function(t){t[t.Visual=1]="Visual",t[t.Code=2]="Code"}(j||(j={}));const I={height:80,resizable:!1,toolbar:[["style",["bold","italic","underline","strikethrough"]]],buttons:{},initialContent:""};var V=o(613);class z{el;ctx;icon;title;onclick;showOnCodeView;popperEl;popper;constructor(t,e){this.ctx=t,this.icon=e.icon,this.title=e.title,this.onclick=e.onclick,this.showOnCodeView=!0===e.showOnCodeView}click(t){t.preventDefault(),this.onclick(this.ctx,t)}showTooltip(){if(void 0!==this.popper)return;this.popperEl=l("div",{textContent:this.title,attributes:{class:"edith-tooltip"}});const t=l("div",{attributes:{class:"arrow","data-popper-arrow":""}});this.popperEl.append(t),this.ctx.toolbar.append(this.popperEl),this.popper=(0,V.createPopper)(this.el,this.popperEl,{placement:"bottom",modifiers:[{name:"arrow",options:{padding:5}},{name:"offset",options:{offset:[0,8]}}]})}hideTooltip(){void 0!==this.popper&&(this.popper.destroy(),this.popper=void 0,this.popperEl?.remove())}onEditorModeChange(t){t.detail.mode===j.Code?this.el.setAttribute("disabled","disabled"):this.el.removeAttribute("disabled")}render(){return this.el=l("button",{attributes:{class:`edith-btn ${this.icon}`,type:"button"}}),this.el.onclick=this.click.bind(this),this.el.onmouseenter=this.showTooltip.bind(this),this.el.onmouseleave=this.hideTooltip.bind(this),!0!==this.showOnCodeView&&this.ctx.on(R.modeChanged,this.onEditorModeChange.bind(this)),this.el}}const P=Object.freeze({bold:t=>new z(t,{icon:"fa-solid fa-bold",title:"Gras",onclick:t=>{t.editor.wrapInsideTag("b")}}),italic:t=>new z(t,{icon:"fa-solid fa-italic",title:"Italique",onclick:t=>{t.editor.wrapInsideTag("i")}}),underline:t=>new z(t,{icon:"fa-solid fa-underline",title:"Souligner",onclick:t=>{t.editor.wrapInsideTag("u")}}),strikethrough:t=>new z(t,{icon:"fa-solid fa-strikethrough",title:"Barrer",onclick:t=>{t.editor.wrapInsideTag("s")}}),subscript:t=>new z(t,{icon:"fa-solid fa-subscript",title:"Indice",onclick:t=>{t.editor.wrapInsideTag("sub")}}),superscript:t=>new z(t,{icon:"fa-solid fa-superscript",title:"Exposant",onclick:t=>{t.editor.wrapInsideTag("sup")}}),nbsp:t=>new z(t,{icon:"edith-btn-nbsp",title:"Ajouter une espace insécable",onclick:t=>{t.editor.replaceByHtml('<span class="edith-nbsp" contenteditable="false">¶</span>')}}),clear:t=>new z(t,{icon:"fa-solid fa-eraser",title:"Effacer la mise en forme",onclick:t=>{t.editor.clearStyle()}}),link:t=>new z(t,{icon:"fa-solid fa-link",title:"Lien",onclick:t=>{t.editor.insertLink()}}),codeview:t=>new z(t,{icon:"fa-solid fa-code",title:"Afficher le code HTML",onclick:t=>{t.editor.toggleCodeView()},showOnCodeView:!0})});var B,$=o(704);function q(t,e,n={}){let i,o,s,r,a,l=0;const c=!!n.leading,d="maxWait"in n,h=d?Math.max(n.maxWait||0,e):void 0,u=!("trailing"in n)||!!n.trailing;function p(e){const n=i,r=o;return i=o=void 0,l=e,s=t.apply(r,n),s}function f(t,e){return setTimeout(t,e)}function m(t){const n=t-a;return void 0===a||n>=e||n<0||d&&t-l>=h}function b(){const t=Date.now();if(m(t))return function(t){if(r=void 0,u&&i)return p(t);return i=o=void 0,s}(t);r=f(b,function(t){const n=t-l,i=e-(t-a);return d?Math.min(i,h-n):i}(t))}return function(...t){const n=Date.now(),h=m(n);if(i=t,o=this,a=n,h){if(void 0===r)return function(t){return l=t,r=f(b,e),c?p(t):s}(a);if(d)return r=f(b,e),p(a)}return void 0===r&&(r=f(b,e)),s}}function K(t){const e=document.createElement("div");e.setAttribute("class","edith-modal-input");const n=document.createElement("label");n.textContent=t.label;const i=document.createElement("input");return i.setAttribute("name",t.name),i.setAttribute("type","text"),null!==t.initialState&&(i.value=t.initialState.toString()),e.append(n),e.append(i),e}function W(t){const e=document.createElement("div");e.setAttribute("class","edith-modal-checkbox");const n=document.createElement("label");n.textContent=t.label;const i=document.createElement("input");return i.setAttribute("name",t.name),i.setAttribute("type","checkbox"),t.initialState&&(i.checked=!0),n.prepend(i),e.append(n),e}function _(t,e,n=null){return{fieldType:B.input,label:t,name:e,initialState:n}}function U(t,e,n=!1){return{fieldType:B.checkbox,label:t,name:e,initialState:n}}!function(t){t[t.input=1]="input",t[t.checkbox=2]="checkbox"}(B||(B={}));class F{el;ctx;title;fields;callback;constructor(t,e){this.ctx=t,this.title=e.title,this.fields=e.fields||[],this.callback=e.callback}cancel(t){t.preventDefault(),this.callback(null),this.close()}submit(t){t.preventDefault();const e={};for(const t of this.el.querySelectorAll("input"))n="name",t.hasAttribute(n)&&(e[i(t,"name")]="checkbox"===i(t,"type")?t.checked:t.value);var n;this.callback(e),this.close()}close(){this.el.remove()}show(){this.el=l("div",{attributes:{class:"edith-modal"}});const t=l("div",{attributes:{class:"edith-modal-header"}}),e=l("span",{textContent:this.title,attributes:{class:"edith-modal-title"}});t.append(e);const n=l("div",{attributes:{class:"edith-modal-content"}});for(const t of this.fields)switch(t.fieldType){case B.input:n.append(K(t));break;case B.checkbox:n.append(W(t));break;default:throw new Error(`Unknown fieldType ${t.fieldType}`)}const i=l("div",{attributes:{class:"edith-modal-footer"}}),o=l("button",{textContent:"Annuler",attributes:{class:"edith-modal-cancel",type:"button"}});i.append(o);const s=l("button",{textContent:"Valider",attributes:{class:"edith-modal-submit",type:"button"}});return i.append(s),this.el.append(t),this.el.append(n),this.el.append(i),this.ctx.modals.append(this.el),o.onclick=this.cancel.bind(this),s.onclick=this.submit.bind(this),this.el}}class G{el;ctx;content;height;resizable;mode;visualEditor;codeEditor;codeMirror;history;throttledSnapshots;constructor(t,e){this.ctx=t,this.content=e.initialContent,this.height=e.height,this.resizable=e.resizable,this.mode=j.Visual,this.history=new D,this.throttledSnapshots=function(t,e,n={}){return q(t,e,{leading:!("leading"in n)||!!n.leading,trailing:!("trailing"in n)||!!n.trailing,maxWait:e})}((()=>this.takeSnapshot()),3e3,{leading:!1,trailing:!0}),this.content=this.content.replace(/&nbsp;/g,'<span class="edith-nbsp" contenteditable="false">¶</span>')}render(){this.el=l("div",{attributes:{class:"edith-editing-area",style:this.resizable?`min-height: ${this.height}px; resize: vertical`:`height: ${this.height}px`}}),this.visualEditor=l("div",{innerHTML:this.content,attributes:{class:"edith-visual",contenteditable:"true",style:this.resizable?`min-height: ${this.height-10}px`:`height: ${this.height-10}px`}}),this.el.append(this.visualEditor),this.codeEditor=l("div",{attributes:{class:"edith-code edith-hidden"}}),this.el.append(this.codeEditor);const t=this.onKeyEvent.bind(this);this.visualEditor.addEventListener("keydown",t),this.visualEditor.addEventListener("keyup",t);const e=this.onPasteEvent.bind(this);return this.visualEditor.addEventListener("paste",e),this.el}getVisualEditorElement(){return this.visualEditor}getCodeEditorElement(){return this.codeEditor}setContent(t){t=t.replace(/&nbsp;/g,'<span class="edith-nbsp" contenteditable="false">¶</span>'),this.mode===j.Visual?this.visualEditor.innerHTML=t:this.codeMirror.dispatch({changes:{from:0,to:this.codeMirror.state.doc.length,insert:t}})}getContent(){const t=this.mode===j.Visual?this.visualEditor.innerHTML:this.codeMirror.state.doc.toJSON().map((t=>t.trim())).join("\n");if("<p><br></p>"===t)return"";const e=l("div",{innerHTML:t});f(e,(t=>n(t)&&!u(t.tagName)&&(null===t.textContent||0===t.textContent.length)));for(const t of e.querySelectorAll("[style]"))t.removeAttribute("style");for(const t of e.querySelectorAll("span"))0===t.attributes.length&&d(t);return e.innerHTML.replace(/\u200B/gi,"").replace(/<\/p>\s*<p>/gi,"<br>").replace(/(<p>|<\/p>)/gi,"").replace(/<span[^>]+class="edith-nbsp"[^>]*>[^<]*<\/span>/gi,"&nbsp;").replace(/(?:<br\s?\/?>)+$/gi,"")}takeSnapshot(){this.history.push(this.visualEditor.innerHTML)}restoreSnapshot(){this.visualEditor.innerHTML=this.history.pop()??""}wrapInsideTag(t){M(this.visualEditor)&&(S(t),this.takeSnapshot())}replaceByHtml(t){M(this.visualEditor)&&(!function(t){const{sel:e,range:n}=x();if(void 0===n)return;const i=document.createDocumentFragment(),o=l("div",{innerHTML:t});i.append(...o.childNodes);const s=i.childNodes[i.childNodes.length-1];e.deleteFromDocument(),n.insertNode(i),k(s)}(t),this.takeSnapshot())}clearStyle(){!function(){const{sel:t,range:e}=x();if(void 0!==e&&n(e.commonAncestorContainer))for(const n of[...e.commonAncestorContainer.children])t.containsNode(n,!0)&&h(n)}(),this.takeSnapshot()}insertLink(){const{sel:t,range:e}=x();if(void 0===e)return;const i=new F(this.ctx,{title:"Insérer un lien",fields:[_("Texte à afficher","text",e.toString()),_("URL du lien","href"),U("Ouvrir dans une nouvelle fenêtre","openInNewTab",!0)],callback:i=>{null!==i&&(function(t){const e=window.getSelection();e.removeAllRanges(),void 0!==t.range&&e.addRange(t.range)}({sel:t,range:e}),function(t,e,i){const o=S("a",{textContent:t});if(void 0!==o)n(o)&&(o.setAttribute("href",e),!0===i&&o.setAttribute("target","_blank"))}(i.text,i.href,i.openInNewTab))}});i.show()}toggleCodeView(){if(this.mode===j.Visual){this.mode=j.Code,this.visualEditor.classList.add("edith-hidden"),this.codeEditor.classList.remove("edith-hidden");const t=document.createElement("div");this.codeEditor.append(t),this.codeMirror=new $.EditorView({doc:this.visualEditor.innerHTML,extensions:[$.basicSetup,$.EditorView.lineWrapping,(0,$.html)({matchClosingTags:!0,autoCloseTags:!0})],parent:t})}else this.mode=j.Visual,this.codeEditor.classList.add("edith-hidden"),this.visualEditor.classList.remove("edith-hidden"),this.visualEditor.innerHTML=this.codeMirror.state.doc.toJSON().map((t=>t.trim())).join("\n"),this.codeMirror.destroy(),this.codeMirror=void 0,this.codeEditor.innerHTML="";this.ctx.trigger(R.modeChanged,{mode:this.mode})}onKeyEvent(t){(t.metaKey||t.ctrlKey?this._processKeyEventWithMeta(t):this._processKeyEvent(t))&&(t.preventDefault(),t.stopPropagation())}_processKeyEvent(t){return 13===t.keyCode?("keydown"===t.type&&this.replaceByHtml("<br />"),!0):(this.throttledSnapshots(),!1)}_processKeyEventWithMeta(t){switch(t.keyCode){case 13:return"keydown"===t.type&&this.replaceByHtml("<br />"),!0;case 32:return"keydown"===t.type&&this.replaceByHtml('<span class="edith-nbsp" contenteditable="false">¶</span>'),!0;case 66:return"keydown"===t.type&&this.wrapInsideTag("b"),!0;case 73:return"keydown"===t.type&&this.wrapInsideTag("i"),!0;case 85:return"keydown"===t.type&&this.wrapInsideTag("u"),!0;case 83:return"keydown"===t.type&&this.wrapInsideTag("s"),!0;case 90:return"keydown"===t.type&&this.restoreSnapshot(),!0}return!1}onPasteEvent(t){t.preventDefault(),t.stopPropagation();const{sel:n,range:i}=x();if(void 0===i||null===t.clipboardData)return;const o=document.createDocumentFragment();if(t.clipboardData.types.includes("text/html")){let i=e(n.anchorNode)?n.anchorNode.parentNode:n.anchorNode;const s={B:!1,I:!1,U:!1,S:!1,Q:!1};for(;null!==i&&!a(i,"edith-visual");)r(i,["b","i","u","s","q"])&&(s[(i.tagName,"Q")]=!0),i=i.parentNode;let l=t.clipboardData.getData("text/html").replace(/[\r\n]+/g," ");/^<html>\s*<body>/.test(l)||(l="<html><body>"+l+"</body></html>");const c=O(l,s);o.append(...c.childNodes)}else{const e=t.clipboardData.getData("text/plain").split(/[\r\n]+/g);for(let t=0;t<e.length;t++)0!==t&&o.append(document.createElement("br")),o.append(document.createTextNode(e[t]))}n.deleteFromDocument(),i.insertNode(o)}destroy(){this.codeMirror?.destroy(),this.codeMirror=void 0,this.el.remove()}}class J{element;toolbar;editor;modals;constructor(t,e){var n,i;this.element=t,n=this.element,"string"==typeof(i="edith")?n.classList.add(i):n.classList.add(...i),this.toolbar=l("div",{attributes:{class:"edith-toolbar"}}),this.element.append(this.toolbar);const o=e.buttons??I.buttons,s=e.toolbar??I.toolbar;for(const{0:t,1:e}of s){const n=document.createElement("div");n.setAttribute("id",t),n.setAttribute("class","edith-btn-group"),this.toolbar.append(n);for(const t of e){const e=o[t]??P[t];n.append(e(this).render())}}this.editor=new G(this,{initialContent:e.initialContent??I.initialContent,height:e.height??I.height,resizable:e.resizable??I.resizable}),this.element.append(this.editor.render()),this.modals=l("div",{attributes:{class:"edith-modals"}}),this.element.append(this.modals),this.element.edith=this,this.trigger(R.initialized)}on(t,e){!function(t,e,n){if(t instanceof NodeList)for(const i of t)w(i,e,n);else w(t,e,n)}(this.element,t,e)}off(t,e){!function(t,e,n){if(t instanceof NodeList)for(const i of t)C(i,e,n);else C(t,e,n)}(this.element,t,e)}trigger(t,e){!function(t,e,n){t.dispatchEvent("string"==typeof e?new CustomEvent(e,{detail:n}):e)}(this.element,t,e)}setContent(t){this.editor.setContent(t)}getContent(){return this.editor.getContent()}destroy(){var t,e;t=this.element,"string"==typeof(e="edith")?t.classList.remove(e):t.classList.remove(...e),this.modals.remove(),this.editor.destroy(),this.toolbar.remove(),this.element.remove()}}}(),s}()}));
@@ -25,6 +25,14 @@ export declare function wrapInsideLink(text: string, href: string, targetBlank:
25
25
  * Clear the style in the current selection.
26
26
  */
27
27
  export declare function clearSelectionStyle(): void;
28
+ /**
29
+ * Clean the DOM content of the node
30
+ * @param {HTMLElement} root the node to process
31
+ * @param {object} style active styles for the root
32
+ */
33
+ export declare function cleanDomContent(root: HTMLElement, style: {
34
+ [keyof: string]: boolean;
35
+ }): void;
28
36
  /**
29
37
  * Clean the given HTML code.
30
38
  * @param {string} html the HTML code to clean
package/dist/core/edit.js CHANGED
@@ -1,4 +1,4 @@
1
- import { cleanDomContent, createNodeWith, hasClass, hasTagName, isHTMLElement, isSelfClosing, removeCommentNodes, removeEmptyTextNodes, removeNodes, textifyNode, unwrapNode, } from "./dom.js";
1
+ import { createNodeWith, hasClass, hasTagName, isHTMLElement, isSelfClosing, removeCommentNodes, removeEmptyTextNodes, removeNodes, replaceNodeStyleByTag, replaceNodeWith, resetAttributesTo, textifyNode, trimTag, unwrapNode, } from "@lesjoursfr/browser-tools";
2
2
  import { getSelection, moveCursorAfterNode, moveCursorInsideNode, selectNodeContents, selectNodes } from "./range.js";
3
3
  /**
4
4
  * Split the node at the caret position.
@@ -226,6 +226,78 @@ export function clearSelectionStyle() {
226
226
  }
227
227
  }
228
228
  }
229
+ /**
230
+ * Clean the DOM content of the node
231
+ * @param {HTMLElement} root the node to process
232
+ * @param {object} style active styles for the root
233
+ */
234
+ export function cleanDomContent(root, style) {
235
+ // Iterate through children
236
+ for (let el of [...root.children]) {
237
+ // Check if the span is an edith-nbsp
238
+ if (hasTagName(el, "span") && hasClass(el, "edith-nbsp")) {
239
+ // Ensure that we have a clean element
240
+ resetAttributesTo(el, { class: "edith-nbsp", contenteditable: "false" });
241
+ el.innerHTML = "¶";
242
+ continue;
243
+ }
244
+ // Check if there is a style attribute on the current node
245
+ if (el.hasAttribute("style")) {
246
+ // Replace the style attribute by tags
247
+ el = replaceNodeStyleByTag(el);
248
+ }
249
+ // Check if the Tag Match a Parent Tag
250
+ if (style[el.tagName]) {
251
+ el = replaceNodeWith(el, createNodeWith("span", { attributes: { style: el.getAttribute("style") || "" }, innerHTML: el.innerHTML }));
252
+ }
253
+ // Save the Current Style Tag
254
+ const newTags = { ...style };
255
+ if (hasTagName(el, ["b", "i", "q", "u", "s"])) {
256
+ newTags[el.tagName] = true;
257
+ }
258
+ // Clean Children
259
+ cleanDomContent(el, newTags);
260
+ // Keep only href & target attributes for <a> tags
261
+ if (hasTagName(el, "a")) {
262
+ const linkAttributes = {};
263
+ if (el.hasAttribute("href")) {
264
+ linkAttributes.href = el.getAttribute("href");
265
+ }
266
+ if (el.hasAttribute("target")) {
267
+ linkAttributes.target = el.getAttribute("target");
268
+ }
269
+ resetAttributesTo(el, linkAttributes);
270
+ continue;
271
+ }
272
+ // Remove all tag attributes for tags in the allowed list
273
+ if (hasTagName(el, ["b", "i", "q", "u", "s", "br", "sup"])) {
274
+ resetAttributesTo(el, {});
275
+ continue;
276
+ }
277
+ // Remove useless tags
278
+ if (hasTagName(el, ["style", "meta", "link"])) {
279
+ el.remove();
280
+ continue;
281
+ }
282
+ // Check if it's a <p> tag
283
+ if (hasTagName(el, "p")) {
284
+ // Check if the element contains text
285
+ if (el.textContent === null || el.textContent.trim().length === 0) {
286
+ // Remove the node
287
+ el.remove();
288
+ continue;
289
+ }
290
+ // Remove all tag attributes
291
+ resetAttributesTo(el, {});
292
+ // Remove leading & trailing <br>
293
+ trimTag(el, "br");
294
+ // Return
295
+ continue;
296
+ }
297
+ // Unwrap the node
298
+ unwrapNode(el);
299
+ }
300
+ }
229
301
  /**
230
302
  * Clean the given HTML code.
231
303
  * @param {string} html the HTML code to clean
@@ -1,47 +1,4 @@
1
- type EdithEvent = {
2
- type: string;
3
- ns: Array<string> | null;
4
- handler: EventListenerOrEventListenerObject;
5
- };
6
- type EdithEvents = {
7
- [key: string]: EdithEvent;
8
- };
9
- declare global {
10
- interface Window {
11
- edithEvents: EdithEvents;
12
- }
13
- interface Document {
14
- edithEvents: EdithEvents;
15
- }
16
- interface HTMLElement {
17
- edithEvents: EdithEvents;
18
- }
19
- }
20
1
  export declare enum Events {
21
2
  modeChanged = "edith-mode-changed",
22
3
  initialized = "edith-initialized"
23
4
  }
24
- /**
25
- * Set an event listener on every node.
26
- * @param {Window|Document|HTMLElement|NodeList} nodes
27
- * @param {string} events
28
- * @param {Function} handler
29
- */
30
- export declare function on(nodes: Window | Document | HTMLElement | NodeListOf<HTMLElement>, events: string, handler: EventListenerOrEventListenerObject): void;
31
- /**
32
- * Remove event listeners from the node.
33
- * @param {Window|Document|HTMLElement|NodeList} node
34
- * @param {string} events
35
- * @param {Function|undefined} handler
36
- */
37
- export declare function off(nodes: Window | Document | HTMLElement | NodeListOf<HTMLElement>, events: string, handler?: EventListenerOrEventListenerObject): void;
38
- /**
39
- * Trigger the EdithEvent on the node.
40
- * @param {Window|Document|HTMLElement} node
41
- * @param {string} event
42
- * @param {Object|undefined} payload
43
- */
44
- export declare function trigger(node: Window | Document | HTMLElement, event: string, payload?: {
45
- [key: string]: unknown;
46
- }): void;
47
- export {};
@@ -1,100 +1,5 @@
1
- const eventsNamespace = "edithEvents";
2
- let eventsGuid = 0;
3
1
  export var Events;
4
2
  (function (Events) {
5
3
  Events["modeChanged"] = "edith-mode-changed";
6
4
  Events["initialized"] = "edith-initialized";
7
5
  })(Events || (Events = {}));
8
- /**
9
- * Parse an event type to separate the type & the namespace
10
- * @param {string} string
11
- */
12
- function parseEventType(string) {
13
- const [type, ...nsArray] = string.split(".");
14
- return {
15
- type,
16
- ns: nsArray ?? null,
17
- };
18
- }
19
- /**
20
- * Set an event listener on the node.
21
- * @param {Window|Document|HTMLElement} node
22
- * @param {string} events
23
- * @param {Function} handler
24
- */
25
- function addEventListener(node, events, handler) {
26
- if (node[eventsNamespace] === undefined) {
27
- node[eventsNamespace] = {};
28
- }
29
- for (const event of events.split(" ")) {
30
- const { type, ns } = parseEventType(event);
31
- const handlerGuid = (++eventsGuid).toString(10);
32
- node.addEventListener(type, handler);
33
- node[eventsNamespace][handlerGuid] = { type, ns, handler };
34
- }
35
- }
36
- /**
37
- * Remove event listeners from the node.
38
- * @param {Window|Document|HTMLElement} node
39
- * @param {string} events
40
- * @param {Function|undefined} handler
41
- */
42
- function removeEventListener(node, events, handler) {
43
- if (node[eventsNamespace] === undefined) {
44
- node[eventsNamespace] = {};
45
- }
46
- for (const event of events.split(" ")) {
47
- const { type, ns } = parseEventType(event);
48
- for (const [guid, handlerObj] of Object.entries(node[eventsNamespace])) {
49
- if (handlerObj.type !== type && type !== "*") {
50
- continue;
51
- }
52
- if ((ns === null || handlerObj.ns?.includes(ns[0])) &&
53
- (handler === undefined || (typeof handler === "function" && handler === handlerObj.handler))) {
54
- delete node[eventsNamespace][guid];
55
- node.removeEventListener(handlerObj.type, handlerObj.handler);
56
- }
57
- }
58
- }
59
- }
60
- /**
61
- * Set an event listener on every node.
62
- * @param {Window|Document|HTMLElement|NodeList} nodes
63
- * @param {string} events
64
- * @param {Function} handler
65
- */
66
- export function on(nodes, events, handler) {
67
- if (nodes instanceof NodeList) {
68
- for (const node of nodes) {
69
- addEventListener(node, events, handler);
70
- }
71
- }
72
- else {
73
- addEventListener(nodes, events, handler);
74
- }
75
- }
76
- /**
77
- * Remove event listeners from the node.
78
- * @param {Window|Document|HTMLElement|NodeList} node
79
- * @param {string} events
80
- * @param {Function|undefined} handler
81
- */
82
- export function off(nodes, events, handler) {
83
- if (nodes instanceof NodeList) {
84
- for (const node of nodes) {
85
- removeEventListener(node, events, handler);
86
- }
87
- }
88
- else {
89
- removeEventListener(nodes, events, handler);
90
- }
91
- }
92
- /**
93
- * Trigger the EdithEvent on the node.
94
- * @param {Window|Document|HTMLElement} node
95
- * @param {string} event
96
- * @param {Object|undefined} payload
97
- */
98
- export function trigger(node, event, payload) {
99
- node.dispatchEvent(new CustomEvent(event, { detail: payload }));
100
- }
@@ -1,7 +1,5 @@
1
- export * from "./dom.js";
2
1
  export * from "./edit.js";
3
2
  export * from "./events.js";
4
3
  export * from "./history.js";
5
4
  export * from "./mode.js";
6
5
  export * from "./range.js";
7
- export * from "./throttle.js";
@@ -1,7 +1,5 @@
1
- export * from "./dom.js";
2
1
  export * from "./edit.js";
3
2
  export * from "./events.js";
4
3
  export * from "./history.js";
5
4
  export * from "./mode.js";
6
5
  export * from "./range.js";
7
- export * from "./throttle.js";
@@ -1,4 +1,4 @@
1
- import { isHTMLElement, isSelfClosing } from "./dom.js";
1
+ import { isHTMLElement, isSelfClosing } from "@lesjoursfr/browser-tools";
2
2
  /**
3
3
  * Get the current selection.
4
4
  * @returns {CurrentSelection} the current selection
package/dist/edith.js CHANGED
@@ -1,4 +1,5 @@
1
- import { Events, addClass, createNodeWith, off, on, removeClass, trigger } from "./core/index.js";
1
+ import { addClass, createNodeWith, off, on, removeClass, trigger } from "@lesjoursfr/browser-tools";
2
+ import { Events } from "./core/index.js";
2
3
  import { DefaultOptions } from "./edith-options.js";
3
4
  import { EdithButton, EdithButtons, EdithEditor } from "./ui/index.js";
4
5
  export { EdithButton };
package/dist/ui/button.js CHANGED
@@ -1,5 +1,6 @@
1
+ import { createNodeWith } from "@lesjoursfr/browser-tools";
1
2
  import { createPopper } from "@popperjs/core";
2
- import { EditorModes, Events, createNodeWith } from "../core/index.js";
3
+ import { EditorModes, Events } from "../core/index.js";
3
4
  export class EdithButton {
4
5
  el;
5
6
  ctx;
@@ -1,4 +1,4 @@
1
- import { throttle } from "../core/index.js";
1
+ import { throttle } from "@lesjoursfr/browser-tools";
2
2
  import { Edith } from "../edith.js";
3
3
  export declare class EdithEditor {
4
4
  private el;
package/dist/ui/editor.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { html } from "@codemirror/lang-html";
2
+ import { createNodeWith, hasClass, hasTagName, isHTMLElement, isSelfClosing, isTextNode, removeNodesRecursively, throttle, unwrapNode, } from "@lesjoursfr/browser-tools";
2
3
  import { EditorView, basicSetup } from "codemirror";
3
- import { EditorModes, Events, History, cleanPastedHtml, clearSelectionStyle, createNodeWith, getSelection, hasClass, hasTagName, isHTMLElement, isSelectionInsideNode, isSelfClosing, removeNodesRecursively, replaceSelectionByHtml, restoreSelection, throttle, unwrapNode, wrapInsideLink, wrapInsideTag, } from "../core/index.js";
4
+ import { EditorModes, Events, History, cleanPastedHtml, clearSelectionStyle, getSelection, isSelectionInsideNode, replaceSelectionByHtml, restoreSelection, wrapInsideLink, wrapInsideTag, } from "../core/index.js";
4
5
  import { EdithModal, createCheckboxModalField, createInputModalField } from "./modal.js";
5
6
  export class EdithEditor {
6
7
  el;
@@ -288,7 +289,7 @@ export class EdithEditor {
288
289
  }
289
290
  else {
290
291
  // Detect style blocs in parents
291
- let dest = sel.anchorNode;
292
+ let dest = (isTextNode(sel.anchorNode) ? sel.anchorNode.parentNode : sel.anchorNode);
292
293
  const style = { B: false, I: false, U: false, S: false, Q: false };
293
294
  while (dest !== null && !hasClass(dest, "edith-visual")) {
294
295
  // Check if it's a style tag
package/dist/ui/modal.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createNodeWith, getAttribute, hasAttribute } from "../core/index.js";
1
+ import { createNodeWith, getAttribute, hasAttribute } from "@lesjoursfr/browser-tools";
2
2
  export var EdithModalFieldType;
3
3
  (function (EdithModalFieldType) {
4
4
  EdithModalFieldType[EdithModalFieldType["input"] = 1] = "input";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lesjoursfr/edith",
3
- "version": "2.1.3",
3
+ "version": "2.1.4",
4
4
  "description": "Simple WYSIWYG editor.",
5
5
  "license": "MIT",
6
6
  "repository": "lesjoursfr/edith",
@@ -23,7 +23,7 @@
23
23
  "types": "./dist/index.d.ts",
24
24
  "type": "module",
25
25
  "scripts": {
26
- "freshlock": "rm -rf node_modules/ && rm .yarn/install-state.gz && rm -r .yarn/cache/ && rm yarn.lock && yarn",
26
+ "freshlock": "rm -rf node_modules/ && rm .yarn/install-state.gz && rm yarn.lock && yarn",
27
27
  "eslint-check": "eslint . --ext .js,.jsx,.ts,.tsx",
28
28
  "eslint-fix": "eslint . --fix --ext .js,.jsx,.ts,.tsx",
29
29
  "stylelint-check": "stylelint **/*.scss",
@@ -46,6 +46,9 @@
46
46
  "WYSIWYG",
47
47
  "editor"
48
48
  ],
49
+ "dependencies": {
50
+ "@lesjoursfr/browser-tools": "^1.0.2"
51
+ },
49
52
  "devDependencies": {
50
53
  "@babel/core": "^7.23.5",
51
54
  "@babel/preset-env": "^7.23.5",
@@ -58,20 +61,20 @@
58
61
  "@types/color": "^3.0.6",
59
62
  "@types/jsdom": "^21.1.6",
60
63
  "@types/mocha": "^10.0.6",
61
- "@types/node": "^20.10.1",
62
- "@typescript-eslint/eslint-plugin": "^6.13.1",
63
- "@typescript-eslint/parser": "^6.13.1",
64
+ "@types/node": "^20.10.4",
65
+ "@typescript-eslint/eslint-plugin": "^6.13.2",
66
+ "@typescript-eslint/parser": "^6.13.2",
64
67
  "babel-loader": "^9.1.3",
65
68
  "codemirror": "^6.0.1",
66
69
  "css-loader": "^6.8.1",
67
70
  "css-minimizer-webpack-plugin": "^5.0.1",
68
- "eslint": "^8.54.0",
69
- "eslint-config-prettier": "^9.0.0",
71
+ "eslint": "^8.55.0",
72
+ "eslint-config-prettier": "^9.1.0",
70
73
  "fs-extra": "^11.2.0",
71
74
  "jsdom": "^23.0.1",
72
75
  "mini-css-extract-plugin": "^2.7.6",
73
76
  "mocha": "^10.2.0",
74
- "postcss": "^8.4.31",
77
+ "postcss": "^8.4.32",
75
78
  "prettier": "^3.1.0",
76
79
  "sass": "^1.69.5",
77
80
  "sass-loader": "^13.3.2",
@@ -80,7 +83,7 @@
80
83
  "terser-webpack-plugin": "^5.3.9",
81
84
  "ts-loader": "^9.5.1",
82
85
  "ts-node": "^10.9.1",
83
- "typescript": "^5.3.2",
86
+ "typescript": "^5.3.3",
84
87
  "webpack": "^5.89.0",
85
88
  "webpack-cli": "^5.1.4",
86
89
  "webpack-dev-server": "^4.15.1"
package/src/core/edit.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import {
2
- cleanDomContent,
3
2
  createNodeWith,
4
3
  hasClass,
5
4
  hasTagName,
@@ -8,9 +7,13 @@ import {
8
7
  removeCommentNodes,
9
8
  removeEmptyTextNodes,
10
9
  removeNodes,
10
+ replaceNodeStyleByTag,
11
+ replaceNodeWith,
12
+ resetAttributesTo,
11
13
  textifyNode,
14
+ trimTag,
12
15
  unwrapNode,
13
- } from "./dom.js";
16
+ } from "@lesjoursfr/browser-tools";
14
17
  import { getSelection, moveCursorAfterNode, moveCursorInsideNode, selectNodeContents, selectNodes } from "./range.js";
15
18
 
16
19
  /**
@@ -292,6 +295,95 @@ export function clearSelectionStyle(): void {
292
295
  }
293
296
  }
294
297
 
298
+ /**
299
+ * Clean the DOM content of the node
300
+ * @param {HTMLElement} root the node to process
301
+ * @param {object} style active styles for the root
302
+ */
303
+ export function cleanDomContent(root: HTMLElement, style: { [keyof: string]: boolean }): void {
304
+ // Iterate through children
305
+ for (let el of [...root.children] as HTMLElement[]) {
306
+ // Check if the span is an edith-nbsp
307
+ if (hasTagName(el, "span") && hasClass(el, "edith-nbsp")) {
308
+ // Ensure that we have a clean element
309
+ resetAttributesTo(el, { class: "edith-nbsp", contenteditable: "false" });
310
+ el.innerHTML = "¶";
311
+
312
+ continue;
313
+ }
314
+
315
+ // Check if there is a style attribute on the current node
316
+ if (el.hasAttribute("style")) {
317
+ // Replace the style attribute by tags
318
+ el = replaceNodeStyleByTag(el);
319
+ }
320
+
321
+ // Check if the Tag Match a Parent Tag
322
+ if (style[el.tagName]) {
323
+ el = replaceNodeWith(
324
+ el,
325
+ createNodeWith("span", { attributes: { style: el.getAttribute("style") || "" }, innerHTML: el.innerHTML })
326
+ );
327
+ }
328
+
329
+ // Save the Current Style Tag
330
+ const newTags = { ...style };
331
+ if (hasTagName(el, ["b", "i", "q", "u", "s"])) {
332
+ newTags[el.tagName] = true;
333
+ }
334
+
335
+ // Clean Children
336
+ cleanDomContent(el, newTags);
337
+
338
+ // Keep only href & target attributes for <a> tags
339
+ if (hasTagName(el, "a")) {
340
+ const linkAttributes: { href?: string; target?: string } = {};
341
+ if (el.hasAttribute("href")) {
342
+ linkAttributes.href = el.getAttribute("href")!;
343
+ }
344
+ if (el.hasAttribute("target")) {
345
+ linkAttributes.target = el.getAttribute("target")!;
346
+ }
347
+ resetAttributesTo(el, linkAttributes);
348
+ continue;
349
+ }
350
+
351
+ // Remove all tag attributes for tags in the allowed list
352
+ if (hasTagName(el, ["b", "i", "q", "u", "s", "br", "sup"])) {
353
+ resetAttributesTo(el, {});
354
+ continue;
355
+ }
356
+
357
+ // Remove useless tags
358
+ if (hasTagName(el, ["style", "meta", "link"])) {
359
+ el.remove();
360
+ continue;
361
+ }
362
+
363
+ // Check if it's a <p> tag
364
+ if (hasTagName(el, "p")) {
365
+ // Check if the element contains text
366
+ if (el.textContent === null || el.textContent.trim().length === 0) {
367
+ // Remove the node
368
+ el.remove();
369
+ continue;
370
+ }
371
+
372
+ // Remove all tag attributes
373
+ resetAttributesTo(el, {});
374
+
375
+ // Remove leading & trailing <br>
376
+ trimTag(el, "br");
377
+
378
+ // Return
379
+ continue;
380
+ }
381
+
382
+ // Unwrap the node
383
+ unwrapNode(el);
384
+ }
385
+ }
386
+
295
387
  /**
296
388
  * Clean the given HTML code.
297
389
  * @param {string} html the HTML code to clean