-
Notifications
You must be signed in to change notification settings - Fork 3
/
turtle.min.js
69 lines (68 loc) · 14 KB
/
turtle.min.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
(function(window,document,undefined){function extend(left,right){for(var key in right)left[key]=right[key];return left;}
function assertNumber(value,errors,message){if(value===undefined){return false;}
if(value!==null&&isNaN(parseFloat(value))){errors.push(message);return false;}
return true;}
function radians(degrees){return degrees*Math.PI/180;}
function degrees(radians){return radians*180/Math.PI;}
function Turtle(){var home={direction:0,x:0,y:0};if(arguments.length==1){extend(home,arguments[0]);}
return extend(this,{_position:{},_home:home}).home();}
extend(Turtle.prototype,{home:function home(){var newhome=arguments[0];if(typeof newhome==="object"){this.position(newhome);extend(this._home,this._position);}
this.position(this._home);return this;},position:function position(){var newposition=arguments[0];if(typeof newposition==="object"){var errors=[];if(assertNumber(newposition.direction,errors,"direction must be a number")){this._position.direction=parseFloat(newposition.direction)%360;(this._position.direction<0)&&(this._position.direction+=360);}
if(assertNumber(newposition.x,errors,"x must be a number")){this._position.x=parseFloat(newposition.x);}
if(assertNumber(newposition.y,errors,"y must be a number")){this._position.y=parseFloat(newposition.y);}
if(errors.length){throw new TypeError(errors.join("\n"));}
return this;}
return extend({},this._position);},turn:function turn(degrees){this.position({direction:this._position.direction+parseFloat(degrees)});return this;},move:function move(pixels){var p=parseFloat(pixels);this.position({x:Math.cos(radians(this._position.direction))*p+this._position.x,y:Math.sin(radians(this._position.direction))*p+this._position.y});return this;}});function TurtlePenDecorator(turtle,context){context.lineCap="round";return extend(this,{"turtle":turtle,"context":context,"pen":"up"});}
extend(TurtlePenDecorator.prototype,{home:function home(){if(arguments.length===0){this.turtle.home();}
else if(arguments.length===1){this.turtle.home(arguments[0]);}
return this;},position:function position(){if(arguments.length===0){return this.turtle.position();}
else if(arguments.length===1){return this.turtle.position(arguments[0]);}},turn:function turn(degrees){this.turtle.turn(degrees);return this;},move:function move(pixels){if(this.pen==="up"){this.turtle.move(pixels);this.context.moveTo(this.turtle.position().x,this.turtle.position().y);}
else if(this.pen==="down"){this.turtle.move(pixels);this.context.lineTo(this.turtle.position().x,this.turtle.position().y);this.context.stroke();}
return this;},penup:function penup(){this.pen="up";return this;},pendown:function pendown(){this.pen="down";this.context.beginPath();this.context.moveTo(this.turtle.position().x,this.turtle.position().y);this.context.strokeStyle=this._pencolor;return this;},pencolor:function pencolor(color){this._pencolor=color;this.context.strokeStyle=color;return this;},pensize:function pensize(size){this.context.lineWidth=size;return this;},clear:function clear(){this.context.clearRect(0,0,this.context.canvas.width,this.context.canvas.height);this.turtle.home();return this;}});function TurtleLineDecorator(turtle,context){context.lineCap="round";return extend(this,{"turtle":turtle,"context":context,"pen":"up"});}
TurtleLineDecorator.prototype=extend(TurtlePenDecorator.prototype,{move:function move(pixels){if(this.pen==="up"){this.turtle.move(pixels);this.context.moveTo(this.turtle.position().x,this.turtle.position().y);}
else if(this.pen==="down"){this.context.beginPath();this.context.moveTo(this.turtle.position().x,this.turtle.position().y);this.turtle.move(pixels);this.context.lineTo(this.turtle.position().x,this.turtle.position().y);this.context.stroke();}
return this;},pendown:function pendown(){this.pen="down";this.context.strokeStyle=this._pencolor;return this;}});function TurtleShapeDecorator(turtlepen){var cs=document.createElement('canvas');cs.width=15;cs.height=10;var ctx=cs.getContext('2d');ctx.beginPath();ctx.moveTo(0,0);ctx.lineTo(0,10);ctx.lineTo(15,5);ctx.lineTo(0,0);ctx.lineWidth=1;ctx.strokeStyle="#000000";return extend(this,{turtle:turtlepen,shape:cs});}
extend(TurtleShapeDecorator.prototype,{home:function home(){this._restore_background();if(arguments.length===0){this.turtle.home();}
else if(arguments.length===1){this.turtle.home(arguments[0]);}
this._play_shape_relative_to_current_position();return this;},position:function position(){if(arguments.length==1){this._restore_background();this.turtle.position(arguments[0]);this._play_shape_relative_to_current_position();}
return this.turtle.position();},turn:function turn(degrees){this._restore_background();this.turtle.turn(degrees);this._play_shape_relative_to_current_position();return this;},move:function move(pixels){this._restore_background();this.turtle.move(pixels);this._play_shape_relative_to_current_position();return this;},penup:function penup(){this._restore_background();this.turtle.penup();this._play_shape_relative_to_current_position();return this;},pendown:function pendown(){this._restore_background();this.turtle.pendown();this._play_shape_relative_to_current_position();return this;},pencolor:function pencolor(color){this._restore_background();this.turtle.pencolor(color);this._play_shape_relative_to_current_position();return this;},pensize:function pensize(size){this._restore_background();this.turtle.pensize(size);this._play_shape_relative_to_current_position();return this;},clear:function clear(){this.turtle.clear();this._play_shape_relative_to_current_position();return this;},_play_shape_relative_to_current_position:function _play_relative(){var context=this.turtle.context;var position=this.turtle.position();var save_pen_state=this.turtle.pen;var save_pen_color=context.strokeStyle;this._save_background();context.save();var sx=this.shape.getContext('2d');sx.fillStyle=save_pen_state=="up"?"#ffffff":save_pen_color;sx.fill();sx.stroke();context.translate(position.x,position.y);context.rotate(radians(position.direction));context.drawImage(this.shape,this.shape.width/-2,this.shape.height/-2);context.restore();this.turtle.position(position);},_save_background:function _save_background(){var context=this.turtle.context;this.savedBackground=context.getImageData(0,0,context.canvas.width,context.canvas.height);},_restore_background:function _restore_background(){if(typeof(this.savedBackground)==="undefined")
return;this.turtle.context.putImageData(this.savedBackground,0,0);}});function TurtleCommandRecorder(){return extend(this,{queue:[]});}
extend(TurtleCommandRecorder.prototype,{home:function home(){var newhome=arguments[0];this.queue.push(function home(turtle){turtle.home(newhome);return;});return this;},position:function position(){var newposition=arguments[0];this.queue.push(function position(turtle){turtle.position(newposition);return;});return this;},turn:function turn(degrees){this.queue.push(function turn(turtle){turtle.turn(degrees);return;});return this;},move:function move(pixels){this.queue.push(function move(turtle){turtle.move(pixels);return;});return this;},pendown:function pendown(){this.queue.push(function(turtle){turtle.pendown();return;});return this;},penup:function penup(){this.queue.push(function penup(turtle){turtle.penup();return;});return this;},pencolor:function pencolor(color){this.queue.push(function pencolor(turtle){turtle.pencolor(color);return;});return this;},pensize:function pensize(size){this.queue.push(function pensize(turtle){turtle.pensize(color);return;});return this;},clear:function clear(){this.queue.push(function clear(turtle){turtle.clear();return;});return this;},play:function play(turtle,interval,commandsPerInterval){if(interval==undefined){this.queue.map(function(fn){fn.apply(undefined,[turtle]);});}
else{if(!commandsPerInterval)
commandsPerInterval=1;var queue=this.queue,i=0,limit=this.queue.length;(function animate(){do{queue[i].apply(undefined,[turtle]);i++;limit--;}
while(limit>0&&limit%commandsPerInterval);if(limit>0)
setTimeout(animate,interval);})();}
return this;}});window.Turtle=Turtle;extend(window.Turtle,{extend:extend,Recorder:TurtleCommandRecorder,Pen:TurtlePenDecorator,Line:TurtleLineDecorator,Shape:TurtleShapeDecorator});})(this,this.document);(function(window,document,undefined){function TurtleStorage(prefix,storage){return Turtle.extend(this,{prefix:prefix,storage:storage});}
Turtle.extend(TurtleStorage.prototype,{prefixedKey:function prefixedKey(key){return this.prefix+"."+key;},keys:function keys(){var limit=this.storage.length,found=[];for(var i=0;i<limit;i++){var key=this.storage.key(i);if(key&&key.indexOf(this.prefix)===0){found.push(key.replace(this.prefix+".",""));}}
return found.sort();},setItem:function setItem(key,value){return this.storage.setItem(this.prefixedKey(key),value);},getItem:function getItem(key){return this.storage.getItem(this.prefixedKey(key));},removeItem:function removeItem(key){return this.storage.removeItem(this.prefixedKey(key));}});window.Turtle.Storage=TurtleStorage;})(this,this.document);(function(window,document,undefined){var REMOVE="remove",LOAD="load",RENAME="rename",FORM="rename-form";var HIDE="turtle-hide";var REVISION_HTML='\
<div class="revision">\
<form class="rename-form turtle-hide">\
<input type="text" name="newname" size="60">\
</form>\
<a href="" class="load">version</a>\
<a href="" class="rename">✎</a>\
<a href="" class="remove">✕</a>\
</div>\
';function TurtleHistory(input,localStorage,options){var historyID=input.id+"-history";if(!localStorage)
localStorage=window.localStorage;var element=document.getElementById(historyID);if(!element){element=document.createElement("section");element.setAttribute("id",historyID);input.form.parentNode.appendChild(element);}
var self=Turtle.extend(this,Turtle.extend({prefix:historyID+"-",input:input,element:element,storage:new Turtle.Storage(historyID,localStorage)},options));input.form.addEventListener("submit",function(event){return self.handleEvent(event);});element.addEventListener("click",function(event){return self.handleEvent(event);});return self;}
Turtle.extend(TurtleHistory.prototype,{fullRevisionName:function(name){return this.prefix+name;},syncLocalStorage:function syncLocalStorage(){var self=this;self.element.innerHTML="";var keys=self.storage.keys();keys.map(function(key){self.createWithoutSave(key);});return;},createWithoutSave:function createWithoutSave(name){return this.create(name,true);},create:function create(name,withoutSave){var self=this;if(document.getElementById(this.fullRevisionName(name))){return;}
var placeholder=document.createElement("div");placeholder.innerHTML=REVISION_HTML;var newnode=placeholder.getElementsByClassName("revision")[0];newnode.setAttribute("id",this.fullRevisionName(name));newnode.setAttribute("data-revision",name);newnode.getElementsByClassName(LOAD)[0].textContent=name;newnode.getElementsByClassName(FORM)[0].addEventListener("submit",function(e){return self.handleEvent(e);});this.element.appendChild(newnode);if(!withoutSave)
this.storage.setItem(name,this.input.value);return;},handleEvent:function(event){event.stopPropagation();event.preventDefault();var revision=event.target.parentNode.getAttribute("data-revision");if(event.target===this.input.form){var revisionName=this.useInputValueForRevisionName?this.input.value:new Date().toJSON();this.create(revisionName);return false;}
if(event.target.className.indexOf(FORM)>-1){this.rename(revision,event.target.newname.value);return false;}
var cases={};cases[REMOVE]=this.remove;cases[LOAD]=this.load;cases[RENAME]=this.show_form;if(cases[event.target.className])
cases[event.target.className].call(this,revision);return false;},load:function(revision){this.input.value=this.storage.getItem(revision);var event=document.createEvent("HTMLEvents");event.initEvent("change",true,true);this.input.dispatchEvent(event);return;},remove:function(revision){this.storage.removeItem(revision);var element=document.getElementById(this.fullRevisionName(revision));if(element&&element.parentNode&&element.parentNode.removeChild)
element.parentNode.removeChild(element);return;},show_form:function(revision){var element=document.getElementById(this.fullRevisionName(revision));var form=element.getElementsByClassName(FORM)[0];if(form){form.className=form.className.replace(HIDE,'').replace(/ +$/,'');}
return;},rename:function(revision,newname){this.storage.setItem(newname,this.storage.getItem(revision));this.storage.removeItem(revision);this.remove(revision);this.create(newname);return;}});window.Turtle.History=TurtleHistory;})(this,this.document);(function(window,document,undefined){function handleEventByEval(event){event.stopPropagation();event.preventDefault();try{eval(this.input.value);}
catch(err){console.log(err);}
return false;}
function handleEventBySplit(event){event.stopPropagation();event.preventDefault();var raw=this.input.value;var has_repeat=raw.match(/(\d+)x:/);var count=1;if(has_repeat){count=has_repeat[1];raw=raw.replace(/\d+x: */,'');}
var cmds=raw.split(/ *\. */);for(var j=0;j<count;j++){for(var i=0;i<cmds.length;i++){var cmd=cmds[i];var args=cmd.split(/ +/);var method=args.shift().toLowerCase();if(!method)
continue;try{this.scope[method].apply(this.scope,args);}
catch(err){console.log("cannot call "+method+"() with ",args,"\n",err);}}}
return false;}
function TurtleShell(inputElement,scope){var self=Turtle.extend(this,{input:inputElement});if(scope)
Turtle.extend(self,{scope:scope,handleEvent:handleEventBySplit});else
Turtle.extend(self,{handleEvent:handleEventByEval});inputElement.form.addEventListener("submit",function(e){e.stopPropagation();e.preventDefault();return false;});inputElement.addEventListener("change",function(e){return self.handleEvent(e);});return self;}
window.Turtle.Shell=TurtleShell;window.Turtle.ShellLite=TurtleShell;})(this,this.document);(function(window,document,undefined){function interactiveCanvas(context,commandline){var turtle=new Turtle.Shape(new Turtle.Pen(new Turtle({x:Math.floor(context.canvas.width/2),y:Math.floor(context.canvas.height/2)}),context)).clear();new Turtle.ShellLite(commandline,turtle);commandline.focus();Turtle.interactiveTurtle=turtle;return;}
window.Turtle.interactiveCanvas=interactiveCanvas;})(this,this.document);