diff --git a/README.md b/README.md index 11e2d9a1..4e1eee87 100644 --- a/README.md +++ b/README.md @@ -1,150 +1,40 @@ -[![npm version](https://badge.fury.io/js/codeflask.svg)](https://www.npmjs.com/package/codeflask) -[![Build Status](https://travis-ci.org/kazzkiq/CodeFlask.svg?branch=master)](https://travis-ci.org/kazzkiq/CodeFlask) - -

-
- CodeFlask: A micro code-editor for awesome web pages. -

- -

- -

- -## Installation - -You can install CodeFlask via npm: - -``` -npm install codeflask -``` - -Or use it directly in browser via cdn service: - -``` -https://unpkg.com/codeflask/build/codeflask.min.js -``` - -## Usage - -```js -import CodeFlask from 'codeflask'; - -const flask = new CodeFlask('#my-selector', { language: 'js' }); -``` -You can also pass a DOM element instead of a selector: -```js -import CodeFlask from 'codeflask'; - -const editorElem = document.getElementById('editor'); -const flask = new CodeFlask(editorElem, { language: 'js' }); -``` -Usage with Shadow DOM: -```js -import CodeFlask from 'codeflask'; -... -const shadowElem = this.shadowRoot.querySelector('#editor'); -const flask = new CodeFlask(shadowElem, { language: 'js', styleParent: this.shadowRoot }); -``` -### Listening for changes in editor - -```js -flask.onUpdate((code) => { - // do something with code here. - // this will trigger whenever the code - // in the editor changes. -}); -``` - -### Updating the editor programatically - -```js -// This will also trigger .onUpdate() -flask.updateCode('const my_new_code_here = "Blabla"'); -``` - -### Getting the current code from editor - -```js -const code = flask.getCode(); -``` - -### Enabling line numbers - -```js -import CodeFlask from 'codeflask'; - -const flask = new CodeFlask('#my-selector', { - language: 'js', - lineNumbers: true -}); -``` - -### Enabling rtl (right to left writing) - -```js -import CodeFlask from 'codeflask'; - -const flask = new CodeFlask('#my-selector', { - language: 'js', - rtl: true -}); -``` - -### Enabling read only mode - -```js -import CodeFlask from 'codeflask'; - -const flask = new CodeFlask('#my-selector', { - language: 'js', - readonly: true -}); -``` - -### Adding other languages support: - -```js -flask.addLanguage('ruby', options) -``` - -#### For Example to add 'Ruby' - -```js -import Prism from 'prismjs'; -import CodeFlask from 'codeflask'; - -const flask = new CodeFlask('#my-selector', { - language: 'ruby', - readonly: true -}); - -flask.addLanguage('ruby', Prism.languages['ruby']); -``` - -This API is simply a proxy to add a new language to [Prism](http://prismjs.com/) itself (the code highlighter). The `options` parameter must be the same accepted in Prism. You can read more about it [here](http://prismjs.com/extending.html#language-definitions). - -By default, CodeFlask supports the following languages (which are also the default supported in Prism): - -- Markup (HTML/XML); -- CSS; -- C-like; -- JavaScript; - -### Adding your own theme to CodeFlask - -By default, CodeFlask comes with a simple theme made from scratch called **[CodeNoon](https://github.com/kazzkiq/CodeFlask.js/blob/master/src/styles/theme-default.js)**. - -You can easily override this theme with your own by writting your own CSS and adding it to your project. If that's the case, you should also disable **CodeNoon** with the `defaultTheme` option: - -```js -import CodeFlask from 'codeflask'; - -const flask = new CodeFlask('#my-selector', { - language: 'js', - defaultTheme: false -}); -``` - -# Credits & Thanks - -CodeFlask.js was made possible by awesome open-source projects such as [Prism.js](https://github.com/PrismJS/prism) and [Rollup](https://github.com/rollup/rollup). +# CodeFlask Mod + +```bash +npm i '@acarl005/codeflask' +``` + +I modified [CodeFlask](https://github.com/kazzkiq/CodeFlask) to be able to... + +1. Make PrismJS a peer dependency +1. Attach (and remove) custom event listeners to the editor +1. Support the [Line Highlight](https://prismjs.com/plugins/line-highlight/) plugin in PrismJS +1. Fix this issue: kazzkiq/CodeFlask#69 +1. Fix bugs with tab hotkey for indentation +1. Make the self-closing characters configurable + +```javascript +import CodeFlask from "codeflask" +import Prism from "prismjs" + +const flask = new CodeFlask(editor, Prism, { + language: "js", + selfClosingCharacters: ['(', '[', '{', "'", '"'], + customEventListeners: { + "keydown": e => { + if (e.key == "Enter") { + e.preventDefault() + e.stopImmediatePropagation() + // do custom stuff + } + } + } +}) + +flask.highlightLines("4-7") +``` + +PrismJS is highly customizable. +It actually offers custom builds with more plugins that you can opt into. +This is an awesome and rare feature b/c you can minimize the bundle by omitting unneeded functionality. +Therefore, it should be a peer dependency, b/c CodeFlask can't know which build with which plugins you'll need. diff --git a/build/codeflask.min.js b/build/codeflask.min.js index faa1b340..b8715681 100644 --- a/build/codeflask.min.js +++ b/build/codeflask.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.CodeFlask=t()}(this,function(){"use strict";var e,t,n,a='"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',s="\n .codeflask {\n position: absolute;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n .codeflask, .codeflask * {\n box-sizing: border-box;\n }\n\n .codeflask__pre {\n pointer-events: none;\n z-index: 3;\n overflow: hidden;\n }\n\n .codeflask__textarea {\n background: none;\n border: none;\n color: "+(e="caret-color",t="#000",(CSS?CSS.supports(e,t):(n=(n=e).split("-").filter(function(e){return!!e}).map(function(e){return e[0].toUpperCase()+e.substr(1)}).join(""))[0].toLowerCase()+n.substr(1)in document.body.style)?"#fff":"#ccc")+";\n z-index: 1;\n resize: none;\n font-family: "+a+";\n -webkit-appearance: pre;\n caret-color: #111;\n z-index: 2;\n width: 100%;\n height: 100%;\n }\n\n .codeflask--has-line-numbers .codeflask__textarea {\n width: calc(100% - 40px);\n }\n\n .codeflask__code {\n display: block;\n font-family: "+a+";\n overflow: hidden;\n }\n\n .codeflask__flatten {\n padding: 10px;\n font-size: 13px;\n line-height: 20px;\n white-space: pre;\n position: absolute;\n top: 0;\n left: 0;\n overflow: auto;\n margin: 0 !important;\n outline: none;\n text-align: left;\n }\n\n .codeflask--has-line-numbers .codeflask__flatten {\n width: calc(100% - 40px);\n left: 40px;\n }\n\n .codeflask__line-highlight {\n position: absolute;\n top: 10px;\n left: 0;\n width: 100%;\n height: 20px;\n background: rgba(0,0,0,0.1);\n z-index: 1;\n }\n\n .codeflask__lines {\n padding: 10px 4px;\n font-size: 12px;\n line-height: 20px;\n font-family: 'Cousine', monospace;\n position: absolute;\n left: 0;\n top: 0;\n width: 40px;\n height: 100%;\n text-align: right;\n color: #999;\n z-index: 2;\n }\n\n .codeflask__lines__line {\n display: block;\n }\n\n .codeflask.codeflask--has-line-numbers {\n padding-left: 40px;\n }\n\n .codeflask.codeflask--has-line-numbers:before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: 40px;\n height: 100%;\n background: #eee;\n z-index: 1;\n }\n";function i(e,t,n){var a=t||"codeflask-style",s=n||document.head;if(!e)return!1;if(document.getElementById(a))return!0;var i=document.createElement("style");return i.innerHTML=e,i.id=a,s.appendChild(i),!0}var r={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function o(e){return String(e).replace(/[&<>"'`=/]/g,function(e){return r[e]})}var l="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var c,u=(function(e){var t="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},n=function(){var e=/\blang(?:uage)?-([\w-]+)\b/i,n=0,a=t.Prism={manual:t.Prism&&t.Prism.manual,disableWorkerMessageHandler:t.Prism&&t.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof s?new s(e.type,a.util.encode(e.content),e.alias):"Array"===a.util.type(e)?e.map(a.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(x instanceof l)){if(f&&k!=t.length-1){if(h.lastIndex=v,!(L=h.exec(e)))break;for(var w=L.index+(g?L[1].length:0),C=L.index+L[0].length,S=k,T=v,A=t.length;S=(T+=t[S].length)&&(++k,v=T);if(t[k]instanceof l)continue;F=S-k,x=e.slice(v,T),L.index-=v}else{h.lastIndex=0;var L=h.exec(x),F=1}if(L){g&&(m=L[1]?L[1].length:0);C=(w=L.index+m)+(L=L[0].slice(m)).length;var E=x.slice(0,w),_=x.slice(C),N=[k,F];E&&(++k,v+=E.length,N.push(E));var j=new l(c,p?a.tokenize(L,p):L,b,L,f);if(N.push(j),_&&N.push(_),Array.prototype.splice.apply(t,N),1!=F&&a.matchGrammar(e,t,n,k,v,!0,c),r)break}else if(r)break}}}}},tokenize:function(e,t,n){var s=[e],i=t.rest;if(i){for(var r in i)t[r]=i[r];delete t.rest}return a.matchGrammar(e,s,t,0,0,!1),s},hooks:{all:{},add:function(e,t){var n=a.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=a.hooks.all[e];if(n&&n.length)for(var s,i=0;s=n[i++];)s(t)}}},s=a.Token=function(e,t,n,a,s){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length,this.greedy=!!s};if(s.stringify=function(e,t,n){if("string"==typeof e)return e;if("Array"===a.util.type(e))return e.map(function(n){return s.stringify(n,t,e)}).join("");var i={type:e.type,content:s.stringify(e.content,t,n),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:n};if(e.alias){var r="Array"===a.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,r)}a.hooks.run("wrap",i);var o=Object.keys(i.attributes).map(function(e){return e+'="'+(i.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""},!t.document)return t.addEventListener?(a.disableWorkerMessageHandler||t.addEventListener("message",function(e){var n=JSON.parse(e.data),s=n.language,i=n.code,r=n.immediateClose;t.postMessage(a.highlight(i,a.languages[s],s)),r&&t.close()},!1),t.Prism):t.Prism;var i=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return i&&(a.filename=i.src,a.manual||i.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(a.highlightAll):window.setTimeout(a.highlightAll,16):document.addEventListener("DOMContentLoaded",a.highlightAll))),t.Prism}();e.exports&&(e.exports=n),void 0!==l&&(l.Prism=n),n.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/(^|[^\\])["']/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},n.languages.markup.tag.inside["attr-value"].inside.entity=n.languages.markup.entity,n.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),n.languages.xml=n.languages.markup,n.languages.html=n.languages.markup,n.languages.mathml=n.languages.markup,n.languages.svg=n.languages.markup,n.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^{}\s][^{};]*?(?=\s*\{)/,string:{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/\B!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},n.languages.css.atrule.inside.rest=n.languages.css,n.languages.markup&&(n.languages.insertBefore("markup","tag",{style:{pattern:/()[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:n.languages.css,alias:"language-css",greedy:!0}}),n.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:n.languages.css}},alias:"language-css"}},n.languages.markup.tag)),n.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/[a-z0-9_]+(?=\()/i,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},n.languages.javascript=n.languages.extend("clike",{keyword:/\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/,function:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),n.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i,alias:"function"},constant:/\b[A-Z][A-Z\d_]*\b/}),n.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\${[^}]+}/,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}}}),n.languages.javascript["template-string"].inside.interpolation.inside.rest=n.languages.javascript,n.languages.markup&&n.languages.insertBefore("markup","tag",{script:{pattern:/()[\s\S]*?(?=<\/script>)/i,lookbehind:!0,inside:n.languages.javascript,alias:"language-javascript",greedy:!0}}),n.languages.js=n.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var a,s=t.getAttribute("data-src"),i=t,r=/\blang(?:uage)?-([\w-]+)\b/i;i&&!r.test(i.className);)i=i.parentNode;if(i&&(a=(t.className.match(r)||[,""])[1]),!a){var o=(s.match(/\.(\w+)$/)||[,""])[1];a=e[o]||o}var l=document.createElement("code");l.className="language-"+a,t.textContent="",l.textContent="Loading…",t.appendChild(l);var c=new XMLHttpRequest;c.open("GET",s,!0),c.onreadystatechange=function(){4==c.readyState&&(c.status<400&&c.responseText?(l.textContent=c.responseText,n.highlightElement(l)):c.status>=400?l.textContent="✖ Error "+c.status+" while fetching file: "+c.statusText:l.textContent="✖ Error: File does not exist or is empty")},c.send(null)}),n.plugins.toolbar&&n.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),a=document.createElement("a");return a.textContent=t.getAttribute("data-download-link-label")||"Download",a.setAttribute("download",""),a.href=n,a}})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight))}(c={exports:{}},c.exports),c.exports),d=function(e,t){if(!e)throw Error("CodeFlask expects a parameter which is Element or a String selector");if(!t)throw Error("CodeFlask expects an object containing options as second parameter");if(e.nodeType)this.editorRoot=e;else{var n=document.querySelector(e);n&&(this.editorRoot=n)}this.opts=t,this.startEditor()};return d.prototype.startEditor=function(){if(!i(s,null,this.opts.styleParent))throw Error("Failed to inject CodeFlask CSS.");this.createWrapper(),this.createTextarea(),this.createPre(),this.createCode(),this.runOptions(),this.listenTextarea(),this.populateDefault(),this.updateCode(this.code)},d.prototype.createWrapper=function(){this.code=this.editorRoot.innerHTML,this.editorRoot.innerHTML="",this.elWrapper=this.createElement("div",this.editorRoot),this.elWrapper.classList.add("codeflask")},d.prototype.createTextarea=function(){this.elTextarea=this.createElement("textarea",this.elWrapper),this.elTextarea.classList.add("codeflask__textarea","codeflask__flatten")},d.prototype.createPre=function(){this.elPre=this.createElement("pre",this.elWrapper),this.elPre.classList.add("codeflask__pre","codeflask__flatten")},d.prototype.createCode=function(){this.elCode=this.createElement("code",this.elPre),this.elCode.classList.add("codeflask__code","language-"+(this.opts.language||"html"))},d.prototype.createLineNumbers=function(){this.elLineNumbers=this.createElement("div",this.elWrapper),this.elLineNumbers.classList.add("codeflask__lines"),this.setLineNumber()},d.prototype.createElement=function(e,t){var n=document.createElement(e);return t.appendChild(n),n},d.prototype.runOptions=function(){this.opts.rtl=this.opts.rtl||!1,this.opts.tabSize=this.opts.tabSize||2,this.opts.enableAutocorrect=this.opts.enableAutocorrect||!1,this.opts.lineNumbers=this.opts.lineNumbers||!1,this.opts.defaultTheme=!1!==this.opts.defaultTheme,this.opts.areaId=this.opts.areaId||null,this.opts.ariaLabelledby=this.opts.ariaLabelledby||null,this.opts.readonly=this.opts.readonly||null,"boolean"!=typeof this.opts.handleTabs&&(this.opts.handleTabs=!0),"boolean"!=typeof this.opts.handleSelfClosingCharacters&&(this.opts.handleSelfClosingCharacters=!0),"boolean"!=typeof this.opts.handleNewLineIndentation&&(this.opts.handleNewLineIndentation=!0),!0===this.opts.rtl&&(this.elTextarea.setAttribute("dir","rtl"),this.elPre.setAttribute("dir","rtl")),!1===this.opts.enableAutocorrect&&(this.elTextarea.setAttribute("spellcheck","false"),this.elTextarea.setAttribute("autocapitalize","off"),this.elTextarea.setAttribute("autocomplete","off"),this.elTextarea.setAttribute("autocorrect","off")),this.opts.lineNumbers&&(this.elWrapper.classList.add("codeflask--has-line-numbers"),this.createLineNumbers()),this.opts.defaultTheme&&i("\n.codeflask {\n background: #fff;\n color: #4f559c;\n}\n\n.codeflask .token.punctuation {\n color: #4a4a4a;\n}\n\n.codeflask .token.keyword {\n color: #8500ff;\n}\n\n.codeflask .token.operator {\n color: #ff5598;\n}\n\n.codeflask .token.string {\n color: #41ad8f;\n}\n\n.codeflask .token.comment {\n color: #9badb7;\n}\n\n.codeflask .token.function {\n color: #8500ff;\n}\n\n.codeflask .token.boolean {\n color: #8500ff;\n}\n\n.codeflask .token.number {\n color: #8500ff;\n}\n\n.codeflask .token.selector {\n color: #8500ff;\n}\n\n.codeflask .token.property {\n color: #8500ff;\n}\n\n.codeflask .token.tag {\n color: #8500ff;\n}\n\n.codeflask .token.attr-value {\n color: #8500ff;\n}\n","theme-default",this.opts.styleParent),this.opts.areaId&&this.elTextarea.setAttribute("id",this.opts.areaId),this.opts.ariaLabelledby&&this.elTextarea.setAttribute("aria-labelledby",this.opts.ariaLabelledby),this.opts.readonly&&this.enableReadonlyMode()},d.prototype.updateLineNumbersCount=function(){for(var e="",t=1;t<=this.lineNumber;t++)e=e+''+t+"";this.elLineNumbers.innerHTML=e},d.prototype.listenTextarea=function(){var e=this;this.elTextarea.addEventListener("input",function(t){e.code=t.target.value,e.elCode.innerHTML=o(t.target.value),e.highlight(),setTimeout(function(){e.runUpdate(),e.setLineNumber()},1)}),this.elTextarea.addEventListener("keydown",function(t){e.handleTabs(t),e.handleSelfClosingCharacters(t),e.handleNewLineIndentation(t)}),this.elTextarea.addEventListener("scroll",function(t){e.elPre.style.transform="translate3d(-"+t.target.scrollLeft+"px, -"+t.target.scrollTop+"px, 0)",e.elLineNumbers&&(e.elLineNumbers.style.transform="translate3d(0, -"+t.target.scrollTop+"px, 0)")})},d.prototype.handleTabs=function(e){if(this.opts.handleTabs){if(9!==e.keyCode)return;e.preventDefault();var t=this.elTextarea,n=t.selectionDirection,a=t.selectionStart,s=t.selectionEnd,i=t.value,r=i.substr(0,a),o=i.substring(a,s),l=i.substring(s),c=" ".repeat(this.opts.tabSize);if(a!==s&&o.length>=c.length){var u=a-r.split("\n").pop().length,d=c.length,h=c.length;if(e.shiftKey)i.substr(u,c.length)===c?(d=-d,u>a?(o=o.substring(0,u)+o.substring(u+c.length),h=0):u===a?(d=0,h=0,o=o.substring(c.length)):(h=-h,r=r.substring(0,u)+r.substring(u+c.length))):(d=0,h=0),o=o.replace(new RegExp("\n"+c.split("").join("\\"),"g"),"\n");else r=r.substr(0,u)+c+r.substring(u,a),o=o.replace(/\n/g,"\n"+c);t.value=r+o+l,t.selectionStart=a+d,t.selectionEnd=a+o.length+h,t.selectionDirection=n}else t.value=r+c+l,t.selectionStart=a+c.length,t.selectionEnd=a+c.length;var p=t.value;this.updateCode(p),this.elTextarea.selectionEnd=s+this.opts.tabSize}},d.prototype.handleSelfClosingCharacters=function(e){if(this.opts.handleSelfClosingCharacters){var t=e.key;if(["(","[","{","<","'",'"'].includes(t)||[")","]","}",">","'",'"'].includes(t))switch(t){case"(":case")":this.closeCharacter(t);break;case"[":case"]":this.closeCharacter(t);break;case"{":case"}":this.closeCharacter(t);break;case"<":case">":case"'":case'"':this.closeCharacter(t)}}},d.prototype.setLineNumber=function(){this.lineNumber=this.code.split("\n").length,this.opts.lineNumbers&&this.updateLineNumbersCount()},d.prototype.handleNewLineIndentation=function(e){if(this.opts.handleNewLineIndentation&&13===e.keyCode){e.preventDefault();var t=this.elTextarea,n=t.selectionStart,a=t.selectionEnd,s=t.value,i=s.substr(0,n),r=s.substring(a),o=s.lastIndexOf("\n",n-1),l=o+s.slice(o+1).search(/[^ ]|$/),c=l>o?l-o:0,u=i+"\n"+" ".repeat(c)+r;t.value=u,t.selectionStart=n+c+1,t.selectionEnd=n+c+1,this.updateCode(t.value)}},d.prototype.closeCharacter=function(e){var t=this.elTextarea.selectionStart,n=this.elTextarea.selectionEnd;if(this.skipCloseChar(e)){var a=this.code.substr(n,1)===e,s=a?n+1:n,i=!a&&["'",'"'].includes(e)?e:"",r=""+this.code.substring(0,t)+i+this.code.substring(s);this.updateCode(r),this.elTextarea.selectionEnd=++this.elTextarea.selectionStart}else{var o=e;switch(e){case"(":o=String.fromCharCode(e.charCodeAt()+1);break;case"<":case"{":case"[":o=String.fromCharCode(e.charCodeAt()+2)}var l=this.code.substring(t,n),c=""+this.code.substring(0,t)+l+o+this.code.substring(n);this.updateCode(c)}this.elTextarea.selectionEnd=t},d.prototype.skipCloseChar=function(e){var t=this.elTextarea.selectionStart,n=this.elTextarea.selectionEnd,a=Math.abs(n-t)>0;return[")","}","]",">"].includes(e)||["'",'"'].includes(e)&&!a},d.prototype.updateCode=function(e){this.code=e,this.elTextarea.value=e,this.elCode.innerHTML=o(e),this.highlight(),this.setLineNumber(),setTimeout(this.runUpdate.bind(this),1)},d.prototype.updateLanguage=function(e){var t=this.opts.language;this.elCode.classList.remove("language-"+t),this.elCode.classList.add("language-"+e),this.opts.language=e,this.highlight()},d.prototype.addLanguage=function(e,t){u.languages[e]=t},d.prototype.populateDefault=function(){this.updateCode(this.code)},d.prototype.highlight=function(){u.highlightElement(this.elCode,!1)},d.prototype.onUpdate=function(e){if(e&&"[object Function]"!=={}.toString.call(e))throw Error("CodeFlask expects callback of type Function");this.updateCallBack=e},d.prototype.getCode=function(){return this.code},d.prototype.runUpdate=function(){this.updateCallBack&&this.updateCallBack(this.code)},d.prototype.enableReadonlyMode=function(){this.elTextarea.setAttribute("readonly",!0)},d.prototype.disableReadonlyMode=function(){this.elTextarea.removeAttribute("readonly")},d}); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).CodeFlask=t()}(this,(function(){"use strict";const e="#fff";const t='"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace';var n,s,o;const a=`\n .codeflask {\n position: absolute;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n .codeflask, .codeflask * {\n box-sizing: border-box;\n }\n\n .codeflask__pre {\n pointer-events: none;\n z-index: 3;\n overflow: hidden;\n }\n\n .codeflask__textarea {\n background: none;\n border: none;\n color: ${n="caret-color",s="#000",("undefined"!=typeof CSS?CSS.supports(n,s):"undefined"!=typeof document&&(o=(o=n).split("-").filter((e=>!!e)).map((e=>e[0].toUpperCase()+e.substr(1))).join(""))[0].toLowerCase()+o.substr(1)in document.body.style)?e:"#ccc"};\n z-index: 1;\n resize: none;\n font-family: ${t};\n -webkit-appearance: pre;\n caret-color: #111;\n z-index: 2;\n width: 100%;\n height: 100%;\n }\n\n .codeflask--has-line-numbers .codeflask__textarea {\n width: calc(100% - 40px);\n }\n\n .codeflask__code {\n display: block;\n font-family: ${t};\n overflow: hidden;\n }\n\n .codeflask__flatten {\n padding: 10px;\n font-size: 16px;\n line-height: 20px;\n white-space: pre;\n position: absolute;\n top: 0;\n left: 0;\n overflow: auto;\n margin: 0 !important;\n outline: none;\n text-align: left;\n }\n\n .codeflask--has-line-numbers .codeflask__flatten {\n width: calc(100% - 40px);\n left: 40px;\n }\n\n .codeflask__line-highlight {\n position: absolute;\n top: 10px;\n left: 0;\n width: 100%;\n height: 20px;\n background: rgba(0,0,0,0.1);\n z-index: 1;\n }\n\n .codeflask__lines {\n padding: 10px 4px;\n font-size: 12px;\n line-height: 20px;\n font-family: 'Cousine', monospace;\n position: absolute;\n left: 0;\n top: 0;\n width: 40px;\n min-height: 100%;\n text-align: right;\n color: #999;\n border-right: 1px solid #f8f8f8;\n background-color: #fafafa;\n z-index: 5;\n }\n\n .codeflask__lines__line {\n display: block;\n }\n\n .codeflask.codeflask--has-line-numbers {\n padding-left: 40px;\n }\n\n .codeflask.codeflask--has-line-numbers:before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: 40px;\n height: 100%;\n background: #eee;\n z-index: 1;\n }\n`;function i(e,t,n){const s=t||"codeflask-style",o=n||document.head;if(!e)return!1;if(document.getElementById(s))return!0;const a=document.createElement("style");return a.innerHTML=e,a.id=s,o.appendChild(a),!0}const l={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function r(e){return String(e).replace(/[&<>"'`=/]/g,(function(e){return l[e]}))}const h={"(":")","[":"]","{":"}","<":">","'":"'",'"':'"'};return class{constructor(e,t,n){if(!e)throw Error("CodeFlask expects 1st parameter to be an Element or a String selector");if(!t)throw Error("CodeFlask expects 2nd parameter to be the Prism peer dependency");if(!n)throw Error("CodeFlask expects 3rd parameter to be an object containing options");if(e.nodeType)this.editorRoot=e;else{const t=document.querySelector(e);t&&(this.editorRoot=t)}this.Prism=t,this.opts=n,this.events={},this.startEditor()}startEditor(){if(!i(a,null,this.opts.styleParent))throw Error("Failed to inject CodeFlask CSS.");this.createWrapper(),this.createTextarea(),this.createPre(),this.createCode(),this.runOptions(),this.listenTextarea(),this.populateDefault(),this.updateCode(this.code)}createWrapper(){this.code=this.editorRoot.innerHTML,this.editorRoot.innerHTML="",this.elWrapper=this.createElement("div",this.editorRoot),this.elWrapper.classList.add("codeflask")}createTextarea(){this.elTextarea=this.createElement("textarea",this.elWrapper),this.elTextarea.classList.add("codeflask__textarea","codeflask__flatten")}createPre(){this.elPre=this.createElement("pre",this.elWrapper),this.elPre.classList.add("codeflask__pre","codeflask__flatten")}createCode(){this.elCode=this.createElement("code",this.elPre),this.elCode.classList.add("codeflask__code",`language-${this.opts.language||"html"}`)}createLineNumbers(){this.elLineNumbers=this.createElement("div",this.elWrapper),this.elLineNumbers.classList.add("codeflask__lines"),this.setLineNumber()}createElement(e,t){const n=document.createElement(e);return t.appendChild(n),n}runOptions(){this.opts.rtl=this.opts.rtl||!1,this.opts.tabSize=this.opts.tabSize||2,this.opts.enableAutocorrect=this.opts.enableAutocorrect||!1,this.opts.lineNumbers=this.opts.lineNumbers||!1,this.opts.defaultTheme=!1!==this.opts.defaultTheme,this.opts.areaId=this.opts.areaId||null,this.opts.ariaLabelledby=this.opts.ariaLabelledby||null,this.opts.readonly=this.opts.readonly||!1,this.opts.customEventListeners=this.opts.customEventListeners||{},this.opts.selfClosingCharacters=this.opts.selfClosingCharacters||["(","[","{","<","'",'"'],"boolean"!=typeof this.opts.handleTabs&&(this.opts.handleTabs=!0),"boolean"!=typeof this.opts.handleNewLineIndentation&&(this.opts.handleNewLineIndentation=!0),!0===this.opts.rtl&&(this.elTextarea.setAttribute("dir","rtl"),this.elPre.setAttribute("dir","rtl")),!1===this.opts.enableAutocorrect&&(this.elTextarea.setAttribute("spellcheck","false"),this.elTextarea.setAttribute("autocapitalize","off"),this.elTextarea.setAttribute("autocomplete","off"),this.elTextarea.setAttribute("autocorrect","off")),this.opts.lineNumbers&&(this.elWrapper.classList.add("codeflask--has-line-numbers"),this.createLineNumbers()),this.opts.defaultTheme&&i("\n.codeflask {\n background: #fff;\n color: #4f559c;\n}\n\n.codeflask .token.punctuation {\n color: #4a4a4a;\n}\n\n.codeflask .token.keyword {\n color: #8500ff;\n}\n\n.codeflask .token.operator {\n color: #ff5598;\n}\n\n.codeflask .token.string {\n color: #41ad8f;\n}\n\n.codeflask .token.comment {\n color: #9badb7;\n}\n\n.codeflask .token.function {\n color: #8500ff;\n}\n\n.codeflask .token.boolean {\n color: #8500ff;\n}\n\n.codeflask .token.number {\n color: #8500ff;\n}\n\n.codeflask .token.selector {\n color: #8500ff;\n}\n\n.codeflask .token.property {\n color: #8500ff;\n}\n\n.codeflask .token.tag {\n color: #8500ff;\n}\n\n.codeflask .token.attr-value {\n color: #8500ff;\n}\n","theme-default",this.opts.styleParent),this.opts.areaId&&this.elTextarea.setAttribute("id",this.opts.areaId),this.opts.ariaLabelledby&&this.elTextarea.setAttribute("aria-labelledby",this.opts.ariaLabelledby),this.opts.readonly&&this.enableReadonlyMode()}updateLineNumbersCount(){let e="";for(let t=1;t<=this.lineNumber;t++)e+=`${t}`;this.elLineNumbers.innerHTML=e}listenTextarea(){const e=this.opts.customEventListeners;for(const[t,n]of Object.entries(e))this.elTextarea.addEventListener(t,n);this.elTextarea.addEventListener("input",this.events.input=e=>{this.opts.readonly||(this.code=e.target.value,this.elCode.innerHTML=r(e.target.value),this.highlight(),setTimeout((()=>{this.runUpdate(),this.setLineNumber()}),1))}),this.elTextarea.addEventListener("keydown",this.events.keydown=e=>{this.opts.readonly||(this.handleTabs(e),this.handleBackspace(e),this.handleSelfClosingCharacters(e),this.handleNewLineIndentation(e))}),this.elTextarea.addEventListener("scroll",this.events.scroll=e=>{this.elPre.style.transform=`translate3d(-${e.target.scrollLeft}px, -${e.target.scrollTop}px, 0)`,this.elLineNumbers&&(this.elLineNumbers.style.transform=`translate3d(0, -${e.target.scrollTop}px, 0)`,this.elPre.style.width=`calc(100% - 40px + ${e.target.scrollLeft}px)`)})}removeEventListeners(){for(const[e,t]of Object.entries(customEventListeners))this.elTextarea.removeEventListener(e,t);for(const[e,t]of Object.entries(this.events))this.elTextarea.removeEventListener(e,t)}getSelectInfo(){var e=this.elTextarea,t=e.selectionStart,n=e.selectionEnd;return{selStartPos:t,selEndPos:n,beforeSelection:e.value.substr(0,t),selectionVal:e.value.substring(t,n),afterSelection:e.value.substring(n)}}handleTabs(e){if(!this.opts.handleTabs||"Tab"!==e.key)return;e.preventDefault();var t=this.elTextarea,n=t.value,{selStartPos:s,selEndPos:o,beforeSelection:a,selectionVal:i,afterSelection:l}=this.getSelectInfo();const r=" ".repeat(this.opts.tabSize);if(s!==o&&i.length>=r.length){var h=s-a.split("\n").pop().length,c=r.length,d=r.length;if(e.shiftKey)n.substr(h,r.length)===r?(c=-c,h>s?(i=i.substring(0,h)+i.substring(h+r.length),d=0):h===s?(c=0,d=0,i=i.substring(r.length)):(d=-d,a=a.substring(0,h)+a.substring(h+r.length))):(c=0,d=0),i=i.replace(new RegExp("\n"+r.split("").join("\\"),"g"),"\n");else a=a.substr(0,h)+r+a.substring(h,s),i=i.replace(/\n/g,"\n"+r);t.value=a+i+l,t.setSelectionRange(s+c,s+i.length+d)}else{const n=new RegExp(`(\\n?)((?:${r})*)([^\\n]*)$`);if(e.shiftKey){const e=a.replace(n,((e,t,n,s)=>t+n.slice(0,-1*r.length)+s));e!==a&&(t.value=e+l,t.setSelectionRange(s-r.length,s-r.length))}else{const e=a.replace(n,((e,t,n,s)=>t+n+r+s));t.value=e+l,t.setSelectionRange(s+r.length,s+r.length)}}this.updateCode(t.value)}handleBackspace(e){if("Backspace"!==e.key)return;var{selStartPos:t,beforeSelection:n,selectionVal:s,afterSelection:o}=this.getSelectInfo();if(""!==s)return;if(!n.match(/\n( ){1,}$/))return;e.preventDefault();const a=n.replace(/ $/,""),i=this.elTextarea;i.value=a+o,this.updateCode(i.value),i.setSelectionRange(t-2,t-2)}handleSelfClosingCharacters(e){if(!this.opts.selfClosingCharacters.length)return;const t=this.opts.selfClosingCharacters,n=this.opts.selfClosingCharacters.map((e=>h[e]));(t.includes(e.key)||n.includes(e.key))&&(e.metaKey||e.ctrlKey||this.closeCharacter(e.key))}setLineNumber(){this.lineNumber=this.code.split("\n").length,this.opts.lineNumbers&&this.updateLineNumbersCount()}handleNewLineIndentation(e){if(this.opts.handleNewLineIndentation&&13===e.keyCode){e.preventDefault();var t=this.elTextarea,n=t.value,{selStartPos:s,beforeSelection:o,afterSelection:a}=this.getSelectInfo(),i=n.lastIndexOf("\n",s-1),l=i+n.slice(i+1).search(/[^ ]|$/),r=l>i?l-i:0,h=o+"\n"+" ".repeat(r)+a;t.value=h,t.selectionStart=s+r+1,t.selectionEnd=s+r+1,this.updateCode(t.value)}}closeCharacter(e){const t=this.elTextarea.selectionStart,n=this.elTextarea.selectionEnd;if(this.skipCloseChar(e)){const s=this.code.substr(n,1)===e,o=s?n+1:n,a=!s&&["'",'"'].includes(e)?e:"",i=`${this.code.substring(0,t)}${a}${this.code.substring(o)}`;this.updateCode(i),this.elTextarea.selectionEnd=++this.elTextarea.selectionStart}else{let s=e;switch(e){case"(":s=String.fromCharCode(e.charCodeAt()+1);break;case"<":case"{":case"[":s=String.fromCharCode(e.charCodeAt()+2)}const o=this.code.substring(t,n),a=`${this.code.substring(0,t)}${o}${s}${this.code.substring(n)}`;this.updateCode(a)}this.elTextarea.selectionEnd=t}skipCloseChar(e){const t=this.elTextarea.selectionStart,n=this.elTextarea.selectionEnd,s=Math.abs(n-t)>0;return[")","}","]",">"].includes(e)||["'",'"'].includes(e)&&!s}updateCode(e){this.code=e,this.elTextarea.value=e,this.elCode.innerHTML=r(e),this.highlight(),this.setLineNumber(),setTimeout(this.runUpdate.bind(this),1)}updateLanguage(e){const t=this.opts.language;this.elCode.classList.remove(`language-${t}`),this.elCode.classList.add(`language-${e}`),this.opts.language=e,this.highlight()}addLanguage(e,t){this.Prism.languages[e]=t}populateDefault(){this.updateCode(this.code)}blur(){this.elTextarea.blur()}highlight(){this.Prism.highlightElement(this.elCode,!1)}highlightLines(e){this.elPre.setAttribute("data-line",e),this.highlight()}onUpdate(e){if(e&&"[object Function]"!=={}.toString.call(e))throw Error("CodeFlask expects callback of type Function");this.updateCallBack=e}getCode(){return this.code}runUpdate(){this.updateCallBack&&this.updateCallBack(this.code)}enableReadonlyMode(){this.elTextarea.setAttribute("readonly",!0)}disableReadonlyMode(){this.elTextarea.removeAttribute("readonly")}}})); diff --git a/build/codeflask.module.js b/build/codeflask.module.js index 809faf3a..ca4b7c46 100644 --- a/build/codeflask.module.js +++ b/build/codeflask.module.js @@ -1 +1 @@ -var BACKGROUND_COLOR="#fff",LINE_HEIGHT="20px",FONT_SIZE="13px",defaultCssTheme="\n.codeflask {\n background: "+BACKGROUND_COLOR+";\n color: #4f559c;\n}\n\n.codeflask .token.punctuation {\n color: #4a4a4a;\n}\n\n.codeflask .token.keyword {\n color: #8500ff;\n}\n\n.codeflask .token.operator {\n color: #ff5598;\n}\n\n.codeflask .token.string {\n color: #41ad8f;\n}\n\n.codeflask .token.comment {\n color: #9badb7;\n}\n\n.codeflask .token.function {\n color: #8500ff;\n}\n\n.codeflask .token.boolean {\n color: #8500ff;\n}\n\n.codeflask .token.number {\n color: #8500ff;\n}\n\n.codeflask .token.selector {\n color: #8500ff;\n}\n\n.codeflask .token.property {\n color: #8500ff;\n}\n\n.codeflask .token.tag {\n color: #8500ff;\n}\n\n.codeflask .token.attr-value {\n color: #8500ff;\n}\n";function cssSupports(e,t){return CSS?CSS.supports(e,t):toCamelCase(e)in document.body.style}function toCamelCase(e){return(e=e.split("-").filter(function(e){return!!e}).map(function(e){return e[0].toUpperCase()+e.substr(1)}).join(""))[0].toLowerCase()+e.substr(1)}var FONT_FAMILY='"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',COLOR=cssSupports("caret-color","#000")?BACKGROUND_COLOR:"#ccc",LINE_NUMBER_WIDTH="40px",editorCss="\n .codeflask {\n position: absolute;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n .codeflask, .codeflask * {\n box-sizing: border-box;\n }\n\n .codeflask__pre {\n pointer-events: none;\n z-index: 3;\n overflow: hidden;\n }\n\n .codeflask__textarea {\n background: none;\n border: none;\n color: "+COLOR+";\n z-index: 1;\n resize: none;\n font-family: "+FONT_FAMILY+";\n -webkit-appearance: pre;\n caret-color: #111;\n z-index: 2;\n width: 100%;\n height: 100%;\n }\n\n .codeflask--has-line-numbers .codeflask__textarea {\n width: calc(100% - "+LINE_NUMBER_WIDTH+");\n }\n\n .codeflask__code {\n display: block;\n font-family: "+FONT_FAMILY+";\n overflow: hidden;\n }\n\n .codeflask__flatten {\n padding: 10px;\n font-size: "+FONT_SIZE+";\n line-height: "+LINE_HEIGHT+";\n white-space: pre;\n position: absolute;\n top: 0;\n left: 0;\n overflow: auto;\n margin: 0 !important;\n outline: none;\n text-align: left;\n }\n\n .codeflask--has-line-numbers .codeflask__flatten {\n width: calc(100% - "+LINE_NUMBER_WIDTH+");\n left: "+LINE_NUMBER_WIDTH+";\n }\n\n .codeflask__line-highlight {\n position: absolute;\n top: 10px;\n left: 0;\n width: 100%;\n height: "+LINE_HEIGHT+";\n background: rgba(0,0,0,0.1);\n z-index: 1;\n }\n\n .codeflask__lines {\n padding: 10px 4px;\n font-size: 12px;\n line-height: "+LINE_HEIGHT+";\n font-family: 'Cousine', monospace;\n position: absolute;\n left: 0;\n top: 0;\n width: "+LINE_NUMBER_WIDTH+";\n height: 100%;\n text-align: right;\n color: #999;\n z-index: 2;\n }\n\n .codeflask__lines__line {\n display: block;\n }\n\n .codeflask.codeflask--has-line-numbers {\n padding-left: "+LINE_NUMBER_WIDTH+";\n }\n\n .codeflask.codeflask--has-line-numbers:before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: "+LINE_NUMBER_WIDTH+";\n height: 100%;\n background: #eee;\n z-index: 1;\n }\n";function injectCss(e,t,n){var a=t||"codeflask-style",s=n||document.head;if(!e)return!1;if(document.getElementById(a))return!0;var o=document.createElement("style");return o.innerHTML=e,o.id=a,s.appendChild(o),!0}var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function escapeHtml(e){return String(e).replace(/[&<>"'`=/]/g,function(e){return entityMap[e]})}var commonjsGlobal="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function createCommonjsModule(e,t){return e(t={exports:{}},t.exports),t.exports}var prism=createCommonjsModule(function(e){var t="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},n=function(){var e=/\blang(?:uage)?-([\w-]+)\b/i,n=0,a=t.Prism={manual:t.Prism&&t.Prism.manual,disableWorkerMessageHandler:t.Prism&&t.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof s?new s(e.type,a.util.encode(e.content),e.alias):"Array"===a.util.type(e)?e.map(a.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(v instanceof l)){if(f&&y!=t.length-1){if(h.lastIndex=C,!(_=h.exec(e)))break;for(var x=_.index+(g?_[1].length:0),w=_.index+_[0].length,F=y,T=C,L=t.length;F=(T+=t[F].length)&&(++y,C=T);if(t[y]instanceof l)continue;E=F-y,v=e.slice(C,T),_.index-=C}else{h.lastIndex=0;var _=h.exec(v),E=1}if(_){g&&(m=_[1]?_[1].length:0);w=(x=_.index+m)+(_=_[0].slice(m)).length;var N=v.slice(0,x),S=v.slice(w),A=[y,E];N&&(++y,C+=N.length,A.push(N));var I=new l(c,p?a.tokenize(_,p):_,b,_,f);if(A.push(I),S&&A.push(S),Array.prototype.splice.apply(t,A),1!=E&&a.matchGrammar(e,t,n,y,C,!0,c),i)break}else if(i)break}}}}},tokenize:function(e,t,n){var s=[e],o=t.rest;if(o){for(var i in o)t[i]=o[i];delete t.rest}return a.matchGrammar(e,s,t,0,0,!1),s},hooks:{all:{},add:function(e,t){var n=a.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=a.hooks.all[e];if(n&&n.length)for(var s,o=0;s=n[o++];)s(t)}}},s=a.Token=function(e,t,n,a,s){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length,this.greedy=!!s};if(s.stringify=function(e,t,n){if("string"==typeof e)return e;if("Array"===a.util.type(e))return e.map(function(n){return s.stringify(n,t,e)}).join("");var o={type:e.type,content:s.stringify(e.content,t,n),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:n};if(e.alias){var i="Array"===a.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(o.classes,i)}a.hooks.run("wrap",o);var r=Object.keys(o.attributes).map(function(e){return e+'="'+(o.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+o.tag+' class="'+o.classes.join(" ")+'"'+(r?" "+r:"")+">"+o.content+""},!t.document)return t.addEventListener?(a.disableWorkerMessageHandler||t.addEventListener("message",function(e){var n=JSON.parse(e.data),s=n.language,o=n.code,i=n.immediateClose;t.postMessage(a.highlight(o,a.languages[s],s)),i&&t.close()},!1),t.Prism):t.Prism;var o=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return o&&(a.filename=o.src,a.manual||o.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(a.highlightAll):window.setTimeout(a.highlightAll,16):document.addEventListener("DOMContentLoaded",a.highlightAll))),t.Prism}();e.exports&&(e.exports=n),void 0!==commonjsGlobal&&(commonjsGlobal.Prism=n),n.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/(^|[^\\])["']/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},n.languages.markup.tag.inside["attr-value"].inside.entity=n.languages.markup.entity,n.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),n.languages.xml=n.languages.markup,n.languages.html=n.languages.markup,n.languages.mathml=n.languages.markup,n.languages.svg=n.languages.markup,n.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^{}\s][^{};]*?(?=\s*\{)/,string:{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/\B!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},n.languages.css.atrule.inside.rest=n.languages.css,n.languages.markup&&(n.languages.insertBefore("markup","tag",{style:{pattern:/()[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:n.languages.css,alias:"language-css",greedy:!0}}),n.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:n.languages.css}},alias:"language-css"}},n.languages.markup.tag)),n.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/[a-z0-9_]+(?=\()/i,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},n.languages.javascript=n.languages.extend("clike",{keyword:/\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/,function:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),n.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i,alias:"function"},constant:/\b[A-Z][A-Z\d_]*\b/}),n.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\${[^}]+}/,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}}}),n.languages.javascript["template-string"].inside.interpolation.inside.rest=n.languages.javascript,n.languages.markup&&n.languages.insertBefore("markup","tag",{script:{pattern:/()[\s\S]*?(?=<\/script>)/i,lookbehind:!0,inside:n.languages.javascript,alias:"language-javascript",greedy:!0}}),n.languages.js=n.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var a,s=t.getAttribute("data-src"),o=t,i=/\blang(?:uage)?-([\w-]+)\b/i;o&&!i.test(o.className);)o=o.parentNode;if(o&&(a=(t.className.match(i)||[,""])[1]),!a){var r=(s.match(/\.(\w+)$/)||[,""])[1];a=e[r]||r}var l=document.createElement("code");l.className="language-"+a,t.textContent="",l.textContent="Loading…",t.appendChild(l);var c=new XMLHttpRequest;c.open("GET",s,!0),c.onreadystatechange=function(){4==c.readyState&&(c.status<400&&c.responseText?(l.textContent=c.responseText,n.highlightElement(l)):c.status>=400?l.textContent="✖ Error "+c.status+" while fetching file: "+c.statusText:l.textContent="✖ Error: File does not exist or is empty")},c.send(null)}),n.plugins.toolbar&&n.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),a=document.createElement("a");return a.textContent=t.getAttribute("data-download-link-label")||"Download",a.setAttribute("download",""),a.href=n,a}})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight))}),CodeFlask=function(e,t){if(!e)throw Error("CodeFlask expects a parameter which is Element or a String selector");if(!t)throw Error("CodeFlask expects an object containing options as second parameter");if(e.nodeType)this.editorRoot=e;else{var n=document.querySelector(e);n&&(this.editorRoot=n)}this.opts=t,this.startEditor()};CodeFlask.prototype.startEditor=function(){if(!injectCss(editorCss,null,this.opts.styleParent))throw Error("Failed to inject CodeFlask CSS.");this.createWrapper(),this.createTextarea(),this.createPre(),this.createCode(),this.runOptions(),this.listenTextarea(),this.populateDefault(),this.updateCode(this.code)},CodeFlask.prototype.createWrapper=function(){this.code=this.editorRoot.innerHTML,this.editorRoot.innerHTML="",this.elWrapper=this.createElement("div",this.editorRoot),this.elWrapper.classList.add("codeflask")},CodeFlask.prototype.createTextarea=function(){this.elTextarea=this.createElement("textarea",this.elWrapper),this.elTextarea.classList.add("codeflask__textarea","codeflask__flatten")},CodeFlask.prototype.createPre=function(){this.elPre=this.createElement("pre",this.elWrapper),this.elPre.classList.add("codeflask__pre","codeflask__flatten")},CodeFlask.prototype.createCode=function(){this.elCode=this.createElement("code",this.elPre),this.elCode.classList.add("codeflask__code","language-"+(this.opts.language||"html"))},CodeFlask.prototype.createLineNumbers=function(){this.elLineNumbers=this.createElement("div",this.elWrapper),this.elLineNumbers.classList.add("codeflask__lines"),this.setLineNumber()},CodeFlask.prototype.createElement=function(e,t){var n=document.createElement(e);return t.appendChild(n),n},CodeFlask.prototype.runOptions=function(){this.opts.rtl=this.opts.rtl||!1,this.opts.tabSize=this.opts.tabSize||2,this.opts.enableAutocorrect=this.opts.enableAutocorrect||!1,this.opts.lineNumbers=this.opts.lineNumbers||!1,this.opts.defaultTheme=!1!==this.opts.defaultTheme,this.opts.areaId=this.opts.areaId||null,this.opts.ariaLabelledby=this.opts.ariaLabelledby||null,this.opts.readonly=this.opts.readonly||null,"boolean"!=typeof this.opts.handleTabs&&(this.opts.handleTabs=!0),"boolean"!=typeof this.opts.handleSelfClosingCharacters&&(this.opts.handleSelfClosingCharacters=!0),"boolean"!=typeof this.opts.handleNewLineIndentation&&(this.opts.handleNewLineIndentation=!0),!0===this.opts.rtl&&(this.elTextarea.setAttribute("dir","rtl"),this.elPre.setAttribute("dir","rtl")),!1===this.opts.enableAutocorrect&&(this.elTextarea.setAttribute("spellcheck","false"),this.elTextarea.setAttribute("autocapitalize","off"),this.elTextarea.setAttribute("autocomplete","off"),this.elTextarea.setAttribute("autocorrect","off")),this.opts.lineNumbers&&(this.elWrapper.classList.add("codeflask--has-line-numbers"),this.createLineNumbers()),this.opts.defaultTheme&&injectCss(defaultCssTheme,"theme-default",this.opts.styleParent),this.opts.areaId&&this.elTextarea.setAttribute("id",this.opts.areaId),this.opts.ariaLabelledby&&this.elTextarea.setAttribute("aria-labelledby",this.opts.ariaLabelledby),this.opts.readonly&&this.enableReadonlyMode()},CodeFlask.prototype.updateLineNumbersCount=function(){for(var e="",t=1;t<=this.lineNumber;t++)e=e+''+t+"";this.elLineNumbers.innerHTML=e},CodeFlask.prototype.listenTextarea=function(){var e=this;this.elTextarea.addEventListener("input",function(t){e.code=t.target.value,e.elCode.innerHTML=escapeHtml(t.target.value),e.highlight(),setTimeout(function(){e.runUpdate(),e.setLineNumber()},1)}),this.elTextarea.addEventListener("keydown",function(t){e.handleTabs(t),e.handleSelfClosingCharacters(t),e.handleNewLineIndentation(t)}),this.elTextarea.addEventListener("scroll",function(t){e.elPre.style.transform="translate3d(-"+t.target.scrollLeft+"px, -"+t.target.scrollTop+"px, 0)",e.elLineNumbers&&(e.elLineNumbers.style.transform="translate3d(0, -"+t.target.scrollTop+"px, 0)")})},CodeFlask.prototype.handleTabs=function(e){if(this.opts.handleTabs){if(9!==e.keyCode)return;e.preventDefault();var t=this.elTextarea,n=t.selectionDirection,a=t.selectionStart,s=t.selectionEnd,o=t.value,i=o.substr(0,a),r=o.substring(a,s),l=o.substring(s),c=" ".repeat(this.opts.tabSize);if(a!==s&&r.length>=c.length){var u=a-i.split("\n").pop().length,d=c.length,h=c.length;if(e.shiftKey)o.substr(u,c.length)===c?(d=-d,u>a?(r=r.substring(0,u)+r.substring(u+c.length),h=0):u===a?(d=0,h=0,r=r.substring(c.length)):(h=-h,i=i.substring(0,u)+i.substring(u+c.length))):(d=0,h=0),r=r.replace(new RegExp("\n"+c.split("").join("\\"),"g"),"\n");else i=i.substr(0,u)+c+i.substring(u,a),r=r.replace(/\n/g,"\n"+c);t.value=i+r+l,t.selectionStart=a+d,t.selectionEnd=a+r.length+h,t.selectionDirection=n}else t.value=i+c+l,t.selectionStart=a+c.length,t.selectionEnd=a+c.length;var p=t.value;this.updateCode(p),this.elTextarea.selectionEnd=s+this.opts.tabSize}},CodeFlask.prototype.handleSelfClosingCharacters=function(e){if(this.opts.handleSelfClosingCharacters){var t=e.key;if(["(","[","{","<","'",'"'].includes(t)||[")","]","}",">","'",'"'].includes(t))switch(t){case"(":case")":this.closeCharacter(t);break;case"[":case"]":this.closeCharacter(t);break;case"{":case"}":this.closeCharacter(t);break;case"<":case">":case"'":case'"':this.closeCharacter(t)}}},CodeFlask.prototype.setLineNumber=function(){this.lineNumber=this.code.split("\n").length,this.opts.lineNumbers&&this.updateLineNumbersCount()},CodeFlask.prototype.handleNewLineIndentation=function(e){if(this.opts.handleNewLineIndentation&&13===e.keyCode){e.preventDefault();var t=this.elTextarea,n=t.selectionStart,a=t.selectionEnd,s=t.value,o=s.substr(0,n),i=s.substring(a),r=s.lastIndexOf("\n",n-1),l=r+s.slice(r+1).search(/[^ ]|$/),c=l>r?l-r:0,u=o+"\n"+" ".repeat(c)+i;t.value=u,t.selectionStart=n+c+1,t.selectionEnd=n+c+1,this.updateCode(t.value)}},CodeFlask.prototype.closeCharacter=function(e){var t=this.elTextarea.selectionStart,n=this.elTextarea.selectionEnd;if(this.skipCloseChar(e)){var a=this.code.substr(n,1)===e,s=a?n+1:n,o=!a&&["'",'"'].includes(e)?e:"",i=""+this.code.substring(0,t)+o+this.code.substring(s);this.updateCode(i),this.elTextarea.selectionEnd=++this.elTextarea.selectionStart}else{var r=e;switch(e){case"(":r=String.fromCharCode(e.charCodeAt()+1);break;case"<":case"{":case"[":r=String.fromCharCode(e.charCodeAt()+2)}var l=this.code.substring(t,n),c=""+this.code.substring(0,t)+l+r+this.code.substring(n);this.updateCode(c)}this.elTextarea.selectionEnd=t},CodeFlask.prototype.skipCloseChar=function(e){var t=this.elTextarea.selectionStart,n=this.elTextarea.selectionEnd,a=Math.abs(n-t)>0;return[")","}","]",">"].includes(e)||["'",'"'].includes(e)&&!a},CodeFlask.prototype.updateCode=function(e){this.code=e,this.elTextarea.value=e,this.elCode.innerHTML=escapeHtml(e),this.highlight(),this.setLineNumber(),setTimeout(this.runUpdate.bind(this),1)},CodeFlask.prototype.updateLanguage=function(e){var t=this.opts.language;this.elCode.classList.remove("language-"+t),this.elCode.classList.add("language-"+e),this.opts.language=e,this.highlight()},CodeFlask.prototype.addLanguage=function(e,t){prism.languages[e]=t},CodeFlask.prototype.populateDefault=function(){this.updateCode(this.code)},CodeFlask.prototype.highlight=function(){prism.highlightElement(this.elCode,!1)},CodeFlask.prototype.onUpdate=function(e){if(e&&"[object Function]"!=={}.toString.call(e))throw Error("CodeFlask expects callback of type Function");this.updateCallBack=e},CodeFlask.prototype.getCode=function(){return this.code},CodeFlask.prototype.runUpdate=function(){this.updateCallBack&&this.updateCallBack(this.code)},CodeFlask.prototype.enableReadonlyMode=function(){this.elTextarea.setAttribute("readonly",!0)},CodeFlask.prototype.disableReadonlyMode=function(){this.elTextarea.removeAttribute("readonly")};export default CodeFlask; +const e='"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace';var t,n,s;const a=`\n .codeflask {\n position: absolute;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n .codeflask, .codeflask * {\n box-sizing: border-box;\n }\n\n .codeflask__pre {\n pointer-events: none;\n z-index: 3;\n overflow: hidden;\n }\n\n .codeflask__textarea {\n background: none;\n border: none;\n color: ${t="caret-color",n="#000",("undefined"!=typeof CSS?CSS.supports(t,n):"undefined"!=typeof document&&(s=(s=t).split("-").filter((e=>!!e)).map((e=>e[0].toUpperCase()+e.substr(1))).join(""))[0].toLowerCase()+s.substr(1)in document.body.style)?"#fff":"#ccc"};\n z-index: 1;\n resize: none;\n font-family: ${e};\n -webkit-appearance: pre;\n caret-color: #111;\n z-index: 2;\n width: 100%;\n height: 100%;\n }\n\n .codeflask--has-line-numbers .codeflask__textarea {\n width: calc(100% - 40px);\n }\n\n .codeflask__code {\n display: block;\n font-family: ${e};\n overflow: hidden;\n }\n\n .codeflask__flatten {\n padding: 10px;\n font-size: 16px;\n line-height: 20px;\n white-space: pre;\n position: absolute;\n top: 0;\n left: 0;\n overflow: auto;\n margin: 0 !important;\n outline: none;\n text-align: left;\n }\n\n .codeflask--has-line-numbers .codeflask__flatten {\n width: calc(100% - 40px);\n left: 40px;\n }\n\n .codeflask__line-highlight {\n position: absolute;\n top: 10px;\n left: 0;\n width: 100%;\n height: 20px;\n background: rgba(0,0,0,0.1);\n z-index: 1;\n }\n\n .codeflask__lines {\n padding: 10px 4px;\n font-size: 12px;\n line-height: 20px;\n font-family: 'Cousine', monospace;\n position: absolute;\n left: 0;\n top: 0;\n width: 40px;\n min-height: 100%;\n text-align: right;\n color: #999;\n border-right: 1px solid #f8f8f8;\n background-color: #fafafa;\n z-index: 5;\n }\n\n .codeflask__lines__line {\n display: block;\n }\n\n .codeflask.codeflask--has-line-numbers {\n padding-left: 40px;\n }\n\n .codeflask.codeflask--has-line-numbers:before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: 40px;\n height: 100%;\n background: #eee;\n z-index: 1;\n }\n`;function i(e,t,n){const s=t||"codeflask-style",a=n||document.head;if(!e)return!1;if(document.getElementById(s))return!0;const i=document.createElement("style");return i.innerHTML=e,i.id=s,a.appendChild(i),!0}const o={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function l(e){return String(e).replace(/[&<>"'`=/]/g,(function(e){return o[e]}))}const r={"(":")","[":"]","{":"}","<":">","'":"'",'"':'"'};class h{constructor(e,t,n){if(!e)throw Error("CodeFlask expects 1st parameter to be an Element or a String selector");if(!t)throw Error("CodeFlask expects 2nd parameter to be the Prism peer dependency");if(!n)throw Error("CodeFlask expects 3rd parameter to be an object containing options");if(e.nodeType)this.editorRoot=e;else{const t=document.querySelector(e);t&&(this.editorRoot=t)}this.Prism=t,this.opts=n,this.events={},this.startEditor()}startEditor(){if(!i(a,null,this.opts.styleParent))throw Error("Failed to inject CodeFlask CSS.");this.createWrapper(),this.createTextarea(),this.createPre(),this.createCode(),this.runOptions(),this.listenTextarea(),this.populateDefault(),this.updateCode(this.code)}createWrapper(){this.code=this.editorRoot.innerHTML,this.editorRoot.innerHTML="",this.elWrapper=this.createElement("div",this.editorRoot),this.elWrapper.classList.add("codeflask")}createTextarea(){this.elTextarea=this.createElement("textarea",this.elWrapper),this.elTextarea.classList.add("codeflask__textarea","codeflask__flatten")}createPre(){this.elPre=this.createElement("pre",this.elWrapper),this.elPre.classList.add("codeflask__pre","codeflask__flatten")}createCode(){this.elCode=this.createElement("code",this.elPre),this.elCode.classList.add("codeflask__code",`language-${this.opts.language||"html"}`)}createLineNumbers(){this.elLineNumbers=this.createElement("div",this.elWrapper),this.elLineNumbers.classList.add("codeflask__lines"),this.setLineNumber()}createElement(e,t){const n=document.createElement(e);return t.appendChild(n),n}runOptions(){this.opts.rtl=this.opts.rtl||!1,this.opts.tabSize=this.opts.tabSize||2,this.opts.enableAutocorrect=this.opts.enableAutocorrect||!1,this.opts.lineNumbers=this.opts.lineNumbers||!1,this.opts.defaultTheme=!1!==this.opts.defaultTheme,this.opts.areaId=this.opts.areaId||null,this.opts.ariaLabelledby=this.opts.ariaLabelledby||null,this.opts.readonly=this.opts.readonly||!1,this.opts.customEventListeners=this.opts.customEventListeners||{},this.opts.selfClosingCharacters=this.opts.selfClosingCharacters||["(","[","{","<","'",'"'],"boolean"!=typeof this.opts.handleTabs&&(this.opts.handleTabs=!0),"boolean"!=typeof this.opts.handleNewLineIndentation&&(this.opts.handleNewLineIndentation=!0),!0===this.opts.rtl&&(this.elTextarea.setAttribute("dir","rtl"),this.elPre.setAttribute("dir","rtl")),!1===this.opts.enableAutocorrect&&(this.elTextarea.setAttribute("spellcheck","false"),this.elTextarea.setAttribute("autocapitalize","off"),this.elTextarea.setAttribute("autocomplete","off"),this.elTextarea.setAttribute("autocorrect","off")),this.opts.lineNumbers&&(this.elWrapper.classList.add("codeflask--has-line-numbers"),this.createLineNumbers()),this.opts.defaultTheme&&i("\n.codeflask {\n background: #fff;\n color: #4f559c;\n}\n\n.codeflask .token.punctuation {\n color: #4a4a4a;\n}\n\n.codeflask .token.keyword {\n color: #8500ff;\n}\n\n.codeflask .token.operator {\n color: #ff5598;\n}\n\n.codeflask .token.string {\n color: #41ad8f;\n}\n\n.codeflask .token.comment {\n color: #9badb7;\n}\n\n.codeflask .token.function {\n color: #8500ff;\n}\n\n.codeflask .token.boolean {\n color: #8500ff;\n}\n\n.codeflask .token.number {\n color: #8500ff;\n}\n\n.codeflask .token.selector {\n color: #8500ff;\n}\n\n.codeflask .token.property {\n color: #8500ff;\n}\n\n.codeflask .token.tag {\n color: #8500ff;\n}\n\n.codeflask .token.attr-value {\n color: #8500ff;\n}\n","theme-default",this.opts.styleParent),this.opts.areaId&&this.elTextarea.setAttribute("id",this.opts.areaId),this.opts.ariaLabelledby&&this.elTextarea.setAttribute("aria-labelledby",this.opts.ariaLabelledby),this.opts.readonly&&this.enableReadonlyMode()}updateLineNumbersCount(){let e="";for(let t=1;t<=this.lineNumber;t++)e+=`${t}`;this.elLineNumbers.innerHTML=e}listenTextarea(){const e=this.opts.customEventListeners;for(const[t,n]of Object.entries(e))this.elTextarea.addEventListener(t,n);this.elTextarea.addEventListener("input",this.events.input=e=>{this.opts.readonly||(this.code=e.target.value,this.elCode.innerHTML=l(e.target.value),this.highlight(),setTimeout((()=>{this.runUpdate(),this.setLineNumber()}),1))}),this.elTextarea.addEventListener("keydown",this.events.keydown=e=>{this.opts.readonly||(this.handleTabs(e),this.handleBackspace(e),this.handleSelfClosingCharacters(e),this.handleNewLineIndentation(e))}),this.elTextarea.addEventListener("scroll",this.events.scroll=e=>{this.elPre.style.transform=`translate3d(-${e.target.scrollLeft}px, -${e.target.scrollTop}px, 0)`,this.elLineNumbers&&(this.elLineNumbers.style.transform=`translate3d(0, -${e.target.scrollTop}px, 0)`,this.elPre.style.width=`calc(100% - 40px + ${e.target.scrollLeft}px)`)})}removeEventListeners(){for(const[e,t]of Object.entries(customEventListeners))this.elTextarea.removeEventListener(e,t);for(const[e,t]of Object.entries(this.events))this.elTextarea.removeEventListener(e,t)}getSelectInfo(){var e=this.elTextarea,t=e.selectionStart,n=e.selectionEnd;return{selStartPos:t,selEndPos:n,beforeSelection:e.value.substr(0,t),selectionVal:e.value.substring(t,n),afterSelection:e.value.substring(n)}}handleTabs(e){if(!this.opts.handleTabs||"Tab"!==e.key)return;e.preventDefault();var t=this.elTextarea,n=t.value,{selStartPos:s,selEndPos:a,beforeSelection:i,selectionVal:o,afterSelection:l}=this.getSelectInfo();const r=" ".repeat(this.opts.tabSize);if(s!==a&&o.length>=r.length){var h=s-i.split("\n").pop().length,c=r.length,d=r.length;if(e.shiftKey)n.substr(h,r.length)===r?(c=-c,h>s?(o=o.substring(0,h)+o.substring(h+r.length),d=0):h===s?(c=0,d=0,o=o.substring(r.length)):(d=-d,i=i.substring(0,h)+i.substring(h+r.length))):(c=0,d=0),o=o.replace(new RegExp("\n"+r.split("").join("\\"),"g"),"\n");else i=i.substr(0,h)+r+i.substring(h,s),o=o.replace(/\n/g,"\n"+r);t.value=i+o+l,t.setSelectionRange(s+c,s+o.length+d)}else{const n=new RegExp(`(\\n?)((?:${r})*)([^\\n]*)$`);if(e.shiftKey){const e=i.replace(n,((e,t,n,s)=>t+n.slice(0,-1*r.length)+s));e!==i&&(t.value=e+l,t.setSelectionRange(s-r.length,s-r.length))}else{const e=i.replace(n,((e,t,n,s)=>t+n+r+s));t.value=e+l,t.setSelectionRange(s+r.length,s+r.length)}}this.updateCode(t.value)}handleBackspace(e){if("Backspace"!==e.key)return;var{selStartPos:t,beforeSelection:n,selectionVal:s,afterSelection:a}=this.getSelectInfo();if(""!==s)return;if(!n.match(/\n( ){1,}$/))return;e.preventDefault();const i=n.replace(/ $/,""),o=this.elTextarea;o.value=i+a,this.updateCode(o.value),o.setSelectionRange(t-2,t-2)}handleSelfClosingCharacters(e){if(!this.opts.selfClosingCharacters.length)return;const t=this.opts.selfClosingCharacters,n=this.opts.selfClosingCharacters.map((e=>r[e]));(t.includes(e.key)||n.includes(e.key))&&(e.metaKey||e.ctrlKey||this.closeCharacter(e.key))}setLineNumber(){this.lineNumber=this.code.split("\n").length,this.opts.lineNumbers&&this.updateLineNumbersCount()}handleNewLineIndentation(e){if(this.opts.handleNewLineIndentation&&13===e.keyCode){e.preventDefault();var t=this.elTextarea,n=t.value,{selStartPos:s,beforeSelection:a,afterSelection:i}=this.getSelectInfo(),o=n.lastIndexOf("\n",s-1),l=o+n.slice(o+1).search(/[^ ]|$/),r=l>o?l-o:0,h=a+"\n"+" ".repeat(r)+i;t.value=h,t.selectionStart=s+r+1,t.selectionEnd=s+r+1,this.updateCode(t.value)}}closeCharacter(e){const t=this.elTextarea.selectionStart,n=this.elTextarea.selectionEnd;if(this.skipCloseChar(e)){const s=this.code.substr(n,1)===e,a=s?n+1:n,i=!s&&["'",'"'].includes(e)?e:"",o=`${this.code.substring(0,t)}${i}${this.code.substring(a)}`;this.updateCode(o),this.elTextarea.selectionEnd=++this.elTextarea.selectionStart}else{let s=e;switch(e){case"(":s=String.fromCharCode(e.charCodeAt()+1);break;case"<":case"{":case"[":s=String.fromCharCode(e.charCodeAt()+2)}const a=this.code.substring(t,n),i=`${this.code.substring(0,t)}${a}${s}${this.code.substring(n)}`;this.updateCode(i)}this.elTextarea.selectionEnd=t}skipCloseChar(e){const t=this.elTextarea.selectionStart,n=this.elTextarea.selectionEnd,s=Math.abs(n-t)>0;return[")","}","]",">"].includes(e)||["'",'"'].includes(e)&&!s}updateCode(e){this.code=e,this.elTextarea.value=e,this.elCode.innerHTML=l(e),this.highlight(),this.setLineNumber(),setTimeout(this.runUpdate.bind(this),1)}updateLanguage(e){const t=this.opts.language;this.elCode.classList.remove(`language-${t}`),this.elCode.classList.add(`language-${e}`),this.opts.language=e,this.highlight()}addLanguage(e,t){this.Prism.languages[e]=t}populateDefault(){this.updateCode(this.code)}blur(){this.elTextarea.blur()}highlight(){this.Prism.highlightElement(this.elCode,!1)}highlightLines(e){this.elPre.setAttribute("data-line",e),this.highlight()}onUpdate(e){if(e&&"[object Function]"!=={}.toString.call(e))throw Error("CodeFlask expects callback of type Function");this.updateCallBack=e}getCode(){return this.code}runUpdate(){this.updateCallBack&&this.updateCallBack(this.code)}enableReadonlyMode(){this.elTextarea.setAttribute("readonly",!0)}disableReadonlyMode(){this.elTextarea.removeAttribute("readonly")}}export{h as default}; diff --git a/index.d.ts b/index.d.ts index 838e3556..4dd1f0cb 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,6 +6,10 @@ export type LanguageDefinition = { [token: string]: prism.LanguageDefinition | RegExp } +export type EventListeners = Partial<{ + [K in keyof WindowEventMap]: (e: WindowEventMap[K]) => void +}> + export interface CodeFlaskOptions { language?: string rtl?: boolean @@ -16,12 +20,14 @@ export interface CodeFlaskOptions { areaId?: string ariaLabelledby?: string readonly?: boolean + selfClosingCharacters: string[] + customEventListeners?: EventListeners } export default class CodeFlask { - constructor(selectorOrElement: Element | string, opts: CodeFlaskOptions) + constructor(selectorOrElement: Element | string, Prism: any, opts: CodeFlaskOptions) - updateCode(newCode: string): void + updateCode(newCode: string): void updateLanguage(newLanguage: string): void addLanguage(name: string, options: LanguageDefinition): void @@ -30,4 +36,9 @@ export default class CodeFlask { disableReadonlyMode(): void enableReadonlyMode(): void + + removeEventListeners(): void + blur(): void + highlight(): void + highlightLines(lineSpec: string): void } diff --git a/package.json b/package.json index bc417db3..bee7609f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "codeflask", - "version": "1.4.1", + "name": "@acarl005/codeflask", + "version": "2.1.6", "description": "A micro code-editor for awesome web pages", "main": "build/codeflask.min.js", "module": "build/codeflask.module.js", @@ -21,10 +21,14 @@ "prepublishOnly": "npm install && npm run build" }, "dependencies": { - "@types/prismjs": "^1.9.1", - "prismjs": "^1.14.0" + "@types/prismjs": "^1.9.1" + }, + "peerDependencies": { + "prismjs": "*" }, "devDependencies": { + "@rollup/plugin-commonjs": "^22.0.0", + "@rollup/plugin-node-resolve": "^13.3.0", "@wdio/cli": "^6.1.15", "@wdio/local-runner": "^6.1.14", "@wdio/mocha-framework": "^6.1.14", @@ -34,11 +38,8 @@ "chromedriver": "^83.0.0", "micro": "^9.3.0", "mocha": "^5.1.1", - "rollup": "^0.58.1", - "rollup-plugin-buble": "^0.19.2", - "rollup-plugin-commonjs": "^9.1.0", - "rollup-plugin-node-resolve": "^3.0.3", - "rollup-plugin-uglify": "^3.0.0", + "rollup": "^2.72.1", + "rollup-plugin-terser": "^7.0.2", "serve": "^7.0.0", "wdio-chromedriver-service": "^6.0.3" }, diff --git a/rollup.config.js b/rollup.config.js index d96277e4..be0601c0 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,7 +1,6 @@ -import resolve from 'rollup-plugin-node-resolve'; -import commonjs from 'rollup-plugin-commonjs'; -import buble from 'rollup-plugin-buble'; -import uglify from 'rollup-plugin-uglify'; +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import { terser } from 'rollup-plugin-terser'; const production = !process.env.ROLLUP_WATCH; @@ -22,17 +21,8 @@ export default { }, ], plugins: [ - // If you have external dependencies installed from - // npm, you'll most likely need these plugins. In - // some cases you'll need additional configuration — - // consult the documentation for details: - // https://github.com/rollup/rollup-plugin-commonjs resolve(), commonjs(), - - // If we're building for production (npm run build - // instead of npm run dev), transpile and minify - production && buble({ exclude: 'node_modules/**' }), - production && uglify() + production && terser() ] }; diff --git a/src/codeflask.js b/src/codeflask.js index 241a318d..59c9d819 100644 --- a/src/codeflask.js +++ b/src/codeflask.js @@ -2,20 +2,28 @@ import { editorCss } from './styles/editor' import { injectCss } from './styles/injector' import { defaultCssTheme } from './styles/theme-default' import { escapeHtml } from './utils/html-escape' -import Prism from 'prismjs' + +const selfClosingCharMap = { + '(': ')', + '[': ']', + '{': '}', + '<': '>', + "'": "'", + '"': '"' +} export default class CodeFlask { - constructor (selectorOrElement, opts) { + constructor (selectorOrElement, Prism, opts) { if (!selectorOrElement) { - // If no selector or element is passed to CodeFlask, - // stop execution and throw error. - throw Error('CodeFlask expects a parameter which is Element or a String selector') + throw Error('CodeFlask expects 1st parameter to be an Element or a String selector') + } + + if (!Prism) { + throw Error('CodeFlask expects 2nd parameter to be the Prism peer dependency') } if (!opts) { - // If no selector or element is passed to CodeFlask, - // stop execution and throw error. - throw Error('CodeFlask expects an object containing options as second parameter') + throw Error('CodeFlask expects 3rd parameter to be an object containing options') } if (selectorOrElement.nodeType) { @@ -32,7 +40,9 @@ export default class CodeFlask { } } + this.Prism = Prism this.opts = opts + this.events = {} this.startEditor() } @@ -99,17 +109,15 @@ export default class CodeFlask { this.opts.defaultTheme = this.opts.defaultTheme !== false this.opts.areaId = this.opts.areaId || null this.opts.ariaLabelledby = this.opts.ariaLabelledby || null - this.opts.readonly = this.opts.readonly || null + this.opts.readonly = this.opts.readonly || false + this.opts.customEventListeners = this.opts.customEventListeners || {} + this.opts.selfClosingCharacters = this.opts.selfClosingCharacters || ['(', '[', '{', '<', "'", '"'] // if handleTabs is not either true or false, make it true by default if (typeof this.opts.handleTabs !== 'boolean') { this.opts.handleTabs = true } // if handleTabs is not either true or false, make it true by default - if (typeof this.opts.handleSelfClosingCharacters !== 'boolean') { - this.opts.handleSelfClosingCharacters = true - } - // if handleTabs is not either true or false, make it true by default if (typeof this.opts.handleNewLineIndentation !== 'boolean') { this.opts.handleNewLineIndentation = true } @@ -159,7 +167,15 @@ export default class CodeFlask { } listenTextarea () { - this.elTextarea.addEventListener('input', (e) => { + const customEventListeners = this.opts.customEventListeners + for (const [eventName, func] of Object.entries(customEventListeners)) { + this.elTextarea.addEventListener(eventName, func) + } + + this.elTextarea.addEventListener('input', this.events.input = (e) => { + if (this.opts.readonly) { + return; + } this.code = e.target.value this.elCode.innerHTML = escapeHtml(e.target.value) this.highlight() @@ -169,136 +185,156 @@ export default class CodeFlask { }, 1) }) - this.elTextarea.addEventListener('keydown', (e) => { + this.elTextarea.addEventListener('keydown', this.events.keydown = (e) => { if (this.opts.readonly) { return; } this.handleTabs(e) + this.handleBackspace(e) this.handleSelfClosingCharacters(e) this.handleNewLineIndentation(e) }) - this.elTextarea.addEventListener('scroll', (e) => { + this.elTextarea.addEventListener('scroll', this.events.scroll = (e) => { this.elPre.style.transform = `translate3d(-${e.target.scrollLeft}px, -${e.target.scrollTop}px, 0)` if (this.elLineNumbers) { this.elLineNumbers.style.transform = `translate3d(0, -${e.target.scrollTop}px, 0)` + this.elPre.style.width = `calc(100% - 40px + ${e.target.scrollLeft}px)` } }) } + removeEventListeners () { + for (const [eventName, func] of Object.entries(customEventListeners)) { + this.elTextarea.removeEventListener(eventName, func) + } + for (const [eventName, func] of Object.entries(this.events)) { + this.elTextarea.removeEventListener(eventName, func) + } + } + + getSelectInfo () { + var input = this.elTextarea + var selStartPos = input.selectionStart + var selEndPos = input.selectionEnd + var beforeSelection = input.value.substr(0, selStartPos) + var selectionVal = input.value.substring(selStartPos, selEndPos) + var afterSelection = input.value.substring(selEndPos) + return { selStartPos, selEndPos, beforeSelection, selectionVal, afterSelection } + } + handleTabs (e) { - if (this.opts.handleTabs) { - if (e.keyCode !== 9) { - return - } - e.preventDefault() - - var input = this.elTextarea - var selectionDir = input.selectionDirection - var selStartPos = input.selectionStart - var selEndPos = input.selectionEnd - var inputVal = input.value - - var beforeSelection = inputVal.substr(0, selStartPos) - var selectionVal = inputVal.substring(selStartPos, selEndPos) - var afterSelection = inputVal.substring(selEndPos) - const indent = ' '.repeat(this.opts.tabSize) - - if (selStartPos !== selEndPos && selectionVal.length >= indent.length) { - var currentLineStart = selStartPos - beforeSelection.split('\n').pop().length - var startIndentLen = indent.length - var endIndentLen = indent.length - - // Unindent - if (e.shiftKey) { - var currentLineStartStr = inputVal.substr(currentLineStart, indent.length) - // Line start whit indent - if (currentLineStartStr === indent) { - startIndentLen = -startIndentLen - - if (currentLineStart > selStartPos) { - // Indent is in selection - selectionVal = selectionVal.substring(0, currentLineStart) + selectionVal.substring(currentLineStart + indent.length) - endIndentLen = 0 - } else if (currentLineStart === selStartPos) { - // Indent is in start of selection - startIndentLen = 0 - endIndentLen = 0 - selectionVal = selectionVal.substring(indent.length) - } else { - // Indent is before selection - endIndentLen = -endIndentLen - beforeSelection = beforeSelection.substring(0, currentLineStart) + beforeSelection.substring(currentLineStart + indent.length) - } - } else { + if (!this.opts.handleTabs || e.key !== "Tab") { + return + } + e.preventDefault() + + var input = this.elTextarea + var inputVal = input.value + var { selStartPos, selEndPos, beforeSelection, selectionVal, afterSelection } = this.getSelectInfo() + const indent = ' '.repeat(this.opts.tabSize) + + if (selStartPos !== selEndPos && selectionVal.length >= indent.length) { + var currentLineStart = selStartPos - beforeSelection.split('\n').pop().length + var startIndentLen = indent.length + var endIndentLen = indent.length + + // Unindent + if (e.shiftKey) { + var currentLineStartStr = inputVal.substr(currentLineStart, indent.length) + // Line start whit indent + if (currentLineStartStr === indent) { + startIndentLen = -startIndentLen + + if (currentLineStart > selStartPos) { + // Indent is in selection + selectionVal = selectionVal.substring(0, currentLineStart) + selectionVal.substring(currentLineStart + indent.length) + endIndentLen = 0 + } else if (currentLineStart === selStartPos) { + // Indent is in start of selection startIndentLen = 0 endIndentLen = 0 + selectionVal = selectionVal.substring(indent.length) + } else { + // Indent is before selection + endIndentLen = -endIndentLen + beforeSelection = beforeSelection.substring(0, currentLineStart) + beforeSelection.substring(currentLineStart + indent.length) } - - selectionVal = selectionVal.replace(new RegExp('\n' + indent.split('').join('\\'), 'g'), '\n') } else { - // Indent - beforeSelection = beforeSelection.substr(0, currentLineStart) + indent + beforeSelection.substring(currentLineStart, selStartPos) - selectionVal = selectionVal.replace(/\n/g, '\n' + indent) + startIndentLen = 0 + endIndentLen = 0 } - // Set new indented value - input.value = beforeSelection + selectionVal + afterSelection + selectionVal = selectionVal.replace(new RegExp('\n' + indent.split('').join('\\'), 'g'), '\n') + } else { + // Indent + beforeSelection = beforeSelection.substr(0, currentLineStart) + indent + beforeSelection.substring(currentLineStart, selStartPos) + selectionVal = selectionVal.replace(/\n/g, '\n' + indent) + } - input.selectionStart = selStartPos + startIndentLen - input.selectionEnd = selStartPos + selectionVal.length + endIndentLen - input.selectionDirection = selectionDir + // Set new indented value + input.value = beforeSelection + selectionVal + afterSelection + input.setSelectionRange(selStartPos + startIndentLen, selStartPos + selectionVal.length + endIndentLen) + } else { + const activeLineRegexp = new RegExp(`(\\n?)((?:${indent})*)([^\\n]*)$`) + if (e.shiftKey) { + const newBeforeSelection = beforeSelection.replace( + activeLineRegexp, + (_, maybeNewline, leadingIndent, content) => maybeNewline + leadingIndent.slice(0, -1 * indent.length) + content + ) + if (newBeforeSelection !== beforeSelection) { + input.value = newBeforeSelection + afterSelection + input.setSelectionRange(selStartPos - indent.length, selStartPos - indent.length) + } } else { - input.value = beforeSelection + indent + afterSelection - input.selectionStart = selStartPos + indent.length - input.selectionEnd = selStartPos + indent.length + const newBeforeSelection = beforeSelection.replace( + activeLineRegexp, + (_, maybeNewline, leadingIndent, content) => maybeNewline + leadingIndent + indent + content + ) + input.value = newBeforeSelection + afterSelection + input.setSelectionRange(selStartPos + indent.length, selStartPos + indent.length) } + } - var newCode = input.value - this.updateCode(newCode) - this.elTextarea.selectionEnd = selEndPos + this.opts.tabSize + this.updateCode(input.value) + } + + handleBackspace (e) { + if (e.key !== "Backspace") { + return + } + + var { selStartPos, beforeSelection, selectionVal, afterSelection } = this.getSelectInfo() + if (selectionVal !== '') { + return + } + + if (!beforeSelection.match(/\n( ){1,}$/)) { + return } + + e.preventDefault() + const newBeforeSelection = beforeSelection.replace(/ $/, '') + const input = this.elTextarea + input.value = newBeforeSelection + afterSelection + this.updateCode(input.value) + input.setSelectionRange(selStartPos - 2, selStartPos - 2) } handleSelfClosingCharacters (e) { - if (!this.opts.handleSelfClosingCharacters) return - const openChars = ['(', '[', '{', '<', '\'', '"'] - const closeChars = [')', ']', '}', '>', '\'', '"'] - const key = e.key + if (!this.opts.selfClosingCharacters.length) return + const openChars = this.opts.selfClosingCharacters + const closeChars = this.opts.selfClosingCharacters.map(c => selfClosingCharMap[c]) - if (!openChars.includes(key) && !closeChars.includes(key)) { + if (!openChars.includes(e.key) && !closeChars.includes(e.key)) { return } - switch (key) { - case '(': - case ')': - this.closeCharacter(key) - break - - case '[': - case ']': - this.closeCharacter(key) - break - - case '{': - case '}': - this.closeCharacter(key) - break - - case '<': - case '>': - this.closeCharacter(key) - break - - case '\'': - this.closeCharacter(key) - break - - case '"': - this.closeCharacter(key) - break + if (e.metaKey || e.ctrlKey) { + return } + + this.closeCharacter(e.key) } setLineNumber () { @@ -310,19 +346,14 @@ export default class CodeFlask { } handleNewLineIndentation (e) { - if (!this.opts.handleNewLineIndentation) return - if (e.keyCode !== 13) { + if (!this.opts.handleNewLineIndentation || e.keyCode !== 13) { return } e.preventDefault() var input = this.elTextarea - var selStartPos = input.selectionStart - var selEndPos = input.selectionEnd var inputVal = input.value - - var beforeSelection = inputVal.substr(0, selStartPos) - var afterSelection = inputVal.substring(selEndPos) + var { selStartPos, beforeSelection, afterSelection } = this.getSelectInfo() var lineStart = inputVal.lastIndexOf('\n', selStartPos - 1) var spaceLast = lineStart + inputVal.slice(lineStart + 1).search(/[^ ]|$/) @@ -392,15 +423,32 @@ export default class CodeFlask { } addLanguage (name, options) { - Prism.languages[name] = options + this.Prism.languages[name] = options } populateDefault () { this.updateCode(this.code) } + blur () { + this.elTextarea.blur() + } + highlight () { - Prism.highlightElement(this.elCode, false) + this.Prism.highlightElement(this.elCode, false) + } + + highlightLines (lineSpec) { + /* This requires the "line-highlight" plugin in PrismJS + Examples + 5: The 5th line + 1-5: Lines 1 through 5 + 1,4: Line 1 and line 4 + 1-2, 5, 9-20: Lines 1 through 2, line 5, lines 9 through 20 + Pass empty string to remove highlighting from all lines + */ + this.elPre.setAttribute('data-line', lineSpec) + this.highlight() } onUpdate (callback) { diff --git a/src/styles/editor.js b/src/styles/editor.js index fb6aba2c..0b44a120 100644 --- a/src/styles/editor.js +++ b/src/styles/editor.js @@ -85,10 +85,12 @@ export const editorCss = ` left: 0; top: 0; width: ${LINE_NUMBER_WIDTH}; - height: 100%; + min-height: 100%; text-align: right; color: #999; - z-index: 2; + border-right: 1px solid #f8f8f8; + background-color: #fafafa; + z-index: 5; } .codeflask__lines__line { @@ -109,4 +111,4 @@ export const editorCss = ` background: #eee; z-index: 1; } -` \ No newline at end of file +` diff --git a/src/styles/theme-default.js b/src/styles/theme-default.js index 7b356167..72a2490b 100644 --- a/src/styles/theme-default.js +++ b/src/styles/theme-default.js @@ -1,6 +1,6 @@ export const BACKGROUND_COLOR = '#fff' export const LINE_HEIGHT = '20px' -export const FONT_SIZE = '13px' +export const FONT_SIZE = '16px' export const defaultCssTheme = ` .codeflask {