diff --git a/.gitignore b/.gitignore index 9daa824..6423b8b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store node_modules +.vscode diff --git a/README.md b/README.md index 7d0b330..c936a37 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Included actions: - Code - Horizontal Rule - Link -- Image +- Image (support uploading) Other available actions (listed at https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand): - Justify Center diff --git a/demo.html b/demo.html index 45583cb..8de87a4 100644 --- a/demo.html +++ b/demo.html @@ -50,6 +50,11 @@

HTML output:

onChange: function (html) { document.getElementById('text-output').innerHTML = html document.getElementById('html-output').textContent = html + }, + // if you want to upload image to server + // you must config this + upload: { + api: 'http://localhost:53595/api/files/img/upload' } }) diff --git a/dist/pell.js b/dist/pell.js index 62ddf0c..a0fe129 100644 --- a/dist/pell.js +++ b/dist/pell.js @@ -138,10 +138,63 @@ var defaultActions = { icon: '📷', title: 'Image', result: function result() { - var url = window.prompt('Enter the image URL'); - if (url) exec('insertImage', url); + // const url = window.prompt('Enter the image URL') + // if (url) exec('insertImage', url) + execInsertImageAction(); } } + + // default for not set `upload` config +};var execInsertImageAction = function execInsertImageAction() { + var uploadImageInput = document.querySelector('.pell input[type="file"]'); + if (!uploadImageInput) { + var url = window.prompt('Enter the image URL'); + if (url) exec('insertImage', url); + } else { + uploadImageInput.click(); + } +}; + +// just set `url`, `method` and `body` for fetch api +var uploadImage = function uploadImage(_ref, success, error) { + var api = _ref.api, + data = _ref.data; + + window.fetch && window.fetch(api, { + method: 'POST', + body: data + }).then(function (res) { + return res.json(); + }).then(function (data) { + // responsive data format: + // { success: true, url: 'xxx' } + if (data.success) success(data.url); + }, function (err) { + return error(err); + }); +}; + +var initUploadImageInput = function initUploadImageInput(settings) { + var uploadAPI = settings.upload && settings.upload.api; + if (uploadAPI) { + var input = createElement('input'); + input.type = 'file'; + input.hidden = true; + addEventListener(input, 'change', function (e) { + var image = e.target.files[0]; + var fd = new window.FormData(); + fd.append('pell-upload-image', image); + uploadImage({ + api: uploadAPI, + data: fd + }, function (url) { + return exec('insertImage', url); + }, function (err) { + return window.alert(err); + }); + }); + appendChild(settings.element, input); + } }; var defaultClasses = { @@ -170,8 +223,8 @@ var init = function init(settings) { var content = settings.element.content = createElement('div'); content.contentEditable = true; content.className = classes.content; - content.oninput = function (_ref) { - var firstChild = _ref.target.firstChild; + content.oninput = function (_ref2) { + var firstChild = _ref2.target.firstChild; if (firstChild && firstChild.nodeType === 3) exec(formatBlock, '<' + defaultParagraphSeparator + '>');else if (content.innerHTML === '
') content.innerHTML = ''; settings.onChange(content.innerHTML); @@ -212,6 +265,9 @@ var init = function init(settings) { if (settings.styleWithCSS) exec('styleWithCSS'); exec(defaultParagraphSeparatorString, defaultParagraphSeparator); + // init a upload image input or not + initUploadImageInput(settings); + return settings.element; }; diff --git a/dist/pell.min.js b/dist/pell.min.js index 8615f8e..96fbcd0 100644 --- a/dist/pell.min.js +++ b/dist/pell.min.js @@ -1 +1 @@ -!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 e=Object.assign||function(t){for(var e=1;e1&&void 0!==arguments[1]?arguments[1]:null;return document.execCommand(t,!1,e)},l={bold:{icon:"B",title:"Bold",state:function(){return o("bold")},result:function(){return c("bold")}},italic:{icon:"I",title:"Italic",state:function(){return o("italic")},result:function(){return c("italic")}},underline:{icon:"U",title:"Underline",state:function(){return o("underline")},result:function(){return c("underline")}},strikethrough:{icon:"S",title:"Strike-through",state:function(){return o("strikeThrough")},result:function(){return c("strikeThrough")}},heading1:{icon:"H1",title:"Heading 1",result:function(){return c("formatBlock","

")}},heading2:{icon:"H2",title:"Heading 2",result:function(){return c("formatBlock","

")}},paragraph:{icon:"¶",title:"Paragraph",result:function(){return c("formatBlock","

")}},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 e=Object.assign||function(t){for(var e=1;e1&&void 0!==arguments[1]?arguments[1]:null;return document.execCommand(t,!1,e)},a={bold:{icon:"B",title:"Bold",state:function(){return o("bold")},result:function(){return c("bold")}},italic:{icon:"I",title:"Italic",state:function(){return o("italic")},result:function(){return c("italic")}},underline:{icon:"U",title:"Underline",state:function(){return o("underline")},result:function(){return c("underline")}},strikethrough:{icon:"S",title:"Strike-through",state:function(){return o("strikeThrough")},result:function(){return c("strikeThrough")}},heading1:{icon:"H1",title:"Heading 1",result:function(){return c("formatBlock","

")}},heading2:{icon:"H2",title:"Heading 2",result:function(){return c("formatBlock","

")}},paragraph:{icon:"¶",title:"Paragraph",result:function(){return c("formatBlock","

")}},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(){l()}}},l=function(){var t=document.querySelector('.pell input[type="file"]');if(t)t.click();else{var e=window.prompt("Enter the image URL");e&&c("insertImage",e)}},s=function(t,e,n){var r=t.api,i=t.data;window.fetch&&window.fetch(r,{method:"POST",body:i}).then(function(t){return t.json()}).then(function(t){t.success&&e(t.url)},function(t){return n(t)})},f=function(t){var e=t.upload&&t.upload.api;if(e){var o=i("input");o.type="file",o.hidden=!0,n(o,"change",function(t){var n=t.target.files[0],r=new window.FormData;r.append("pell-upload-image",n),s({api:e,data:r},function(t){return c("insertImage",t)},function(t){return window.alert(t)})}),r(t.element,o)}},d={actionbar:"pell-actionbar",button:"pell-button",content:"pell-content",selected:"pell-button-selected"},p=function(t){var o=t.actions?t.actions.map(function(t){return"string"==typeof t?a[t]:a[t.name]?e({},a[t.name],t):t}):Object.keys(a).map(function(t){return a[t]}),l=e({},d,t.classes),s=t.defaultParagraphSeparator||"div",p=i("div");p.className=l.actionbar,r(t.element,p);var m=t.element.content=i("div");return m.contentEditable=!0,m.className=l.content,m.oninput=function(e){var n=e.target.firstChild;n&&3===n.nodeType?c("formatBlock","<"+s+">"):"
"===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","<"+s+">")},0)},r(t.element,m),o.forEach(function(t){var e=i("button");if(e.className=l.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"](l.selected)};n(m,"keyup",o),n(m,"mouseup",o),n(e,"click",o)}r(p,e)}),t.styleWithCSS&&c("styleWithCSS"),c("defaultParagraphSeparator",s),f(t),t.element},m={exec:c,init:p};t.exec=c,t.init=p,t.default=m,Object.defineProperty(t,"__esModule",{value:!0})}); diff --git a/src/pell.js b/src/pell.js index 3e2fe64..ef159c6 100644 --- a/src/pell.js +++ b/src/pell.js @@ -85,12 +85,67 @@ const defaultActions = { icon: '📷', title: 'Image', result: () => { - const url = window.prompt('Enter the image URL') - if (url) exec('insertImage', url) + // const url = window.prompt('Enter the image URL') + // if (url) exec('insertImage', url) + execInsertImageAction() } } } +// default for not set `upload` config +const execInsertImageAction = function () { + const uploadImageInput = document.querySelector('.pell input[type="file"]') + if (!uploadImageInput) { + const url = window.prompt('Enter the image URL') + if (url) exec('insertImage', url) + } else { + uploadImageInput.click() + } +} + +// just set `url`, `method` and `body` for fetch api +const uploadImage = function ({ api, data }, success, error) { + window.fetch && window.fetch( + api, + { + method: 'POST', + body: data + } + ) + .then(res => res.json()) + .then( + data => { + // responsive data format: + // { success: true, url: 'xxx' } + if (data.success) success(data.url) + }, + err => error(err) + ) +} + +const initUploadImageInput = function (settings) { + const uploadAPI = settings.upload && settings.upload.api + if (uploadAPI) { + const input = createElement('input') + input.type = 'file' + input.hidden = true + addEventListener(input, 'change', e => { + const image = e.target.files[0] + const fd = new window.FormData() + fd.append('pell-upload-image', image) + uploadImage( + { + api: uploadAPI, + data: fd + }, + url => exec('insertImage', url), + err => window.alert(err) + ) + }) + appendChild(settings.element, input) + } +} + const defaultClasses = { actionbar: 'pell-actionbar', button: 'pell-button', @@ -155,6 +210,9 @@ export const init = settings => { if (settings.styleWithCSS) exec('styleWithCSS') exec(defaultParagraphSeparatorString, defaultParagraphSeparator) + // init a upload image input or not + initUploadImageInput(settings) + return settings.element } diff --git a/src/pell.scss b/src/pell.scss index 16c1819..7130cda 100644 --- a/src/pell.scss +++ b/src/pell.scss @@ -38,4 +38,4 @@ $pell-content-padding: 10px !default; .pell-button-selected { background-color: $pell-button-selected-color; -} +} \ No newline at end of file