A simple JavaScript clone of the LOGO drawing language. Write turtle programs in JavaScript, saved in your browser.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

327 lines
8.8 KiB

"use strict";
var myglobal = this;
myglobal.__tjs = new __TurtleJS(myglobal);
function __TurtleJS(myglobal) {
var self = this;
var svgNamespace = "http://www.w3.org/2000/svg";
var __turtleColor = '#d0d0c0';
var __turtleStrokeWidth = 1;
self.stage = myglobal.document.getElementById('__stage');
self.drawTurtle = function (x,y,rotation) {
self.turtleTransform = myglobal.document.createElementNS(svgNamespace, 'g');
self.turtleTransform.setAttribute(
'transform', 'translate('+x+', '+y+'), rotate('+rotation+')');
var turtle = self.createSvgLine();
self.turtleTransform.appendChild(turtle);
turtle.setAttribute('d', 'M 0 0 L -4 3 L 0 -8 L 4 3 z');
self.stage.appendChild(self.turtleTransform);
};
self.createSvgLine = function (lineObject) {
lineObject = lineObject || {};
var line = myglobal.document.createElementNS(svgNamespace, 'path');
line.setAttribute('fill', lineObject.fillColor || 'none');
line.setAttribute('opacity', lineObject.opacity || 1);
line.setAttribute('stroke', lineObject.color || __turtleColor);
line.setAttribute('stroke-width', lineObject.width || __turtleStrokeWidth);
line.setAttribute('stroke-linejoin', "miter");
return line;
};
self.drawTurtle(0,0,0);
self.logs = [];
self.logger = {
// addLog is defined in the ui.js file
log: function(logObject) {
logObject.type = 'info';
myglobal.__tjs.addLog(logObject);
},
error: function(logObject) {
logObject.type = 'error';
myglobal.__tjs.addLog(logObject);
}
};
self.runTurtleJS = function() {
var __turtleJS = self.editor.getValue();
var __oldConsole = myglobal.console;
myglobal.console = {
log: function() {
var position = {};
try {
__ExceptionTime();
} catch (ex) {
position = __getErrorLineNumber(ex, 1);
}
var toLog = [];
Array.prototype.slice.call(arguments).forEach(function(argument){
if(typeof argument === 'object') {
toLog.push(JSON.stringify(argument));
} else {
toLog.push(''+argument)
}
});
self.logger.log({
lineNumber: position.lineNumber,
charOffset: position.charOffset,
message: "INFO: `" + toLog.join(',')
+ (position.lineNumber ? '` at line ' + position.lineNumber : '`')
});
}
};
self.shapeBreak = function () {
if(self.turtCurrentLine.points.length > 1) {
penUp();
penDown();
}
};
self.stage.innerHTML = '';
self.clearTurt();
penDown();
var __turtleJSWithTryCatch = [
'var exception = null;'
,'try {'
,__turtleJS
,'} catch (ex) {'
,' exception = {'
,' message: ex.message,'
,' name: ex.name,'
,' stack: __getErrorLineNumber(ex)'
,' };'
,'}'
,'exception;'].join('\n');
var error = null;
try {
error = eval(__turtleJSWithTryCatch);
} catch (outerErr) {
error = outerErr;
}
myglobal.console = __oldConsole;
if(error) {
self.logger.error({
lineNumber: error.stack.lineNumber,
charOffset: error.stack.charOffset,
message: error.name +': ' + error.message
+ (error.stack.lineNumber ? ' at line ' + error.stack.lineNumber : '')
});
}
penUp();
self.turtLines.sort(function (a, b) {
if ((a.layer || 0) > (b.layer || 0)) {
return 1;
}
if ((a.layer || 0) < (b.layer || 0)) {
return -1;
}
return 0;
});
self.turtLines.forEach(function (lineObject) {
var line = self.createSvgLine(lineObject);
line.setAttribute('d', lineObject.points.reduce(function (prev, current, i){
return prev + (i == 0 ? 'M ' : 'L ')
+ current.x.toFixed(2) + ' ' + current.y.toFixed(2) + ' ';
}, '')+(lineObject.fillColor && lineObject.fillColor != 'none' ? 'z' : ''));
self.stage.appendChild(line);
});
self.drawTurtle(self.turt.x, self.turt.y, self.turt.rotation);
function forward(px) {
self.moveTurt(
self.turt.x + (Math.sin(self.turt.rotation*self.deg2Rad)*px),
self.turt.y - (Math.cos(self.turt.rotation*self.deg2Rad)*px)
);
}
function left(deg) {
self.turt.rotation -= deg;
while(self.turt.rotation < 0) {
self.turt.rotation += 360;
}
}
function right(deg) {
self.turt.rotation += deg;
while(self.turt.rotation > 360) {
self.turt.rotation -= 360;
}
}
function penUp() {
self.turtLines.push(self.turtCurrentLine);
self.turtCurrentLine = {
points: [],
width: self.turtCurrentLine.width,
color: self.turtCurrentLine.color,
fillColor: self.turtCurrentLine.fillColor,
opacity: self.turtCurrentLine.opacity,
layer: self.turtCurrentLine.layer
};
self.penIsUp = true;
}
function penDown() {
self.turtCurrentLine.points.push({x: self.turt.x, y: self.turt.y});
self.penIsUp = false;
}
function pushColor(color) {
self.shapeBreak();
self.turtCurrentLine.color = color;
self.colorStack.push(color);
}
function pushFillColor(color) {
self.shapeBreak();
self.turtCurrentLine.fillColor = color;
self.fillColorStack.push(color);
}
function pushWidth(width) {
self.shapeBreak();
self.turtCurrentLine.width = width;
self.widthStack.push(width);
}
function pushOpacity(opacity) {
self.shapeBreak();
self.turtCurrentLine.opacity = opacity;
self.opacityStack.push(opacity);
}
function pushLayer(layer) {
self.shapeBreak();
self.turtCurrentLine.layer = layer;
self.layerStack.push(layer);
}
function popColor() {
self.colorStack.pop();
self.shapeBreak();
self.turtCurrentLine.color
= self.colorStack.length ? self.colorStack[self.colorStack.length-1] : __turtleColor;
}
function popFillColor() {
self.fillColorStack.pop();
self.shapeBreak();
self.turtCurrentLine.fillColor
= self.fillColorStack.length ?
self.fillColorStack[self.fillColorStack.length-1]
: 'none';
}
function popWidth() {
self.widthStack.pop();
self.shapeBreak();
self.turtCurrentLine.width
= self.widthStack.length ?
self.widthStack[self.widthStack.length-1]
: 1;
}
function popOpacity(opacity) {
self.opacityStack.pop();
self.shapeBreak();
self.turtCurrentLine.opacity
= self.opacityStack.length ?
self.opacityStack[self.opacityStack.length-1]
: 1;
}
function popLayer(layer) {
self.layerStack.pop();
self.shapeBreak();
self.turtCurrentLine.layer
= self.layerStack.length ?
self.layerStack[self.layerStack.length-1]
: 0;
}
function home() {
self.moveTurt(0,0);
self.turt.rotation = 0;
}
};
self.colorStack = [];
self.fillColorStack = [];
self.widthStack = [];
self.opacityStack = [];
self.layerStack = [];
self.deg2Rad = (Math.PI*2)/360;
self.penIsUp = false;
self.turt = {
x: 0,
y: 0,
rotation: 0
}
self.turtCurrentLine = { points: [{x:0, y:0}] };
self.turtLines = [];
self.moveTurt = function (x, y) {
self.turt.x = x;
self.turt.y = y;
if(!self.penIsUp) {
self.turtCurrentLine.points.push({x: self.turt.x, y: self.turt.y});
}
};
self.clearTurt = function () {
self.colorStack = [];
self.fillColorStack = [];
self.widthStack = [];
self.opacityStack = [];
self.layerStack = [];
self.turt = {
x: 0,
y: 0,
rotation: 0
}
self.turtCurrentLine = { points: [{x:0, y:0}] };
self.turtLines = [];
};
};
var __lineNumberOffset = 2;
function __getErrorLineNumber(ex, stackFramesToSkip) {
var result = null;
var stackFramesToSkip = stackFramesToSkip || 0;
if(ex.stack) {
var lines = ex.stack.split('\n');
var lineToParse = null;
var browser = 'firefox';
if(lines.length) {
var cappedMessage =
ex.message.substring(0, Math.max(ex.message.length-1, 20));
if(lines[0].indexOf(ex.name) !== -1
&& lines[0].indexOf(cappedMessage) !== -1) {
stackFramesToSkip += 1;
browser = 'chrome';
}
if(lines.length > stackFramesToSkip) {
lineToParse = lines[stackFramesToSkip];
}
}
if(lineToParse) {
var values = lineToParse.split(':');
var charOffset = values[values.length - 1].replace(/[^\d]/g,'');
var lineNumber = values[values.length - 2].replace(/[^\d]/g,'');
result = {
charOffset: Number(charOffset),
lineNumber: Number(lineNumber)-__lineNumberOffset,
};
}
}
return result;
}