Last active
September 2, 2018 12:01
-
-
Save antimatter15/87e9f88c07e8e4c4f8ed1afc934f2748 to your computer and use it in GitHub Desktop.
FakeTalk
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
<title>FakeTalk</title> | |
<style> | |
body { | |
background: #eee; | |
} | |
* { | |
box-sizing: border-box; | |
} | |
.paper { | |
padding: 10px; | |
border: 1px solid #ccc; | |
width: 350px; | |
white-space: pre-wrap; | |
font-family: monospace; | |
background: white; | |
display: inline-block; | |
} | |
.paper h1 { | |
margin: 0; | |
font: inherit; | |
font-weight: bold; | |
background: #ddd; | |
padding: 5px 10px; | |
margin: -10px; | |
margin-bottom: 5px; | |
} | |
.label { | |
background: #fff0d0; | |
padding: 5px 10px; | |
margin: 5px 0; | |
border-radius: 10px; | |
} | |
.paper textarea { | |
width: 100%; | |
padding: 0; | |
border: 0; | |
min-height: 150px; | |
background: transparent; | |
} | |
#background { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
z-index: -1; | |
} | |
textarea.stale:not(:focus) { | |
font-style: italic; | |
background: #fff0d0; | |
} | |
</style> | |
<div id="background"></div> | |
<div id="root"></div> | |
<script src="https://cdn.jsdelivr.net/npm/preact/dist/preact.min.js"></script> | |
<script> | |
const { Component, h } = window.preact; | |
let functionCache = {}; | |
function render(){ | |
let _ = Symbol('_'), $ = Symbol('$'); | |
let $db = new Map(), $hooks = new Map(); | |
function match_data(obj, pattern, cb, args=[]){ | |
if(pattern.length === 0){ | |
if(obj.has($)) cb(...args); | |
}else if(pattern[0] === _){ | |
for(let [key, val] of obj.entries()) | |
match_data(val, pattern.slice(1), cb, [...args, key]); | |
}else{ | |
let val = obj.get(pattern[0]); | |
if(val) match_data(val, pattern.slice(1), cb, args); | |
} | |
} | |
function match_patterns(obj, datum, args=[]){ | |
if(datum.length === 0){ | |
for(let cb of obj.get($)) cb(...args); | |
}else{ | |
let next = datum.slice(1); | |
let a = obj.get(datum[0]), | |
b = obj.get(_) | |
if(a) match_patterns(a, next, args); | |
if(b) match_patterns(b, next, [...args, datum[0]]); | |
} | |
} | |
function fill_path(obj, path){ | |
let ptr = obj; | |
for(let part of path){ | |
if(!ptr.has(part)) ptr.set(part, new Map()); | |
ptr = ptr.get(part) | |
} | |
return ptr | |
} | |
function when(...args){ | |
let cb = args[args.length - 1], pattern = args.slice(0, -1); | |
claim('when', ...pattern.map(k => k === _ ? '_' : k)) | |
let ptr = fill_path($hooks, pattern) | |
if(!ptr.has($)) ptr.set($, []); | |
ptr.get($).push(cb); | |
match_data($db, pattern, cb) | |
} | |
function claim(...args){ | |
let ptr = fill_path($db, args) | |
if(ptr.has($)) return; | |
ptr.set($, true) | |
match_patterns($hooks, args) | |
} | |
let latercb = [] | |
const later = cb => latercb.push(cb) | |
let newFunctionCache = {} | |
papers.map((code, i) => { | |
let me = { index: i } | |
claim(me, 'paper') | |
claim(me, 'code', code) | |
try { | |
newFunctionCache[code] = functionCache[code] || | |
new Function('_', 'me', 'when', 'claim', 'later', code) | |
newFunctionCache[code](_, me, when, claim, later) | |
} catch (err) { | |
console.log(code) | |
console.error(err) | |
claim(me, 'error', err) | |
} | |
}) | |
functionCache = newFunctionCache; | |
for(let cb of latercb) | |
try { cb(); } catch (err) { console.error(err) }; | |
} | |
const FILENAME = 'papers.js' | |
function savePapers(){ | |
fetch(FILENAME, { | |
method: 'PUT', | |
body: papers.join('\n\n///////////////////////////////////////\n\n') | |
}) | |
.then(res => res.text()) | |
.then(res => console.log(res)) | |
} | |
let papers; | |
fetch(FILENAME) | |
.then(res => res.text()) | |
.then(text => text.split(/\n+\/{30,}\n+/g)) | |
.then(list => { | |
papers = list; | |
render() | |
}) | |
</script> |
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
claim(me, 'name', 'mouse') | |
claim(me, 'x', 901) | |
claim(me, 'y', 29) | |
when('fox is out', () => { | |
claim(me, 'wish', 'labelled', 'squeak') | |
claim(me, 'wish', 'outlined', 'red') | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'fox') | |
claim(me, 'x', 1075) | |
claim(me, 'y', 114) | |
claim('fox is out') | |
/////////////////////////////////////// | |
claim(me, 'name', 'leprechaun labeler') | |
claim(me, 'x', 1531) | |
claim(me, 'y', 103) | |
when(_, 'is a leprechaun', (lep) => { | |
claim(lep, 'wish', 'labelled', 'is a leprechaun') | |
claim(lep, 'wish', 'background', 'rgb(200, 255, 200)') | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'leprechaun') | |
claim(me, 'x', 1482) | |
claim(me, 'y', 370) | |
claim(me, 'is a leprechaun') | |
/////////////////////////////////////// | |
claim(me, 'name', 'bob') | |
claim(me, 'x', 1306) | |
claim(me, 'y', 649) | |
/////////////////////////////////////// | |
claim(me, 'name', 'bob the leper') | |
claim(me, 'x', 1726) | |
claim(me, 'y', 647) | |
when(_, 'name', 'bob', (person) => { | |
claim(person, 'is a leprechaun') | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'frog') | |
claim(me, 'x', 2287) | |
claim(me, 'y', 25) | |
claim(me, 'is a frog') | |
/////////////////////////////////////// | |
claim(me, 'name', 'label frogs') | |
claim(me, 'x', 2688) | |
claim(me, 'y', 174) | |
when(_, 'is a frog', (lep) => { | |
claim(lep, 'wish', 'labelled', 'is a frog') | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'hungry snek') | |
claim(me, 'x', 2259) | |
claim(me, 'y', 334) | |
return //disabled | |
when(_, 'is a frog', (frog) => { | |
claim(me, 'eats', frog) | |
claim(frog, 'wish', 'labelled', 'ded') | |
claim(frog, 'wish', 'outlined', 'red') | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'poison frog') | |
claim(me, 'x', 2303) | |
claim(me, 'y', 624) | |
claim(me, 'is a frog') | |
claim(me, 'is poison') | |
/////////////////////////////////////// | |
claim(me, 'name', 'poison behavior') | |
claim(me, 'x', 2660) | |
claim(me, 'y', 356) | |
when(_, 'is poison', (poison_frog) => { | |
when(_, 'eats', poison_frog, (snek) => { | |
claim(snek, 'wish', 'labelled', 'ded') | |
claim(snek, 'wish', 'outlined', 'red') | |
}) | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'read-only renderer') | |
claim(me, 'x', 16) | |
claim(me, 'y', 258) | |
return //disabled | |
when(_, 'paper', obj => | |
when(obj, 'code', _, code => { | |
let el = h('div', { class: 'paper' }, code) | |
claim('wish', 'preact render', el) | |
claim(obj, 'el', el) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'editable renderer') | |
claim(me, 'x', 87) | |
claim(me, 'y', 353) | |
when(_, 'paper', obj => | |
when(obj, 'code', _, code => { | |
let textarea; | |
function save(){ | |
textarea.lastValue = textarea.value; | |
papers[obj.index] = textarea.value | |
render() | |
savePapers() | |
} | |
let el = h('div', { class: 'paper' }, [ | |
h('textarea', { | |
ref: e => { | |
textarea = e; | |
if(e){ | |
if(e.lastValue == e.value || !e.value){ | |
e.lastValue = code; | |
e.value = code; | |
} | |
textarea.classList.toggle('stale', | |
textarea.lastValue != textarea.value) | |
} | |
}, | |
onInput: e => { | |
if(textarea){ | |
textarea.classList.toggle('stale', | |
textarea.lastValue != textarea.value) | |
} | |
}, | |
onKeyDown: e => { | |
if((e.metaKey || e.ctrlKey) && e.keyCode == 83){ // Cmd-S | |
e.preventDefault(); | |
save() | |
} | |
}}), | |
h('button', { | |
onClick: e => save() | |
}, 'Save') | |
]) | |
claim('wish', 'preact render', el) | |
claim(obj, 'el', el) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'preact') | |
claim(me, 'x', 38) | |
claim(me, 'y', 9) | |
let elements = [] | |
when('wish', 'preact render', _, el => { | |
elements.push(el) | |
}) | |
later(() => { | |
let root = document.getElementById('root'); | |
preact.render( | |
h('div', { }, elements), | |
root, | |
root.firstChild) | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'titlebar') | |
claim(me, 'x', 508) | |
claim(me, 'y', 702) | |
when(_, 'name', _, (obj, name) => | |
when(obj, 'el', _, el => { | |
let h1 = h('h1', { class: 'title' }, name) | |
claim(obj, 'title', h1) | |
el.children.unshift(h1) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'position absolute') | |
claim(me, 'x', 32) | |
claim(me, 'y', 626) | |
when(_, 'paper', obj => | |
when(obj, 'x', _, x => | |
when(obj, 'y', _, y => | |
when(obj, 'el', _, el => { | |
el.attributes.style = { | |
...el.attributes.style, | |
position: 'absolute', | |
top: y, | |
left: x | |
} | |
})))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'close button') | |
claim(me, 'x', 504) | |
claim(me, 'y', 1024) | |
when(_, 'paper', obj => | |
when(obj, 'code', _, code => | |
when(obj, 'title', _, el => { | |
el.children.push(h('button', { style: { float: 'right' }, onClick: () => { | |
papers.splice(obj.index, 1) | |
render() | |
}}, '×')) | |
}))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'fork button') | |
claim(me, 'x', 109) | |
claim(me, 'y', 1026) | |
when(_, 'paper', obj => | |
when(obj, 'code', _, code => | |
when(obj, 'el', _, el => { | |
el.children.push(h('button', { | |
onClick: () => { | |
papers.push(code | |
.replace(/claim\s*\(\s*me\s*,\s*'x'\s*,\s*(\d+)\)/, (a, b) => a.replace(b, +b + 20)) | |
.replace(/claim\s*\(\s*me\s*,\s*'y'\s*,\s*(\d+)\)/, (a, b) => a.replace(b, +b + 40)) | |
) | |
render() | |
} | |
}, 'Fork')) | |
}))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'toggle button') | |
claim(me, 'x', 893) | |
claim(me, 'y', 1041) | |
when(_, 'paper', obj => | |
when(obj, 'code', _, code => | |
when(obj, 'title', _, el => { | |
let enabled = !/\n\nreturn/.test(code); | |
if(!enabled) claim(obj, 'disabled'); | |
el.children.unshift(h('input', { | |
type: 'checkbox', | |
checked: enabled, | |
onClick: e => { | |
papers[obj.index] = code.replace(/\n\n+(return[^\n]*)?/, | |
e.target.checked ? '\n\n' : '\n\nreturn //disabled\n') | |
render() | |
} | |
})) | |
}))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'outliner') | |
claim(me, 'x', 475) | |
claim(me, 'y', 281) | |
when(_, 'wish', 'outlined', _, (obj, color) => | |
when(obj, 'el', _, el => { | |
el.attributes.style = { | |
...el.attributes.style, | |
borderColor: color, | |
borderWidth: 3 | |
} | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'labeler') | |
claim(me, 'x', 551) | |
claim(me, 'y', 338) | |
when(_, 'wish', 'labelled', _, (obj, text) => | |
when(obj, 'el', _, el => { | |
el.children.push(h('div', { class: 'label' }, text + '')) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'draw backgrounds') | |
claim(me, 'x', 628) | |
claim(me, 'y', 414) | |
when(_, 'wish', 'background', _, (obj, color) => | |
when(obj, 'el', _, el => { | |
el.attributes.style = { | |
...el.attributes.style, | |
background: color, | |
} | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'make titlebar draggable') | |
claim(me, 'x', 712) | |
claim(me, 'y', 750) | |
when(_, 'title', _, (obj, h1) => | |
when(obj, 'x', _, x => | |
when(obj, 'y', _, y => | |
when(obj, 'code', _, code => { | |
h1.attributes.style = { | |
...h1.attributes.style, | |
userSelect: 'none', | |
cursor: 'move' | |
} | |
h1.attributes.onMouseDown = e => { | |
document.body.onmousemove = k => { | |
papers[obj.index] = code | |
.replace(/claim\s*\(\s*me\s*,\s*'x'\s*,\s*(\d+)\)/, | |
(a, b) => a.replace(b, x + k.pageX - e.pageX)) | |
.replace(/claim\s*\(\s*me\s*,\s*'y'\s*,\s*(\d+)\)/, | |
(a, b) => a.replace(b, y + k.pageY - e.pageY)) | |
render() | |
} | |
document.body.onmouseup = k => { | |
document.body.onmousemove = null; | |
document.body.onmouseup = null; | |
savePapers() | |
} | |
} | |
})))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'thingy') | |
claim(me, 'x', 1484) | |
claim(me, 'y', 1239) | |
/////////////////////////////////////// | |
claim(me, 'name', 'glow when near thingy') | |
claim(me, 'x', 1960) | |
claim(me, 'y', 1210) | |
when(_, 'name', 'thingy', thingy => | |
when(me, 'near', thingy, 'with radius', 400, () => { | |
claim(me, 'wish', 'glow', 'green') | |
claim(me, 'wish', 'labelled', 'is near thingy') | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'handle proximity') | |
claim(me, 'x', 2478) | |
claim(me, 'y', 1240) | |
when('when', _, 'near', _, 'with radius', _, (page, other, radius) => { | |
if(other === '_') return; // don't trigger for literal wildcard | |
//claim(page, 'wish', 'labelled', 'has a geofence') | |
when(page, 'x', _, page_x => | |
when(page, 'y', _, page_y => { // got page xy | |
when(other, 'x', _, x => | |
when(other, 'y', _, y => { // got obj xy | |
if(Math.abs(page_x - x) < radius | |
&& Math.abs(page_y - y) < radius){ | |
claim(page, 'near', other, 'with radius', radius) | |
} | |
})) | |
})) | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'periodically re-render') | |
claim(me, 'x', 2062) | |
claim(me, 'y', 2181) | |
return //disabled | |
clearTimeout(window.renderTimeout) | |
window.renderTimeout = setTimeout(() => render(), 100) | |
/////////////////////////////////////// | |
claim(me, 'name', 'current date and time') | |
claim(me, 'x', 2469) | |
claim(me, 'y', 2185) | |
claim(me, 'wish', 'labelled', new Date) | |
/////////////////////////////////////// | |
claim(me, 'name', 'display value at whisker') | |
claim(me, 'x', 311) | |
claim(me, 'y', 1973) | |
when(me, 'points up at', _, page => | |
when(page, 'value', _, value => { | |
claim(me, 'wish', 'labelled', value) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'the answer to life the universe and everything') | |
claim(me, 'x', 35) | |
claim(me, 'y', 1666) | |
claim(me, 'value', 42) | |
/////////////////////////////////////// | |
claim(me, 'name', 'whisker behavior') | |
claim(me, 'x', 457) | |
claim(me, 'y', 1373) | |
const WHISKER_LENGTH = 150; | |
const PAPER_HEIGHT = 213; | |
const PAPER_WIDTH = 350; | |
when('when', _, 'points up at', '_', page => { | |
when(page, 'x', _, page_x => | |
when(page, 'y', _, page_y => { // got page xy | |
let el = h('div', { | |
style: { | |
position: 'absolute', | |
top: page_y - WHISKER_LENGTH, | |
zIndex: -1, | |
left: page_x + (PAPER_WIDTH/2), | |
width: 5, | |
height: WHISKER_LENGTH, | |
background: '#ccc' | |
} | |
}) | |
claim('wish', 'preact render', el) | |
when(_, 'x', _, (obj, x) => | |
when(obj, 'y', _, y => { | |
if(Math.abs(page_x - x) < PAPER_WIDTH / 2 | |
&& Math.abs(page_y - PAPER_HEIGHT - y) < WHISKER_LENGTH | |
&& obj !== page){ | |
el.attributes.style = { | |
...el.attributes.style, | |
background: '#acacff' | |
} | |
claim(page, 'points up at', obj) | |
} | |
})) | |
})) | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'extract x coordinate') | |
claim(me, 'x', 455) | |
claim(me, 'y', 1686) | |
when(me, 'points up at', _, page => | |
when(page, 'x', _, value => { | |
claim(me, 'value', value) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'extract y coordinate') | |
claim(me, 'x', 887) | |
claim(me, 'y', 1647) | |
when(me, 'points up at', _, page => | |
when(page, 'y', _, value => { | |
claim(me, 'value', value) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'output as hue') | |
claim(me, 'x', 598) | |
claim(me, 'y', 2017) | |
when(me, 'points up at', _, page => | |
when(page, 'value', _, value => { | |
claim(me, 'wish', 'background', 'hsl(' + value + ', 100%, 50%)') | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'number of pages') | |
claim(me, 'x', 1299) | |
claim(me, 'y', 1653) | |
claim(me, 'value', papers.length) | |
/////////////////////////////////////// | |
claim(me, 'name', 'UNIX timestamp') | |
claim(me, 'x', 1683) | |
claim(me, 'y', 1615) | |
claim(me, 'value', Date.now() / 100) | |
/////////////////////////////////////// | |
claim(me, 'name', 'Slider Input') | |
claim(me, 'x', 2100) | |
claim(me, 'y', 1585) | |
claim(me, 'value', 24) | |
when(me, 'value', _, value => | |
when(me, 'code', _, code => | |
when(me, 'el', _, el => { | |
el.children.push(h('input', { | |
type: 'range', | |
value: value, | |
min: 0, | |
max: 100, | |
step: 1, | |
onInput: e => { | |
papers[me.index] = code | |
.replace(/claim\s*\(\s*me\s*,\s*'value'\s*,\s*(\d+)\)/, | |
(a, b) => a.replace(b, e.target.value)) | |
render() | |
} | |
})) | |
}))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'output as progress bar') | |
claim(me, 'x', 2108) | |
claim(me, 'y', 1899) | |
when(me, 'points up at', _, page => | |
when(page, 'value', _, value => | |
when(me, 'el', _, el => { | |
el.children.push(h('progress', { | |
value: value % 100, | |
max: 100 | |
})) | |
}))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'extract code length') | |
claim(me, 'x', 2471) | |
claim(me, 'y', 1535) | |
when(me, 'points up at', _, page => | |
when(page, 'code', _, value => { | |
claim(me, 'value', value.length) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'output as my rotation') | |
claim(me, 'x', 1674) | |
claim(me, 'y', 1934) | |
when(me, 'points up at', _, page => | |
when(page, 'value', _, value => | |
when(me, 'el', _, el => { | |
el.attributes.style = { | |
...el.attributes.style, | |
transform: `rotate(${value}deg)` | |
} | |
}))) | |
/////////////////////////////////////// | |
// based on https://rsnous.com/posts/notes-from-dynamicland-geokit/ | |
claim(me, 'name', 'Made-up claim demo') | |
claim(me, 'x', 3442) | |
claim(me, 'y', 1527) | |
claim(me, 'blahblahblah') | |
/////////////////////////////////////// | |
// based on https://rsnous.com/posts/notes-from-dynamicland-geokit/ | |
claim(me, 'name', 'Made-up claim when demo') | |
claim(me, 'x', 3488) | |
claim(me, 'y', 1763) | |
when(_, 'blahblahblah', page => { | |
claim(page, 'wish', 'background', '#007fff') | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'Button Counter') | |
claim(me, 'x', 2533) | |
claim(me, 'y', 1798) | |
claim(me, 'value', 8) | |
when(me, 'value', _, value => | |
when(me, 'code', _, code => | |
when(me, 'el', _, el => { | |
el.children.push(h('button', { | |
style: { | |
display: 'block', | |
fontSize: 32 | |
}, | |
onClick: e => { | |
papers[me.index] = code | |
.replace(/claim\s*\(\s*me\s*,\s*'value'\s*,\s*(\d+)\)/, | |
(a, b) => a.replace(b, (+b)+1)) | |
render() | |
} | |
}, `Clicked ${value} times`)) | |
}))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'Button Counter Resetter') | |
claim(me, 'x', 2921) | |
claim(me, 'y', 1884) | |
claim(me, 'value', 82) | |
when(_, 'name', 'Button Counter', page => | |
when(page, 'value', _, value => | |
when(page, 'code', _, code => | |
when(me, 'el', _, el => { | |
el.children.push(h('button', { | |
style: { | |
display: 'block', | |
fontSize: 32 | |
}, | |
onClick: e => { | |
papers[page.index] = code | |
.replace(/claim\s*\(\s*me\s*,\s*'value'\s*,\s*(\d+)\)/, | |
(a, b) => a.replace(b, 0)) | |
render() | |
} | |
}, `Reset Button`)) | |
})))) | |
/////////////////////////////////////// | |
claim(me, 'name', 'display value at whisker') | |
claim(me, 'x', 1287) | |
claim(me, 'y', 1915) | |
when(me, 'points up at', _, page => | |
when(page, 'value', _, value => { | |
claim(me, 'wish', 'labelled', value) | |
})) | |
/////////////////////////////////////// | |
claim(me, 'name', 'highlight long code snippets') | |
claim(me, 'x', 2967) | |
claim(me, 'y', 1413) | |
when(_, 'code', _, (page, value) => { | |
if(value.split('\n').length > 25){ | |
claim(page, 'wish', 'background', 'rgb(255,200,255)') | |
} | |
}) | |
/////////////////////////////////////// | |
claim(me, 'name', 'frog') | |
claim(me, 'x', 3083) | |
claim(me, 'y', 84) | |
claim(me, 'is a frog') | |
/////////////////////////////////////// | |
claim(me, 'name', 'frog') | |
claim(me, 'x', 3103) | |
claim(me, 'y', 124) | |
claim(me, 'is a frog') | |
/////////////////////////////////////// | |
claim(me, 'name', 'frog') | |
claim(me, 'x', 3123) | |
claim(me, 'y', 164) | |
claim(me, 'is a frog') | |
/////////////////////////////////////// | |
claim(me, 'name', 'frog') | |
claim(me, 'x', 3143) | |
claim(me, 'y', 204) | |
claim(me, 'is a frog') | |
/////////////////////////////////////// | |
claim(me, 'name', 'frog') | |
claim(me, 'x', 3163) | |
claim(me, 'y', 244) | |
claim(me, 'is a frog') | |
/////////////////////////////////////// | |
claim(me, 'name', 'frog') | |
claim(me, 'x', 3191) | |
claim(me, 'y', 287) | |
claim(me, 'is a frog') | |
/////////////////////////////////////// | |
claim(me, 'name', 'glower') | |
claim(me, 'x', 712) | |
claim(me, 'y', 468) | |
when(_, 'wish', 'glow', _, (obj, color) => | |
when(obj, 'el', _, el => { | |
el.attributes.style = { | |
...el.attributes.style, | |
boxShadow: `0 0 30px ${color}`, | |
} | |
})) |
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
# based on https://gist.github.com/mildred/67d22d7289ae8f16cae7 | |
import argparse | |
import http.server | |
import os | |
class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler): | |
def do_PUT(self): | |
path = self.translate_path(self.path) | |
if path.endswith('/'): | |
self.send_response(405, "Method Not Allowed") | |
self.end_headers() | |
self.wfile.write("PUT not allowed on a directory\n".encode()) | |
return | |
else: | |
try: | |
os.makedirs(os.path.dirname(path)) | |
except FileExistsError: pass | |
length = int(self.headers['Content-Length']) | |
with open(path, 'wb') as f: | |
f.write(self.rfile.read(length)) | |
self.send_response(201, "Created") | |
self.end_headers() | |
self.wfile.write(("Saved %d bytes to '%s'\n" % (length, path)).encode()) | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--bind', '-b', default='', metavar='ADDRESS', | |
help='Specify alternate bind address ' | |
'[default: all interfaces]') | |
parser.add_argument('port', action='store', | |
default=8000, type=int, | |
nargs='?', | |
help='Specify alternate port [default: 8000]') | |
args = parser.parse_args() | |
http.server.test(HandlerClass=HTTPRequestHandler, port=args.port, bind=args.bind) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment