diff options
Diffstat (limited to 'js/dojo/dojox/gfx/vml.js')
| -rw-r--r-- | js/dojo/dojox/gfx/vml.js | 1265 |
1 files changed, 1265 insertions, 0 deletions
diff --git a/js/dojo/dojox/gfx/vml.js b/js/dojo/dojox/gfx/vml.js new file mode 100644 index 0000000..4c7fefd --- /dev/null +++ b/js/dojo/dojox/gfx/vml.js @@ -0,0 +1,1265 @@ +//>>built +define("dojox/gfx/vml", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "dojo/_base/Color", "dojo/_base/sniff", + "dojo/_base/config", "dojo/dom", "dojo/dom-geometry", "dojo/_base/window", + "./_base", "./shape", "./path", "./arc", "./gradient", "./matrix"], + function(lang, declare, arr, Color, has, config, dom, domGeom, win, g, gs, pathLib, arcLib, gradient, m){ +/*===== + dojox.gfx.vml = { + // module: + // dojox/gfx/vml + // summary: + // This the default graphics rendering bridge for IE6-7. + // This renderer is very slow. For best performance on IE6-8, use Silverlight plugin. + // IE9+ defaults to the standard W3C SVG renderer. + }; + g = dojox.gfx; + pathLib.Path = dojox.gfx.path.Path; + pathLib.TextPath = dojox.gfx.path.TextPath; + vml.Shape = dojox.gfx.canvas.Shape; + gs.Shape = dojox.gfx.shape.Shape; + gs.Rect = dojox.gfx.shape.Rect; + gs.Ellipse = dojox.gfx.shape.Ellipse; + gs.Circle = dojox.gfx.shape.Circle; + gs.Line = dojox.gfx.shape.Line; + gs.PolyLine = dojox.gfx.shape.PolyLine; + gs.Image = dojox.gfx.shape.Image; + gs.Text = dojox.gfx.shape.Text; + gs.Surface = dojox.gfx.shape.Surface; + =====*/ + var vml = g.vml = {}; + + // dojox.gfx.vml.xmlns: String: a VML's namespace + vml.xmlns = "urn:schemas-microsoft-com:vml"; + + document.namespaces.add("v", vml.xmlns); + var vmlElems = ["*", "group", "roundrect", "oval", "shape", "rect", "imagedata", "path", "textpath", "text"], + i = 0, l = 1, s = document.createStyleSheet(); + if(has("ie") >= 8){ + i = 1; + l = vmlElems.length; + } + for (; i < l; ++i) { + s.addRule("v\\:" + vmlElems[i], "behavior:url(#default#VML); display:inline-block"); + } + + // dojox.gfx.vml.text_alignment: Object: mapping from SVG alignment to VML alignment + vml.text_alignment = {start: "left", middle: "center", end: "right"}; + + 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 + }; + + vml._bool = {"t": 1, "true": 1}; + + declare("dojox.gfx.vml.Shape", gs.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; + } + var i, f, fo, a, s; + if(typeof fill == "object" && "type" in fill){ + // gradient + switch(fill.type){ + case "linear": + var matrix = this._getRealMatrix(), bbox = this.getBoundingBox(), + tbbox = this._getRealBBox ? this._getRealBBox() : this.getTransformedBoundingBox(); + s = []; + if(this.fillStyle !== fill){ + this.fillStyle = g.makeParameters(g.defaultLinearGradient, fill); + } + f = g.gradient.project(matrix, this.fillStyle, + {x: bbox.x, y: bbox.y}, + {x: bbox.x + bbox.width, y: bbox.y + bbox.height}, + tbbox[0], tbbox[2]); + a = f.colors; + if(a[0].offset.toFixed(5) != "0.00000"){ + s.push("0 " + g.normalizeColor(a[0].color).toHex()); + } + for(i = 0; i < a.length; ++i){ + s.push(a[i].offset.toFixed(5) + " " + g.normalizeColor(a[i].color).toHex()); + } + i = a.length - 1; + if(a[i].offset.toFixed(5) != "1.00000"){ + s.push("1 " + g.normalizeColor(a[i].color).toHex()); + } + fo = this.rawNode.fill; + fo.colors.value = s.join(";"); + fo.method = "sigma"; + fo.type = "gradient"; + fo.angle = (270 - m._radToDeg(f.angle)) % 360; + fo.on = true; + break; + case "radial": + f = g.makeParameters(g.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: g.normalizeColor(f.colors[0].color)}); + } + // massage colors + arr.forEach(f.colors, function(v, i){ + a.push({offset: 1 - v.offset * c, color: g.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 = Color.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(5) + " " + 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(5) + " " + ((f.cy - t) / h).toFixed(5); + } + fo.focussize = "0 0"; + fo.on = true; + break; + case "pattern": + f = g.makeParameters(g.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 = g.px2pt(f.width); + fo.size.y = g.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 = g.normalizeColor(fill); + fo = this.rawNode.fill; + if(!fo){ + fo = this.rawNode.ownerDocument.createElement("v:fill"); + } + fo.method = "any"; + fo.type = "solid"; + fo.opacity = this.fillStyle.a; + var alphaFilter = this.rawNode.filters["DXImageTransform.Microsoft.Alpha"]; + if(alphaFilter){ + alphaFilter.opacity = Math.round(this.fillStyle.a * 100); + } + this.rawNode.fillcolor = this.fillStyle.toHex(); + 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" || lang.isArray(stroke) || stroke instanceof Color){ + stroke = {color: stroke}; + } + var s = this.strokeStyle = g.makeParameters(g.defaultStroke, stroke); + s.color = g.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() { + var matrix = this._getRealMatrix(); + if(matrix){ + 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) w = 1; + if(isNaN(h) || !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; + } + } + if(this.fillStyle && this.fillStyle.type == "linear"){ + this.setFill(this.fillStyle); + } + return this; + }, + + _setDimensions: function(width, height){ + // summary: sets the width and height of the rawNode, + // if the surface sixe has been changed + // width: String: width in pixels + // height: String: height in pixels + + // default implementation does nothing + return this; // self + }, + + 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; + this.rawNode.__gfxObject__ = this.getUID(); + }, + + // 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 m.Matrix2D([this.parentMatrix, this.matrix]) : this.matrix; // dojox.gfx.Matrix2D + } + }); + + declare("dojox.gfx.vml.Group", vml.Shape, { + // summary: a group shape (VML), which can be used + // to logically group shapes (e.g, to propagate matricies) + constructor: function(){ + gs.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 + }, + _setDimensions: function(width, height){ + // summary: sets the width and height of the rawNode, + // if the surface sixe has been changed + // width: String: width in pixels + // height: String: height in pixels + var r = this.rawNode, rs = r.style, + bs = this.bgNode.style; + rs.width = width; + rs.height = height; + r.coordsize = width + " " + height; + bs.width = width; + bs.height = height; + for(var i = 0; i < this.children.length; ++i){ + this.children[i]._setDimensions(width, height); + } + return this; // self + } + }); + vml.Group.nodeType = "group"; + + declare("dojox.gfx.vml.Rect", [vml.Shape, gs.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 = g.makeParameters(this.shape, newShape); + this.bbox = null; + 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); + } + if(has("ie") > 7){ + var node = this.rawNode.ownerDocument.createElement("v:roundrect"); + node.arcsize = r; + node.style.display = "inline-block"; + this.rawNode = node; + this.rawNode.__gfxObject__ = this.getUID(); + }else{ + this.rawNode.arcsize = r; + } + if(parent){ + if(before){ + parent.insertBefore(this.rawNode, before); + }else{ + parent.appendChild(this.rawNode); + } + } + 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 : Math.max(shape.width.toFixed(),0); + style.height = (typeof shape.height == "string" && shape.height.indexOf("%") >= 0) ? shape.height : Math.max(shape.height.toFixed(),0); + // 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 + } + }); + vml.Rect.nodeType = "roundrect"; // use a roundrect so the stroke join type is respected + + declare("dojox.gfx.vml.Ellipse", [vml.Shape, gs.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 = g.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 + } + }); + vml.Ellipse.nodeType = "oval"; + + declare("dojox.gfx.vml.Circle", [vml.Shape, gs.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 = g.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 + } + }); + vml.Circle.nodeType = "oval"; + + declare("dojox.gfx.vml.Line", [vml.Shape, gs.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 = g.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 + } + }); + vml.Line.nodeType = "shape"; + + declare("dojox.gfx.vml.Polyline", [vml.Shape, gs.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 = g.makeParameters(this.shape, { points: points }); + if(closed && this.shape.points.length) this.shape.points.push(this.shape.points[0]); + }else{ + this.shape = g.makeParameters(this.shape, points); + } + this.bbox = null; + this._normalizePoints(); + var attr = [], p = this.shape.points; + if(p.length > 0){ + attr.push("m"); + attr.push(p[0].x.toFixed(), p[0].y.toFixed()); + if(p.length > 1){ + attr.push("l"); + for(var i = 1; i < p.length; ++i){ + attr.push(p[i].x.toFixed(), p[i].y.toFixed()); + } + } + } + attr.push("e"); + this.rawNode.path.v = attr.join(" "); + return this.setTransform(this.matrix); // self + } + }); + vml.Polyline.nodeType = "shape"; + + declare("dojox.gfx.vml.Image", [vml.Shape, gs.Image], { + // summary: an image (VML) + setShape: function(newShape){ + // summary: sets an image shape object (VML) + // newShape: Object: an image shape object + var shape = this.shape = g.makeParameters(this.shape, newShape); + this.bbox = null; + this.rawNode.firstChild.src = shape.src; + return this.setTransform(this.matrix); // self + }, + _applyTransform: function() { + var matrix = this._getRealMatrix(), + rawNode = this.rawNode, + s = rawNode.style, + shape = this.shape; + if(matrix){ + matrix = m.multiply(matrix, {dx: shape.x, dy: shape.y}); + }else{ + matrix = m.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 + s.filter = ""; + s.width = Math.floor(matrix.xx * shape.width); + s.height = Math.floor(matrix.yy * shape.height); + s.left = Math.floor(matrix.dx); + s.top = Math.floor(matrix.dy); + }else{ + var ps = rawNode.parentNode.style; + s.left = "0px"; + s.top = "0px"; + s.width = ps.width; + s.height = ps.height; + matrix = m.multiply(matrix, + {xx: shape.width / parseInt(s.width), yy: shape.height / parseInt(s.height)}); + var f = 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{ + s.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; // self + }, + _setDimensions: function(width, height){ + // summary: sets the width and height of the rawNode, + // if the surface sixe has been changed + // width: String: width in pixels + // height: String: height in pixels + + var r = this.rawNode, f = r.filters["DXImageTransform.Microsoft.Matrix"]; + if(f){ + var s = r.style; + s.width = width; + s.height = height; + return this._applyTransform(); // self + } + return this; // self + } + }); + vml.Image.nodeType = "rect"; + + declare("dojox.gfx.vml.Text", [vml.Shape, gs.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 = g.makeParameters(this.shape, newShape); + this.bbox = null; + var r = this.rawNode, s = this.shape, x = s.x, y = s.y.toFixed(), path; + switch(s.align){ + case "middle": + x -= 5; + break; + case "end": + x -= 10; + break; + } + path = "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 = r.ownerDocument.createElement("v:path"); + r.appendChild(p); + } + if(!t){ + t = r.ownerDocument.createElement("v:textpath"); + r.appendChild(t); + } + p.v = path; + p.textPathOk = true; + t.on = true; + var a = 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 = g.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 = this.inherited(arguments); + // 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 = m.multiply(matrix, + {dy: -g.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 = g.pt2px(parseFloat(rawNode.currentStyle.width)); + rawNode.style.display = _display; + return _width; + } + }); + vml.Text.nodeType = "shape"; + + declare("dojox.gfx.vml.Path", [vml.Shape, pathLib.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 = lang.clone(this.last); + this.inherited(arguments); + if(arguments.length > 1){ return; } // skip transfomed bbox calculations + // 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{ + Array.prototype.push.apply(this.vmlPath, path); //FIXME: why not push()? + } + }, + 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.type = ""; // no prior control point + this.inherited(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, segment, from, upto){ + var n = segment instanceof Array ? segment : segment.args; + for(var i = from; i < upto; ++i){ + path.push(" ", n[i].toFixed()); + } + }, + _adjustRelCrd: function(last, segment, step){ + var n = segment instanceof Array ? segment : segment.args, l = n.length, + result = new Array(l), i = 0, x = last.x, y = last.y; + if(typeof x != "number"){ + // there is no last coordinate => + // treat the first pair as an absolute coordinate + result[0] = x = n[0]; + result[1] = y = n[1]; + i = 2; + } + if(typeof step == "number" && step != 2){ + var j = step; + while(j <= l){ + for(; i < j; i += 2){ + result[i] = x + n[i]; + result[i + 1] = y + n[i + 1]; + } + x = result[j - 2]; + y = result[j - 1]; + j += step; + } + }else{ + for(; i < l; i += 2){ + result[i] = (x += n[i]); + result[i + 1] = (y += n[i + 1]); + } + } + return result; + }, + _adjustRelPos: function(last, segment){ + var n = segment instanceof Array ? segment : segment.args, l = n.length, + result = new Array(l); + for(var i = 0; i < l; ++i){ + result[i] = (last += n[i]); + } + return result; + }, + _moveToA: function(segment){ + var p = [" m"], n = segment instanceof Array ? segment : segment.args, l = n.length; + this._addArgs(p, n, 0, 2); + if(l > 2){ + p.push(" l"); + this._addArgs(p, n, 2, l); + } + this.lastControl.type = ""; // no control point after this primitive + return p; + }, + _moveToR: function(segment, last){ + return this._moveToA(this._adjustRelCrd(last, segment)); + }, + _lineToA: function(segment){ + var p = [" l"], n = segment instanceof Array ? segment : segment.args; + this._addArgs(p, n, 0, n.length); + this.lastControl.type = ""; // no control point after this primitive + return p; + }, + _lineToR: function(segment, last){ + return this._lineToA(this._adjustRelCrd(last, segment)); + }, + _hLineToA: function(segment, last){ + var p = [" l"], y = " " + last.y.toFixed(), + n = segment instanceof Array ? segment : segment.args, l = n.length; + for(var i = 0; i < l; ++i){ + p.push(" ", n[i].toFixed(), y); + } + this.lastControl.type = ""; // no control point after this primitive + return p; + }, + _hLineToR: function(segment, last){ + return this._hLineToA(this._adjustRelPos(last.x, segment), last); + }, + _vLineToA: function(segment, last){ + var p = [" l"], x = " " + last.x.toFixed(), + n = segment instanceof Array ? segment : segment.args, l = n.length; + for(var i = 0; i < l; ++i){ + p.push(x, " ", n[i].toFixed()); + } + this.lastControl.type = ""; // no control point after this primitive + return p; + }, + _vLineToR: function(segment, last){ + return this._vLineToA(this._adjustRelPos(last.y, segment), last); + }, + _curveToA: function(segment){ + var p = [], n = segment instanceof Array ? segment : segment.args, l = n.length, + lc = this.lastControl; + for(var i = 0; i < l; i += 6){ + p.push(" c"); + this._addArgs(p, n, i, i + 6); + } + lc.x = n[l - 4]; + lc.y = n[l - 3]; + lc.type = "C"; + return p; + }, + _curveToR: function(segment, last){ + return this._curveToA(this._adjustRelCrd(last, segment, 6)); + }, + _smoothCurveToA: function(segment, last){ + var p = [], n = segment instanceof Array ? segment : segment.args, l = n.length, + lc = this.lastControl, i = 0; + if(lc.type != "C"){ + p.push(" c"); + this._addArgs(p, [last.x, last.y], 0, 2); + this._addArgs(p, n, 0, 4); + lc.x = n[0]; + lc.y = n[1]; + lc.type = "C"; + i = 4; + } + for(; i < l; i += 4){ + p.push(" c"); + this._addArgs(p, [ + 2 * last.x - lc.x, + 2 * last.y - lc.y + ], 0, 2); + this._addArgs(p, n, i, i + 4); + lc.x = n[i]; + lc.y = n[i + 1]; + } + return p; + }, + _smoothCurveToR: function(segment, last){ + return this._smoothCurveToA(this._adjustRelCrd(last, segment, 4), last); + }, + _qCurveToA: function(segment){ + var p = [], n = segment instanceof Array ? segment : segment.args, l = n.length, + lc = this.lastControl; + for(var i = 0; i < l; i += 4){ + p.push(" qb"); + this._addArgs(p, n, i, i + 4); + } + lc.x = n[l - 4]; + lc.y = n[l - 3]; + lc.type = "Q"; + return p; + }, + _qCurveToR: function(segment, last){ + return this._qCurveToA(this._adjustRelCrd(last, segment, 4)); + }, + _qSmoothCurveToA: function(segment, last){ + var p = [], n = segment instanceof Array ? segment : segment.args, l = n.length, + lc = this.lastControl, i = 0; + if(lc.type != "Q"){ + p.push(" qb"); + this._addArgs(p, [ + lc.x = last.x, + lc.y = last.y + ], 0, 2); + lc.type = "Q"; + this._addArgs(p, n, 0, 2); + i = 2; + } + for(; i < l; i += 2){ + p.push(" qb"); + this._addArgs(p, [ + lc.x = 2 * last.x - lc.x, + lc.y = 2 * last.y - lc.y + ], 0, 2); + this._addArgs(p, n, i, i + 2); + } + return p; + }, + _qSmoothCurveToR: function(segment, last){ + return this._qSmoothCurveToA(this._adjustRelCrd(last, segment, 2), last); + }, + _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 = arcLib.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"); + var t = result[j]; + this._addArgs(p, t, 0, t.length); + this._updateBBox(t[0], t[1]); + this._updateBBox(t[2], t[3]); + this._updateBBox(t[4], t[5]); + } + last.x = x1; + last.y = y1; + } + this.lastControl.type = ""; // no control point after this primitive + return p; + }, + _closePath: function(){ + this.lastControl.type = ""; // no control point after this primitive + return ["x"]; + } + }); + vml.Path.nodeType = "shape"; + + declare("dojox.gfx.vml.TextPath", [vml.Path, pathLib.TextPath], { + // summary: a textpath shape (VML) + constructor: function(rawNode){ + if(rawNode){rawNode.setAttribute("dojoGfxType", "textpath");} + this.fontStyle = null; + if(!("text" in this)){ + this.text = lang.clone(g.defaultTextPath); + } + if(!("fontStyle" in this)){ + this.fontStyle = lang.clone(g.defaultFont); + } + }, + setText: function(newText){ + // summary: sets a text to be drawn along the path + this.text = g.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" ? + g.splitFontString(newFont) : + g.makeParameters(g.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 = 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 = g.makeFontString(f); + break; + } + } + } + }); + vml.TextPath.nodeType = "shape"; + + declare("dojox.gfx.vml.Surface", gs.Surface, { + // summary: a surface object to be used for drawings (VML) + constructor: function(){ + gs.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 = g.normalizedLength(width); // in pixels + this.height = g.normalizedLength(height); // in pixels + if(!this.rawNode) return this; + var cs = this.clipNode.style, + r = this.rawNode, rs = r.style, + bs = this.bgNode.style, + ps = this._parent.style, i; + ps.width = width; + ps.height = height; + cs.width = width; + cs.height = height; + cs.clip = "rect(0px " + width + "px " + height + "px 0px)"; + rs.width = width; + rs.height = height; + r.coordsize = width + " " + height; + bs.width = width; + bs.height = height; + for(i = 0; i < this.children.length; ++i){ + this.children[i]._setDimensions(width, height); + } + return this; // self + }, + getDimensions: function(){ + // summary: returns an object with properties "width" and "height" + var t = this.rawNode ? { + width: g.normalizedLength(this.rawNode.style.width), + height: g.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 + } + }); + + vml.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 && !height){ + var pos = domGeom.position(parentNode); + width = width || pos.w; + height = height || pos.h; + } + if(typeof width == "number"){ + width = width + "px"; + } + if(typeof height == "number"){ + height = height + "px"; + } + + var s = new vml.Surface(), p = dom.byId(parentNode), + c = s.clipNode = p.ownerDocument.createElement("div"), + r = s.rawNode = p.ownerDocument.createElement("v:group"), + cs = c.style, rs = r.style; + + if(has("ie") > 7){ + rs.display = "inline-block"; + } + + s._parent = p; + s._nodes.push(c); // other elements will be deleted as parts of "c" + + p.style.width = width; + p.style.height = height; + + cs.position = "absolute"; + cs.width = width; + cs.height = height; + cs.clip = "rect(0px " + width + " " + height + " 0px)"; + 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 = g.normalizedLength(width); // in pixels + s.height = g.normalizedLength(height); // in pixels + + return s; // dojox.gfx.Surface + }; + + // Extenders + + // copied from dojox.gfx.utils + function forEach(object, f, o){ + o = o || win.global; + f.call(o, object); + if(object instanceof g.Surface || object instanceof g.Group){ + arr.forEach(object.children, function(shape){ + forEach(shape, f, o); + }); + } + } + + var addPatch9624 = function(shape){ + if(this != shape.getParent()){ + // cleanup from old parent + var oldParent = shape.getParent(); + if(oldParent) { oldParent.remove(shape); } + // then move the raw node + this.rawNode.appendChild(shape.rawNode); + C.add.apply(this, arguments); + // reapply visual attributes (slow..) + forEach(this, function(s){ + if (typeof(s.getFont) == 'function'){ // text shapes need to be completely refreshed + s.setShape(s.getShape()); + s.setFont(s.getFont()); + } + if (typeof(s.setFill) == 'function'){ // if setFill is available a setStroke should be safe to assume also + s.setFill(s.getFill()); + s.setStroke(s.getStroke()); + } + }); + } + return this; // self + }; + + var add15 = function(shape){ + if(this != shape.getParent()){ + this.rawNode.appendChild(shape.rawNode); + if(!shape.getParent()){ + // reapply visual attributes + shape.setFill(shape.getFill()); + shape.setStroke(shape.getStroke()); + } + C.add.apply(this, arguments); + } + return this; // self + }; + + var C = gs.Container, Container = { + add: config.fixVmlAdd === true ? addPatch9624 : add15, + 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); + } + C.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 C.clear.apply(this, arguments); + }, + _moveChildToFront: C._moveChildToFront, + _moveChildToBack: C._moveChildToBack + }; + + var Creator = { + // summary: VML shape creators + createGroup: function(){ + // summary: creates a VML group shape + var node = this.createObject(vml.Group, null); // dojox.gfx.Group + // create a background rectangle, which is required to show all other shapes + var r = node.rawNode.ownerDocument.createElement("v:rect"); + r.style.left = r.style.top = 0; + r.style.width = node.rawNode.style.width; + r.style.height = node.rawNode.style.height; + r.filled = r.stroked = "f"; + node.rawNode.appendChild(r); + node.bgNode = r; + return node; // 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 vml.Image(), + doc = this.rawNode.ownerDocument, + node = doc.createElement('v:rect'); + node.stroked = "f"; + node.style.width = this.rawNode.style.width; + node.style.height = this.rawNode.style.height; + var img = doc.createElement('v:imagedata'); + node.appendChild(img); + shape.setRawNode(node); + this.rawNode.appendChild(node); + shape.setShape(image); + this.add(shape); + return shape; // dojox.gfx.Image + }, + createRect: function(rect){ + // summary: creates a rectangle shape + // rect: Object: a path object (see dojox.gfx.defaultRect) + if(!this.rawNode) return null; + var shape = new vml.Rect, + node = this.rawNode.ownerDocument.createElement("v:roundrect"); + if(has("ie") > 7){ + node.style.display = "inline-block"; + } + shape.setRawNode(node); + this.rawNode.appendChild(node); + shape.setShape(rect); + this.add(shape); + return shape; // dojox.gfx.Rect + }, + 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 vml.Group: + case vml.Line: + case vml.Polyline: + case vml.Image: + case vml.Text: + case vml.Path: + case vml.TextPath: + this._overrideSize(node); + } + shape.setShape(rawShape); + this.add(shape); + return shape; // dojox.gfx.Shape + }, + _overrideSize: function(node){ + var s = this.rawNode.style, w = s.width, h = s.height; + node.style.width = w; + node.style.height = h; + node.coordsize = parseInt(w) + " " + parseInt(h); + } + }; + + lang.extend(vml.Group, Container); + lang.extend(vml.Group, gs.Creator); + lang.extend(vml.Group, Creator); + + lang.extend(vml.Surface, Container); + lang.extend(vml.Surface, gs.Creator); + lang.extend(vml.Surface, Creator); + + // Mouse/Touch event + vml.fixTarget = function(event, gfxElement){ + // summary: + // Adds the gfxElement to event.gfxTarget if none exists. This new + // property will carry the GFX element associated with this event. + // event: Object + // The current input event (MouseEvent or TouchEvent) + // gfxElement: Object + // The GFX target element + if (!event.gfxTarget) { + event.gfxTarget = gs.byId(event.target.__gfxObject__); + } + return true; + }; + + return vml; +}); |
