├── .gitattributes ├── images ├── tex_logo.png └── screenshot.jpg ├── LICENSE ├── src ├── tex.min.css ├── tex.css ├── tex.min.js └── tex.js ├── examples ├── plugin.html └── index.html └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /images/tex_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcellov7/tex/HEAD/images/tex_logo.png -------------------------------------------------------------------------------- /images/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcellov7/tex/HEAD/images/screenshot.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Marcello Violini (marcellov7) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/tex.min.css: -------------------------------------------------------------------------------- 1 | .tex-container{border:1px solid #efefef;box-sizing:border-box;border-radius:4px;overflow:hidden}.tex-container.theme-dark{border-color:#383838}.htmlContent,.tex-content{box-sizing:border-box;background-color:#fff;height:300px;width:100%;outline:0;overflow-y:auto;padding:10px}.theme-dark .htmlContent,.theme-dark .tex-content{background-color:#10131a;color:#fff}.tex-actionbar{background-color:#fff;border-bottom:1px solid #efefef;padding:5px}.theme-dark .tex-actionbar{background-color:#10131a;border-color:#383838}.tex-button{position:relative;background-color:#f7f7f7;border:none;cursor:pointer;height:30px;outline:0;min-width:30px;vertical-align:bottom;color:#000;margin-right:3px;border-radius:4px;border-bottom:1px solid #d3d3d3}.theme-dark .tex-button{background-color:#181d27;color:#fff;border-bottom:1px solid #383838}.tex-button:hover{background-color:#f2f2f2}.theme-dark .tex-button:hover{background-color:#353434}.tex-button-selected{background-color:#f0f0f0}.tex-modal{position:absolute;background-color:#fff;border:1px solid #ccc;padding:5px}.tex-button>:nth-child(2){position:absolute;top:0;left:0;opacity:0;height:30px;width:30px;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:0 0;cursor:pointer;border:0;padding:0} -------------------------------------------------------------------------------- /examples/plugin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TEX.js 7 | 8 | 9 | 10 | 11 | 12 |
Hello
13 | 14 | 53 | 54 | -------------------------------------------------------------------------------- /src/tex.css: -------------------------------------------------------------------------------- 1 | .tex-container { 2 | border: 1px solid #efefef; 3 | box-sizing: border-box; 4 | border-radius: 4px; 5 | overflow: hidden; 6 | } 7 | 8 | .tex-container.theme-dark { 9 | border-color: #383838; 10 | } 11 | 12 | .tex-content, .htmlContent { 13 | box-sizing: border-box; 14 | background-color: #fff; 15 | height: 300px; 16 | width: 100%; 17 | outline: 0; 18 | overflow-y: auto; 19 | padding: 10px; 20 | } 21 | 22 | .theme-dark .tex-content, .theme-dark .htmlContent { 23 | background-color: #10131a; 24 | color: white; 25 | } 26 | 27 | .tex-actionbar { 28 | background-color: #FFF; 29 | border-bottom: 1px solid #efefef; 30 | padding:5px; 31 | } 32 | .theme-dark .tex-actionbar { 33 | background-color: #10131a; 34 | border-color: #383838; 35 | } 36 | 37 | .tex-button { 38 | position: relative; 39 | background-color: #f7f7f7; 40 | border: none; 41 | cursor: pointer; 42 | height: 30px; 43 | outline: 0; 44 | min-width: 30px; 45 | vertical-align: bottom; 46 | color: #000; 47 | margin-right: 3px; 48 | border-radius: 4px; 49 | border-bottom: 1px solid lightgray; 50 | } 51 | 52 | .theme-dark .tex-button { 53 | background-color: #181d27; 54 | color: #ffF; 55 | border-bottom: 1px solid #383838; 56 | } 57 | 58 | .tex-button:hover{ 59 | background-color: #f2f2f2; 60 | } 61 | 62 | .theme-dark .tex-button:hover{ 63 | background-color: #353434; 64 | } 65 | 66 | .tex-button-selected { 67 | background-color: #F0F0F0; 68 | } 69 | 70 | .tex-modal{ 71 | position: absolute; 72 | background-color: white; 73 | border: 1px solid #ccc; 74 | padding:5px; 75 | } 76 | 77 | .tex-button > *:nth-child(2) { 78 | position: absolute; 79 | top: 0; 80 | left: 0; 81 | opacity: 0; 82 | height: 30px; 83 | width: 30px; 84 | -webkit-appearance: none; 85 | -moz-appearance: none; 86 | appearance: none; 87 | background: transparent; 88 | cursor: pointer; 89 | border: 0; 90 | padding: 0; 91 | } -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TEX.js 7 | 8 | 9 | 10 | 11 | 12 |
Hello
13 | 14 |
Hello world!
15 | 16 | 46 | 47 | -------------------------------------------------------------------------------- /src/tex.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.tex={})}(this,function(e){"use strict";var t=function(e,t){var n=e.querySelector('input[type="color"]');return(n=document.createElement("input")).type="color",n.style.display="block",e.appendChild(n),n.addEventListener("input",()=>{s(t,n.value),"textColor"==t&&(e.querySelector("span").style.color=n.value,s("foreColor",n.value)),"textBackColor"==t&&(e.querySelector("#textBackColor").style.background=n.value,s("backColor",n.value))}),!1},n=function(e){var t=e.querySelector("select");return t=document.createElement("select"),[3,4,5,6,7].forEach(e=>{let n=document.createElement("option");n.value=e,n.textContent=e,t.appendChild(n)}),e.appendChild(t),t.addEventListener("change",()=>{s("fontSize",t.value)}),!1},i="defaultParagraphSeparator",r="formatBlock",l=function e(t,n,i){return t.addEventListener(n,i)},o=function e(t,n){return t.appendChild(n)},a=function e(t){return document.createElement(t)},u=function e(t){return document.queryCommandState(t)},s=function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return document.execCommand(t,!1,n)},c={fontSize:{icon:"\uD83D\uDDDA",title:"Font Size",result(){}},bold:{icon:"B",title:"Bold",state:()=>u("bold"),result:()=>s("bold")},italic:{icon:"I",title:"Italic",state:()=>u("italic"),result:()=>s("italic")},underline:{icon:"U",title:"Underline",state:()=>u("underline"),result:()=>s("underline")},removeFormat:{icon:"⌫",title:"Remove Format",result:()=>s("removeFormat")},strikethrough:{icon:"S",title:"Strike-through",state:()=>u("strikeThrough"),result:()=>s("strikeThrough")},heading1:{icon:"H1",title:"Heading 1",result:()=>s(r,"

")},heading2:{icon:"H2",title:"Heading 2",result:()=>s(r,"

")},paragraph:{icon:"¶",title:"Paragraph",result:()=>s(r,"

")},quote:{icon:"“ ”",title:"Quote",result:()=>s(r,"

")},olist:{icon:"#",title:"Ordered List",result:()=>s("insertOrderedList")},ulist:{icon:"•",title:"Unordered List",result:()=>s("insertUnorderedList")},code:{icon:"</>",title:"Code",result:()=>s(r,"
")},line:{icon:"―",title:"Horizontal Line",result:()=>s("insertHorizontalRule")},link:{icon:"🔗",title:"Link",result(){let e=window.prompt("Enter the link URL");e&&s("createLink",e)}},image:{icon:"Img",title:"Image",result(){let e=window.prompt("Enter the image URL");e&&s("insertImage",e)}},html:{icon:"HTML",title:"Html",result({content:e}){let t=e.parentNode,n=t.querySelector(".htmlContent"),i=t.querySelector(".tex-content");n&&("none"===n.style.display?(n.style.display="block",i.style.display="none"):(n.style.display="none",i.style.display="block"))}},textColor:{icon:"A",title:"Text Color",result(){}},textBackColor:{icon:'A',title:"Text Color",result(){}},indent:{icon:"›",title:"Indent",result:()=>s("indent")},outdent:{icon:"‹",title:"Outdent",result:()=>s("outdent")},undo:{icon:"↶",title:"Undo",result:()=>s("undo")},redo:{icon:"↷",title:"Redo",result:()=>s("redo")},justifyCenter:{icon:"⇥⇤",title:"Justify Center",result:()=>s("justifyCenter")},justifyFull:{icon:"⇤⇥",title:"Justify Full",result:()=>s("justifyFull")},justifyLeft:{icon:"⇤",title:"Justify Left",result:()=>s("justifyLeft")},justifyRight:{icon:"⇥",title:"Justify Right",result:()=>s("justifyRight")}},d=e=>{var t=document.querySelector(`[tex-id="${e.id}"]`),n=document.getElementById(e.id),i=t.querySelector(".tex-content"),r=t.querySelector(".tex-actionbar");if(t){var l=i.cloneNode(!0);i.parentNode.replaceChild(l,i);var o=r.cloneNode(!0);r.parentNode.replaceChild(o,r),o.remove(),"textarea"===n.tagName.toLowerCase()?(n.style.display="block",t.remove()):(t.innerHTML=l.innerHTML,t.removeAttribute("tex-id"),t.classList.remove("theme-light","theme-dark","tex-container"))}},m=e=>{let t=document.querySelector(`[tex-id="${e.id}"]`),n=t.querySelector(".tex-content");return n.innerHTML},y=e=>{var u=e.theme||"light",d=e[i]||"div",m=a("div");m.className="tex-container",m.classList.add(`theme-${u}`),m.setAttribute("tex-id",e.element.id);var y=a("div");y.className="tex-actionbar",o(m,y);var p=e.element.content=a("div");p.contentEditable=!0,p.className="tex-content tex-editor","textarea"===e.element.tagName.toLowerCase()?p.innerHTML=e.element.value:p.innerHTML=e.element.innerHTML,p.onkeydown=e=>{var t;"Enter"===e.key&&"blockquote"===(t=r,document.queryCommandValue(t))&&setTimeout(()=>s(r,`<${d}>`),0)},o(m,p),e.element.parentNode.insertBefore(m,e.element.nextSibling);let f=[];e.buttons?(e.plugins&&(console.log(e.plugins),e.plugins.forEach(function(e){c[e.name]=e})),f=e.buttons.map(e=>{var t=c[e];if(t)return{icon:t.icon,title:t.title,key:e,result:t.result}}).filter(e=>void 0!==e)):e.actions&&(f=e.actions.map(e=>"string"==typeof e?c[e]:"custom"===e.name?{icon:e.icon,title:e.title,result:e.result}:c[e.name]?{...c[e.name],...e}:e)),f.forEach(e=>{var i=a("button");if(i.className="tex-button",i.innerHTML=""+e.icon+"",i.title=e.title,i.setAttribute("type","button"),i.onclick=()=>e.result({action:e,content:p,button:i})&&p.focus(),e.state){var r=()=>i.classList[e.state()?"add":"remove"]("tex-button-selected");l(p,"keyup",r),l(p,"mouseup",r),l(i,"click",r)}o(y,i),"fontSize"==e.key&&n(i),"textColor"==e.key&&t(i,"textColor"),"textBackColor"==e.key&&t(i,"textBackColor")}),e.cssStyle&&s("styleWithCSS"),s(i,d),"textarea"===e.element.tagName.toLowerCase()?e.element.style.display="none":(document.getElementById(e.element.id).remove(),m.id=e.element.id);var g=a("textarea");return g.setAttribute("class","htmlContent"),o(m,g),g.value=p.innerHTML,g.style.display="none",p.oninput=({target:{firstChild:t}})=>{t&&3===t.nodeType?s(r,`<${d}>`):"
"===p.innerHTML&&(p.innerHTML=""),e.onChange(p.innerHTML),"textarea"===e.element.tagName.toLowerCase()&&(e.element.value=p.innerHTML),g.value=p.innerHTML},g.oninput=({target:{firstChild:t}})=>{p.innerHTML=g.value,e.element.value=p.innerHTML,e.onChange(p.innerHTML)},e.element};e.exec=s,e.init=y,e.destroy=d,e.getContent=m,e.default={exec:s,init:y,destroy:d,getContent:m},Object.defineProperty(e,"__esModule",{value:!0})}); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Logo 2 | 3 | TEX is a ultra-lightweight and straightforward JavaScript library for creating rich text editors (WYSIWYG) directly in the browser. It is designed to work with both ` 49 | ``` 50 | 51 | ## Usage 52 | 53 | ### Initialization 54 | 55 | To initialize TEX, use the `tex.init()` method, passing in an object with the desired settings. Here's how you can do it: 56 | 57 | ```javascript 58 | const tex = window.tex; 59 | 60 | tex.init({ 61 | element: document.getElementById("editor"), 62 | buttons: ['bold', 'italic', 'underline', 'textColor', 'heading1', 'heading2', 'paragraph', 'removeFormat', 'olist', 'ulist', 'code', 'line', 'link', 'image', 'html'], 63 | onChange: (content) => { 64 | console.log("Editor :", content); 65 | } 66 | }); 67 | ``` 68 | 69 | ### API 70 | ```javascript 71 | // ES6 72 | import tex from 'tex' 73 | // or 74 | import { exec, init, destroy, getContent } from 'tex' 75 | ``` 76 | 77 | ### Parameters 78 | 79 | - `element`: The HTML element (either `