[ Launch: codemirror number control ] 5565402 by enjalot
[ Launch: codemirror log line widget ] 5564930 by enjalot
[ Launch: codemirror log widget ] 5562666 by enjalot
[ Launch: codemirror widget test ] 5562642 by enjalot
-
-
Save enjalot/5565402 to your computer and use it in GitHub Desktop.
codemirror number control
This file contains 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
{"description":"codemirror number control","endpoint":"","display":"div","public":true,"require":[{"name":"esprima","url":"http://esprima.org/esprima.js"},{"name":"escodegen","url":"http://esprima.org/test/3rdparty/escodegen.browser.js"}],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"style.css":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"log.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"esprima.js":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"thumbnail":"http://i.imgur.com/1C1Pn2T.png"} |
This file contains 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
//this is where we use esprima to interperet our code | |
//mainly taken from https://github.com/nornagon/live/blob/master/xform.coffee | |
tributary.esprima = function(code, prefix) { | |
if(!prefix) prefix = ''; | |
var parsed = esprima.parse(code, {loc:true}); | |
//console.log(parsed) | |
__hasProp = {}.hasOwnProperty; | |
var transformed; | |
var $values = {}; | |
var id, nextId = 0; | |
function replace(e) { | |
//Check for number | |
if (e.type === 'Literal' && typeof e.value === 'number') { | |
id = nextId++; | |
$values[prefix + id] = { | |
value: e.value, | |
loc: e.loc, | |
__trib__: true | |
}; | |
var ret = { | |
type: "MemberExpression", | |
computed: true, | |
object: { | |
type: "MemberExpression", | |
computed: true, | |
object :{ | |
type: 'Identifier', | |
name: '$values' | |
}, | |
property: { | |
type: 'Literal', | |
value: prefix + '' + id | |
} | |
}, | |
"property": { | |
"type": "Literal", | |
"value": "value" | |
} | |
} | |
//console.log(ret) | |
return ret; | |
} else if (e.type === 'UnaryExpression' && e.operator === '-' && e.argument.type === 'Literal' && typeof e.argument.value === 'number') { | |
id = nextId++; | |
$values[prefix + id] = { | |
value: -e.argument.value, | |
loc: e.loc, | |
__trib__: true | |
}; | |
var ret = { | |
type: "MemberExpression", | |
computed: true, | |
object: { | |
type: "MemberExpression", | |
computed: true, | |
object :{ | |
type: 'Identifier', | |
name: '$values' | |
}, | |
property: { | |
type: 'Literal', | |
value: prefix + '' + id | |
} | |
}, | |
"property": { | |
"type": "Literal", | |
"value": "value" | |
} | |
} | |
return ret; | |
//check for console.log | |
} else if(e.type === 'CallExpression' || (e.expression && e.expression.type === 'CallExpression')) { | |
expression = e.expression || e; | |
var callee = expression.callee; | |
//console.log("callee", callee) | |
if(callee.object && callee.object.name === 'console' && callee.property && callee.property.name === 'log') { | |
callee.property.name = 'logJack'; | |
var pos = expression.loc.end; | |
pos.line -= 1; | |
var newArgs = [ | |
{ | |
"type": "ObjectExpression", | |
"properties": [ | |
{ | |
"type": "Property", | |
"key": { | |
"type": "Identifier", | |
"name": "line" | |
}, | |
"value": { | |
"type": "Literal", | |
"value": pos.line, | |
"raw": pos.line + "" | |
}, | |
"kind": "init" | |
}, | |
{ | |
"type": "Property", | |
"key": { | |
"type": "Identifier", | |
"name": "ch" | |
}, | |
"value": { | |
"type": "Literal", | |
"value": pos.column, | |
"raw": pos.column + "" | |
}, | |
"kind": "init" | |
} | |
] | |
}, | |
{ | |
"type": "ArrayExpression", | |
"elements": expression['arguments'] | |
} | |
]; | |
expression['arguments'] = newArgs; | |
//console.log("pos", e, callee.loc.end); | |
return transform(e,replace); | |
} else if((callee.object && callee.object.name === '_t') || (callee.name === '_t')) { | |
//we put the valueIndex as the first argument; | |
var replaced = replace(e['arguments'][0]) | |
if(replaced.object && replaced.object.hasOwnProperty('property')) { | |
//this is actually looking at index in the values object | |
var valueIndex = replaced.object.property.value | |
$values[valueIndex].__control__ = true; | |
} | |
e['arguments'][0] = replaced; | |
e['arguments'].unshift({ | |
"type": "Literal", | |
"value": valueIndex, | |
"raw": valueIndex + "" | |
}); | |
return transform(e,replace) | |
} else { | |
return transform(e,replace); | |
} | |
} else { | |
return transform(e, replace); | |
} | |
} | |
function transform(object, f) { | |
var i, key, newObject, v, value, _i, _len; | |
if (object instanceof Array) { | |
newObject = []; | |
for (i = _i = 0, _len = object.length; _i < _len; i = ++_i) { | |
v = object[i]; | |
if (typeof v === 'object' && v !== null) { | |
newObject[i] = f(v); | |
} else { | |
newObject[i] = v; | |
} | |
} | |
} else { | |
newObject = {}; | |
for (key in object) { | |
if (!__hasProp.call(object, key)) continue; | |
value = object[key]; | |
if (typeof value === 'object' && value !== null) { | |
newObject[key] = f(value); | |
} else { | |
newObject[key] = value; | |
} | |
} | |
} | |
return newObject; | |
} | |
transformed = transform(parsed, replace) | |
return { | |
ast: transformed, | |
values: $values | |
} | |
} | |
This file contains 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
//TODO: persist tributary.$values when saving object | |
//this way we retain information about our control | |
//we can also save tributary.transformed which is | |
//the user code run thru esprima | |
//TODO: color scheme for reserved stuff ($control) | |
//always use dictionary {name: '___1'} | |
//console.log("values", tributary.$values); | |
if(!tributary.$values) tributary.$values = {}; | |
//prototyping interactions with codemirror. | |
//cm and editor are the two main things you want to manipulate in this scope | |
var options = { | |
mode: 'javascript', | |
lineNumbers: true, | |
viewportMargin: Infinity, | |
theme: 'vibrant-ink' | |
} | |
var display = d3.select("#display") | |
var editor = display.append("div").classed("editor", true) | |
.classed("test-editor", true); | |
var cm = CodeMirror(editor.node(), options); | |
//cm.setValue("5") | |
cm.setValue("var x = _t(5)\nconsole.log(\"x\",x);\nx = _t(1337, 'elite');\nconsole.log(\"x\", x, x+42);\ny = _t(2, {min:0, max:10});\nconsole.log('y', y)") | |
var widgets = []; | |
cm.on("cursorActivity", function() { | |
//clear(); | |
}) | |
editor.on("click", function() { | |
//console.log("editor click", arguments); | |
// clear(); | |
}) | |
display.append("button") | |
.text("clear") | |
.on("click", clear); | |
function clear() { | |
for(var i = widgets.length; i--;) { | |
widgets.pop().clear(); //this remove's CodeMirror's handle to the widget; | |
} | |
// display.selectAll(".log-widget").remove() | |
} | |
cm.on("change", execute) | |
function execute() { | |
clear(); | |
if(!tributary.__changing__) { | |
var js = cm.getValue(); | |
try { | |
var transformed = tributary.esprima(js); | |
tributary.transformed = escodegen.generate(transformed.ast); | |
} catch(e) { | |
//e.stack(); | |
console.log("esprima error", e) | |
return; | |
} | |
//console.log("ast", transformed.ast); | |
console.log("really?") | |
var values = 'var $values = ' + JSON.stringify(transformed.values) + '\n'; | |
try { | |
var code = values + escodegen.generate(transformed.ast); | |
} catch (e) { | |
//e.stack(); | |
console.log("AST", transformed.ast) | |
console.log("gen error", e) | |
return; | |
} | |
} else { | |
var values = 'var $values = ' + JSON.stringify(tributary.$values) + '\n'; | |
code = values + tributary.transformed; | |
} | |
//console.log("code", code); | |
try { | |
eval(code); | |
} catch(e) { } | |
if(!tributary.__changing__) { | |
updateControls(); | |
} | |
tributary.__changing__ = false; | |
} | |
if (typeof console !== "undefined") { | |
console.logJack = function(pos, args) { | |
var text = JSON.stringify(args) | |
text = text.slice(1, text.length-1); | |
var widget = display.append("div") | |
.text(text) | |
.classed("log-widget", true) | |
var lwidget = cm.addLineWidget(pos.line, widget.node()); | |
widgets.push(lwidget); | |
console.log.apply(console, args); | |
} | |
} | |
function _t(valuesIndex, value, options) { | |
//TODO: we can inspect num and in the future handle | |
//other cases, like color | |
if(!tributary.__controls) tributary.__controls = {}; | |
var controls = tributary.__controls; | |
var name; | |
var control = {}; | |
if(!options) { | |
options = {} | |
} | |
if(typeof options === 'string') { | |
name = options | |
} else if (typeof options === 'object') { | |
if(options.hasOwnProperty('min')) { | |
control.min = options.min; | |
if(value < control.min) value = control.min; | |
} | |
if(options.max) { | |
control.max = options.max || 100; | |
if(value > control.max) value = control.max; | |
} | |
if(options.name) { | |
name = options.name | |
} | |
if(options.pos) { | |
//can add a widget if we want | |
} | |
} | |
if(!name) { | |
name = '___' + (Object.keys(controls).length + 1); | |
} | |
if(!control.hasOwnProperty('min')) { | |
control.min = -value * 3 | |
} | |
if(!control.hasOwnProperty('max')) { | |
control.max = value * 5 | |
} | |
control.value = value; | |
control.name = name; | |
//console.log(control) | |
controls[name] = control; | |
tributary.__controls = controls; | |
var v = tributary.$values[valuesIndex]; | |
v.__control__ = control; | |
return value; | |
} | |
tributary.__controls = {}; | |
var controldiv = display.append("div").classed("controls", true); | |
function updateControls() { | |
var controls = _.map(tributary.$values, function(d) { | |
return d | |
}).filter(function(d) { | |
return d.hasOwnProperty('__control__'); | |
}) | |
//console.log("controls", controls) | |
var csel = controldiv.selectAll("div.control") | |
.data(controls, function(d) { return d.__control__.name }); | |
var enter = csel.enter().append("div").classed("control", true); | |
enter.append("input").classed("name", true) | |
.attr("value", function(d) { | |
var name = d.__control__.name.replace(/___/,'') | |
if(!d.alias) { | |
d.alias = name; | |
} | |
return d.alias | |
}) | |
.on("keyup", function(d) { | |
console.log(d); | |
d.alias = this.value; | |
}) | |
enter.append("div").classed("value", true) | |
.attr("text", function(d) { | |
return d.value | |
}) | |
enter.append("input") | |
.classed("range", true) | |
.attr({ | |
"type":"range", | |
"value": function(d) { return d.value }, | |
"min": function(d) { return d.__control__.min }, | |
"max": function(d) { return d.__control__.max } | |
}) | |
.on("change", function(d,i) { | |
var oldvalue = d.value; | |
d.value = d.__control__.value = +this.value; | |
var start = { | |
line: d.loc.start.line -1, | |
ch: d.loc.start.column | |
} | |
var end = { | |
line: d.loc.end.line -1, | |
ch: d.loc.start.column + (''+oldvalue).length | |
} | |
//regex style | |
tributary.__changing__ = true; | |
cm.replaceRange(''+d.value, start, end); | |
d3.select(this.parentNode).select(".value").text(function(d) { return d.value }) | |
//or one could just rerun the existing tributary function | |
//tributary.trigger("execute") | |
}) | |
csel.exit().remove(); | |
csel.select("div.value").text(function(d) { return d.value }) | |
csel.select("input.range") | |
.text(function(d) { | |
d3.select(this).attr({ | |
"value": function(d) { this.value = d.value; return d.value }, | |
"min": function(d) { return d.__control__.min }, | |
"max": function(d) { return d.__control__.max } | |
}) | |
}) | |
} | |
execute(); | |
This file contains 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
#display { | |
overflow: scroll; | |
} | |
.test-editor { | |
width: 400px; | |
height: 400px; | |
} | |
.test-editor .CodeMirror { | |
width: 100%; | |
height: 100%; | |
overflow-x: scroll; | |
} | |
.log-widget { | |
border: 1px solid red; | |
background: rgba(240,240,240,0.5); | |
} | |
.range, .value, .name { | |
float:left; | |
} | |
.name { | |
clear:left; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment