*scratch*.js
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<title>*scratch*</title> | |
<style> | |
body { | |
font-family: Hack, Menlo, Monaco, 'Droid Sans Mono', 'Courier New', monospace; | |
white-space: pre; | |
} | |
body style { | |
display: inline; | |
} | |
style::before { | |
content: '<style>' | |
} | |
style::after { | |
content: '<\/style>' | |
} | |
*::before, | |
*::after { | |
color: rgba(136, 18, 128, 0.5); | |
} | |
</style> | |
<script> | |
const selectOuterMostNonBodyNode = (node) => node.parentNode.tagName === 'BODY' ? node : node.parentNode; | |
function insertAfterSelection(selection, html) { | |
if (html === '') | |
return; | |
let nodeToInsert = document.createElement('div'); | |
nodeToInsert.innerHTML = html + '<br>'; | |
let range = selection.getRangeAt(0); | |
let clone = range.cloneRange(); | |
let { endContainer } = range; | |
range.setStartAfter(selectOuterMostNonBodyNode(endContainer)); | |
range.insertNode(nodeToInsert); | |
clone.setStart(clone.endContainer, clone.endOffset); | |
selection.removeRange(range); | |
selection.addRange(clone); | |
} | |
var globalEval = eval; | |
function evaluate() { | |
let selection = document.getSelection(); | |
let text = selection.toString(); | |
if (text.trim().length === 0) { | |
selection.modify("move", "backward", "lineboundary"); | |
selection.modify("extend", "forward", "lineboundary"); | |
text = selection.toString(); | |
} | |
if (text.trim().length > 0) { | |
let result = '<b>#!Error</b>'; | |
try { | |
result = globalEval(text); | |
} catch (e) { | |
console.log(e); | |
} | |
insertAfterSelection(selection, result); | |
} | |
} | |
let tutorial = `<div><br></div><div><br></div><div><br></div><div>"Press Ctrl+Enter to evaluate current line or selection"<br></div><div><br></div><div><br></div><div>3 * 33 + 7<br></div><div><br></div><div><br></div><div>"<b>Result is added as HTML! (execute this line)</b>"<br></div><div><br></div><div><br></div><div>Math.random() > 0.3 ? "<style>body {background-color: #fdf6e3;}</style>" : "try again!";<br></div><div><br></div><div><br></div><div style="color: green;">// The document state is stored in localStorage on page-unload.<br></div><div><br></div><div><br></div><div style="color: green;">// Built-in functions<br></div><div><br></div><div style="color: green;"><i style="color: red;">reset()</i> // - reset the doc to this tutorial<br></div><div style="color: green;"><i style="color: red;">clear()</i> // - empty the doc<br></div><div style="color: green;"><i style="color: blue;">save()</i> // - save doc state to localStorage<br></div><div><br></div><div><br></div><div style="color: green;">// Feel free to save the source of this page and use it locally, it'll work the same.<br></div><div><br></div><div style="color: #585858;"> window.open("https://gist.github.com/kahole/651990b888c19b84d5700422daa961de", "_blank")<br></div><div><br></div><div><br></div><div style="color: green;">// Inspirational works<br></div><div><br></div><div style="color: #585858;">[ "Emacs scratch buffer", window.open("https://www.gnu.org/software/emacs/manual/html_node/emacs/Lisp-Interaction.html", "_blank") ]<br></div><div><br></div><div style="color: #585858;">[ "Secretgeek's html quine", window.open("https://secretgeek.github.io/html_wysiwyg/html.html", "_blank") ]<br></div><div><br></div>`; | |
if (navigator.userAgent.indexOf('Mac OS X') != -1) { | |
tutorial = tutorial.replace('Ctrl', 'Cmd'); | |
} | |
const SCRATCH_STATE = 'scratch_state'; | |
function reset() { | |
document.body.innerHTML = tutorial; | |
return ''; | |
} | |
function clear() { | |
document.body.innerHTML = '<div><br></div>'; | |
return ''; | |
} | |
function save() { | |
localStorage.setItem(SCRATCH_STATE, document.body.innerHTML); | |
return 'saved'; | |
} | |
document.addEventListener('keydown', (e) => (e.code === 'Tab' && document.execCommand('insertText', false, ' ') && e.preventDefault())); | |
document.addEventListener('keypress', (e) => (e.ctrlKey && e.code === 'Enter' && evaluate() && e.preventDefault())); | |
document.addEventListener('keydown', (e) => (e.metaKey && e.code === 'Enter' && evaluate() && e.preventDefault())); | |
window.onbeforeunload = () => { save() }; | |
window.addEventListener('DOMContentLoaded', (e) => { | |
let storedState = localStorage.getItem(SCRATCH_STATE); | |
if (storedState !== null) { | |
document.body.innerHTML = storedState; | |
} else { | |
reset(); | |
} | |
document.querySelector('[contenteditable]').addEventListener('paste', function (event) { | |
event.preventDefault(); | |
document.execCommand('inserttext', false, event.clipboardData.getData('text/plain')); | |
}); | |
}); | |
</script> | |
</head> | |
<body contenteditable="true" spellcheck="false"></body> | |
</html> | |
<!-- MIT License | |
Copyright (c) 2020 Kristian Andersen Hole | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. --> |
This comment has been minimized.
This comment has been minimized.
[reposted from other gist because forks are confusing)] Implemented the suggestions I made on HN. https://gist.github.com/Lerc/6c5e30d68be2e386c9e36d2054c9bd87 Executes the contents of querySelectorAll(".definitions"); before executing the ctrl-enter line; hosted version at http://fingswotidun.com/tests/scratch/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
https://gist.github.com/fitsum/c34981fa729bf878ea823b4c4f79d15b 👋🏾