

/*
 * DrawingCanvas.js  0.03
 *
 * nanto_vi (TOYAMA Nao), 2005-10-10
 *
 * 0.03
 *   - CSS Positioning バックエンドのパフォーマンス向上。
 *     特にx軸、y軸に平行な直線の描画に効果あり。
 *   - その他細かい修正。
 *
 * 0.02
 *   - Safari で HTML Canvas バックエンドのとき
 *     描画がまったくできなかった問題を修正。
 */


// SVG Tiny Backend

function DrawingCanvasSVGT(parent, width, height)
{
  if (!parent) throw new Error("No canvas parent!");

  if (!width)  width  = parent.clientWidth;
  if (!height) height = parent.clientHeight;

	this._XMLNS_SVG 	 = "http://www.w3.org/2000/svg";
  this.parent        = parent;
  this.width         = width;
  this.height        = height;
  this._bgColor      = "none";
  this._lineWidth    = 2;
  this._lineColor    = "#000";
  this._isDrawing    = false;
  this._currentShape = null;
  this._points       = "";
  this._dummyDot     = null;
  this._stack        = [];
  this._stackSize    = 0;
  this._dashStyle		 = "";
  this._opacity			 = 1;
  this._fillColor		 = "#fff";

  var svg = document.createElementNS(this._XMLNS_SVG, "svg");
  svg.setAttribute("width", width);
  svg.setAttribute("height", height);
	
  var rect = document.createElementNS(this._XMLNS_SVG, "rect");
  rect.setAttribute("width", width);
  rect.setAttribute("height", height);
  rect.setAttribute("fill", this._bgColor);
  svg.appendChild(rect);

  var container = document.createElementNS("http://www.w3.org/1999/xhtml",
                                          "div");
  container.setAttribute("style", "width: "  + width  + "px; " +
                                  "height: " + height + "px; " +
                                  "vertical-align: bottom; ");
  container.appendChild(svg);

  parent.appendChild(container);

  this.container = container;
  this._svgRoot  = svg;
  this._bg       = rect;
}

DrawingCanvasSVGT.prototype = {

  setBgColor: function (color) {
    this._bgColor = (color == "transparent") ? "none" : color;
    this._refresh();
  },

  setLineColor: function (color) {
    this._lineColor = color;
  },

  setLineWidth: function (width) {
    this._lineWidth = Number(width) || 0;
  },
	
	setDashStyle: function (style) {
		this._dashStyle = style;
	},
	
	setOpacity: function (opacity) {
		this._opacity = opacity;	
	},
	
	setFillColor: function (fillColor) {
		this._fillColor = fillColor;	
	},
	
	drawCircle: function (x, y, radius) {
		var circle = document.createElementNS(this._XMLNS_SVG, "circle");
		circle.setAttribute('cx', x);
		circle.setAttribute('cy', y);
		circle.setAttribute('r', radius);
		circle.setAttribute('fill', this._fillColor);
		circle.setAttribute("fill-opacity", this._opacity);
		circle.setAttribute("stroke-width", this._lineWidth);
		circle.setAttribute("stroke", this._lineColor);
		
		this._svgRoot.appendChild(circle);
	},
	
  startLine: function (x, y) {
    if (this._isDrawing)
      this.endLine();
    this._isDrawing = true;

    var dot = document.createElementNS(this._XMLNS_SVG, "circle");
    dot.setAttribute("cx", x);
    dot.setAttribute("cy", y);
    dot.setAttribute("r", this._lineWidth / 2);
    this._svgRoot.appendChild(dot);
    dot.setAttribute("fill", this._lineColor);

    this._dummyDot = dot;
    this._points   = x + "," + y;

    var polyline = document.createElementNS(this._XMLNS_SVG, "polyline");
    polyline.setAttribute("fill", "none");
    polyline.setAttribute("stroke", this._lineColor);
    polyline.setAttribute("stroke-width", this._lineWidth);
    polyline.setAttribute("stroke-dasharray", this._dashStyle);
    polyline.setAttribute("stroke-opacity", this._opacity);
    polyline.setAttribute("stroke-linecap", "butt");
    polyline.setAttribute("stroke-linejoin", "miter");
    polyline.setAttribute("points", this._points);
    this._svgRoot.appendChild(polyline);
    this._currentShape = polyline;
  },

  endLine: function () {
    if (!this._isDrawing) return;

    if (this._dummyDot) {
      this._svgRoot.removeChild(this._currentShape);
      this._pushStack(this._dummyDot);
      this._dummyDot = null;
    } else {
      this._pushStack(this._currentShape);
    }
    this._isDrawing    = false;
    this._currentShape = null;
    this._points       = "";
  },

  lineTo: function (x, y) {
    if (!this._isDrawing) return;
    if (this._dummyDot) {
      this._svgRoot.removeChild(this._dummyDot);
      this._dummyDot = null;
    }

    this._points += " " + x + "," + y;
    this._currentShape.setAttribute("points", this._points);
  },

  undo: function () {
    if (this._isDrawing)
      this.endLine();
    if (this._stackSize <= 0)
      return false;

    this._svgRoot.removeChild(this._stack[--this._stackSize]);
    this._refresh();
    return true;
  },

  redo: function () {
    if (this._isDrawing)
      this.endLine();
    if (this._stackSize >= this._stack.length)
      return false;

    this._svgRoot.appendChild(this._stack[this._stackSize++]);
    this._refresh();
    return true;
  },

  clear: function () {
    if (this._isDrawing)
      this.endLine();

    for (var i = this._stackSize; i--;)
      this._svgRoot.removeChild(this._stack[i]);
    this._refresh();
    this._stack.length = 0;
    this._stackSize    = 0;
  },

  getX: function () {
    var box = this.container;
    var x   = box.offsetLeft;
    while ((box = box.offsetParent))
      x += box.offsetLeft;

    return x;
  },

  getY: function () {
    var box = this.container;
    var y   = box.offsetTop;
    while ((box = box.offsetParent))
      y += box.offsetTop;

    return y;
  },

  _pushStack: function (shape) {
    if (this._stackSize + 1 < this._stack.length)
      this._stack.length = this._stackSize + 1;
    this._stack[this._stackSize++] = shape;
  },

  _refresh: function () {
    this._bg.setAttribute("fill", this._bgColor);
  }
};

// VML Backend

function DrawingCanvasVML(parent, width, height)
{
  if (!document.namespaces) throw new Error("Not supported!");
  if (!document.namespaces.v) {
    document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
    document.createStyleSheet().addRule("v\\:*",
                                        "behavior: url(#default#VML);");
  }
  if (!parent) throw new Error("No canvas parent!");

  if (!width)  width  = parent.clientWidth;
  if (!height) height = parent.clientHeight;

  this.parent        = parent;
  this.width         = width;
  this.height        = height;
  this._bgColor      = "none";
  this._lineWidth    = 2;
  this._lineColor    = "#000";
  this._isDrawing    = false;
  this._currentShape = null;
  this._dummyDot     = null;
  this._points       = "";
  this._stack        = [];
  this._stackSize    = 0;
  this._dashStyle		 = "";
  this._opacity			 = 1;

  var container = document.createElement("div");
  container.style.cssText = "position: relative; " +
                            "width: "  + width  + "px; " +
                            "height: " + height + "px; " +
                            "overflow: hidden; ";
  parent.appendChild(container);
  this.container = container;
}

DrawingCanvasVML.prototype = {
  setBgColor: function (color) {
    this.container.style.backgroundColor = this._bgColor = color;
  },

  setLineColor: function (color) {
    this._lineColor = color;
  },

  setLineWidth: function (width) {
    this._lineWidth = Number(width) || 0;
  },

	setDashStyle: function (style) {
		this._dashStyle = style;
	},
	
	setOpacity: function (opacity) {
		this._opacity = opacity;	
	},
	
  startLine: function (x, y) {
    if (this._isDrawing)
      this.endLine();
    this._isDrawing = true;

    var dot  = document.createElement("v:oval");
    var size = this._lineWidth
    dot.fillcolor     = this._lineColor;
    dot.strokecolor   = this._lineColor;
    //dot.stroked       = false;
    dot.style.cssText = "position: absolute; " +
                        "width: "  + size + "px; " +
                        "height: " + size + "px; " +
                        "left: " + (x - size / 2) + "px; " +
                        "top: "  + (y - size / 2) + "px; ";
    this.container.appendChild(dot);

    this._dummyDot = dot;
    this._points   = x + "," + y;

    var polyline = document.createElement("v:polyline");
    polyline.filled       = false;
    polyline.strokecolor  = this._lineColor;
    polyline.strokeweight = this._lineWidth;
    polyline.points       = this._points;
    var stroke = document.createElement("v:stroke");
    stroke.endcap = "round";
    polyline.appendChild(stroke);
    this.container.appendChild(polyline);
    this._currentShape = polyline;
  },

  endLine: function () {
    if (!this._isDrawing) return;

    if (this._dummyDot) {
      this.container.removeChild(this._currentShape);
      this._pushStack(this._dummyDot);
      this._dummyDot = null;
    } else {
      this._pushStack(this._currentShape);
    }
    this._isDrawing    = false;
    this._currentShape = null;
    this._points       = "";
  },

  lineTo: function (x, y) {
    if (!this._isDrawing) return;
    if (this._dummyDot) {
      this.container.removeChild(this._dummyDot);
      this._dummyDot = null;
    }

    this._points +=  " " + x + "," + y;
    this._currentShape.points.value = this._points;
  },

  undo: function () {
    if (this._isDrawing)
      this.endLine();
    if (this._stackSize <= 0)
      return false;

    this._stack[--this._stackSize].style.visibility = "hidden";
    return true;
  },

  redo: function () {
    if (this._isDrawing)
      this.endLine();
    if (this._stackSize >= this._stack.length)
      return false;

    this._stack[this._stackSize++].style.visibility = "visible";
    return true;
  },

  clear: function () {
    if (this._isDrawing)
      this.endLine();

    for (var i = this._stack.length; i--;)
      this.container.removeChild(this._stack[i]);
    this._stack.length = 0;
    this._stackSize    = 0;
  },

  _htmlIsRoot: (typeof document.compatMode == "string") &&
               (document.compatMode == "CSS1Compat"),

  getX: function () {
    var box = this.container;
    var x   = box.offsetLeft;
    while ((box = box.offsetParent))
      x += box.offsetLeft;

    x += (this._htmlIsRoot ? document.documentElement
                           : document.body).clientLeft;
    return x;
  },

  getY: function () {
    var box = this.container;
    var y   = box.offsetTop;
    while ((box = box.offsetParent))
      y += box.offsetTop;

    y += (this._htmlIsRoot ? document.documentElement
                           : document.body).clientTop;
    return y;
  },

  _pushStack: function (shape) {
    var stackLength = this._stack.length;
    if (this._stackSize < stackLength) {
      for (var i = this._stackSize; i < stackLength; i++)
        this.container.removeChild(this._stack[i]);
      this._stack.length = this._stackSize + 1;
    }
    this._stack[this._stackSize++] = shape;
  }
};


function DrawingCanvas()
{
  var backend = "CSSP";

  if (document.createElementNS) {
    if (document.implementation.hasFeature("org.w3c.svg", null))
      backend = "SVGT";
  }

  switch (backend) {
    case "SVGT":   DrawingCanvas = DrawingCanvasSVGT;   break;
    case "VML":    DrawingCanvas = DrawingCanvasVML;    break;
  }
  DrawingCanvas.backend = backend;
  DrawingCanvas.prototype.constructor = DrawingCanvas;
}
DrawingCanvas();
