├── LICENSE ├── README.md ├── index.html ├── main.js └── style.css /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nisarga Adhikary 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevPad 2 | A fully web based code editor with a try it option to try out your HTML, CSS & JS directly in your web browser. 3 | ## Features 4 | - Separate spaces for HTML, CSS & JS code 5 | - Preview (Try It) option 6 | - Clear Code option 7 | ## Self Hosting 8 | ### GitHub Pages 9 | In order to host the site through GitHub Pages, fork this repository and enable GitHub Pages from the forked repository's settings. 10 | ### Vercel 11 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fni5arga%2FDevPad) 12 | ### Netlify 13 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/ni5arga/DevPad/) 14 | ### Railway 15 | [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/C1Vonv) 16 | ## License 17 | DevPad is licensed under the [MIT License](https://github.com/ni5arga/DevPad/blob/main/LICENSE) 18 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DevPad 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

DevPad

15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 37 | 41 | 42 |
 
23 |
24 | HTML 25 |
26 | 27 | 30 |
31 | CSS 32 | 33 | JS 34 | 35 |
36 |
38 | Output 39 | 40 |
43 |
44 | 45 | 46 |
47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const html = document.getElementById('htmlSource'); 2 | const css = document.getElementById('cssSource'); 3 | const js = document.getElementById('jsSource'); 4 | let timer, timeoutVal = 450; 5 | 6 | if (!localStorage.getItem('savedCode')) { 7 | localStorage.setItem('savedCode', JSON.stringify({ 8 | 'html': `

Hello!

9 |

Write HTML, CSS or JavaScript code here and click 'Run Code'.

`, 'css': '/* CSS goes here */', 'js': '// JS code here' 10 | })) 11 | } 12 | let saveData = JSON.parse(localStorage.getItem('savedCode')); 13 | 14 | function runCode() { 15 | var htmlCode = html.value; 16 | var cssCode = css.value; 17 | var jsCode = js.value; 18 | if (jsCode.includes('alert')) { 19 | jsCode = jsCode.replace(/alert\(/g, 'console.log(\'[Alerted Output] \'+'); 20 | } 21 | if (jsCode.includes('console.log')) { 22 | jsCode = jsCode.replace(/console.log\(/g, 'console.log(\'[Console Output] \'+'); 23 | } 24 | let iframe = document.createElement('iframe'); 25 | let oi = document.querySelector('#targetCode') 26 | iframe.id = 'targetCode'; 27 | oi.parentElement.appendChild(iframe); 28 | oi.remove(); 29 | let iframecw = iframe.contentWindow; 30 | iframecw.document.open(); 31 | iframecw.document.write(`DevPad Output${htmlCode}`); 32 | iframecw.document.close(); 33 | return false; 34 | } 35 | 36 | function exportCode(t) { 37 | var htmlCode = html.value; 38 | var cssCode = css.value; 39 | var jsCode = js.value; 40 | var download = document.getElementById('download'); 41 | download.setAttribute('href', 'data:text/html;charset=utf-8,' + `DevPad Output${htmlCode}`); 42 | download.setAttribute('download', 'devpadoutput-' + new Date() + '.html'); 43 | download.click() 44 | } 45 | 46 | function clearCode() { 47 | localStorage.setItem('savedCode', JSON.stringify({ 48 | 'html': `

Hello!

49 |

Write HTML, CSS or JavaScript code here and click 'Run Code'.

`, 'css': '/* CSS goes here */', 'js': '// JS code here' 50 | })); 51 | loadCode(); 52 | } 53 | 54 | function saveCode() { 55 | localStorage.setItem('savedCode', 56 | JSON.stringify({ 57 | 'html': html.value, 58 | 'css': css.value, 59 | 'js': js.value 60 | }) 61 | ); 62 | } 63 | 64 | function loadCode() { 65 | let saveData = JSON.parse(localStorage.getItem('savedCode')); 66 | html.value = saveData.html; 67 | css.value = saveData.css; 68 | js.value = saveData.js; 69 | update(html.value); 70 | } 71 | 72 | html.addEventListener('keypress', handleKeyPress); 73 | html.addEventListener('keyup', handleKeyUp); 74 | 75 | css.addEventListener('keypress', handleKeyPress); 76 | css.addEventListener('keyup', handleKeyUp); 77 | 78 | js.addEventListener('keypress', handleKeyPress); 79 | js.addEventListener('keyup', handleKeyUp); 80 | 81 | function handleKeyPress(e) { window.clearTimeout(timer); } 82 | 83 | function handleKeyUp(e) { 84 | window.clearTimeout(timer); 85 | timer = window.setTimeout(() => { 86 | runCode(); 87 | }, timeoutVal); 88 | } 89 | 90 | setInterval(function () { 91 | saveCode(); 92 | }, 500) 93 | 94 | loadCode(); 95 | runCode(); 96 | update(html.value); 97 | 98 | function update(text) { 99 | let result_element = document.querySelector("#highlighting-content"); 100 | if(text[text.length-1] == "\n") { 101 | text += " "; 102 | } 103 | result_element.innerHTML = text.replace(new RegExp("&", "g"), "&").replace(new RegExp("<", "g"), "<"); /* Global RegExp */ 104 | Prism.highlightElement(result_element); 105 | } 106 | 107 | function sync_scroll(element) { 108 | let result_element = document.querySelector("#highlighting"); 109 | result_element.scrollTop = element.scrollTop; 110 | result_element.scrollLeft = element.scrollLeft; 111 | } 112 | 113 | function check_tab(element, event) { 114 | let code = element.value; 115 | if(event.key == "Tab") { 116 | event.preventDefault(); 117 | let before_tab = code.slice(0, element.selectionStart); // text before tab 118 | let after_tab = code.slice(element.selectionEnd, element.value.length); // text after tab 119 | let cursor_pos = element.selectionStart + 1; // where cursor moves after tab - moving forward by 1 char to after tab 120 | element.value = before_tab + "\t" + after_tab; // add tab char 121 | element.selectionStart = cursor_pos; 122 | element.selectionEnd = cursor_pos; 123 | console.log(element.id) 124 | let text = element.value; 125 | if(element.id === 'htmlSource') { 126 | update(text); 127 | }else{ 128 | if(text[text.length-1] == "\n") { 129 | text += " "; 130 | } 131 | element.innerHTML = text.replace(new RegExp("&", "g"), "&").replace(new RegExp("<", "g"), "<"); /* Global RegExp */ 132 | Prism.highlightElement(element); 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); 2 | 3 | :root { 4 | /* Customize parts of DevPad to your liking! */ 5 | --button-color: #4caf50; 6 | --button-hover: #3e8e41; 7 | --button-pressed: #297c37; 8 | 9 | --background-color: #f2f2f2; 10 | 11 | --border-color: #a00; 12 | --border-size: 2px; 13 | --border-radius: 5px; 14 | 15 | --text-color: #000; 16 | --font-size: 14px; 17 | --font: 'Roboto'; 18 | } 19 | 20 | * { 21 | font-family: "Fira Code", monospace; 22 | } 23 | 24 | table { 25 | table-layout: fixed; 26 | } 27 | 28 | textarea{ 29 | background: black; 30 | color: white; 31 | } 32 | 33 | body { 34 | background-color: var(--background-color); 35 | font-family: var(--font); 36 | } 37 | 38 | h1 { 39 | text-align: center; 40 | margin-top: 20px; 41 | } 42 | 43 | iframe { 44 | border: var(--border-size) solid #a00; 45 | border-radius: var(--border-radius);; 46 | height: 535px; 47 | width: 95%; 48 | padding: 10px; 49 | } 50 | 51 | button { 52 | background-color: var(--button-color); 53 | color: white; 54 | padding: 12px 20px; 55 | border: none; 56 | border-radius: 4px; 57 | cursor: pointer; 58 | transition: background-color 0.3s ease-in-out; 59 | } 60 | 61 | button:hover { 62 | background-color: var(--button-hover); 63 | } 64 | 65 | button:active { 66 | background-color: var(--button-pressed); 67 | } 68 | 69 | #htmlSource, #highlighting,textarea { 70 | padding: 10px; 71 | border: 0; 72 | width: calc(100% - 32px); 73 | height: 150px; 74 | border-radius: 5px; 75 | white-space: nowrap; 76 | } 77 | #htmlSource, #highlighting, #highlighting *, textarea { 78 | font-size: 15pt; 79 | font-family: monospace; 80 | line-height: 1.5; 81 | tab-size: 2; 82 | } 83 | 84 | #htmlSource { 85 | z-index: 1; 86 | position: absolute; 87 | top: 0; 88 | left: 0; 89 | } 90 | 91 | #htmlSource{ 92 | color: transparent; 93 | background: transparent; 94 | caret-color: rgb(247, 244, 244); /* Or choose your favourite color */ 95 | resize: none; 96 | } 97 | 98 | #editing, #highlighting { 99 | overflow: auto; 100 | white-space: nowrap; 101 | } 102 | 103 | p code { 104 | border-radius: 2px; 105 | background-color: #eee; 106 | color: #111; 107 | } 108 | 109 | code[class*="language-"], 110 | pre[class*="language-"] { 111 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 112 | font-size: 1em; 113 | text-align: left; 114 | white-space: pre; 115 | word-spacing: normal; 116 | word-break: normal; 117 | word-wrap: normal; 118 | line-height: 1.5; 119 | 120 | -moz-tab-size: 4; 121 | -o-tab-size: 4; 122 | tab-size: 4; 123 | 124 | -webkit-hyphens: none; 125 | -moz-hyphens: none; 126 | -ms-hyphens: none; 127 | hyphens: none; 128 | } 129 | 130 | /* Code blocks */ 131 | pre[class*="language-"] { 132 | padding: .4em .8em; 133 | margin: .5em 0; 134 | overflow: auto; 135 | background: black; 136 | } 137 | 138 | code[class*="language-"] { 139 | background: black; 140 | color: white; 141 | box-shadow: -.3em 0 0 .3em black, .3em 0 0 .3em black; 142 | } 143 | 144 | :not(pre) > code[class*="language-"] { 145 | padding: .2em; 146 | border-radius: .3em; 147 | box-shadow: none; 148 | white-space: normal; 149 | } 150 | 151 | .token.comment, 152 | .token.prolog, 153 | .token.doctype, 154 | .token.cdata { 155 | color: #aaa; 156 | } 157 | 158 | .token.punctuation { 159 | color: #999; 160 | } 161 | 162 | .token.namespace { 163 | opacity: .7; 164 | } 165 | 166 | .token.property, 167 | .token.tag, 168 | .token.boolean, 169 | .token.number, 170 | .token.constant, 171 | .token.symbol { 172 | color: #0cf; 173 | } 174 | 175 | .token.selector, 176 | .token.attr-name, 177 | .token.string, 178 | .token.char, 179 | .token.builtin { 180 | color: yellow; 181 | } 182 | 183 | .token.operator, 184 | .token.entity, 185 | .token.url, 186 | .language-css .token.string, 187 | .token.variable, 188 | .token.inserted { 189 | color: yellowgreen; 190 | } 191 | 192 | .token.atrule, 193 | .token.attr-value, 194 | .token.keyword { 195 | color: deeppink; 196 | } 197 | 198 | .token.regex, 199 | .token.important { 200 | color: orange; 201 | } 202 | 203 | .token.important, 204 | .token.bold { 205 | font-weight: bold; 206 | } 207 | .token.italic { 208 | font-style: italic; 209 | } 210 | 211 | .token.entity { 212 | cursor: help; 213 | } 214 | 215 | .token.deleted { 216 | color: red; 217 | } 218 | 219 | pre.diff-highlight.diff-highlight > code .token.deleted:not(.prefix), 220 | pre > code.diff-highlight.diff-highlight .token.deleted:not(.prefix) { 221 | background-color: rgba(255, 0, 0, .3); 222 | display: inline; 223 | } 224 | 225 | pre.diff-highlight.diff-highlight > code .token.inserted:not(.prefix), 226 | pre > code.diff-highlight.diff-highlight .token.inserted:not(.prefix) { 227 | background-color: rgba(0, 255, 128, .3); 228 | display: inline; 229 | } 230 | --------------------------------------------------------------------------------