Skip to content

Instantly share code, notes, and snippets.

@benchristel
Created September 10, 2016 18:21
Show Gist options
  • Save benchristel/61e71f3aa69deedafa7f3438f7a0d366 to your computer and use it in GitHub Desktop.
Save benchristel/61e71f3aa69deedafa7f3438f7a0d366 to your computer and use it in GitHub Desktop.
An example of how to adapt the Elm architecture to vanilla JS
<!DOCTYPE html>
<html>
<head>
<title>Working Title</title>
<style type="text/css">
body {
margin: 0;
padding: 10px;
background-color: black;
color: goldenrod;
}
#terminal > p {
margin: 0;
padding: 0;
font-family: Menlo, Monaco, monospace;
font-size: 16px;
}
</style>
</head>
<body>
<div id="terminal">
<p id="0"></p>
<p id="1"></p>
<p id="2"></p>
<p id="3"></p>
<p id="4"></p>
<p id="5"></p>
<p id="6"></p>
<p id="7"></p>
<p id="8"></p>
<p id="9"></p>
<p id="10"></p>
<p id="11"></p>
<p id="12"></p>
<p id="13"></p>
<p id="14"></p>
<p id="15"></p>
<p id="16"></p>
<p id="17"></p>
<p id="18"></p>
<p id="19"></p>
<p id="20"></p>
<p id="21"></p>
<p id="22"></p>
<p id="23"></p>
<p id="24"></p>
<p id="25"></p>
<p id="26"></p>
</div>
<div id="state" style="white-space:pre;font-family:monospace;font-size:12px">
</div>
<script type="text/javascript">
"use strict";
// --- graphix -----------------------------------------------------------------
var terminal = document.getElementById('terminal')
var SCREEN_WIDTH = 80
function truncateToScreenWidth(text) {
return text.slice(0, SCREEN_WIDTH)
}
function printScreen(lines) {
var i, line, truncatedLine, node, nodes
nodes = terminal.getElementsByTagName('p')
for (i = 0; i < nodes.length; i++) {
node = nodes[i]
line = lines[i]
if (typeof line !== 'undefined') {
truncatedLine = truncateToScreenWidth(line)
if (truncatedLine !== node.innerText) {
node.innerText = truncatedLine
}
}
}
}
// --- kernel ------------------------------------------------------------------
// initial state
var state = {
render: splashScreen,
tick: tickOnSplashScreen,
ticksElapsed: 0,
}
function doEvent(handler, arg) {
state = handler(state, arg)
document.getElementById('state').innerText = debugState(state)
printScreen(state.render(state))
}
function debugState(state) {
var output = ''
forEachOwnPropertyOf(state, (key, val) => {
output += key + ': (' + (typeof val) + ') ' + val + '\n'
})
return output
}
doEvent(identity)
window.setInterval(function() {
if (typeof state.tick === 'function') {
doEvent(state.tick)
}
}, 250)
window.addEventListener('keypress', function(event) {
if (typeof state.keyPress === 'function') {
var char = String.fromCharCode(event.charCode)
doEvent(state.keyPress, char)
}
})
// --- events ------------------------------------------------------------------
function prompt() {
return {
render: promptScreen,
tick: blinkCursor,
keyPress: typeChar,
typed: '',
cursor: '_'
}
}
function blinkCursor(state) {
var newState = clone(state)
var countdown =
state.ticksUntilNextCursorBlink === undefined ? 0
: state.ticksUntilNextCursorBlink
return merge(state, {
ticksUntilNextCursorBlink:
countdown === 0
? 2
: countdown - 1,
cursor:
countdown === 0
? (
state.cursor === '_'
? ''
: '_'
)
: state.cursor
})
}
function typeChar(state, char) {
return merge(state, {
typed: state.typed + char
})
}
function tickOnSplashScreen(state) {
if (state.ticksElapsed === 3) {
return prompt()
} else {
return merge(state, {
ticksElapsed: state.ticksElapsed + 1
})
}
}
// --- renderers ---------------------------------------------------------------
function splashScreen(state) {
return ['initializing...']
}
function promptScreen(state) {
return ['> ' + state.typed + state.cursor]
}
// --- utility functions -------------------------------------------------------
function identity(x) {
return x
}
function forEachOwnPropertyOf(obj, fn) {
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
fn(prop, obj[prop])
}
}
}
function clone(obj) {
var copy = {}
forEachOwnPropertyOf(obj, (key, val) => {
copy[key] = val
})
return copy
}
function merge(obj1, obj2) {
var merged = {}
forEachOwnPropertyOf(obj1, (key, val) => {
merged[key] = val
})
forEachOwnPropertyOf(obj2, (key, val) => {
merged[key] = val
})
return merged
}
/* Coerces the given thing to a number.
* If it's NaN, returns 0 instead.
*/
function number(thing) {
var n = +thing
// n !== n iff it's NaN
return (n !== n) ? 0 : n
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment