There's not a good, simple embeddable REPL for JS out there that I could find. I wanted one for presentations on JavaScript concepts. The developer tools' console would be ideal, but bailing out of my presentation and opening a console brakes the flow. Tools like codepen.io et al. are great, but more overhead and set up than I need. I just want to type JS and show the result. This is a decent first step for my needs.
Last active
April 1, 2024 18:32
-
-
Save uniqname/479a40788164e3f9332f to your computer and use it in GitHub Desktop.
JS REPL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<output></output> | |
<div class="custextareasdontdopseudoelements"> | |
<textarea name="repl" id="repl"></textarea> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
'use strict'; | |
var parseQuery = function () { | |
var search = window.location.search, | |
q = {}; | |
search.slice(1).split('&').forEach(function (term) { | |
var kv = term.split('='); | |
q[kv[0]] = (kv[1] !== undefined) ? decodeURIComponent(kv[1]) : true; | |
}); | |
return q; | |
}, | |
evalHeight = function (input) { | |
input.style.height = input.value.split('\n').length + 'em'; | |
}, | |
log = (function() { | |
var console = document.querySelector('output'); | |
return function () { | |
var rep = document.createElement('div'); | |
rep.classList.add('rep'); | |
[].slice.call(arguments).forEach(function (arg) { | |
var entry = document.createElement('pre'), | |
result = document.createElement('pre'), | |
evaled, | |
resultType; | |
entry.classList.add('entry'); result.classList.add('result'); | |
try { | |
evaled = eval.call(this, arg); | |
} catch (e) { | |
evaled = e.toString(); | |
} | |
if (evaled === undefined) { | |
evaled = 'undefined'; | |
resultType = 'undefined'; | |
} else if (evaled === null) { | |
evaled = 'null'; | |
resultType = 'null'; | |
} else { | |
resultType = ({}).toString.call(evaled).replace('[object ', '').replace(']', '').toLowerCase(); | |
} | |
if (resultType === 'object' || resultType === 'array') { | |
try { | |
evaled = JSON.stringify(evaled); | |
} catch (e) { | |
} | |
} | |
result.classList.add(resultType); | |
if (resultType === 'string') { | |
evaled = '"' + evaled + '"'; | |
} | |
stack.unshift(arg); | |
entry.textContent = arg; | |
result.textContent = evaled; | |
rep.appendChild(entry); | |
rep.appendChild(result); | |
}); | |
console.appendChild(rep); | |
} | |
})(), | |
input = document.querySelector('textarea'), | |
stack = [], | |
q = parseQuery(); | |
input.addEventListener('keypress', function (evt) { | |
var key = evt.which, | |
retVal; | |
if (key === 13 && !evt.shiftKey) { | |
log(input.value); | |
input.value = ''; | |
input.removeAttribute('style'); | |
} | |
// TODO: implement stack navigation. | |
}); | |
input.addEventListener('keyup', function (evt) { | |
if (evt.which === 13) { | |
if (!evt.shiftKey) { | |
input.value = ''; | |
} else { | |
evalHeight(input); | |
} | |
} | |
// Backspace | |
if (evt.which === 8) { | |
input.style.height = input.value.split('\n').length + 'em'; | |
} | |
}); | |
input.addEventListener('keydown', function (evt) { | |
var val, | |
beforeTab, | |
afterTab, | |
caratPos; | |
// Tabs | |
if (evt.which === 9 ) { | |
evt.preventDefault(); | |
val = input.value; | |
caratPos = input.selectionStart; | |
beforeTab = val.substring(0, input.selectionStart); | |
afterTab = val.substring(input.selectionEnd); | |
input.value = beforeTab + '\t' + afterTab; | |
input.setSelectionRange(caratPos + 1, caratPos + 1); | |
} | |
}); | |
if (q.prime) { | |
input.value = q.prime; | |
evalHeight(input); | |
} | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
'use strict'; | |
var parseQuery = function () { | |
var search = window.location.search, | |
q = {}; | |
search.slice(1).split('&').forEach(function (term) { | |
var kv = term.split('='); | |
q[kv[0]] = (kv[1] !== undefined) ? decodeURIComponent(kv[1]) : true; | |
}); | |
return q; | |
}, | |
evalHeight = function (input) { | |
if (input.value === '') { | |
input.removeAttribute('style'); | |
} else { | |
input.style.height = input.scrollHeight + 'px'; | |
} | |
}, | |
log = (function() { | |
var console = document.querySelector('output'); | |
return function () { | |
var rep = document.createElement('div'); | |
rep.classList.add('rep'); | |
[].slice.call(arguments).forEach(function (arg) { | |
var entry = document.createElement('pre'), | |
result = document.createElement('pre'), | |
evaled, | |
resultType; | |
inputHistory.push(arg); | |
entry.classList.add('entry'); | |
result.classList.add('result'); | |
try { | |
evaled = eval.call(this, arg); | |
} catch (e) { | |
evaled = e.toString(); | |
} | |
if (evaled === undefined) { | |
evaled = 'undefined'; | |
resultType = 'undefined'; | |
} else if (evaled === null) { | |
evaled = 'null'; | |
resultType = 'null'; | |
} else { | |
resultType = ({}).toString.call(evaled).replace('[object ', '').replace(']', '').toLowerCase(); | |
} | |
if (resultType === 'object' || resultType === 'array') { | |
try { | |
evaled = JSON.stringify(evaled); | |
} catch (e) { | |
} | |
} | |
result.classList.add(resultType); | |
if (resultType === 'string') { | |
evaled = '"' + evaled + '"'; | |
} | |
entry.textContent = arg; | |
result.textContent = evaled; | |
rep.appendChild(entry); | |
rep.appendChild(result); | |
}); | |
console.appendChild(rep); | |
} | |
})(), | |
input = document.querySelector('textarea'), | |
inputHistory = (function() { | |
var stack = [], | |
partialVal, | |
resetCurrentIndex = function () { | |
currentIndex = -1; //-1 represents off the stack | |
}, | |
currentIndex = resetCurrentIndex(); | |
return { | |
push: function () { | |
stack.unshift.apply(stack, [].slice.call(arguments)); | |
partialVal = ''; | |
resetCurrentIndex(); | |
}, | |
next: function () { | |
if (currentIndex <= 0) { | |
resetCurrentIndex(); | |
return partialVal; | |
} else { | |
currentIndex -= 1; | |
return stack[currentIndex]; | |
} | |
}, | |
prev: function () { | |
if (currentIndex >= stack.length) { | |
currentIndex = stack.length - 1; | |
return stack[currentIndex]; | |
} else { | |
currentIndex += 1; | |
if (currentIndex === 0) { | |
partialVal = input.value; | |
} | |
return stack[currentIndex]; | |
} | |
} | |
}; | |
})(), | |
q = parseQuery(), | |
setInputValue = function (input, val) { | |
input.value = val; | |
evalHeight(input); | |
}, | |
cursorPos = function (input) { | |
var start = input.selectionStart, | |
end = input.selectionEnd; | |
return { | |
start: start, | |
end: end | |
}; | |
}; | |
input.addEventListener('keydown', function (evt) { | |
var key = evt.which, | |
first = 0, | |
last = input.value.length, | |
pos = cursorPos(input), | |
val, | |
beforeTab, | |
afterTab, | |
caratPos; | |
if (evt.which === 9 ) { //Tab | |
evt.preventDefault(); | |
val = input.value; | |
caratPos = input.selectionStart; | |
beforeTab = val.substring(0, input.selectionStart); | |
afterTab = val.substring(input.selectionEnd); | |
setInputValue(input, beforeTab + '\t' + afterTab); | |
input.setSelectionRange(caratPos + 1, caratPos + 1); | |
} else if (key === 13) { //enter | |
if (!evt.shiftKey) { | |
log(input.value); | |
//annoyed | |
setTimeout(function () { | |
setInputValue(input, ''); | |
}, 0); | |
} | |
} else if (key === 38) { //up arrow | |
if (pos.start === first && pos.end === first) { | |
setInputValue(input, inputHistory.prev()); | |
} | |
} else if (key === 40) { //down arrow | |
if (pos.start === last && pos.end === last) { | |
setInputValue(input, inputHistory.next()); | |
} | |
} | |
}); | |
input.addEventListener('input', function (evt) { | |
evalHeight(input); | |
}); | |
if (q.prime) { | |
setInputValue(input, q.prime); | |
evalHeight(input); | |
} | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html { | |
background: hsla(0, 100%, 100%, .4); | |
box-sizing: border-box; | |
} | |
*, *::before, *::after { | |
box-sizing: inherit; | |
} | |
body { | |
background:transparent; | |
display: flex; | |
flex-flow: column nowrap; | |
flex-direction: column; | |
font-family: monospace; | |
font-size: 2em; | |
position: relative; | |
min-height: 100vh; | |
margin: 0; | |
justify-content: flex-end; | |
} | |
.custextareasdontdopseudoelements { | |
display: flex; | |
flex-flow: row nowrap; | |
border-top: 1px solid #ccc; | |
padding: .25em; | |
/* position: absolute; | |
bottom: 0;*/ | |
width: 100%; | |
background-color: #eee; | |
&::before { | |
content: '>'; | |
display: block; | |
color: #ccc; | |
padding: .25em 0; | |
} | |
} | |
#repl { | |
height: 1.5em; | |
max-height: 80vh; | |
font-size: 1em; | |
border: none; | |
padding: .25em 0 .25em 1em; | |
font-family: monospace; | |
flex: 1 1 100%; | |
background: transparent; | |
tab-size: 4; | |
&:focus { | |
outline: none; | |
} | |
} | |
.rep { | |
margin: 1em 0; | |
} | |
.entry, | |
.result { | |
padding-left: 1em; | |
position: relative; | |
margin: 0; | |
&::before { | |
position: absolute; | |
left: .125em; | |
} | |
} | |
.entry { | |
color: hsla(200, 50%, 50%, 1); | |
&::before { | |
content: '>'; | |
} | |
} | |
.result { | |
color: #333; | |
&::before { | |
color: hsla(200, 50%, 50%, 1); | |
content: '<'; | |
} | |
} | |
.undefined, | |
.null { | |
color: #999; | |
} | |
.string { | |
color: hsl(10, 50%, 50%); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment