")}},quote:{icon:"“ ”",title:"Quote",result:function(){return c("formatBlock","
")}},olist:{icon:"#",title:"Ordered List",result:function(){return c("insertOrderedList")}},ulist:{icon:"•",title:"Unordered List",result:function(){return c("insertUnorderedList")}},code:{icon:"</>",title:"Code",result:function(){return c("formatBlock","")}},line:{icon:"―",title:"Horizontal Line",result:function(){return c("insertHorizontalRule")}},link:{icon:"🔗",title:"Link",result:function(){var t=window.prompt("Enter the link URL");t&&c("createLink",t)}},image:{icon:"📷",title:"Image",result:function(){var t=window.prompt("Enter the image URL");t&&c("insertImage",t)}}},a={actionbar:"pell-actionbar",button:"pell-button",content:"pell-content",selected:"pell-button-selected"},s=function(t){var o=t.actions?t.actions.map(function(t){return"string"==typeof t?l[t]:l[t.name]?e({},l[t.name],t):t}):Object.keys(l).map(function(t){return l[t]}),s=e({},a,t.classes),f=t.defaultParagraphSeparator||"div",d=i("div");d.className=s.actionbar,r(t.element,d);var m=t.element.content=i("div");return m.contentEditable=!0,m.className=s.content,m.oninput=function(e){var n=e.target.firstChild;n&&3===n.nodeType?c("formatBlock","<"+f+">"):"
"===m.innerHTML&&(m.innerHTML=""),t.onChange(m.innerHTML)},m.onkeydown=function(t){"Tab"===t.key?t.preventDefault():"Enter"===t.key&&"blockquote"===u("formatBlock")&&setTimeout(function(){return c("formatBlock","<"+f+">")},0)},r(t.element,m),o.forEach(function(t){var e=i("button");if(e.className=s.button,e.innerHTML=t.icon,e.title=t.title,e.setAttribute("type","button"),e.onclick=function(){return t.result()&&m.focus()},t.state){var o=function(){return e.classList[t.state()?"add":"remove"](s.selected)};n(m,"keyup",o),n(m,"mouseup",o),n(e,"click",o)}r(d,e)}),t.styleWithCSS&&c("styleWithCSS"),c("defaultParagraphSeparator",f),t.element},f={exec:c,init:s};t.exec=c,t.init=s,t.default=f,Object.defineProperty(t,"__esModule",{value:!0})}); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.pell={})}(this,function(t){"use strict";var s=Object.assign||function(t){for(var e=1;eB",title:"Bold",state:function(){return e("bold")},result:function(){return h("bold")}},italic:{icon:"I",title:"Italic",state:function(){return e("italic")},result:function(){return h("italic")}},underline:{icon:"U",title:"Underline",state:function(){return e("underline")},result:function(){return h("underline")}},strikethrough:{icon:" S",title:"Strike-through",state:function(){return e("strikeThrough")},result:function(){return h("strikeThrough")}},heading1:{icon:"H1",title:"Heading 1",result:function(){return h(f,"")}},heading2:{icon:"H2",title:"Heading 2",result:function(){return h(f,"
")}},paragraph:{icon:"¶",title:"Paragraph",result:function(){return h(f,"
")}},quote:{icon:"“ ”",title:"Quote",result:function(){return h(f,"
")}},olist:{icon:"#",title:"Ordered List",result:function(){return h("insertOrderedList")}},ulist:{icon:"•",title:"Unordered List",result:function(){return h("insertUnorderedList")}},code:{icon:"</>",title:"Code",result:function(){return h(f,"")}},line:{icon:"―",title:"Horizontal Line",result:function(){return h("insertHorizontalRule")}},link:{icon:"🔗",title:"Link",result:function(){var t=window.prompt("Enter the link URL");t&&h("createLink",t)}},image:{icon:"📷",title:"Image",result:function(){var t=window.prompt("Enter the image URL");t&&h("insertImage",t)}}},g={actionbar:"pell-actionbar",button:"pell-button",content:"pell-content",selected:"pell-button-selected"},n=function(n){var t=n.actions?n.actions.map(function(t){return"string"==typeof t?v[t]:v[t.name]?s({},v[t.name],t):t}):Object.keys(v).map(function(t){return v[t]}),r=s({},g,n.classes),i=n[d]||"div",o=m("div");o.className=r.actionbar,b(n.element,o);var u=n.element.content=m("div");u.contentEditable=!0,u.className=r.content;var e,c,l,a;return e=u,c=function(t){var e=t[0].target.firstChild;e&&3===e.nodeType?h(f,"<"+i+">"):"
"===u.innerHTML&&(u.innerHTML=""),n.onChange(u.innerHTML)},l=250,a=void 0,new MutationObserver(function(){var t=this,e=arguments;clearTimeout(a),a=setTimeout(function(){return c.apply(t,e)},l)}).observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0}),u.onkeydown=function(t){var e;"Tab"===t.key?t.preventDefault():"Enter"===t.key&&"blockquote"===(e=f,document.queryCommandValue(e))&&setTimeout(function(){return h(f,"<"+i+">")},0)},b(n.element,u),t.forEach(function(t){var e=m("button");if(e.className=r.button,e.innerHTML=t.icon,e.title=t.title,e.setAttribute("type","button"),e.onclick=function(){return t.result()&&u.focus()},t.state){var n=function(){return e.classList[t.state()?"add":"remove"](r.selected)};p(u,"keyup",n),p(u,"mouseup",n),p(e,"click",n)}b(o,e)}),n.styleWithCSS&&h("styleWithCSS"),h(d,i),n.element},r={exec:h,init:n};t.exec=h,t.init=n,t.default=r,Object.defineProperty(t,"__esModule",{value:!0})}); diff --git a/src/pell.js b/src/pell.js index 3e2fe64..cc3e3e4 100644 --- a/src/pell.js +++ b/src/pell.js @@ -1,5 +1,28 @@ const defaultParagraphSeparatorString = 'defaultParagraphSeparator' const formatBlock = 'formatBlock' + +const debounce = (fn, time) => { + let timeout + + return function() { + const functionCall = () => fn.apply(this, arguments); + + clearTimeout(timeout) + timeout = setTimeout(functionCall, time) + } +} +const getMutationObserver = (elementSelector, callback) => { + const observer = new MutationObserver(callback) + const config = { + attributes: true, + childList: true, + characterData: true, + subtree: true + } + observer.observe(elementSelector, config) + + return observer +} const addEventListener = (parent, type, listener) => parent.addEventListener(type, listener) const appendChild = (parent, child) => parent.appendChild(child) const createElement = tag => document.createElement(tag) @@ -120,11 +143,22 @@ export const init = settings => { const content = settings.element.content = createElement('div') content.contentEditable = true content.className = classes.content - content.oninput = ({ target: { firstChild } }) => { - if (firstChild && firstChild.nodeType === 3) exec(formatBlock, `<${defaultParagraphSeparator}>`) - else if (content.innerHTML === '
') content.innerHTML = '' + + const divOnInputChange = (observer) => { + const firstChild = observer[0].target.firstChild; + + if (firstChild && firstChild.nodeType === 3) { + exec(formatBlock, '<' + defaultParagraphSeparator + '>') + } else if (content.innerHTML === '
') { + content.innerHTML = '' + } settings.onChange(content.innerHTML) } + + // Observe any changes on content block (work on IE) + // Rate-limit event handling with debounce to reduce performance impact + getMutationObserver(content, debounce(divOnInputChange, 250)) + content.onkeydown = event => { if (event.key === 'Tab') { event.preventDefault()