<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Worm"
    description="Interactive Turtle Graphics"
    author="Takashi Yamamiya"
    author_email="yamamiya.takashi@gmail.com"
    screenshot="http://yamamiya.takashi.googlepages.com/worm_thumb_1.gif"
    thumbnail="http://yamamiya.takashi.googlepages.com/worm_thumb_2.gif"
    author_location="LA, USA"
    author_affiliation="squeak-ja"
    title_url="http://languagegame.org/tmp/jstile/exp/worm.html"
    height="305">

  </ModulePrefs>
  <Content type="html">
     <![CDATA[

<script type="text/javascript">
// Turtle graphics in Google Gadget
// 
// tested with IE6 & Firefox2 & Safari
//

IE = !!(window.attachEvent && !window.opera);
if (IE) {
  document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
  document.styleSheets[0].addRule("v\\:*", "behavior: url(#default#VML)");
}
 
// ---------- Canvas Object for IE
var VMLCanvas = new Object();

VMLCanvas.newImageShape = function(obj) {
  var shape = document.createElement("v:image");
  return shape;
}

VMLCanvas.newWorldShape = function(obj) {
  var group = document.createElement("v:group");
  group.coordsize = obj.width + "," + obj.height;
  group.style.width = obj.width;
  group.style.height = obj.height;

  var rect = document.createElement("v:rect");
  rect.style.width = "100%";
  rect.style.height = "100%";
  rect.fillcolor = obj.fill;
  group.appendChild(rect);
  return group;
}

VMLCanvas.newPolylineShape = function(obj) {
  var shape = document.createElement("v:polyline");
  shape.setAttribute("strokecolor", obj.stroke);
  shape.setAttribute("strokeweight", obj.strokeWidth);
  shape.setAttribute("filled", "false");
  return shape;
}

VMLCanvas.add = function(parent, child) {
  parent._shape.appendChild(child._shape);
}

VMLCanvas.updateRect = function(obj) {
  obj._shape.src = obj.src;
  obj._shape.style.width = obj.width;
  obj._shape.style.height = obj.height;
  obj._shape.style.left = obj._x - obj.width / 2;
  obj._shape.style.top = obj._y - obj.height / 2;
  obj._shape.style.rotation = obj._angle;
}

VMLCanvas.updatePolyline = function(obj) {
  var points = "";
  if (obj._vertices.length < 2) return;
  for (var i = 0; i < obj._vertices.length; i++) {
    points += Math.floor(obj._vertices[i][0]) + "," + Math.floor(obj._vertices[i][1]) + " ";
  }
  obj._shape.points.Value = points;
}

VMLCanvas.showAsWorld = function(obj) {
  document.getElementById("world").appendChild(obj._shape);
}

// ---------- Canvas Object for SVG (Firefox and Safari)

var SVGNS = "http://www.w3.org/2000/svg";
var SVGCanvas = new Object();

SVGCanvas.newImageShape = function(obj) {
  var shape = document.createElementNS(SVGNS, "image");
  return shape;
}

SVGCanvas.newWorldShape = function(obj) {
  var svg = document.createElementNS(SVGNS, "svg");
  svg.setAttribute("width", obj.width);
  svg.setAttribute("height", obj.height);

  var rect = document.createElementNS(SVGNS, "rect");
  rect.setAttribute("fill", obj.fill);
  rect.setAttribute("width", obj.width);
  rect.setAttribute("height", obj.height);
  svg.appendChild(rect);

  return svg;
}

SVGCanvas.newPolylineShape = function(obj) {
  var shape = document.createElementNS(SVGNS, "polyline");
  shape.setAttribute("stroke", obj.stroke);
  shape.setAttribute("stroke-width", obj.strokeWidth);
  shape.setAttribute("fill", "none");
  return shape;
}

SVGCanvas.showAsWorld = function(obj) {
  document.getElementById("world").appendChild(obj._shape);
}

SVGCanvas.add = function(parent, child) {
  parent._shape.appendChild(child._shape);
}

SVGCanvas.updateRect = function(obj) {
  obj._shape.setAttributeNS("http://www.w3.org/1999/xlink", "href", obj.src);
  obj._shape.setAttribute("width", obj.width);
  obj._shape.setAttribute("height", obj.height);
  var left = obj._x - obj.width / 2;
  var top = obj._y - obj.height / 2;
  obj._shape.setAttribute("transform",
    "translate(" + left + ", " + top + ") " + 
    "rotate(" + obj._angle + ", " + obj.width / 2 + ", " + obj.height / 2 + ")");
}

SVGCanvas.updatePolyline = function(obj) {
  var points = "";
  if (obj._vertices.length < 2) return;
  for (var i = 0; i < obj._vertices.length; i++) {
    points += Math.floor(obj._vertices[i][0]) + "," + Math.floor(obj._vertices[i][1]) + " ";
  }
  obj._shape.setAttribute("points", points);
}


Canvas =  IE ? VMLCanvas : SVGCanvas;

// ---------- Scheduler
var scheduler = {
  tasks: [],
  add: function(func) { this.tasks.push(func); },
  tick: function() {
    if (this.tasks.length > 0) {
      (this.tasks.shift())();
    }
  },
  timer: undefined,
  initialize: function() {
    this.tasks = [];
    if (this.timer) clearInterval(this.timer);
    this.timer = setInterval(function () { scheduler.tick() }, 50);
  }
}
scheduler.initialize();

// ---------- World
var world = {
  width: 200,
  height: 200,
  fill: "#CF8E6B",
  add: function (obj) {
    Canvas.add(this, obj)
  }
}
world._shape = Canvas.newWorldShape(world);

// ---------- Trail
var trail = {
  stroke: "#632D01",
  strokeWidth: 2,
  _vertices: [],
  initialize: function () { this._vertices = [] },
  addVertex: function (p) {
    this._vertices.push(p);
    Canvas.updatePolyline(this);
  }
}
trail._shape = Canvas.newPolylineShape(trail);

// ---------- Turtle
var turtle = new Image();
turtle.exportNames = ["src", "_angle", "_x", "_y"];
turtle.src = "http://yamamiya.takashi.googlepages.com/worm_s.png";
turtle._angle = 0;
turtle._x = world.width / 2;
turtle._y = world.height / 2;
turtle._shape = Canvas.newImageShape(turtle);

turtle.setAngle = function(degree) {
  this._angle = degree;
  Canvas.updateRect(turtle);
};
turtle.getAngle = function() { return this._angle }

turtle.forwardBy = function(distance) {
  var t = this;
  scheduler.add(function() {
    var radian = t._angle * Math.PI / 180;
    t._x = t._x + (distance * (Math.sin(radian)));
    t._y = t._y - (distance * (Math.cos(radian)));
    Canvas.updateRect(turtle);
    trail.addVertex([t._x, t._y]);
   });
}
turtle.forwardBy(0);

turtle.turnBy = function(degree) {
  var t = this;
  scheduler.add(function() {
    t._angle = (t._angle + degree) % 360;
    Canvas.updateRect(turtle);
  });
}

// ---------- Initialization

world.add(trail);
Canvas.updatePolyline(trail);
world.add(turtle);
Canvas.updateRect(turtle);

window.onload = function () {
  Canvas.showAsWorld(world);
}

function doIt() {
  scheduler.initialize();
  eval(document.getElementById("workspace").value);
}
function reset() {
  scheduler.initialize();
  trail.initialize();
  turtle._x = world.width / 2;
  turtle._y = world.height / 2;
  turtle._angle = 0;
  turtle.forwardBy(0);
  Canvas.updatePolyline(trail);
  Canvas.updateRect(turtle);
}

</script>
<style>
DIV {  text-align: center; }
</style>

<div class="world" id="world"></div>

<div>
<textarea id="workspace"  cols="30" rows="4" style="font-size: small">
for (var i = 2; i < 2000; i += 20) {
  turtle.forwardBy(20);
  turtle.turnBy(i);
}
</textarea>
</div>
<div>
<input type="button" value="RUN" onclick="doIt()"/>
<input type="button" value="RESET" onclick="reset()"/>
</div>
     ]]>
  </Content>
</Module>
