1 if(!dojo._hasResource["dojox.gfx.vml"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojox.gfx.vml"] = true;
3 dojo.provide("dojox.gfx.vml");
5 dojo.require("dojox.gfx._base");
6 dojo.require("dojox.gfx.shape");
7 dojo.require("dojox.gfx.path");
8 dojo.require("dojox.gfx.arc");
10 // dojox.gfx.vml.xmlns: String: a VML's namespace
11 dojox.gfx.vml.xmlns = "urn:schemas-microsoft-com:vml";
13 // dojox.gfx.vml.text_alignment: Object: mapping from SVG alignment to VML alignment
14 dojox.gfx.vml.text_alignment = {start: "left", middle: "center", end: "right"};
16 dojox.gfx.vml._parseFloat = function(str) {
17 // summary: a helper function to parse VML-specific floating-point values
18 // str: String: a representation of a floating-point number
19 return str.match(/^\d+f$/i) ? parseInt(str) / 65536 : parseFloat(str); // Number
22 dojox.gfx.vml._bool = {"t": 1, "true": 1};
24 dojo.extend(dojox.gfx.Shape, {
25 // summary: VML-specific implementation of dojox.gfx.Shape methods
27 setFill: function(fill){
28 // summary: sets a fill object (VML)
29 // fill: Object: a fill object
30 // (see dojox.gfx.defaultLinearGradient,
31 // dojox.gfx.defaultRadialGradient,
32 // dojox.gfx.defaultPattern,
37 this.fillStyle = null;
38 this.rawNode.filled = "f";
41 if(typeof fill == "object" && "type" in fill){
46 var matrix = this._getRealMatrix(), m = dojox.gfx.matrix;
48 f = dojox.gfx.makeParameters(dojox.gfx.defaultLinearGradient, fill);
51 dojo.forEach(a, function(v, i, a){
52 a[i].color = dojox.gfx.normalizeColor(v.color);
55 s.push("0 " + a[0].color.toHex());
57 for(i = 0; i < a.length; ++i){
58 s.push(a[i].offset.toFixed(8) + " " + a[i].color.toHex());
62 s.push("1 " + a[i].color.toHex());
64 fo = this.rawNode.fill;
65 fo.colors.value = s.join(";");
68 var fc1 = matrix ? m.multiplyPoint(matrix, f.x1, f.y1) : {x: f.x1, y: f.y1},
69 fc2 = matrix ? m.multiplyPoint(matrix, f.x2, f.y2) : {x: f.x2, y: f.y2};
70 fo.angle = (m._radToDeg(Math.atan2(fc2.x - fc1.x, fc2.y - fc1.y)) + 180) % 360;
74 f = dojox.gfx.makeParameters(dojox.gfx.defaultRadialGradient, fill);
76 var l = parseFloat(this.rawNode.style.left),
77 t = parseFloat(this.rawNode.style.top),
78 w = parseFloat(this.rawNode.style.width),
79 h = parseFloat(this.rawNode.style.height),
80 c = isNaN(w) ? 1 : 2 * f.r / w;
82 // add a color at the offset 0 (1 in VML coordinates)
83 if(f.colors[0].offset > 0){
84 a.push({offset: 1, color: dojox.gfx.normalizeColor(f.colors[0].color)});
87 dojo.forEach(f.colors, function(v, i){
88 a.push({offset: 1 - v.offset * c, color: dojox.gfx.normalizeColor(v.color)});
91 while(i >= 0 && a[i].offset < 0){ --i; }
93 // correct excessive colors
94 var q = a[i], p = a[i + 1];
95 p.color = dojo.blendColors(q.color, p.color, q.offset / (q.offset - p.offset));
97 while(a.length - i > 2) a.pop();
100 i = a.length - 1, s = [];
102 s.push("0 " + a[i].color.toHex());
105 s.push(a[i].offset.toFixed(8) + " " + a[i].color.toHex());
107 fo = this.rawNode.fill;
108 fo.colors.value = s.join(";");
110 fo.type = "gradientradial";
111 if(isNaN(w) || isNaN(h) || isNaN(l) || isNaN(t)){
112 fo.focusposition = "0.5 0.5";
114 fo.focusposition = ((f.cx - l) / w).toFixed(8) + " " + ((f.cy - t) / h).toFixed(8);
116 fo.focussize = "0 0";
120 f = dojox.gfx.makeParameters(dojox.gfx.defaultPattern, fill);
122 fo = this.rawNode.fill;
125 if(f.width && f.height){
127 fo.size.x = dojox.gfx.px2pt(f.width);
128 fo.size.y = dojox.gfx.px2pt(f.height);
133 fo.origin.x = f.width ? f.x / f.width : 0;
134 fo.origin.y = f.height ? f.y / f.height : 0;
138 this.rawNode.fill.opacity = 1;
142 this.fillStyle = dojox.gfx.normalizeColor(fill);
143 this.rawNode.fill.method = "any";
144 this.rawNode.fill.type = "solid";
145 this.rawNode.fillcolor = this.fillStyle.toHex();
146 this.rawNode.fill.opacity = this.fillStyle.a;
147 this.rawNode.filled = true;
151 setStroke: function(stroke){
152 // summary: sets a stroke object (VML)
153 // stroke: Object: a stroke object
154 // (see dojox.gfx.defaultStroke)
158 this.strokeStyle = null;
159 this.rawNode.stroked = "f";
162 // normalize the stroke
163 if(typeof stroke == "string"){
164 stroke = {color: stroke};
166 var s = this.strokeStyle = dojox.gfx.makeParameters(dojox.gfx.defaultStroke, stroke);
167 s.color = dojox.gfx.normalizeColor(s.color);
168 // generate attributes
169 var rn = this.rawNode;
171 rn.strokecolor = s.color.toCss();
172 rn.strokeweight = s.width + "px"; // TODO: should we assume that the width is always in pixels?
174 rn.stroke.opacity = s.color.a;
175 rn.stroke.endcap = this._translate(this._capMap, s.cap);
176 if(typeof s.join == "number") {
177 rn.stroke.joinstyle = "miter";
178 rn.stroke.miterlimit = s.join;
180 rn.stroke.joinstyle = s.join;
181 // rn.stroke.miterlimit = s.width;
183 rn.stroke.dashstyle = s.style == "none" ? "Solid" : s.style;
188 _capMap: { butt: 'flat' },
189 _capMapReversed: { flat: 'butt' },
191 _translate: function(dict, value) {
192 return (value in dict) ? dict[value] : value;
195 _applyTransform: function() {
196 if(this.fillStyle && this.fillStyle.type == "linear"){
197 this.setFill(this.fillStyle);
199 var matrix = this._getRealMatrix();
200 if(!matrix) return this;
201 var skew = this.rawNode.skew;
202 if(typeof skew == "undefined"){
203 for(var i = 0; i < this.rawNode.childNodes.length; ++i){
204 if(this.rawNode.childNodes[i].tagName == "skew"){
205 skew = this.rawNode.childNodes[i];
212 var mt = matrix.xx.toFixed(8) + " " + matrix.xy.toFixed(8) + " " +
213 matrix.yx.toFixed(8) + " " + matrix.yy.toFixed(8) + " 0 0",
214 offset = Math.floor(matrix.dx).toFixed() + "px " + Math.floor(matrix.dy).toFixed() + "px",
215 s = this.rawNode.style,
216 l = parseFloat(s.left),
217 t = parseFloat(s.top),
218 w = parseFloat(s.width),
219 h = parseFloat(s.height);
224 var origin = (-l / w - 0.5).toFixed(8) + " " + (-t / h - 0.5).toFixed(8);
226 skew.origin = origin;
227 skew.offset = offset;
233 setRawNode: function(rawNode){
235 // assigns and clears the underlying node that will represent this
236 // shape. Once set, transforms, gradients, etc, can be applied.
237 // (no fill & stroke by default)
238 rawNode.stroked = "f";
239 rawNode.filled = "f";
240 this.rawNode = rawNode;
245 _moveToFront: function(){
246 // summary: moves a shape to front of its parent's list of shapes (VML)
247 this.rawNode.parentNode.appendChild(this.rawNode);
250 _moveToBack: function(){
251 // summary: moves a shape to back of its parent's list of shapes (VML)
252 var r = this.rawNode, p = r.parentNode, n = p.firstChild;
253 p.insertBefore(r, n);
254 if(n.tagName == "rect"){
255 // surface has a background rectangle, which position should be preserved
261 _getRealMatrix: function(){
262 // summary: returns the cumulative ("real") transformation matrix
263 // by combining the shape's matrix with its parent's matrix
264 return this.parentMatrix ? new dojox.gfx.Matrix2D([this.parentMatrix, this.matrix]) : this.matrix; // dojox.gfx.Matrix2D
268 dojo.declare("dojox.gfx.Group", dojox.gfx.Shape, {
269 // summary: a group shape (VML), which can be used
270 // to logically group shapes (e.g, to propagate matricies)
271 constructor: function(){
272 dojox.gfx.vml.Container._init.call(this);
274 // apply transformation
275 _applyTransform: function(){
276 // summary: applies a transformation matrix to a group
277 var matrix = this._getRealMatrix();
278 for(var i = 0; i < this.children.length; ++i){
279 this.children[i]._updateParentMatrix(matrix);
284 dojox.gfx.Group.nodeType = "group";
286 dojo.declare("dojox.gfx.Rect", dojox.gfx.shape.Rect, {
287 // summary: a rectangle shape (VML)
288 setShape: function(newShape){
289 // summary: sets a rectangle shape object (VML)
290 // newShape: Object: a rectangle shape object
291 var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
293 var style = this.rawNode.style;
294 style.left = shape.x.toFixed();
295 style.top = shape.y.toFixed();
296 style.width = (typeof shape.width == "string" && shape.width.indexOf("%") >= 0) ? shape.width : shape.width.toFixed();
297 style.height = (typeof shape.width == "string" && shape.height.indexOf("%") >= 0) ? shape.height : shape.height.toFixed();
298 var r = Math.min(1, (shape.r / Math.min(parseFloat(shape.width), parseFloat(shape.height)))).toFixed(8);
299 // a workaround for the VML's arcsize bug: cannot read arcsize of an instantiated node
300 var parent = this.rawNode.parentNode, before = null;
302 if(parent.lastChild != this.rawNode){
303 for(var i = 0; i < parent.childNodes.length; ++i){
304 if(parent.childNodes[i] == this.rawNode){
305 before = parent.childNodes[i+1];
310 parent.removeChild(this.rawNode);
312 this.rawNode.arcsize = r;
315 parent.insertBefore(this.rawNode, before);
317 parent.appendChild(this.rawNode);
320 // set all necessary styles, which are lost by VML (yes, it's a VML's bug)
321 return this.setTransform(this.matrix).setFill(this.fillStyle).setStroke(this.strokeStyle); // self
324 dojox.gfx.Rect.nodeType = "roundrect"; // use a roundrect so the stroke join type is respected
326 dojo.declare("dojox.gfx.Ellipse", dojox.gfx.shape.Ellipse, {
327 // summary: an ellipse shape (VML)
328 setShape: function(newShape){
329 // summary: sets an ellipse shape object (VML)
330 // newShape: Object: an ellipse shape object
331 var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
333 var style = this.rawNode.style;
334 style.left = (shape.cx - shape.rx).toFixed();
335 style.top = (shape.cy - shape.ry).toFixed();
336 style.width = (shape.rx * 2).toFixed();
337 style.height = (shape.ry * 2).toFixed();
338 return this.setTransform(this.matrix); // self
341 dojox.gfx.Ellipse.nodeType = "oval";
343 dojo.declare("dojox.gfx.Circle", dojox.gfx.shape.Circle, {
344 // summary: a circle shape (VML)
345 setShape: function(newShape){
346 // summary: sets a circle shape object (VML)
347 // newShape: Object: a circle shape object
348 var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
350 var style = this.rawNode.style;
351 style.left = (shape.cx - shape.r).toFixed();
352 style.top = (shape.cy - shape.r).toFixed();
353 style.width = (shape.r * 2).toFixed();
354 style.height = (shape.r * 2).toFixed();
358 dojox.gfx.Circle.nodeType = "oval";
360 dojo.declare("dojox.gfx.Line", dojox.gfx.shape.Line, {
361 // summary: a line shape (VML)
362 constructor: function(rawNode){
363 if(rawNode) rawNode.setAttribute("dojoGfxType", "line");
365 setShape: function(newShape){
366 // summary: sets a line shape object (VML)
367 // newShape: Object: a line shape object
368 var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
370 this.rawNode.path.v = "m" + shape.x1.toFixed() + " " + shape.y1.toFixed() +
371 "l" + shape.x2.toFixed() + " " + shape.y2.toFixed() + "e";
372 return this.setTransform(this.matrix); // self
375 dojox.gfx.Line.nodeType = "shape";
377 dojo.declare("dojox.gfx.Polyline", dojox.gfx.shape.Polyline, {
378 // summary: a polyline/polygon shape (VML)
379 constructor: function(rawNode){
380 if(rawNode) rawNode.setAttribute("dojoGfxType", "polyline");
382 setShape: function(points, closed){
383 // summary: sets a polyline/polygon shape object (VML)
384 // points: Object: a polyline/polygon shape object
385 // closed: Boolean?: if true, close the polyline explicitely
386 if(points && points instanceof Array){
388 // points: Array: an array of points
389 this.shape = dojox.gfx.makeParameters(this.shape, { points: points });
390 if(closed && this.shape.points.length) this.shape.points.push(this.shape.points[0]);
392 this.shape = dojox.gfx.makeParameters(this.shape, points);
395 var attr = [], p = this.shape.points;
399 if(typeof p[0] == "number"){
400 attr.push(p[0].toFixed());
401 attr.push(p[1].toFixed());
404 attr.push(p[0].x.toFixed());
405 attr.push(p[0].y.toFixed());
409 for(var i = k; i < p.length; ++i){
410 if(typeof p[i] == "number"){
411 attr.push(p[i].toFixed());
413 attr.push(p[i].x.toFixed());
414 attr.push(p[i].y.toFixed());
420 this.rawNode.path.v = attr.join(" ");
421 return this.setTransform(this.matrix); // self
424 dojox.gfx.Polyline.nodeType = "shape";
426 dojo.declare("dojox.gfx.Image", dojox.gfx.shape.Image, {
427 // summary: an image (VML)
428 constructor: function(rawNode){
429 if(rawNode) rawNode.setAttribute("dojoGfxType", "image");
431 getEventSource: function() {
432 // summary: returns a Node, which is used as
433 // a source of events for this shape
434 return this.rawNode ? this.rawNode.firstChild : null; // Node
436 setShape: function(newShape){
437 // summary: sets an image shape object (VML)
438 // newShape: Object: an image shape object
439 var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
441 this.rawNode.firstChild.src = shape.src;
442 return this.setTransform(this.matrix); // self
444 _setDimensions: function(s, w, h){
450 _resetImage: function(){
451 var s = this.rawNode.firstChild.style,
455 this._setDimensions(s, shape.width, shape.height);
457 _applyTransform: function() {
458 var matrix = this._getRealMatrix(),
459 img = this.rawNode.firstChild,
463 matrix = dojox.gfx.matrix.multiply(matrix, {dx: shape.x, dy: shape.y});
465 matrix = dojox.gfx.matrix.normalize({dx: shape.x, dy: shape.y});
467 if(matrix.xy == 0 && matrix.yx == 0 && matrix.xx > 0 && matrix.yy > 0){
468 // special case to avoid filters
469 this.rawNode.style.filter = "";
470 s.left = Math.floor(matrix.dx) + "px";
471 s.top = Math.floor(matrix.dy) + "px";
472 this._setDimensions(s, Math.floor(matrix.xx * shape.width), Math.floor(matrix.yy * shape.height));
475 var f = this.rawNode.filters["DXImageTransform.Microsoft.Matrix"];
484 this.rawNode.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11=" + matrix.xx +
485 ", M12=" + matrix.xy + ", M21=" + matrix.yx + ", M22=" + matrix.yy +
486 ", Dx=" + matrix.dx + ", Dy=" + matrix.dy + ")";
492 dojox.gfx.Image.nodeType = "div";
494 dojo.declare("dojox.gfx.Text", dojox.gfx.shape.Text, {
495 // summary: an anchored text (VML)
496 constructor: function(rawNode){
497 if(rawNode){rawNode.setAttribute("dojoGfxType", "text");}
498 this.fontStyle = null;
500 _alignment: {start: "left", middle: "center", end: "right"},
501 setShape: function(newShape){
502 // summary: sets a text shape object (VML)
503 // newShape: Object: a text shape object
504 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
506 var r = this.rawNode, s = this.shape, x = s.x, y = s.y.toFixed();
515 this.rawNode.path.v = "m" + x.toFixed() + "," + y +
516 "l" + (x + 10).toFixed() + "," + y + "e";
517 // find path and text path
518 var p = null, t = null, c = r.childNodes;
519 for(var i = 0; i < c.length; ++i){
520 var tag = c[i].tagName;
524 }else if(tag == "textpath"){
530 p = this.rawNode.ownerDocument.createElement("v:path");
534 t = this.rawNode.ownerDocument.createElement("v:textpath");
539 var a = dojox.gfx.vml.text_alignment[s.align];
540 t.style["v-text-align"] = a ? a : "left";
541 t.style["text-decoration"] = s.decoration;
542 t.style["v-rotate-letters"] = s.rotated;
543 t.style["v-text-kern"] = s.kerning;
545 return this.setTransform(this.matrix); // self
547 _setFont: function(){
548 // summary: sets a font object (VML)
549 var f = this.fontStyle, c = this.rawNode.childNodes;
550 for(var i = 0; i < c.length; ++i){
551 if(c[i].tagName == "textpath"){
552 c[i].style.font = dojox.gfx.makeFontString(f);
556 this.setTransform(this.matrix);
558 _getRealMatrix: function(){
559 // summary: returns the cumulative ("real") transformation matrix
560 // by combining the shape's matrix with its parent's matrix;
561 // it makes a correction for a font size
562 var matrix = dojox.gfx.Shape.prototype._getRealMatrix.call(this);
563 // It appears that text is always aligned vertically at a middle of x-height (???).
564 // It is impossible to obtain these metrics from VML => I try to approximate it with
565 // more-or-less util value of 0.7 * FontSize, which is typical for European fonts.
567 matrix = dojox.gfx.matrix.multiply(matrix,
568 {dy: -dojox.gfx.normalizedLength(this.fontStyle ? this.fontStyle.size : "10pt") * 0.35});
570 return matrix; // dojox.gfx.Matrix2D
572 getTextWidth: function(){
573 // summary: get the text width, in px
574 var rawNode = this.rawNode, _display = rawNode.style.display;
575 rawNode.style.display = "inline";
576 var _width = dojox.gfx.pt2px(parseFloat(rawNode.currentStyle.width));
577 rawNode.style.display = _display;
581 dojox.gfx.Text.nodeType = "shape";
583 dojox.gfx.path._calcArc = function(alpha){
584 // return a start point, 1st and 2nd control points, and an end point
585 var cosa = Math.cos(alpha), sina = Math.sin(alpha),
586 p2 = {x: cosa + (4 / 3) * (1 - cosa), y: sina - (4 / 3) * cosa * (1 - cosa) / sina};
588 s: {x: cosa, y: -sina},
589 c1: {x: p2.x, y: -p2.y},
591 e: {x: cosa, y: sina}
595 dojo.declare("dojox.gfx.Path", dojox.gfx.path.Path, {
596 // summary: a path shape (VML)
597 constructor: function(rawNode){
598 if(rawNode && !rawNode.getAttribute("dojoGfxType")){
599 rawNode.setAttribute("dojoGfxType", "path");
602 this.lastControl = {};
604 _updateWithSegment: function(segment){
605 // summary: updates the bounding box of path with new segment
606 // segment: Object: a segment
607 var last = dojo.clone(this.last);
608 dojox.gfx.Path.superclass._updateWithSegment.apply(this, arguments);
609 // add a VML path segment
610 var path = this[this.renderers[segment.action]](segment, last);
611 if(typeof this.vmlPath == "string"){
612 this.vmlPath += path.join("");
613 this.rawNode.path.v = this.vmlPath + " r0,0 e";
615 this.vmlPath = this.vmlPath.concat(path);
618 setShape: function(newShape){
619 // summary: forms a path using a shape (VML)
620 // newShape: Object: an VML path string or a path object (see dojox.gfx.defaultPath)
622 this.lastControl = {};
623 dojox.gfx.Path.superclass.setShape.apply(this, arguments);
624 this.vmlPath = this.vmlPath.join("");
625 this.rawNode.path.v = this.vmlPath + " r0,0 e";
628 _pathVmlToSvgMap: {m: "M", l: "L", t: "m", r: "l", c: "C", v: "c", qb: "Q", x: "z", e: ""},
629 // VML-specific segment renderers
631 M: "_moveToA", m: "_moveToR",
632 L: "_lineToA", l: "_lineToR",
633 H: "_hLineToA", h: "_hLineToR",
634 V: "_vLineToA", v: "_vLineToR",
635 C: "_curveToA", c: "_curveToR",
636 S: "_smoothCurveToA", s: "_smoothCurveToR",
637 Q: "_qCurveToA", q: "_qCurveToR",
638 T: "_qSmoothCurveToA", t: "_qSmoothCurveToR",
639 A: "_arcTo", a: "_arcTo",
640 Z: "_closePath", z: "_closePath"
642 _addArgs: function(path, args, from, upto){
643 if(typeof upto == "undefined"){
646 if(typeof from == "undefined"){
649 for(var i = from; i < upto; ++i){
651 path.push(args[i].toFixed());
654 _addArgsAdjusted: function(path, last, args, from, upto){
655 if(typeof upto == "undefined"){
658 if(typeof from == "undefined"){
661 for(var i = from; i < upto; i += 2){
663 path.push((last.x + args[i]).toFixed());
665 path.push((last.y + args[i + 1]).toFixed());
668 _moveToA: function(segment){
669 var p = [" m"], n = segment.args, l = n.length;
673 this._addArgs(p, n, 0, 2);
675 this._addArgs(p, n, 2);
677 this.lastControl = {};
680 _moveToR: function(segment, last){
681 var p = ["x" in last ? " t" : " m"], n = segment.args, l = n.length;
685 this._addArgs(p, n, 0, 2);
687 this._addArgs(p, n, 2);
689 this.lastControl = {};
692 _lineToA: function(segment){
694 this._addArgs(p, segment.args);
695 this.lastControl = {};
698 _lineToR: function(segment){
700 this._addArgs(p, segment.args);
701 this.lastControl = {};
704 _hLineToA: function(segment, last){
705 var p = [" l"], n = segment.args, l = n.length, y = " " + last.y.toFixed();
706 for(var i = 0; i < l; ++i){
708 p.push(n[i].toFixed());
711 this.lastControl = {};
714 _hLineToR: function(segment){
715 var p = [" r"], n = segment.args, l = n.length;
716 for(var i = 0; i < l; ++i){
718 p.push(n[i].toFixed());
721 this.lastControl = {};
724 _vLineToA: function(segment, last){
725 var p = [" l"], n = segment.args, l = n.length, x = " " + last.x.toFixed();
726 for(var i = 0; i < l; ++i){
729 p.push(n[i].toFixed());
731 this.lastControl = {};
734 _vLineToR: function(segment){
735 var p = [" r"], n = segment.args, l = n.length;
736 for(var i = 0; i < l; ++i){
738 p.push(n[i].toFixed());
740 this.lastControl = {};
743 _curveToA: function(segment){
744 var p = [], n = segment.args, l = n.length;
745 for(var i = 0; i < l; i += 6){
747 this._addArgs(p, n, i, i + 6);
749 this.lastControl = {x: n[l - 4], y: n[l - 3], type: "C"};
752 _curveToR: function(segment, last){
753 var p = [], n = segment.args, l = n.length;
754 for(var i = 0; i < l; i += 6){
756 this._addArgs(p, n, i, i + 6);
757 this.lastControl = {x: last.x + n[i + 2], y: last.y + n[i + 3]};
761 this.lastControl.type = "C";
764 _smoothCurveToA: function(segment, last){
765 var p = [], n = segment.args, l = n.length;
766 for(var i = 0; i < l; i += 4){
768 if(this.lastControl.type == "C"){
770 2 * last.x - this.lastControl.x,
771 2 * last.y - this.lastControl.y
774 this._addArgs(p, [last.x, last.y]);
776 this._addArgs(p, n, i, i + 4);
778 this.lastControl = {x: n[l - 4], y: n[l - 3], type: "C"};
781 _smoothCurveToR: function(segment, last){
782 var p = [], n = segment.args, l = n.length;
783 for(var i = 0; i < l; i += 4){
785 if(this.lastControl.type == "C"){
787 last.x - this.lastControl.x,
788 last.y - this.lastControl.y
791 this._addArgs(p, [0, 0]);
793 this._addArgs(p, n, i, i + 4);
794 this.lastControl = {x: last.x + n[i], y: last.y + n[i + 1]};
798 this.lastControl.type = "C";
801 _qCurveToA: function(segment){
802 var p = [], n = segment.args, l = n.length;
803 for(var i = 0; i < l; i += 4){
805 this._addArgs(p, n, i, i + 4);
807 this.lastControl = {x: n[l - 4], y: n[l - 3], type: "Q"};
810 _qCurveToR: function(segment, last){
811 var p = [], n = segment.args, l = n.length;
812 for(var i = 0; i < l; i += 4){
814 this._addArgsAdjusted(p, last, n, i, i + 4);
815 this.lastControl = {x: last.x + n[i], y: last.y + n[i + 1]};
819 this.lastControl.type = "Q";
822 _qSmoothCurveToA: function(segment, last){
823 var p = [], n = segment.args, l = n.length;
824 for(var i = 0; i < l; i += 2){
826 if(this.lastControl.type == "Q"){
828 this.lastControl.x = 2 * last.x - this.lastControl.x,
829 this.lastControl.y = 2 * last.y - this.lastControl.y
833 this.lastControl.x = last.x,
834 this.lastControl.y = last.y
837 this._addArgs(p, n, i, i + 2);
839 this.lastControl.type = "Q";
842 _qSmoothCurveToR: function(segment, last){
843 var p = [], n = segment.args, l = n.length;
844 for(var i = 0; i < l; i += 2){
846 if(this.lastControl.type == "Q"){
848 this.lastControl.x = 2 * last.x - this.lastControl.x,
849 this.lastControl.y = 2 * last.y - this.lastControl.y
853 this.lastControl.x = last.x,
854 this.lastControl.y = last.y
857 this._addArgsAdjusted(p, last, n, i, i + 2);
859 this.lastControl.type = "Q";
862 _arcTo: function(segment, last){
863 var p = [], n = segment.args, l = n.length, relative = segment.action == "a";
864 for(var i = 0; i < l; i += 7){
865 var x1 = n[i + 5], y1 = n[i + 6];
870 var result = dojox.gfx.arc.arcAsBezier(
871 last, n[i], n[i + 1], n[i + 2],
872 n[i + 3] ? 1 : 0, n[i + 4] ? 1 : 0,
875 for(var j = 0; j < result.length; ++j){
877 this._addArgs(p, result[j]);
879 last = {x: x1, y: y1};
881 this.lastControl = {};
884 _closePath: function(){
885 this.lastControl = {};
889 dojox.gfx.Path.nodeType = "shape";
891 dojo.declare("dojox.gfx.TextPath", dojox.gfx.Path, {
892 // summary: a textpath shape (VML)
893 constructor: function(rawNode){
894 if(rawNode){rawNode.setAttribute("dojoGfxType", "textpath");}
895 this.fontStyle = null;
896 if(!("text" in this)){
897 this.text = dojo.clone(dojox.gfx.defaultTextPath);
899 if(!("fontStyle" in this)){
900 this.fontStyle = dojo.clone(dojox.gfx.defaultFont);
903 setText: function(newText){
904 // summary: sets a text to be drawn along the path
905 this.text = dojox.gfx.makeParameters(this.text,
906 typeof newText == "string" ? {text: newText} : newText);
910 setFont: function(newFont){
911 // summary: sets a font for text
912 this.fontStyle = typeof newFont == "string" ?
913 dojox.gfx.splitFontString(newFont) :
914 dojox.gfx.makeParameters(dojox.gfx.defaultFont, newFont);
919 _setText: function(){
920 // summary: sets a text shape object (VML)
922 var r = this.rawNode, s = this.text,
923 // find path and text path
924 p = null, t = null, c = r.childNodes;
925 for(var i = 0; i < c.length; ++i){
926 var tag = c[i].tagName;
930 }else if(tag == "textpath"){
936 p = this.rawNode.ownerDocument.createElement("v:path");
940 t = this.rawNode.ownerDocument.createElement("v:textpath");
945 var a = dojox.gfx.vml.text_alignment[s.align];
946 t.style["v-text-align"] = a ? a : "left";
947 t.style["text-decoration"] = s.decoration;
948 t.style["v-rotate-letters"] = s.rotated;
949 t.style["v-text-kern"] = s.kerning;
952 _setFont: function(){
953 // summary: sets a font object (VML)
954 var f = this.fontStyle, c = this.rawNode.childNodes;
955 for(var i = 0; i < c.length; ++i){
956 if(c[i].tagName == "textpath"){
957 c[i].style.font = dojox.gfx.makeFontString(f);
963 dojox.gfx.TextPath.nodeType = "shape";
965 dojo.declare("dojox.gfx.Surface", dojox.gfx.shape.Surface, {
966 // summary: a surface object to be used for drawings (VML)
967 constructor: function(){
968 dojox.gfx.vml.Container._init.call(this);
970 setDimensions: function(width, height){
971 // summary: sets the width and height of the rawNode
972 // width: String: width of surface, e.g., "100px"
973 // height: String: height of surface, e.g., "100px"
974 this.width = dojox.gfx.normalizedLength(width); // in pixels
975 this.height = dojox.gfx.normalizedLength(height); // in pixels
976 if(!this.rawNode) return this;
977 var cs = this.clipNode.style,
978 r = this.rawNode, rs = r.style,
979 bs = this.bgNode.style;
982 cs.clip = "rect(0 " + width + " " + height + " 0)";
985 r.coordsize = width + " " + height;
990 getDimensions: function(){
991 // summary: returns an object with properties "width" and "height"
992 var t = this.rawNode ? {
993 width: dojox.gfx.normalizedLength(this.rawNode.style.width),
994 height: dojox.gfx.normalizedLength(this.rawNode.style.height)} : null;
995 if(t.width <= 0){ t.width = this.width; }
996 if(t.height <= 0){ t.height = this.height; }
1001 dojox.gfx.createSurface = function(parentNode, width, height){
1002 // summary: creates a surface (VML)
1003 // parentNode: Node: a parent node
1004 // width: String: width of surface, e.g., "100px"
1005 // height: String: height of surface, e.g., "100px"
1007 if(!width){ width = "100%"; }
1008 if(!height){ height = "100%"; }
1009 var s = new dojox.gfx.Surface(), p = dojo.byId(parentNode),
1010 c = s.clipNode = p.ownerDocument.createElement("div"),
1011 r = s.rawNode = p.ownerDocument.createElement("v:group"),
1012 cs = c.style, rs = r.style;
1014 p.style.width = width;
1015 p.style.height = height;
1017 cs.position = "absolute";
1020 cs.clip = "rect(0 " + width + " " + height + " 0)";
1021 rs.position = "absolute";
1024 r.coordsize = (width == "100%" ? width : parseFloat(width)) + " " +
1025 (height == "100%" ? height : parseFloat(height));
1026 r.coordorigin = "0 0";
1028 // create a background rectangle, which is required to show all other shapes
1029 var b = s.bgNode = r.ownerDocument.createElement("v:rect"), bs = b.style;
1030 bs.left = bs.top = 0;
1031 bs.width = rs.width;
1032 bs.height = rs.height;
1033 b.filled = b.stroked = "f";
1039 s.width = dojox.gfx.normalizedLength(width); // in pixels
1040 s.height = dojox.gfx.normalizedLength(height); // in pixels
1042 return s; // dojox.gfx.Surface
1047 dojox.gfx.vml.Container = {
1049 dojox.gfx.shape.Container._init.call(this);
1051 add: function(shape){
1052 // summary: adds a shape to a group/surface
1053 // shape: dojox.gfx.Shape: an VML shape object
1054 if(this != shape.getParent()){
1055 this.rawNode.appendChild(shape.rawNode);
1056 //dojox.gfx.Group.superclass.add.apply(this, arguments);
1057 //this.inherited(arguments);
1058 dojox.gfx.shape.Container.add.apply(this, arguments);
1060 return this; // self
1062 remove: function(shape, silently){
1063 // summary: remove a shape from a group/surface
1064 // shape: dojox.gfx.Shape: an VML shape object
1065 // silently: Boolean?: if true, regenerate a picture
1066 if(this == shape.getParent()){
1067 if(this.rawNode == shape.rawNode.parentNode){
1068 this.rawNode.removeChild(shape.rawNode);
1070 //dojox.gfx.Group.superclass.remove.apply(this, arguments);
1071 //this.inherited(arguments);
1072 dojox.gfx.shape.Container.remove.apply(this, arguments);
1074 return this; // self
1077 // summary: removes all shapes from a group/surface
1078 var r = this.rawNode;
1079 while(r.firstChild != r.lastChild){
1080 if(r.firstChild != this.bgNode){
1081 r.removeChild(r.firstChild);
1083 if(r.lastChild != this.bgNode){
1084 r.removeChild(r.lastChild);
1087 //return this.inherited(arguments); // self
1088 return dojox.gfx.shape.Container.clear.apply(this, arguments);
1090 _moveChildToFront: dojox.gfx.shape.Container._moveChildToFront,
1091 _moveChildToBack: dojox.gfx.shape.Container._moveChildToBack
1094 dojo.mixin(dojox.gfx.shape.Creator, {
1095 // summary: VML shape creators
1096 createGroup: function(){
1097 // summary: creates a VML group shape
1098 var g = this.createObject(dojox.gfx.Group, null); // dojox.gfx.Group
1099 // create a background rectangle, which is required to show all other shapes
1100 var r = g.rawNode.ownerDocument.createElement("v:rect");
1101 r.style.left = r.style.top = 0;
1102 r.style.width = g.rawNode.style.width;
1103 r.style.height = g.rawNode.style.height;
1104 r.filled = r.stroked = "f";
1105 g.rawNode.appendChild(r);
1107 return g; // dojox.gfx.Group
1109 createImage: function(image){
1110 // summary: creates a VML image shape
1111 // image: Object: an image object (see dojox.gfx.defaultImage)
1112 if(!this.rawNode) return null;
1113 var shape = new dojox.gfx.Image(), node = this.rawNode.ownerDocument.createElement('div');
1114 node.style.position = "absolute";
1115 node.style.width = this.rawNode.style.width;
1116 node.style.height = this.rawNode.style.height;
1117 //node.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=0, M21=0, M22=1, Dx=0, Dy=0)";
1118 var img = this.rawNode.ownerDocument.createElement('img');
1119 img.style.position = "relative";
1120 node.appendChild(img);
1121 shape.setRawNode(node);
1122 this.rawNode.appendChild(node);
1123 shape.setShape(image);
1125 return shape; // dojox.gfx.Image
1127 createObject: function(shapeType, rawShape) {
1128 // summary: creates an instance of the passed shapeType class
1129 // shapeType: Function: a class constructor to create an instance of
1130 // rawShape: Object: properties to be passed in to the classes "setShape" method
1131 // overrideSize: Boolean: set the size explicitly, if true
1132 if(!this.rawNode) return null;
1133 var shape = new shapeType(),
1134 node = this.rawNode.ownerDocument.createElement('v:' + shapeType.nodeType);
1135 shape.setRawNode(node);
1136 this.rawNode.appendChild(node);
1138 case dojox.gfx.Group:
1139 case dojox.gfx.Line:
1140 case dojox.gfx.Polyline:
1141 case dojox.gfx.Text:
1142 case dojox.gfx.Path:
1143 case dojox.gfx.TextPath:
1144 this._overrideSize(node);
1146 shape.setShape(rawShape);
1148 return shape; // dojox.gfx.Shape
1150 _overrideSize: function(node){
1152 while(p && !(p instanceof dojox.gfx.Surface)){ p = p.parent; }
1153 node.style.width = p.width;
1154 node.style.height = p.height;
1155 node.coordsize = p.width + " " + p.height;
1159 dojo.extend(dojox.gfx.Group, dojox.gfx.vml.Container);
1160 dojo.extend(dojox.gfx.Group, dojox.gfx.shape.Creator);
1162 dojo.extend(dojox.gfx.Surface, dojox.gfx.vml.Container);
1163 dojo.extend(dojox.gfx.Surface, dojox.gfx.shape.Creator);