]> git.pond.sub.org Git - eow/blobdiff - static/dojo-release-1.1.1/dojox/gfx/vml.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dojox / gfx / vml.js
diff --git a/static/dojo-release-1.1.1/dojox/gfx/vml.js b/static/dojo-release-1.1.1/dojox/gfx/vml.js
new file mode 100644 (file)
index 0000000..44f01db
--- /dev/null
@@ -0,0 +1,1165 @@
+if(!dojo._hasResource["dojox.gfx.vml"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.gfx.vml"] = true;
+dojo.provide("dojox.gfx.vml");
+
+dojo.require("dojox.gfx._base");
+dojo.require("dojox.gfx.shape");
+dojo.require("dojox.gfx.path");
+dojo.require("dojox.gfx.arc");
+
+// dojox.gfx.vml.xmlns: String: a VML's namespace
+dojox.gfx.vml.xmlns = "urn:schemas-microsoft-com:vml";
+
+// dojox.gfx.vml.text_alignment: Object: mapping from SVG alignment to VML alignment
+dojox.gfx.vml.text_alignment = {start: "left", middle: "center", end: "right"};
+
+dojox.gfx.vml._parseFloat = function(str) {
+       // summary: a helper function to parse VML-specific floating-point values
+       // str: String: a representation of a floating-point number
+       return str.match(/^\d+f$/i) ? parseInt(str) / 65536 : parseFloat(str);  // Number
+};
+
+dojox.gfx.vml._bool = {"t": 1, "true": 1};
+
+dojo.extend(dojox.gfx.Shape, {
+       // summary: VML-specific implementation of dojox.gfx.Shape methods
+
+       setFill: function(fill){
+               // summary: sets a fill object (VML)
+               // fill: Object: a fill object
+               //      (see dojox.gfx.defaultLinearGradient, 
+               //      dojox.gfx.defaultRadialGradient, 
+               //      dojox.gfx.defaultPattern, 
+               //      or dojo.Color)
+
+               if(!fill){
+                       // don't fill
+                       this.fillStyle = null;
+                       this.rawNode.filled = "f";
+                       return this;
+               }
+               if(typeof fill == "object" && "type" in fill){
+                       // gradient
+                       var i, f, fo, a, s;
+                       switch(fill.type){
+                               case "linear":
+                                       var matrix = this._getRealMatrix(), m = dojox.gfx.matrix;
+                                       s = [];
+                                       f = dojox.gfx.makeParameters(dojox.gfx.defaultLinearGradient, fill);
+                                       a = f.colors;
+                                       this.fillStyle = f;
+                                       dojo.forEach(a, function(v, i, a){
+                                               a[i].color = dojox.gfx.normalizeColor(v.color);
+                                       });
+                                       if(a[0].offset > 0){
+                                               s.push("0 " + a[0].color.toHex());
+                                       }
+                                       for(i = 0; i < a.length; ++i){
+                                               s.push(a[i].offset.toFixed(8) + " " + a[i].color.toHex());
+                                       }
+                                       i = a.length - 1;
+                                       if(a[i].offset < 1){
+                                               s.push("1 " + a[i].color.toHex());
+                                       }
+                                       fo = this.rawNode.fill;
+                                       fo.colors.value = s.join(";");
+                                       fo.method = "sigma";
+                                       fo.type = "gradient";
+                                       var fc1 = matrix ? m.multiplyPoint(matrix, f.x1, f.y1) : {x: f.x1, y: f.y1},
+                                               fc2 = matrix ? m.multiplyPoint(matrix, f.x2, f.y2) : {x: f.x2, y: f.y2};
+                                       fo.angle = (m._radToDeg(Math.atan2(fc2.x - fc1.x, fc2.y - fc1.y)) + 180) % 360;
+                                       fo.on = true;
+                                       break;
+                               case "radial":
+                                       f = dojox.gfx.makeParameters(dojox.gfx.defaultRadialGradient, fill);
+                                       this.fillStyle = f;
+                                       var l = parseFloat(this.rawNode.style.left),
+                                               t = parseFloat(this.rawNode.style.top),
+                                               w = parseFloat(this.rawNode.style.width),
+                                               h = parseFloat(this.rawNode.style.height),
+                                               c = isNaN(w) ? 1 : 2 * f.r / w;
+                                       a = [];
+                                       // add a color at the offset 0 (1 in VML coordinates)
+                                       if(f.colors[0].offset > 0){
+                                               a.push({offset: 1, color: dojox.gfx.normalizeColor(f.colors[0].color)});
+                                       }
+                                       // massage colors
+                                       dojo.forEach(f.colors, function(v, i){
+                                               a.push({offset: 1 - v.offset * c, color: dojox.gfx.normalizeColor(v.color)});
+                                       });
+                                       i = a.length - 1;
+                                       while(i >= 0 && a[i].offset < 0){ --i; }
+                                       if(i < a.length - 1){
+                                               // correct excessive colors
+                                               var q = a[i], p = a[i + 1];
+                                               p.color = dojo.blendColors(q.color, p.color, q.offset / (q.offset - p.offset));
+                                               p.offset = 0;
+                                               while(a.length - i > 2) a.pop();
+                                       }
+                                       // set colors
+                                       i = a.length - 1, s = [];
+                                       if(a[i].offset > 0){
+                                               s.push("0 " + a[i].color.toHex());
+                                       }
+                                       for(; i >= 0; --i){
+                                               s.push(a[i].offset.toFixed(8) + " " + a[i].color.toHex());
+                                       }
+                                       fo = this.rawNode.fill;
+                                       fo.colors.value = s.join(";");
+                                       fo.method = "sigma";
+                                       fo.type = "gradientradial";
+                                       if(isNaN(w) || isNaN(h) || isNaN(l) || isNaN(t)){
+                                               fo.focusposition = "0.5 0.5";
+                                       }else{
+                                               fo.focusposition = ((f.cx - l) / w).toFixed(8) + " " + ((f.cy - t) / h).toFixed(8);
+                                       }
+                                       fo.focussize = "0 0";
+                                       fo.on = true;
+                                       break;
+                               case "pattern":
+                                       f = dojox.gfx.makeParameters(dojox.gfx.defaultPattern, fill);
+                                       this.fillStyle = f;
+                                       fo = this.rawNode.fill;
+                                       fo.type = "tile";
+                                       fo.src = f.src;
+                                       if(f.width && f.height){
+                                               // in points
+                                               fo.size.x = dojox.gfx.px2pt(f.width);
+                                               fo.size.y = dojox.gfx.px2pt(f.height);
+                                       }
+                                       fo.alignShape = "f";
+                                       fo.position.x = 0;
+                                       fo.position.y = 0;
+                                       fo.origin.x = f.width  ? f.x / f.width  : 0;
+                                       fo.origin.y = f.height ? f.y / f.height : 0;
+                                       fo.on = true;
+                                       break;
+                       }
+                       this.rawNode.fill.opacity = 1;
+                       return this;
+               }
+               // color object
+               this.fillStyle = dojox.gfx.normalizeColor(fill);
+               this.rawNode.fill.method = "any";
+               this.rawNode.fill.type = "solid";
+               this.rawNode.fillcolor = this.fillStyle.toHex();
+               this.rawNode.fill.opacity = this.fillStyle.a;
+               this.rawNode.filled = true;
+               return this;    // self
+       },
+
+       setStroke: function(stroke){
+               // summary: sets a stroke object (VML)
+               // stroke: Object: a stroke object
+               //      (see dojox.gfx.defaultStroke) 
+       
+               if(!stroke){
+                       // don't stroke
+                       this.strokeStyle = null;
+                       this.rawNode.stroked = "f";
+                       return this;
+               }
+               // normalize the stroke
+               if(typeof stroke == "string"){
+                       stroke = {color: stroke};
+               }
+               var s = this.strokeStyle = dojox.gfx.makeParameters(dojox.gfx.defaultStroke, stroke);
+               s.color = dojox.gfx.normalizeColor(s.color);
+               // generate attributes
+               var rn = this.rawNode;
+               rn.stroked = true;
+               rn.strokecolor = s.color.toCss();
+               rn.strokeweight = s.width + "px";       // TODO: should we assume that the width is always in pixels?
+               if(rn.stroke) {
+                       rn.stroke.opacity = s.color.a;
+                       rn.stroke.endcap = this._translate(this._capMap, s.cap);
+                       if(typeof s.join == "number") {
+                               rn.stroke.joinstyle = "miter";
+                               rn.stroke.miterlimit = s.join;
+                       }else{
+                               rn.stroke.joinstyle = s.join;
+                               // rn.stroke.miterlimit = s.width;
+                       }
+                       rn.stroke.dashstyle = s.style == "none" ? "Solid" : s.style;
+               }
+               return this;    // self
+       },
+       
+       _capMap: { butt: 'flat' },
+       _capMapReversed: { flat: 'butt' },
+       
+       _translate: function(dict, value) {
+               return (value in dict) ? dict[value] : value;
+       },
+       
+       _applyTransform: function() {
+               if(this.fillStyle && this.fillStyle.type == "linear"){
+                       this.setFill(this.fillStyle);
+               }
+               var matrix = this._getRealMatrix();
+               if(!matrix) return this;
+               var skew = this.rawNode.skew;
+               if(typeof skew == "undefined"){
+                       for(var i = 0; i < this.rawNode.childNodes.length; ++i){
+                               if(this.rawNode.childNodes[i].tagName == "skew"){
+                                       skew = this.rawNode.childNodes[i];
+                                       break;
+                               }
+                       }
+               }
+               if(skew){
+                       skew.on = "f";
+                       var mt = matrix.xx.toFixed(8) + " " + matrix.xy.toFixed(8) + " " + 
+                               matrix.yx.toFixed(8) + " " + matrix.yy.toFixed(8) + " 0 0",
+                               offset = Math.floor(matrix.dx).toFixed() + "px " + Math.floor(matrix.dy).toFixed() + "px",
+                               s = this.rawNode.style,
+                               l = parseFloat(s.left),
+                               t = parseFloat(s.top),
+                               w = parseFloat(s.width),
+                               h = parseFloat(s.height);
+                       if(isNaN(l)) l = 0;
+                       if(isNaN(t)) t = 0;
+                       if(isNaN(w)) w = 1;
+                       if(isNaN(h)) h = 1;
+                       var origin = (-l / w - 0.5).toFixed(8) + " " + (-t / h - 0.5).toFixed(8);
+                       skew.matrix =  mt;
+                       skew.origin = origin;
+                       skew.offset = offset;
+                       skew.on = true;
+               }
+               return this;
+       },
+
+       setRawNode: function(rawNode){
+               // summary:
+               //      assigns and clears the underlying node that will represent this
+               //      shape. Once set, transforms, gradients, etc, can be applied.
+               //      (no fill & stroke by default)
+               rawNode.stroked = "f";
+               rawNode.filled  = "f";
+               this.rawNode = rawNode;
+       },
+       
+       // move family
+
+       _moveToFront: function(){
+               // summary: moves a shape to front of its parent's list of shapes (VML)
+               this.rawNode.parentNode.appendChild(this.rawNode);
+               return this;
+       },
+       _moveToBack: function(){
+               // summary: moves a shape to back of its parent's list of shapes (VML)
+               var r = this.rawNode, p = r.parentNode, n = p.firstChild;
+               p.insertBefore(r, n);
+               if(n.tagName == "rect"){
+                       // surface has a background rectangle, which position should be preserved
+                       n.swapNode(r);
+               }
+               return this;
+       },
+
+       _getRealMatrix: function(){
+               // summary: returns the cumulative ("real") transformation matrix
+               //      by combining the shape's matrix with its parent's matrix
+               return this.parentMatrix ? new dojox.gfx.Matrix2D([this.parentMatrix, this.matrix]) : this.matrix;      // dojox.gfx.Matrix2D
+       }
+});
+
+dojo.declare("dojox.gfx.Group", dojox.gfx.Shape, {
+       // summary: a group shape (VML), which can be used 
+       //      to logically group shapes (e.g, to propagate matricies)
+       constructor: function(){
+               dojox.gfx.vml.Container._init.call(this);
+       },
+       // apply transformation
+       _applyTransform: function(){
+               // summary: applies a transformation matrix to a group
+               var matrix = this._getRealMatrix();
+               for(var i = 0; i < this.children.length; ++i){
+                       this.children[i]._updateParentMatrix(matrix);
+               }
+               return this;    // self
+       }
+});
+dojox.gfx.Group.nodeType = "group";
+
+dojo.declare("dojox.gfx.Rect", dojox.gfx.shape.Rect, {
+       // summary: a rectangle shape (VML)
+       setShape: function(newShape){
+               // summary: sets a rectangle shape object (VML)
+               // newShape: Object: a rectangle shape object
+               var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
+               this.bbox = null;
+               var style = this.rawNode.style;
+               style.left   = shape.x.toFixed();
+               style.top    = shape.y.toFixed();
+               style.width  = (typeof shape.width == "string" && shape.width.indexOf("%") >= 0)  ? shape.width  : shape.width.toFixed();
+               style.height = (typeof shape.width == "string" && shape.height.indexOf("%") >= 0) ? shape.height : shape.height.toFixed();
+               var r = Math.min(1, (shape.r / Math.min(parseFloat(shape.width), parseFloat(shape.height)))).toFixed(8);
+               // a workaround for the VML's arcsize bug: cannot read arcsize of an instantiated node
+               var parent = this.rawNode.parentNode, before = null;
+               if(parent){
+                       if(parent.lastChild != this.rawNode){
+                               for(var i = 0; i < parent.childNodes.length; ++i){
+                                       if(parent.childNodes[i] == this.rawNode){
+                                               before = parent.childNodes[i+1];
+                                               break;
+                                       }
+                               }
+                       }
+                       parent.removeChild(this.rawNode);
+               }
+               this.rawNode.arcsize = r;
+               if(parent){
+                       if(before){
+                               parent.insertBefore(this.rawNode, before);
+                       }else{
+                               parent.appendChild(this.rawNode);
+                       }
+               }
+               // set all necessary styles, which are lost by VML (yes, it's a VML's bug)
+               return this.setTransform(this.matrix).setFill(this.fillStyle).setStroke(this.strokeStyle);      // self
+       }
+});
+dojox.gfx.Rect.nodeType = "roundrect"; // use a roundrect so the stroke join type is respected
+
+dojo.declare("dojox.gfx.Ellipse", dojox.gfx.shape.Ellipse, {
+       // summary: an ellipse shape (VML)
+       setShape: function(newShape){
+               // summary: sets an ellipse shape object (VML)
+               // newShape: Object: an ellipse shape object
+               var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
+               this.bbox = null;
+               var style = this.rawNode.style;
+               style.left   = (shape.cx - shape.rx).toFixed();
+               style.top    = (shape.cy - shape.ry).toFixed();
+               style.width  = (shape.rx * 2).toFixed();
+               style.height = (shape.ry * 2).toFixed();
+               return this.setTransform(this.matrix);  // self
+       }
+});
+dojox.gfx.Ellipse.nodeType = "oval";
+
+dojo.declare("dojox.gfx.Circle", dojox.gfx.shape.Circle, {
+       // summary: a circle shape (VML)
+       setShape: function(newShape){
+               // summary: sets a circle shape object (VML)
+               // newShape: Object: a circle shape object
+               var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
+               this.bbox = null;
+               var style = this.rawNode.style;
+               style.left   = (shape.cx - shape.r).toFixed();
+               style.top    = (shape.cy - shape.r).toFixed();
+               style.width  = (shape.r * 2).toFixed();
+               style.height = (shape.r * 2).toFixed();
+               return this;    // self
+       }
+});
+dojox.gfx.Circle.nodeType = "oval";
+
+dojo.declare("dojox.gfx.Line", dojox.gfx.shape.Line, {
+       // summary: a line shape (VML)
+       constructor: function(rawNode){
+               if(rawNode) rawNode.setAttribute("dojoGfxType", "line");
+       },
+       setShape: function(newShape){
+               // summary: sets a line shape object (VML)
+               // newShape: Object: a line shape object
+               var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
+               this.bbox = null;
+               this.rawNode.path.v = "m" + shape.x1.toFixed() + " " + shape.y1.toFixed() +
+                       "l" + shape.x2.toFixed() + " " + shape.y2.toFixed() + "e";
+               return this.setTransform(this.matrix);  // self
+       }
+});
+dojox.gfx.Line.nodeType = "shape";
+
+dojo.declare("dojox.gfx.Polyline", dojox.gfx.shape.Polyline, {
+       // summary: a polyline/polygon shape (VML)
+       constructor: function(rawNode){
+               if(rawNode) rawNode.setAttribute("dojoGfxType", "polyline");
+       },
+       setShape: function(points, closed){
+               // summary: sets a polyline/polygon shape object (VML)
+               // points: Object: a polyline/polygon shape object
+               // closed: Boolean?: if true, close the polyline explicitely
+               if(points && points instanceof Array){
+                       // branch
+                       // points: Array: an array of points
+                       this.shape = dojox.gfx.makeParameters(this.shape, { points: points });
+                       if(closed && this.shape.points.length) this.shape.points.push(this.shape.points[0]);
+               }else{
+                       this.shape = dojox.gfx.makeParameters(this.shape, points);
+               }
+               this.bbox = null;
+               var attr = [], p = this.shape.points;
+               if(p.length > 0){
+                       attr.push("m");
+                       var k = 1;
+                       if(typeof p[0] == "number"){
+                               attr.push(p[0].toFixed());
+                               attr.push(p[1].toFixed());
+                               k = 2;
+                       }else{
+                               attr.push(p[0].x.toFixed());
+                               attr.push(p[0].y.toFixed());
+                       }
+                       if(p.length > k){
+                               attr.push("l");
+                               for(var i = k; i < p.length; ++i){
+                                       if(typeof p[i] == "number"){
+                                               attr.push(p[i].toFixed());
+                                       }else{
+                                               attr.push(p[i].x.toFixed());
+                                               attr.push(p[i].y.toFixed());
+                                       }
+                               }
+                       }
+               }
+               attr.push("e");
+               this.rawNode.path.v = attr.join(" ");
+               return this.setTransform(this.matrix);  // self
+       }
+});
+dojox.gfx.Polyline.nodeType = "shape";
+
+dojo.declare("dojox.gfx.Image", dojox.gfx.shape.Image, {
+       // summary: an image (VML)
+       constructor: function(rawNode){
+               if(rawNode) rawNode.setAttribute("dojoGfxType", "image");
+       },
+       getEventSource: function() {
+               // summary: returns a Node, which is used as 
+               //      a source of events for this shape
+               return this.rawNode ? this.rawNode.firstChild : null;   // Node
+       },
+       setShape: function(newShape){
+               // summary: sets an image shape object (VML)
+               // newShape: Object: an image shape object
+               var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
+               this.bbox = null;
+               this.rawNode.firstChild.src = shape.src;
+               return this.setTransform(this.matrix);  // self
+       },
+       _setDimensions: function(s, w, h){
+               if(w || h){
+                       s.width  = w + "px";
+                       s.height = h + "px";
+               }
+       },
+       _resetImage: function(){
+               var s = this.rawNode.firstChild.style,
+                       shape = this.shape;
+               s.left = "0px";
+               s.top  = "0px";
+               this._setDimensions(s, shape.width, shape.height);
+       },
+       _applyTransform: function() {
+               var matrix = this._getRealMatrix(),
+                       img = this.rawNode.firstChild,
+                       s = img.style,
+                       shape = this.shape;
+               if(matrix){
+                       matrix = dojox.gfx.matrix.multiply(matrix, {dx: shape.x, dy: shape.y});
+               }else{
+                       matrix = dojox.gfx.matrix.normalize({dx: shape.x, dy: shape.y});
+               }
+               if(matrix.xy == 0 && matrix.yx == 0 && matrix.xx > 0 && matrix.yy > 0){
+                       // special case to avoid filters
+                       this.rawNode.style.filter = "";
+                       s.left = Math.floor(matrix.dx) + "px";
+                       s.top  = Math.floor(matrix.dy) + "px";
+                       this._setDimensions(s, Math.floor(matrix.xx * shape.width), Math.floor(matrix.yy * shape.height));
+               }else{
+                       this._resetImage();
+                       var f = this.rawNode.filters["DXImageTransform.Microsoft.Matrix"];
+                       if(f){
+                               f.M11 = matrix.xx;
+                               f.M12 = matrix.xy;
+                               f.M21 = matrix.yx;
+                               f.M22 = matrix.yy;
+                               f.Dx = matrix.dx;
+                               f.Dy = matrix.dy;
+                       }else{
+                               this.rawNode.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11=" + matrix.xx + 
+                                       ", M12=" + matrix.xy + ", M21=" + matrix.yx + ", M22=" + matrix.yy + 
+                                       ", Dx=" + matrix.dx + ", Dy=" + matrix.dy + ")";
+                       }
+               }
+               return this;
+       }
+});
+dojox.gfx.Image.nodeType = "div";
+
+dojo.declare("dojox.gfx.Text", dojox.gfx.shape.Text, {
+       // summary: an anchored text (VML)
+       constructor: function(rawNode){
+               if(rawNode){rawNode.setAttribute("dojoGfxType", "text");}
+               this.fontStyle = null;
+       },
+       _alignment: {start: "left", middle: "center", end: "right"},
+       setShape: function(newShape){
+               // summary: sets a text shape object (VML)
+               // newShape: Object: a text shape object
+               this.shape = dojox.gfx.makeParameters(this.shape, newShape);
+               this.bbox = null;
+               var r = this.rawNode, s = this.shape, x = s.x, y = s.y.toFixed();
+               switch(s.align){
+                       case "middle":
+                               x -= 5;
+                               break;
+                       case "end":
+                               x -= 10;
+                               break;
+               }
+               this.rawNode.path.v = "m" + x.toFixed() + "," + y + 
+                       "l" + (x + 10).toFixed() + "," + y + "e";
+               // find path and text path
+               var p = null, t = null, c = r.childNodes;
+               for(var i = 0; i < c.length; ++i){
+                       var tag = c[i].tagName;
+                       if(tag == "path"){
+                               p = c[i];
+                               if(t) break;
+                       }else if(tag == "textpath"){
+                               t = c[i];
+                               if(p) break;
+                       }
+               }
+               if(!p){
+                       p = this.rawNode.ownerDocument.createElement("v:path");
+                       r.appendChild(p);
+               }
+               if(!t){
+                       t = this.rawNode.ownerDocument.createElement("v:textpath");
+                       r.appendChild(t);
+               }
+               p.textPathOk = true;
+               t.on = true;
+               var a = dojox.gfx.vml.text_alignment[s.align];
+               t.style["v-text-align"] = a ? a : "left";
+               t.style["text-decoration"] = s.decoration;
+               t.style["v-rotate-letters"] = s.rotated;
+               t.style["v-text-kern"] = s.kerning;
+               t.string = s.text;
+               return this.setTransform(this.matrix);  // self
+       },
+       _setFont: function(){
+               // summary: sets a font object (VML)
+               var f = this.fontStyle, c = this.rawNode.childNodes;
+               for(var i = 0; i < c.length; ++i){
+                       if(c[i].tagName == "textpath"){
+                               c[i].style.font = dojox.gfx.makeFontString(f);
+                               break;
+                       }
+               }
+               this.setTransform(this.matrix);
+       },
+       _getRealMatrix: function(){
+               // summary: returns the cumulative ("real") transformation matrix
+               //      by combining the shape's matrix with its parent's matrix;
+               //      it makes a correction for a font size
+               var matrix = dojox.gfx.Shape.prototype._getRealMatrix.call(this);
+               // It appears that text is always aligned vertically at a middle of x-height (???).
+               // It is impossible to obtain these metrics from VML => I try to approximate it with 
+               // more-or-less util value of 0.7 * FontSize, which is typical for European fonts.
+               if(matrix){
+                       matrix = dojox.gfx.matrix.multiply(matrix, 
+                               {dy: -dojox.gfx.normalizedLength(this.fontStyle ? this.fontStyle.size : "10pt") * 0.35});
+               }
+               return matrix;  // dojox.gfx.Matrix2D
+       },
+       getTextWidth: function(){ 
+               // summary: get the text width, in px 
+               var rawNode = this.rawNode, _display = rawNode.style.display; 
+               rawNode.style.display = "inline"; 
+               var _width = dojox.gfx.pt2px(parseFloat(rawNode.currentStyle.width)); 
+               rawNode.style.display = _display; 
+               return _width; 
+       } 
+});
+dojox.gfx.Text.nodeType = "shape";
+
+dojox.gfx.path._calcArc = function(alpha){
+       // return a start point, 1st and 2nd control points, and an end point
+       var cosa  = Math.cos(alpha), sina  = Math.sin(alpha),
+               p2 = {x: cosa + (4 / 3) * (1 - cosa), y: sina - (4 / 3) * cosa * (1 - cosa) / sina};
+       return {
+               s:  {x: cosa, y: -sina},
+               c1: {x: p2.x, y: -p2.y},
+               c2: p2,
+               e:  {x: cosa, y: sina}
+       };
+};
+
+dojo.declare("dojox.gfx.Path", dojox.gfx.path.Path, {
+       // summary: a path shape (VML)
+       constructor: function(rawNode){
+               if(rawNode && !rawNode.getAttribute("dojoGfxType")){
+                       rawNode.setAttribute("dojoGfxType", "path");
+               }
+               this.vmlPath = "";
+               this.lastControl = {};
+       },
+       _updateWithSegment: function(segment){
+               // summary: updates the bounding box of path with new segment
+               // segment: Object: a segment
+               var last = dojo.clone(this.last);
+               dojox.gfx.Path.superclass._updateWithSegment.apply(this, arguments);
+               // add a VML path segment
+               var path = this[this.renderers[segment.action]](segment, last);
+               if(typeof this.vmlPath == "string"){
+                       this.vmlPath += path.join("");
+                       this.rawNode.path.v = this.vmlPath + " r0,0 e";
+               }else{
+                       this.vmlPath = this.vmlPath.concat(path);
+               }
+       },
+       setShape: function(newShape){
+               // summary: forms a path using a shape (VML)
+               // newShape: Object: an VML path string or a path object (see dojox.gfx.defaultPath)
+               this.vmlPath = [];
+               this.lastControl = {};
+               dojox.gfx.Path.superclass.setShape.apply(this, arguments);
+               this.vmlPath = this.vmlPath.join("");
+               this.rawNode.path.v = this.vmlPath + " r0,0 e";
+               return this;
+       },
+       _pathVmlToSvgMap: {m: "M", l: "L", t: "m", r: "l", c: "C", v: "c", qb: "Q", x: "z", e: ""},
+       // VML-specific segment renderers
+       renderers: {
+               M: "_moveToA", m: "_moveToR", 
+               L: "_lineToA", l: "_lineToR", 
+               H: "_hLineToA", h: "_hLineToR", 
+               V: "_vLineToA", v: "_vLineToR", 
+               C: "_curveToA", c: "_curveToR", 
+               S: "_smoothCurveToA", s: "_smoothCurveToR", 
+               Q: "_qCurveToA", q: "_qCurveToR", 
+               T: "_qSmoothCurveToA", t: "_qSmoothCurveToR", 
+               A: "_arcTo", a: "_arcTo", 
+               Z: "_closePath", z: "_closePath"
+       },
+       _addArgs: function(path, args, from, upto){
+               if(typeof upto == "undefined"){
+                       upto = args.length;
+               }
+               if(typeof from == "undefined"){
+                       from = 0;
+               }
+               for(var i = from; i < upto; ++i){
+                       path.push(" ");
+                       path.push(args[i].toFixed());
+               }
+       },
+       _addArgsAdjusted: function(path, last, args, from, upto){
+               if(typeof upto == "undefined"){
+                       upto = args.length;
+               }
+               if(typeof from == "undefined"){
+                       from = 0;
+               }
+               for(var i = from; i < upto; i += 2){
+                       path.push(" ");
+                       path.push((last.x + args[i]).toFixed());
+                       path.push(" ");
+                       path.push((last.y + args[i + 1]).toFixed());
+               }
+       },
+       _moveToA: function(segment){
+               var p = [" m"], n = segment.args, l = n.length;
+               if(l == 2){
+                       this._addArgs(p, n);
+               }else{
+                       this._addArgs(p, n, 0, 2);
+                       p.push(" l");
+                       this._addArgs(p, n, 2);
+               }
+               this.lastControl = {};
+               return p;
+       },
+       _moveToR: function(segment, last){
+               var p = ["x" in last ? " t" : " m"], n = segment.args, l = n.length;
+               if(l == 2){
+                       this._addArgs(p, n);
+               }else{
+                       this._addArgs(p, n, 0, 2);
+                       p.push(" r");
+                       this._addArgs(p, n, 2);
+               }
+               this.lastControl = {};
+               return p;
+       },
+       _lineToA: function(segment){
+               var p = [" l"];
+               this._addArgs(p, segment.args);
+               this.lastControl = {};
+               return p;
+       },
+       _lineToR: function(segment){
+               var p = [" r"];
+               this._addArgs(p, segment.args);
+               this.lastControl = {};
+               return p;
+       },
+       _hLineToA: function(segment, last){
+               var p = [" l"], n = segment.args, l = n.length, y = " " + last.y.toFixed();
+               for(var i = 0; i < l; ++i){
+                       p.push(" ");
+                       p.push(n[i].toFixed());
+                       p.push(y);
+               }
+               this.lastControl = {};
+               return p;
+       },
+       _hLineToR: function(segment){
+               var p = [" r"], n = segment.args, l = n.length;
+               for(var i = 0; i < l; ++i){
+                       p.push(" ");
+                       p.push(n[i].toFixed());
+                       p.push(" 0");
+               }
+               this.lastControl = {};
+               return p;
+       },
+       _vLineToA: function(segment, last){
+               var p = [" l"], n = segment.args, l = n.length, x = " " + last.x.toFixed();
+               for(var i = 0; i < l; ++i){
+                       p.push(x);
+                       p.push(" ");
+                       p.push(n[i].toFixed());
+               }
+               this.lastControl = {};
+               return p;
+       },
+       _vLineToR: function(segment){
+               var p = [" r"], n = segment.args, l = n.length;
+               for(var i = 0; i < l; ++i){
+                       p.push(" 0 ");
+                       p.push(n[i].toFixed());
+               }
+               this.lastControl = {};
+               return p;
+       },
+       _curveToA: function(segment){
+               var p = [], n = segment.args, l = n.length;
+               for(var i = 0; i < l; i += 6){
+                       p.push(" c");
+                       this._addArgs(p, n, i, i + 6);
+               }
+               this.lastControl = {x: n[l - 4], y: n[l - 3], type: "C"};
+               return p;
+       },
+       _curveToR: function(segment, last){
+               var p = [], n = segment.args, l = n.length;
+               for(var i = 0; i < l; i += 6){
+                       p.push(" v");
+                       this._addArgs(p, n, i, i + 6);
+                       this.lastControl = {x: last.x + n[i + 2], y: last.y + n[i + 3]};
+                       last.x += n[i + 4];
+                       last.y += n[i + 5];
+               }
+               this.lastControl.type = "C";
+               return p;
+       },
+       _smoothCurveToA: function(segment, last){
+               var p = [], n = segment.args, l = n.length;
+               for(var i = 0; i < l; i += 4){
+                       p.push(" c");
+                       if(this.lastControl.type == "C"){
+                               this._addArgs(p, [
+                                       2 * last.x - this.lastControl.x, 
+                                       2 * last.y - this.lastControl.y
+                               ]);
+                       }else{
+                               this._addArgs(p, [last.x, last.y]);
+                       }
+                       this._addArgs(p, n, i, i + 4);
+               }
+               this.lastControl = {x: n[l - 4], y: n[l - 3], type: "C"};
+               return p;
+       },
+       _smoothCurveToR: function(segment, last){
+               var p = [], n = segment.args, l = n.length;
+               for(var i = 0; i < l; i += 4){
+                       p.push(" v");
+                       if(this.lastControl.type == "C"){
+                               this._addArgs(p, [
+                                       last.x - this.lastControl.x, 
+                                       last.y - this.lastControl.y
+                               ]);
+                       }else{
+                               this._addArgs(p, [0, 0]);
+                       }
+                       this._addArgs(p, n, i, i + 4);
+                       this.lastControl = {x: last.x + n[i], y: last.y + n[i + 1]};
+                       last.x += n[i + 2];
+                       last.y += n[i + 3];
+               }
+               this.lastControl.type = "C";
+               return p;
+       },
+       _qCurveToA: function(segment){
+               var p = [], n = segment.args, l = n.length;
+               for(var i = 0; i < l; i += 4){
+                       p.push(" qb");
+                       this._addArgs(p, n, i, i + 4);
+               }
+               this.lastControl = {x: n[l - 4], y: n[l - 3], type: "Q"};
+               return p;
+       },
+       _qCurveToR: function(segment, last){
+               var p = [], n = segment.args, l = n.length;
+               for(var i = 0; i < l; i += 4){
+                       p.push(" qb");
+                       this._addArgsAdjusted(p, last, n, i, i + 4);
+                       this.lastControl = {x: last.x + n[i], y: last.y + n[i + 1]};
+                       last.x += n[i + 2];
+                       last.y += n[i + 3];
+               }
+               this.lastControl.type = "Q";
+               return p;
+       },
+       _qSmoothCurveToA: function(segment, last){
+               var p = [], n = segment.args, l = n.length;
+               for(var i = 0; i < l; i += 2){
+                       p.push(" qb");
+                       if(this.lastControl.type == "Q"){
+                               this._addArgs(p, [
+                                       this.lastControl.x = 2 * last.x - this.lastControl.x, 
+                                       this.lastControl.y = 2 * last.y - this.lastControl.y
+                               ]);
+                       }else{
+                               this._addArgs(p, [
+                                       this.lastControl.x = last.x, 
+                                       this.lastControl.y = last.y
+                               ]);
+                       }
+                       this._addArgs(p, n, i, i + 2);
+               }
+               this.lastControl.type = "Q";
+               return p;
+       },
+       _qSmoothCurveToR: function(segment, last){
+               var p = [], n = segment.args, l = n.length;
+               for(var i = 0; i < l; i += 2){
+                       p.push(" qb");
+                       if(this.lastControl.type == "Q"){
+                               this._addArgs(p, [
+                                       this.lastControl.x = 2 * last.x - this.lastControl.x, 
+                                       this.lastControl.y = 2 * last.y - this.lastControl.y
+                               ]);
+                       }else{
+                               this._addArgs(p, [
+                                       this.lastControl.x = last.x, 
+                                       this.lastControl.y = last.y
+                               ]);
+                       }
+                       this._addArgsAdjusted(p, last, n, i, i + 2);
+               }
+               this.lastControl.type = "Q";
+               return p;
+       },
+       _arcTo: function(segment, last){
+               var p = [], n = segment.args, l = n.length, relative = segment.action == "a";
+               for(var i = 0; i < l; i += 7){
+                       var x1 = n[i + 5], y1 = n[i + 6];
+                       if(relative){
+                               x1 += last.x;
+                               y1 += last.y;
+                       }
+                       var result = dojox.gfx.arc.arcAsBezier(
+                               last, n[i], n[i + 1], n[i + 2], 
+                               n[i + 3] ? 1 : 0, n[i + 4] ? 1 : 0,
+                               x1, y1
+                       );
+                       for(var j = 0; j < result.length; ++j){
+                               p.push(" c");
+                               this._addArgs(p, result[j]);
+                       }
+                       last = {x: x1, y: y1};
+               }
+               this.lastControl = {};
+               return p;
+       },
+       _closePath: function(){
+               this.lastControl = {};
+               return ["x"];
+       }
+});
+dojox.gfx.Path.nodeType = "shape";
+
+dojo.declare("dojox.gfx.TextPath", dojox.gfx.Path, {
+       // summary: a textpath shape (VML)
+       constructor: function(rawNode){
+               if(rawNode){rawNode.setAttribute("dojoGfxType", "textpath");}
+               this.fontStyle = null;
+               if(!("text" in this)){
+                       this.text = dojo.clone(dojox.gfx.defaultTextPath);
+               }
+               if(!("fontStyle" in this)){
+                       this.fontStyle = dojo.clone(dojox.gfx.defaultFont);
+               }
+       },
+       setText: function(newText){
+               // summary: sets a text to be drawn along the path
+               this.text = dojox.gfx.makeParameters(this.text, 
+                       typeof newText == "string" ? {text: newText} : newText);
+               this._setText();
+               return this;    // self
+       },
+       setFont: function(newFont){
+               // summary: sets a font for text
+               this.fontStyle = typeof newFont == "string" ? 
+                       dojox.gfx.splitFontString(newFont) :
+                       dojox.gfx.makeParameters(dojox.gfx.defaultFont, newFont);
+               this._setFont();
+               return this;    // self
+       },
+
+       _setText: function(){
+               // summary: sets a text shape object (VML)
+               this.bbox = null;
+               var r = this.rawNode, s = this.text,
+                       // find path and text path
+                       p = null, t = null, c = r.childNodes;
+               for(var i = 0; i < c.length; ++i){
+                       var tag = c[i].tagName;
+                       if(tag == "path"){
+                               p = c[i];
+                               if(t) break;
+                       }else if(tag == "textpath"){
+                               t = c[i];
+                               if(p) break;
+                       }
+               }
+               if(!p){
+                       p = this.rawNode.ownerDocument.createElement("v:path");
+                       r.appendChild(p);
+               }
+               if(!t){
+                       t = this.rawNode.ownerDocument.createElement("v:textpath");
+                       r.appendChild(t);
+               }
+               p.textPathOk = true;
+               t.on = true;
+               var a = dojox.gfx.vml.text_alignment[s.align];
+               t.style["v-text-align"] = a ? a : "left";
+               t.style["text-decoration"] = s.decoration;
+               t.style["v-rotate-letters"] = s.rotated;
+               t.style["v-text-kern"] = s.kerning;
+               t.string = s.text;
+       },
+       _setFont: function(){
+               // summary: sets a font object (VML)
+               var f = this.fontStyle, c = this.rawNode.childNodes;
+               for(var i = 0; i < c.length; ++i){
+                       if(c[i].tagName == "textpath"){
+                               c[i].style.font = dojox.gfx.makeFontString(f);
+                               break;
+                       }
+               }
+       }
+});
+dojox.gfx.TextPath.nodeType = "shape";
+
+dojo.declare("dojox.gfx.Surface", dojox.gfx.shape.Surface, {
+       // summary: a surface object to be used for drawings (VML)
+       constructor: function(){
+               dojox.gfx.vml.Container._init.call(this);
+       },
+       setDimensions: function(width, height){
+               // summary: sets the width and height of the rawNode
+               // width: String: width of surface, e.g., "100px"
+               // height: String: height of surface, e.g., "100px"
+               this.width  = dojox.gfx.normalizedLength(width);        // in pixels
+               this.height = dojox.gfx.normalizedLength(height);       // in pixels
+               if(!this.rawNode) return this;
+               var cs = this.clipNode.style, 
+                       r = this.rawNode, rs = r.style,
+                       bs = this.bgNode.style;
+               cs.width  = width;
+               cs.height = height;
+               cs.clip = "rect(0 " + width + " " + height + " 0)";
+               rs.width = width;
+               rs.height = height;
+               r.coordsize = width + " " + height;
+               bs.width = width;
+               bs.height = height;
+               return this;    // self
+       },
+       getDimensions: function(){
+               // summary: returns an object with properties "width" and "height"
+               var t = this.rawNode ? {
+                       width:  dojox.gfx.normalizedLength(this.rawNode.style.width), 
+                       height: dojox.gfx.normalizedLength(this.rawNode.style.height)} : null;
+               if(t.width  <= 0){ t.width  = this.width; }
+               if(t.height <= 0){ t.height = this.height; }
+               return t;       // Object
+       }
+});
+
+dojox.gfx.createSurface = function(parentNode, width, height){
+       // summary: creates a surface (VML)
+       // parentNode: Node: a parent node
+       // width: String: width of surface, e.g., "100px"
+       // height: String: height of surface, e.g., "100px"
+
+       if(!width){ width = "100%"; }
+       if(!height){ height = "100%"; }
+       var s = new dojox.gfx.Surface(), p = dojo.byId(parentNode),
+               c = s.clipNode = p.ownerDocument.createElement("div"),
+               r = s.rawNode = p.ownerDocument.createElement("v:group"),
+               cs = c.style, rs = r.style;
+               
+       p.style.width  = width;
+       p.style.height = height;
+               
+       cs.position = "absolute";
+       cs.width  = width;
+       cs.height = height;
+       cs.clip = "rect(0 " + width + " " + height + " 0)";
+       rs.position = "absolute";
+       rs.width  = width;
+       rs.height = height;
+       r.coordsize = (width == "100%" ? width : parseFloat(width)) + " " +
+               (height == "100%" ? height : parseFloat(height));
+       r.coordorigin = "0 0";
+       
+       // create a background rectangle, which is required to show all other shapes
+       var b = s.bgNode = r.ownerDocument.createElement("v:rect"), bs = b.style;
+       bs.left = bs.top = 0;
+       bs.width  = rs.width;
+       bs.height = rs.height;
+       b.filled = b.stroked = "f";
+
+       r.appendChild(b);
+       c.appendChild(r);
+       p.appendChild(c);
+       
+       s.width  = dojox.gfx.normalizedLength(width);   // in pixels
+       s.height = dojox.gfx.normalizedLength(height);  // in pixels
+
+       return s;       // dojox.gfx.Surface
+};
+
+// Extenders
+
+dojox.gfx.vml.Container = {
+       _init: function(){
+               dojox.gfx.shape.Container._init.call(this);
+       },
+       add: function(shape){
+               // summary: adds a shape to a group/surface
+               // shape: dojox.gfx.Shape: an VML shape object
+               if(this != shape.getParent()){
+                       this.rawNode.appendChild(shape.rawNode);
+                       //dojox.gfx.Group.superclass.add.apply(this, arguments);
+                       //this.inherited(arguments);
+                       dojox.gfx.shape.Container.add.apply(this, arguments);
+               }
+               return this;    // self
+       },
+       remove: function(shape, silently){
+               // summary: remove a shape from a group/surface
+               // shape: dojox.gfx.Shape: an VML shape object
+               // silently: Boolean?: if true, regenerate a picture
+               if(this == shape.getParent()){
+                       if(this.rawNode == shape.rawNode.parentNode){
+                               this.rawNode.removeChild(shape.rawNode);
+                       }
+                       //dojox.gfx.Group.superclass.remove.apply(this, arguments);
+                       //this.inherited(arguments);
+                       dojox.gfx.shape.Container.remove.apply(this, arguments);
+               }
+               return this;    // self
+       },
+       clear: function(){
+               // summary: removes all shapes from a group/surface
+               var r = this.rawNode;
+               while(r.firstChild != r.lastChild){
+                       if(r.firstChild != this.bgNode){
+                               r.removeChild(r.firstChild);
+                       }
+                       if(r.lastChild != this.bgNode){
+                               r.removeChild(r.lastChild);
+                       }
+               }
+               //return this.inherited(arguments);     // self
+               return dojox.gfx.shape.Container.clear.apply(this, arguments);
+       },
+       _moveChildToFront: dojox.gfx.shape.Container._moveChildToFront,
+       _moveChildToBack:  dojox.gfx.shape.Container._moveChildToBack
+};
+
+dojo.mixin(dojox.gfx.shape.Creator, {
+       // summary: VML shape creators
+       createGroup: function(){
+               // summary: creates a VML group shape
+               var g = this.createObject(dojox.gfx.Group, null);       // dojox.gfx.Group
+               // create a background rectangle, which is required to show all other shapes
+               var r = g.rawNode.ownerDocument.createElement("v:rect");
+               r.style.left = r.style.top = 0;
+               r.style.width  = g.rawNode.style.width;
+               r.style.height = g.rawNode.style.height;
+               r.filled = r.stroked = "f";
+               g.rawNode.appendChild(r);
+               g.bgNode = r;
+               return g;       // dojox.gfx.Group
+       },
+       createImage: function(image){
+               // summary: creates a VML image shape
+               // image: Object: an image object (see dojox.gfx.defaultImage)
+               if(!this.rawNode) return null;
+               var shape = new dojox.gfx.Image(), node = this.rawNode.ownerDocument.createElement('div');
+               node.style.position = "absolute";
+               node.style.width  = this.rawNode.style.width;
+               node.style.height = this.rawNode.style.height;
+               //node.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=0, M21=0, M22=1, Dx=0, Dy=0)";
+               var img  = this.rawNode.ownerDocument.createElement('img');
+               img.style.position = "relative";
+               node.appendChild(img);
+               shape.setRawNode(node);
+               this.rawNode.appendChild(node);
+               shape.setShape(image);
+               this.add(shape);
+               return shape;   // dojox.gfx.Image
+       },
+       createObject: function(shapeType, rawShape) {
+               // summary: creates an instance of the passed shapeType class
+               // shapeType: Function: a class constructor to create an instance of
+               // rawShape: Object: properties to be passed in to the classes "setShape" method
+               // overrideSize: Boolean: set the size explicitly, if true
+               if(!this.rawNode) return null;
+               var shape = new shapeType(),
+                       node = this.rawNode.ownerDocument.createElement('v:' + shapeType.nodeType);
+               shape.setRawNode(node);
+               this.rawNode.appendChild(node);
+               switch(shapeType){
+                       case dojox.gfx.Group:
+                       case dojox.gfx.Line:
+                       case dojox.gfx.Polyline:
+                       case dojox.gfx.Text:
+                       case dojox.gfx.Path:
+                       case dojox.gfx.TextPath:
+                               this._overrideSize(node);
+               }
+               shape.setShape(rawShape);
+               this.add(shape);
+               return shape;   // dojox.gfx.Shape
+       },
+       _overrideSize: function(node){
+               var p = this;
+               while(p && !(p instanceof dojox.gfx.Surface)){ p = p.parent; }
+               node.style.width  = p.width;
+               node.style.height = p.height;
+               node.coordsize = p.width + " " + p.height;
+       }
+});
+
+dojo.extend(dojox.gfx.Group, dojox.gfx.vml.Container);
+dojo.extend(dojox.gfx.Group, dojox.gfx.shape.Creator);
+
+dojo.extend(dojox.gfx.Surface, dojox.gfx.vml.Container);
+dojo.extend(dojox.gfx.Surface, dojox.gfx.shape.Creator);
+
+}