Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 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");
4
 
5
dojo.require("dojox.gfx._base");
6
dojo.require("dojox.gfx.shape");
7
dojo.require("dojox.gfx.path");
8
dojo.require("dojox.gfx.arc");
9
 
10
// dojox.gfx.vml.xmlns: String: a VML's namespace
11
dojox.gfx.vml.xmlns = "urn:schemas-microsoft-com:vml";
12
 
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"};
15
 
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
20
};
21
 
22
dojox.gfx.vml._bool = {"t": 1, "true": 1};
23
 
24
dojo.extend(dojox.gfx.Shape, {
25
	// summary: VML-specific implementation of dojox.gfx.Shape methods
26
 
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,
33
		//	or dojo.Color)
34
 
35
		if(!fill){
36
			// don't fill
37
			this.fillStyle = null;
38
			this.rawNode.filled = "f";
39
			return this;
40
		}
41
		if(typeof fill == "object" && "type" in fill){
42
			// gradient
43
			switch(fill.type){
44
				case "linear":
45
					var f = dojox.gfx.makeParameters(dojox.gfx.defaultLinearGradient, fill),
46
						s = [], a = f.colors, matrix = this._getRealMatrix(), m = dojox.gfx.matrix;
47
					this.fillStyle = f;
48
					dojo.forEach(a, function(v, i, a){
49
						a[i].color = dojox.gfx.normalizeColor(v.color);
50
					});
51
					if(a[0].offset > 0){
52
						s.push("0 " + a[0].color.toHex());
53
					}
54
					for(var i = 0; i < a.length; ++i){
55
						s.push(a[i].offset.toFixed(8) + " " + a[i].color.toHex());
56
					}
57
					var i = a.length - 1;
58
					if(a[i].offset < 1){
59
						s.push("1 " + a[i].color.toHex());
60
					}
61
					var fo = this.rawNode.fill;
62
					fo.colors.value = s.join(";");
63
					fo.method = "sigma";
64
					fo.type = "gradient";
65
					var fc1 = matrix ? m.multiplyPoint(matrix, f.x1, f.y1) : {x: f.x1, y: f.y1},
66
						fc2 = matrix ? m.multiplyPoint(matrix, f.x2, f.y2) : {x: f.x2, y: f.y2};
67
					fo.angle = (m._radToDeg(Math.atan2(fc2.x - fc1.x, fc2.y - fc1.y)) + 180) % 360;
68
					fo.on = true;
69
					break;
70
				case "radial":
71
					var f = dojox.gfx.makeParameters(dojox.gfx.defaultRadialGradient, fill);
72
					this.fillStyle = f;
73
					var l = parseFloat(this.rawNode.style.left),
74
						t = parseFloat(this.rawNode.style.top),
75
						w = parseFloat(this.rawNode.style.width),
76
						h = parseFloat(this.rawNode.style.height),
77
						c = isNaN(w) ? 1 : 2 * f.r / w,
78
						a = new Array(f.colors.length);
79
					// massage colors
80
					dojo.forEach(f.colors, function(v, i){
81
						a[i] = {offset: 1 - v.offset * c, color: dojox.gfx.normalizeColor(v.color)};
82
					});
83
					var i = a.length - 1;
84
					while(i >= 0 && a[i].offset < 0){ --i; }
85
					if(i < a.length - 1){
86
						// correct excessive colors
87
						var q = a[i], p = a[i + 1];
88
						p.color = dojo.blendColors(q.color, p.color, q.offset / (q.offset - p.offset));
89
						p.offset = 0;
90
						while(a.length - i > 2) a.pop();
91
					}
92
					// set colors
93
					var i = a.length - 1, s = [];
94
					if(a[i].offset > 0){
95
						s.push("0 " + a[i].color.toHex());
96
					}
97
					for(; i >= 0; --i){
98
						s.push(a[i].offset.toFixed(8) + " " + a[i].color.toHex());
99
					}
100
					if(a[0].offset < 1){
101
						s.push("1 " + a[0].color.toHex());
102
					}
103
					var fo = this.rawNode.fill;
104
					fo.colors.value = s.join(";");
105
					fo.method = "sigma";
106
					fo.type = "gradientradial";
107
					if(isNaN(w) || isNaN(h) || isNaN(l) || isNaN(t)){
108
						fo.focusposition = "0.5 0.5";
109
					}else{
110
						fo.focusposition = ((f.cx - l) / w).toFixed(8) + " " + ((f.cy - t) / h).toFixed(8);
111
					}
112
					fo.focussize = "0 0";
113
					fo.on = true;
114
					break;
115
				case "pattern":
116
					var f = dojox.gfx.makeParameters(dojox.gfx.defaultPattern, fill);
117
					this.fillStyle = f;
118
					var fo = this.rawNode.fill;
119
					fo.type = "tile";
120
					fo.src = f.src;
121
					if(f.width && f.height){
122
						// in points
123
						fo.size.x = dojox.gfx.px2pt(f.width);
124
						fo.size.y = dojox.gfx.px2pt(f.height);
125
					}
126
					fo.alignShape = "f";
127
					fo.position.x = 0;
128
					fo.position.y = 0;
129
					fo.origin.x = f.width  ? f.x / f.width  : 0;
130
					fo.origin.y = f.height ? f.y / f.height : 0;
131
					fo.on = true;
132
					break;
133
			}
134
			this.rawNode.fill.opacity = 1;
135
			return this;
136
		}
137
		// color object
138
		this.fillStyle = dojox.gfx.normalizeColor(fill);
139
		this.rawNode.fillcolor = this.fillStyle.toHex();
140
		this.rawNode.fill.opacity = this.fillStyle.a;
141
		this.rawNode.filled = true;
142
		return this;	// self
143
	},
144
 
145
	setStroke: function(stroke){
146
		// summary: sets a stroke object (VML)
147
		// stroke: Object: a stroke object
148
		//	(see dojox.gfx.defaultStroke)
149
 
150
		if(!stroke){
151
			// don't stroke
152
			this.strokeStyle = null;
153
			this.rawNode.stroked = "f";
154
			return this;
155
		}
156
		// normalize the stroke
157
		if(typeof stroke == "string"){
158
			stroke = {color: stroke};
159
		}
160
		var s = this.strokeStyle = dojox.gfx.makeParameters(dojox.gfx.defaultStroke, stroke);
161
		s.color = dojox.gfx.normalizeColor(s.color);
162
		// generate attributes
163
		var rn = this.rawNode;
164
		rn.stroked = true;
165
		rn.strokecolor = s.color.toCss();
166
		rn.strokeweight = s.width + "px";	// TODO: should we assume that the width is always in pixels?
167
		if(rn.stroke) {
168
			rn.stroke.opacity = s.color.a;
169
			rn.stroke.endcap = this._translate(this._capMap, s.cap);
170
			if(typeof s.join == "number") {
171
				rn.stroke.joinstyle = "miter";
172
				rn.stroke.miterlimit = s.join;
173
			}else{
174
				rn.stroke.joinstyle = s.join;
175
				// rn.stroke.miterlimit = s.width;
176
			}
177
			rn.stroke.dashstyle = s.style == "none" ? "Solid" : s.style;
178
		}
179
		return this;	// self
180
	},
181
 
182
	_capMap: { butt: 'flat' },
183
	_capMapReversed: { flat: 'butt' },
184
 
185
	_translate: function(dict, value) {
186
		return (value in dict) ? dict[value] : value;
187
	},
188
 
189
	_applyTransform: function() {
190
		if(this.fillStyle && this.fillStyle.type == "linear"){
191
			this.setFill(this.fillStyle);
192
		}
193
		var matrix = this._getRealMatrix();
194
		if(!matrix) return this;
195
		var skew = this.rawNode.skew;
196
		if(typeof skew == "undefined"){
197
			for(var i = 0; i < this.rawNode.childNodes.length; ++i){
198
				if(this.rawNode.childNodes[i].tagName == "skew"){
199
					skew = this.rawNode.childNodes[i];
200
					break;
201
				}
202
			}
203
		}
204
		if(skew){
205
			skew.on = "f";
206
			var mt = matrix.xx.toFixed(8) + " " + matrix.xy.toFixed(8) + " " +
207
				matrix.yx.toFixed(8) + " " + matrix.yy.toFixed(8) + " 0 0",
208
				offset = Math.floor(matrix.dx).toFixed() + "px " + Math.floor(matrix.dy).toFixed() + "px",
209
				s = this.rawNode.style,
210
				l = parseFloat(s.left),
211
				t = parseFloat(s.top),
212
				w = parseFloat(s.width),
213
				h = parseFloat(s.height);
214
			if(isNaN(l)) l = 0;
215
			if(isNaN(t)) t = 0;
216
			if(isNaN(w)) w = 1;
217
			if(isNaN(h)) h = 1;
218
			var origin = (-l / w - 0.5).toFixed(8) + " " + (-t / h - 0.5).toFixed(8);
219
			skew.matrix =  mt;
220
			skew.origin = origin;
221
			skew.offset = offset;
222
			skew.on = true;
223
		}
224
		return this;
225
	},
226
 
227
	setRawNode: function(rawNode){
228
		// summary:
229
		//	assigns and clears the underlying node that will represent this
230
		//	shape. Once set, transforms, gradients, etc, can be applied.
231
		//	(no fill & stroke by default)
232
		rawNode.stroked = "f";
233
		rawNode.filled  = "f";
234
		this.rawNode = rawNode;
235
	},
236
 
237
	// move family
238
 
239
	_moveToFront: function(){
240
		// summary: moves a shape to front of its parent's list of shapes (VML)
241
		this.rawNode.parentNode.appendChild(this.rawNode);
242
		return this;
243
	},
244
	_moveToBack: function(){
245
		// summary: moves a shape to back of its parent's list of shapes (VML)
246
		var r = this.rawNode, p = r.parentNode, n = p.firstChild;
247
		p.insertBefore(r, n);
248
		if(n.tagName == "rect"){
249
			// surface has a background rectangle, which position should be preserved
250
			n.swapNode(r);
251
		}
252
		return this;
253
	},
254
 
255
	_getRealMatrix: function(){
256
		// summary: returns the cumulative ("real") transformation matrix
257
		//	by combining the shape's matrix with its parent's matrix
258
		return this.parentMatrix ? new dojox.gfx.Matrix2D([this.parentMatrix, this.matrix]) : this.matrix;	// dojox.gfx.Matrix2D
259
	}
260
});
261
 
262
dojo.declare("dojox.gfx.Group", dojox.gfx.Shape, {
263
	// summary: a group shape (VML), which can be used
264
	//	to logically group shapes (e.g, to propagate matricies)
265
	constructor: function(){
266
		dojox.gfx.vml.Container._init.call(this);
267
	},
268
	// apply transformation
269
	_applyTransform: function(){
270
		// summary: applies a transformation matrix to a group
271
		var matrix = this._getRealMatrix();
272
		for(var i = 0; i < this.children.length; ++i){
273
			this.children[i]._updateParentMatrix(matrix);
274
		}
275
		return this;	// self
276
	}
277
});
278
dojox.gfx.Group.nodeType = "group";
279
 
280
dojo.declare("dojox.gfx.Rect", dojox.gfx.shape.Rect, {
281
	// summary: a rectangle shape (VML)
282
	setShape: function(newShape){
283
		// summary: sets a rectangle shape object (VML)
284
		// newShape: Object: a rectangle shape object
285
		var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
286
		this.bbox = null;
287
		var style = this.rawNode.style;
288
		style.left   = shape.x.toFixed();
289
		style.top    = shape.y.toFixed();
290
		style.width  = (typeof shape.width == "string" && shape.width.indexOf("%") >= 0)  ? shape.width  : shape.width.toFixed();
291
		style.height = (typeof shape.width == "string" && shape.height.indexOf("%") >= 0) ? shape.height : shape.height.toFixed();
292
		var r = Math.min(1, (shape.r / Math.min(parseFloat(shape.width), parseFloat(shape.height)))).toFixed(8);
293
		// a workaround for the VML's arcsize bug: cannot read arcsize of an instantiated node
294
		var parent = this.rawNode.parentNode, before = null;
295
		if(parent){
296
			if(parent.lastChild != this.rawNode){
297
				for(var i = 0; i < parent.childNodes.length; ++i){
298
					if(parent.childNodes[i] == this.rawNode){
299
						before = parent.childNodes[i+1];
300
						break;
301
					}
302
				}
303
			}
304
			parent.removeChild(this.rawNode);
305
		}
306
		this.rawNode.arcsize = r;
307
		if(parent){
308
			if(before){
309
				parent.insertBefore(this.rawNode, before);
310
			}else{
311
				parent.appendChild(this.rawNode);
312
			}
313
		}
314
		// set all necessary styles, which are lost by VML (yes, it's a VML's bug)
315
		return this.setTransform(this.matrix).setFill(this.fillStyle).setStroke(this.strokeStyle);	// self
316
	}
317
});
318
dojox.gfx.Rect.nodeType = "roundrect"; // use a roundrect so the stroke join type is respected
319
 
320
dojo.declare("dojox.gfx.Ellipse", dojox.gfx.shape.Ellipse, {
321
	// summary: an ellipse shape (VML)
322
	setShape: function(newShape){
323
		// summary: sets an ellipse shape object (VML)
324
		// newShape: Object: an ellipse shape object
325
		var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
326
		this.bbox = null;
327
		var style = this.rawNode.style;
328
		style.left   = (shape.cx - shape.rx).toFixed();
329
		style.top    = (shape.cy - shape.ry).toFixed();
330
		style.width  = (shape.rx * 2).toFixed();
331
		style.height = (shape.ry * 2).toFixed();
332
		return this.setTransform(this.matrix);	// self
333
	}
334
});
335
dojox.gfx.Ellipse.nodeType = "oval";
336
 
337
dojo.declare("dojox.gfx.Circle", dojox.gfx.shape.Circle, {
338
	// summary: a circle shape (VML)
339
	setShape: function(newShape){
340
		// summary: sets a circle shape object (VML)
341
		// newShape: Object: a circle shape object
342
		var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
343
		this.bbox = null;
344
		var style = this.rawNode.style;
345
		style.left   = (shape.cx - shape.r).toFixed();
346
		style.top    = (shape.cy - shape.r).toFixed();
347
		style.width  = (shape.r * 2).toFixed();
348
		style.height = (shape.r * 2).toFixed();
349
		return this;	// self
350
	}
351
});
352
dojox.gfx.Circle.nodeType = "oval";
353
 
354
dojo.declare("dojox.gfx.Line", dojox.gfx.shape.Line, {
355
	// summary: a line shape (VML)
356
	constructor: function(rawNode){
357
		if(rawNode) rawNode.setAttribute("dojoGfxType", "line");
358
	},
359
	setShape: function(newShape){
360
		// summary: sets a line shape object (VML)
361
		// newShape: Object: a line shape object
362
		var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
363
		this.bbox = null;
364
		this.rawNode.path.v = "m" + shape.x1.toFixed() + " " + shape.y1.toFixed() +
365
			"l" + shape.x2.toFixed() + " " + shape.y2.toFixed() + "e";
366
		return this.setTransform(this.matrix);	// self
367
	}
368
});
369
dojox.gfx.Line.nodeType = "shape";
370
 
371
dojo.declare("dojox.gfx.Polyline", dojox.gfx.shape.Polyline, {
372
	// summary: a polyline/polygon shape (VML)
373
	constructor: function(rawNode){
374
		if(rawNode) rawNode.setAttribute("dojoGfxType", "polyline");
375
	},
376
	setShape: function(points, closed){
377
		// summary: sets a polyline/polygon shape object (VML)
378
		// points: Object: a polyline/polygon shape object
379
		// closed: Boolean?: if true, close the polyline explicitely
380
		if(points && points instanceof Array){
381
			// branch
382
			// points: Array: an array of points
383
			this.shape = dojox.gfx.makeParameters(this.shape, { points: points });
384
			if(closed && this.shape.points.length) this.shape.points.push(this.shape.points[0]);
385
		}else{
386
			this.shape = dojox.gfx.makeParameters(this.shape, points);
387
		}
388
		this.bbox = null;
389
		var attr = [], p = this.shape.points;
390
		if(p.length > 0){
391
			attr.push("m");
392
			var k = 1;
393
			if(typeof p[0] == "number"){
394
				attr.push(p[0].toFixed());
395
				attr.push(p[1].toFixed());
396
				k = 2;
397
			}else{
398
				attr.push(p[0].x.toFixed());
399
				attr.push(p[0].y.toFixed());
400
			}
401
			if(p.length > k){
402
				attr.push("l");
403
				for(var i = k; i < p.length; ++i){
404
					if(typeof p[i] == "number"){
405
						attr.push(p[i].toFixed());
406
					}else{
407
						attr.push(p[i].x.toFixed());
408
						attr.push(p[i].y.toFixed());
409
					}
410
				}
411
			}
412
		}
413
		attr.push("e");
414
		this.rawNode.path.v = attr.join(" ");
415
		return this.setTransform(this.matrix);	// self
416
	}
417
});
418
dojox.gfx.Polyline.nodeType = "shape";
419
 
420
dojo.declare("dojox.gfx.Image", dojox.gfx.shape.Image, {
421
	// summary: an image (VML)
422
	constructor: function(rawNode){
423
		if(rawNode) rawNode.setAttribute("dojoGfxType", "image");
424
	},
425
	getEventSource: function() {
426
		// summary: returns a Node, which is used as
427
		//	a source of events for this shape
428
		return this.rawNode ? this.rawNode.firstChild : null;	// Node
429
	},
430
	setShape: function(newShape){
431
		// summary: sets an image shape object (VML)
432
		// newShape: Object: an image shape object
433
		var shape = this.shape = dojox.gfx.makeParameters(this.shape, newShape);
434
		this.bbox = null;
435
		var firstChild = this.rawNode.firstChild;
436
        firstChild.src = shape.src;
437
        if(shape.width || shape.height){
438
			firstChild.style.width  = shape.width;
439
			firstChild.style.height = shape.height;
440
        }
441
		return this.setTransform(this.matrix);	// self
442
	},
443
	_applyTransform: function() {
444
		var matrix = this._getRealMatrix();
445
		if(!matrix) return this;
446
		matrix = dojox.gfx.matrix.multiply(matrix, {dx: this.shape.x, dy: this.shape.y});
447
		var f = this.rawNode.filters["DXImageTransform.Microsoft.Matrix"];
448
		f.M11 = matrix.xx;
449
		f.M12 = matrix.xy;
450
		f.M21 = matrix.yx;
451
		f.M22 = matrix.yy;
452
		f.Dx  = matrix.dx;
453
		f.Dy  = matrix.dy;
454
		return this;
455
	}
456
});
457
dojox.gfx.Image.nodeType = "div";
458
 
459
dojo.declare("dojox.gfx.Text", dojox.gfx.shape.Text, {
460
	// summary: an anchored text (VML)
461
	constructor: function(rawNode){
462
		if(rawNode){rawNode.setAttribute("dojoGfxType", "text");}
463
		this.fontStyle = null;
464
	},
465
	_alignment: {start: "left", middle: "center", end: "right"},
466
	setShape: function(newShape){
467
		// summary: sets a text shape object (VML)
468
		// newShape: Object: a text shape object
469
		this.shape = dojox.gfx.makeParameters(this.shape, newShape);
470
		this.bbox = null;
471
		var r = this.rawNode, s = this.shape, x = s.x, y = s.y.toFixed();
472
		switch(s.align){
473
			case "middle":
474
				x -= 5;
475
				break;
476
			case "end":
477
				x -= 10;
478
				break;
479
		}
480
		this.rawNode.path.v = "m" + x.toFixed() + "," + y +
481
			"l" + (x + 10).toFixed() + "," + y + "e";
482
		// find path and text path
483
		var p = null, t = null, c = r.childNodes;
484
		for(var i = 0; i < c.length; ++i){
485
			var tag = c[i].tagName;
486
			if(tag == "path"){
487
				p = c[i];
488
				if(t) break;
489
			}else if(tag == "textpath"){
490
				t = c[i];
491
				if(p) break;
492
			}
493
		}
494
		if(!p){
495
			p = this.rawNode.ownerDocument.createElement("v:path");
496
			r.appendChild(p);
497
		}
498
		if(!t){
499
			t = this.rawNode.ownerDocument.createElement("v:textpath");
500
			r.appendChild(t);
501
		}
502
		p.textPathOk = true;
503
		t.on = true;
504
		var a = dojox.gfx.vml.text_alignment[s.align];
505
		t.style["v-text-align"] = a ? a : "left";
506
		t.style["text-decoration"] = s.decoration;
507
		t.style["v-rotate-letters"] = s.rotated;
508
		t.style["v-text-kern"] = s.kerning;
509
		t.string = s.text;
510
		return this.setTransform(this.matrix);	// self
511
	},
512
	_setFont: function(){
513
		// summary: sets a font object (VML)
514
		var f = this.fontStyle, c = this.rawNode.childNodes;
515
		for(var i = 0; i < c.length; ++i){
516
			if(c[i].tagName == "textpath"){
517
				c[i].style.font = dojox.gfx.makeFontString(f);
518
				break;
519
			}
520
		}
521
		this.setTransform(this.matrix);
522
	},
523
	_getRealMatrix: function(){
524
		// summary: returns the cumulative ("real") transformation matrix
525
		//	by combining the shape's matrix with its parent's matrix;
526
		//	it makes a correction for a font size
527
		var matrix = dojox.gfx.Shape.prototype._getRealMatrix.call(this);
528
		// It appears that text is always aligned vertically at a middle of x-height (???).
529
		// It is impossible to obtain these metrics from VML => I try to approximate it with
530
		// more-or-less util value of 0.7 * FontSize, which is typical for European fonts.
531
		if(matrix){
532
			matrix = dojox.gfx.matrix.multiply(matrix,
533
				{dy: -dojox.gfx.normalizedLength(this.fontStyle ? this.fontStyle.size : "10pt") * 0.35});
534
		}
535
		return matrix;	// dojox.gfx.Matrix2D
536
	},
537
	getTextWidth: function(){
538
		// summary: get the text width, in px
539
		var rawNode = this.rawNode, _display = rawNode.style.display;
540
		rawNode.style.display = "inline";
541
		var _width = dojox.gfx.pt2px(parseFloat(rawNode.currentStyle.width));
542
		rawNode.style.display = _display;
543
		return _width;
544
	}
545
});
546
dojox.gfx.Text.nodeType = "shape";
547
 
548
dojox.gfx.path._calcArc = function(alpha){
549
	// return a start point, 1st and 2nd control points, and an end point
550
	var cosa  = Math.cos(alpha), sina  = Math.sin(alpha),
551
		p2 = {x: cosa + (4 / 3) * (1 - cosa), y: sina - (4 / 3) * cosa * (1 - cosa) / sina};
552
	return {
553
		s:  {x: cosa, y: -sina},
554
		c1: {x: p2.x, y: -p2.y},
555
		c2: p2,
556
		e:  {x: cosa, y: sina}
557
	};
558
};
559
 
560
dojo.declare("dojox.gfx.Path", dojox.gfx.path.Path, {
561
	// summary: a path shape (VML)
562
	constructor: function(rawNode){
563
		if(rawNode && !rawNode.getAttribute("dojoGfxType")){
564
			rawNode.setAttribute("dojoGfxType", "path");
565
		}
566
		this.vmlPath = "";
567
		this.lastControl = {};
568
	},
569
	_updateWithSegment: function(segment){
570
		// summary: updates the bounding box of path with new segment
571
		// segment: Object: a segment
572
		var last = dojo.clone(this.last);
573
		dojox.gfx.Path.superclass._updateWithSegment.apply(this, arguments);
574
		// add a VML path segment
575
		var path = this[this.renderers[segment.action]](segment, last);
576
		if(typeof this.vmlPath == "string"){
577
			this.vmlPath += path.join("");
578
			this.rawNode.path.v = this.vmlPath + " r0,0 e";
579
		}else{
580
			this.vmlPath = this.vmlPath.concat(path);
581
		}
582
	},
583
	setShape: function(newShape){
584
		// summary: forms a path using a shape (VML)
585
		// newShape: Object: an VML path string or a path object (see dojox.gfx.defaultPath)
586
		this.vmlPath = [];
587
		this.lastControl = {};
588
		dojox.gfx.Path.superclass.setShape.apply(this, arguments);
589
		this.vmlPath = this.vmlPath.join("");
590
		this.rawNode.path.v = this.vmlPath + " r0,0 e";
591
		return this;
592
	},
593
	_pathVmlToSvgMap: {m: "M", l: "L", t: "m", r: "l", c: "C", v: "c", qb: "Q", x: "z", e: ""},
594
	// VML-specific segment renderers
595
	renderers: {
596
		M: "_moveToA", m: "_moveToR",
597
		L: "_lineToA", l: "_lineToR",
598
		H: "_hLineToA", h: "_hLineToR",
599
		V: "_vLineToA", v: "_vLineToR",
600
		C: "_curveToA", c: "_curveToR",
601
		S: "_smoothCurveToA", s: "_smoothCurveToR",
602
		Q: "_qCurveToA", q: "_qCurveToR",
603
		T: "_qSmoothCurveToA", t: "_qSmoothCurveToR",
604
		A: "_arcTo", a: "_arcTo",
605
		Z: "_closePath", z: "_closePath"
606
	},
607
	_addArgs: function(path, args, from, upto){
608
		if(typeof upto == "undefined"){
609
			upto = args.length;
610
		}
611
		if(typeof from == "undefined"){
612
			from = 0;
613
		}
614
		for(var i = from; i < upto; ++i){
615
			path.push(" ");
616
			path.push(args[i].toFixed());
617
		}
618
	},
619
	_addArgsAdjusted: function(path, last, args, from, upto){
620
		if(typeof upto == "undefined"){
621
			upto = args.length;
622
		}
623
		if(typeof from == "undefined"){
624
			from = 0;
625
		}
626
		for(var i = from; i < upto; i += 2){
627
			path.push(" ");
628
			path.push((last.x + args[i]).toFixed());
629
			path.push(" ");
630
			path.push((last.y + args[i + 1]).toFixed());
631
		}
632
	},
633
	_moveToA: function(segment){
634
		var p = [" m"], n = segment.args, l = n.length;
635
		if(l == 2){
636
			this._addArgs(p, n);
637
		}else{
638
			this._addArgs(p, n, 0, 2);
639
			p.push(" l");
640
			this._addArgs(p, n, 2);
641
		}
642
		this.lastControl = {};
643
		return p;
644
	},
645
	_moveToR: function(segment, last){
646
		var p = ["x" in last ? " t" : " m"], n = segment.args, l = n.length;
647
		if(l == 2){
648
			this._addArgs(p, n);
649
		}else{
650
			this._addArgs(p, n, 0, 2);
651
			p.push(" r");
652
			this._addArgs(p, n, 2);
653
		}
654
		this.lastControl = {};
655
		return p;
656
	},
657
	_lineToA: function(segment){
658
		var p = [" l"];
659
		this._addArgs(p, segment.args);
660
		this.lastControl = {};
661
		return p;
662
	},
663
	_lineToR: function(segment){
664
		var p = [" r"];
665
		this._addArgs(p, segment.args);
666
		this.lastControl = {};
667
		return p;
668
	},
669
	_hLineToA: function(segment, last){
670
		var p = [" l"], n = segment.args, l = n.length, y = " " + last.y.toFixed();
671
		for(var i = 0; i < l; ++i){
672
			p.push(" ");
673
			p.push(n[i].toFixed());
674
			p.push(y);
675
		}
676
		this.lastControl = {};
677
		return p;
678
	},
679
	_hLineToR: function(segment){
680
		var p = [" r"], n = segment.args, l = n.length;
681
		for(var i = 0; i < l; ++i){
682
			p.push(" ");
683
			p.push(n[i].toFixed());
684
			p.push(" 0");
685
		}
686
		this.lastControl = {};
687
		return p;
688
	},
689
	_vLineToA: function(segment, last){
690
		var p = [" l"], n = segment.args, l = n.length, x = " " + last.x.toFixed();
691
		for(var i = 0; i < l; ++i){
692
			p.push(x);
693
			p.push(" ");
694
			p.push(n[i].toFixed());
695
		}
696
		this.lastControl = {};
697
		return p;
698
	},
699
	_vLineToR: function(segment){
700
		var p = [" r"], n = segment.args, l = n.length;
701
		for(var i = 0; i < l; ++i){
702
			p.push(" 0 ");
703
			p.push(n[i].toFixed());
704
		}
705
		this.lastControl = {};
706
		return p;
707
	},
708
	_curveToA: function(segment){
709
		var p = [], n = segment.args, l = n.length;
710
		for(var i = 0; i < l; i += 6){
711
			p.push(" c");
712
			this._addArgs(p, n, i, i + 6);
713
		}
714
		this.lastControl = {x: n[l - 4], y: n[l - 3], type: "C"};
715
		return p;
716
	},
717
	_curveToR: function(segment, last){
718
		var p = [], n = segment.args, l = n.length;
719
		for(var i = 0; i < l; i += 6){
720
			p.push(" v");
721
			this._addArgs(p, n, i, i + 6);
722
			this.lastControl = {x: last.x + n[i + 2], y: last.y + n[i + 3]};
723
			last.x += n[i + 4];
724
			last.y += n[i + 5];
725
		}
726
		this.lastControl.type = "C";
727
		return p;
728
	},
729
	_smoothCurveToA: function(segment, last){
730
		var p = [], n = segment.args, l = n.length;
731
		for(var i = 0; i < l; i += 4){
732
			p.push(" c");
733
			if(this.lastControl.type == "C"){
734
				this._addArgs(p, [
735
					2 * last.x - this.lastControl.x,
736
					2 * last.y - this.lastControl.y
737
				]);
738
			}else{
739
				this._addArgs(p, [last.x, last.y]);
740
			}
741
			this._addArgs(p, n, i, i + 4);
742
		}
743
		this.lastControl = {x: n[l - 4], y: n[l - 3], type: "C"};
744
		return p;
745
	},
746
	_smoothCurveToR: function(segment, last){
747
		var p = [], n = segment.args, l = n.length;
748
		for(var i = 0; i < l; i += 4){
749
			p.push(" v");
750
			if(this.lastControl.type == "C"){
751
				this._addArgs(p, [
752
					last.x - this.lastControl.x,
753
					last.y - this.lastControl.y
754
				]);
755
			}else{
756
				this._addArgs(p, [0, 0]);
757
			}
758
			this._addArgs(p, n, i, i + 4);
759
			this.lastControl = {x: last.x + n[i], y: last.y + n[i + 1]};
760
			last.x += n[i + 2];
761
			last.y += n[i + 3];
762
		}
763
		this.lastControl.type = "C";
764
		return p;
765
	},
766
	_qCurveToA: function(segment){
767
		var p = [], n = segment.args, l = n.length;
768
		for(var i = 0; i < l; i += 4){
769
			p.push(" qb");
770
			this._addArgs(p, n, i, i + 4);
771
		}
772
		this.lastControl = {x: n[l - 4], y: n[l - 3], type: "Q"};
773
		return p;
774
	},
775
	_qCurveToR: function(segment, last){
776
		var p = [], n = segment.args, l = n.length;
777
		for(var i = 0; i < l; i += 4){
778
			p.push(" qb");
779
			this._addArgsAdjusted(p, last, n, i, i + 4);
780
			this.lastControl = {x: last.x + n[i], y: last.y + n[i + 1]};
781
			last.x += n[i + 2];
782
			last.y += n[i + 3];
783
		}
784
		this.lastControl.type = "Q";
785
		return p;
786
	},
787
	_qSmoothCurveToA: function(segment, last){
788
		var p = [], n = segment.args, l = n.length;
789
		for(var i = 0; i < l; i += 2){
790
			p.push(" qb");
791
			if(this.lastControl.type == "Q"){
792
				this._addArgs(p, [
793
					this.lastControl.x = 2 * last.x - this.lastControl.x,
794
					this.lastControl.y = 2 * last.y - this.lastControl.y
795
				]);
796
			}else{
797
				this._addArgs(p, [
798
					this.lastControl.x = last.x,
799
					this.lastControl.y = last.y
800
				]);
801
			}
802
			this._addArgs(p, n, i, i + 2);
803
		}
804
		this.lastControl.type = "Q";
805
		return p;
806
	},
807
	_qSmoothCurveToR: function(segment, last){
808
		var p = [], n = segment.args, l = n.length;
809
		for(var i = 0; i < l; i += 2){
810
			p.push(" qb");
811
			if(this.lastControl.type == "Q"){
812
				this._addArgs(p, [
813
					this.lastControl.x = 2 * last.x - this.lastControl.x,
814
					this.lastControl.y = 2 * last.y - this.lastControl.y
815
				]);
816
			}else{
817
				this._addArgs(p, [
818
					this.lastControl.x = last.x,
819
					this.lastControl.y = last.y
820
				]);
821
			}
822
			this._addArgsAdjusted(p, last, n, i, i + 2);
823
		}
824
		this.lastControl.type = "Q";
825
		return p;
826
	},
827
	_arcTo: function(segment, last){
828
		var p = [], n = segment.args, l = n.length, relative = segment.action == "a";
829
		for(var i = 0; i < l; i += 7){
830
			var x1 = n[i + 5], y1 = n[i + 6];
831
			if(relative){
832
				x1 += last.x;
833
				y1 += last.y;
834
			}
835
			var result = dojox.gfx.arc.arcAsBezier(
836
				last, n[i], n[i + 1], n[i + 2],
837
				n[i + 3] ? 1 : 0, n[i + 4] ? 1 : 0,
838
				x1, y1
839
			);
840
			for(var j = 0; j < result.length; ++j){
841
				p.push(" c");
842
				this._addArgs(p, result[j]);
843
			}
844
			last = {x: x1, y: y1};
845
		}
846
		this.lastControl = {};
847
		return p;
848
	},
849
	_closePath: function(){
850
		this.lastControl = {};
851
		return ["x"];
852
	}
853
});
854
dojox.gfx.Path.nodeType = "shape";
855
 
856
dojo.declare("dojox.gfx.TextPath", dojox.gfx.Path, {
857
	// summary: a textpath shape (VML)
858
	constructor: function(rawNode){
859
		if(rawNode){rawNode.setAttribute("dojoGfxType", "textpath");}
860
		this.fontStyle = null;
861
		if(!("text" in this)){
862
			this.text = dojo.clone(dojox.gfx.defaultTextPath);
863
		}
864
		if(!("fontStyle" in this)){
865
			this.fontStyle = dojo.clone(dojox.gfx.defaultFont);
866
		}
867
	},
868
	setText: function(newText){
869
		// summary: sets a text to be drawn along the path
870
		this.text = dojox.gfx.makeParameters(this.text,
871
			typeof newText == "string" ? {text: newText} : newText);
872
		this._setText();
873
		return this;	// self
874
	},
875
	setFont: function(newFont){
876
		// summary: sets a font for text
877
		this.fontStyle = typeof newFont == "string" ?
878
			dojox.gfx.splitFontString(newFont) :
879
			dojox.gfx.makeParameters(dojox.gfx.defaultFont, newFont);
880
		this._setFont();
881
		return this;	// self
882
	},
883
 
884
	_setText: function(){
885
		// summary: sets a text shape object (VML)
886
		this.bbox = null;
887
		var r = this.rawNode, s = this.text,
888
			// find path and text path
889
			p = null, t = null, c = r.childNodes;
890
		for(var i = 0; i < c.length; ++i){
891
			var tag = c[i].tagName;
892
			if(tag == "path"){
893
				p = c[i];
894
				if(t) break;
895
			}else if(tag == "textpath"){
896
				t = c[i];
897
				if(p) break;
898
			}
899
		}
900
		if(!p){
901
			p = this.rawNode.ownerDocument.createElement("v:path");
902
			r.appendChild(p);
903
		}
904
		if(!t){
905
			t = this.rawNode.ownerDocument.createElement("v:textpath");
906
			r.appendChild(t);
907
		}
908
		p.textPathOk = true;
909
		t.on = true;
910
		var a = dojox.gfx.vml.text_alignment[s.align];
911
		t.style["v-text-align"] = a ? a : "left";
912
		t.style["text-decoration"] = s.decoration;
913
		t.style["v-rotate-letters"] = s.rotated;
914
		t.style["v-text-kern"] = s.kerning;
915
		t.string = s.text;
916
	},
917
	_setFont: function(){
918
		// summary: sets a font object (VML)
919
		var f = this.fontStyle, c = this.rawNode.childNodes;
920
		for(var i = 0; i < c.length; ++i){
921
			if(c[i].tagName == "textpath"){
922
				c[i].style.font = dojox.gfx.makeFontString(f);
923
				break;
924
			}
925
		}
926
	}
927
});
928
dojox.gfx.TextPath.nodeType = "shape";
929
 
930
dojo.declare("dojox.gfx.Surface", dojox.gfx.shape.Surface, {
931
	// summary: a surface object to be used for drawings (VML)
932
	constructor: function(){
933
		dojox.gfx.vml.Container._init.call(this);
934
	},
935
	setDimensions: function(width, height){
936
		// summary: sets the width and height of the rawNode
937
		// width: String: width of surface, e.g., "100px"
938
		// height: String: height of surface, e.g., "100px"
939
		this.width  = dojox.gfx.normalizedLength(width);	// in pixels
940
		this.height = dojox.gfx.normalizedLength(height);	// in pixels
941
		if(!this.rawNode) return this;
942
		var cs = this.clipNode.style,
943
			r = this.rawNode, rs = r.style,
944
			bs = this.bgNode.style;
945
		cs.width  = width;
946
		cs.height = height;
947
		cs.clip = "rect(0 " + width + " " + height + " 0)";
948
		rs.width = width;
949
		rs.height = height;
950
		r.coordsize = width + " " + height;
951
		bs.width = width;
952
		bs.height = height;
953
		return this;	// self
954
	},
955
	getDimensions: function(){
956
		// summary: returns an object with properties "width" and "height"
957
		var t = this.rawNode ? {
958
			width:  dojox.gfx.normalizedLength(this.rawNode.style.width),
959
			height: dojox.gfx.normalizedLength(this.rawNode.style.height)} : null;
960
		if(t.width  <= 0){ t.width  = this.width; }
961
		if(t.height <= 0){ t.height = this.height; }
962
		return t;	// Object
963
	}
964
});
965
 
966
dojox.gfx.createSurface = function(parentNode, width, height){
967
	// summary: creates a surface (VML)
968
	// parentNode: Node: a parent node
969
	// width: String: width of surface, e.g., "100px"
970
	// height: String: height of surface, e.g., "100px"
971
 
972
	if(!width){ width = "100%"; }
973
	if(!height){ height = "100%"; }
974
	var s = new dojox.gfx.Surface(), p = dojo.byId(parentNode),
975
		c = s.clipNode = p.ownerDocument.createElement("div"),
976
		r = s.rawNode = p.ownerDocument.createElement("v:group"),
977
		cs = c.style, rs = r.style;
978
 
979
	p.style.width  = width;
980
	p.style.height = height;
981
 
982
	cs.width  = width;
983
	cs.height = height;
984
	cs.clip = "rect(0 " + width + " " + height + " 0)";
985
	cs.position = "absolute";
986
	rs.width  = width;
987
	rs.height = height;
988
	r.coordsize = (width == "100%" ? width : parseFloat(width)) + " " +
989
		(height == "100%" ? height : parseFloat(height));
990
	r.coordorigin = "0 0";
991
 
992
	// create a background rectangle, which is required to show all other shapes
993
	var b = s.bgNode = r.ownerDocument.createElement("v:rect"), bs = b.style;
994
	bs.left = bs.top = 0;
995
	bs.width  = rs.width;
996
	bs.height = rs.height;
997
	b.filled = b.stroked = "f";
998
 
999
	r.appendChild(b);
1000
	c.appendChild(r);
1001
	p.appendChild(c);
1002
 
1003
	s.width  = dojox.gfx.normalizedLength(width);	// in pixels
1004
	s.height = dojox.gfx.normalizedLength(height);	// in pixels
1005
 
1006
	return s;	// dojox.gfx.Surface
1007
};
1008
 
1009
// Extenders
1010
 
1011
dojox.gfx.vml.Container = {
1012
	_init: function(){
1013
		dojox.gfx.shape.Container._init.call(this);
1014
	},
1015
	add: function(shape){
1016
		// summary: adds a shape to a group/surface
1017
		// shape: dojox.gfx.Shape: an VML shape object
1018
		if(this != shape.getParent()){
1019
			this.rawNode.appendChild(shape.rawNode);
1020
			//dojox.gfx.Group.superclass.add.apply(this, arguments);
1021
			//this.inherited(arguments);
1022
			dojox.gfx.shape.Container.add.apply(this, arguments);
1023
		}
1024
		return this;	// self
1025
	},
1026
	remove: function(shape, silently){
1027
		// summary: remove a shape from a group/surface
1028
		// shape: dojox.gfx.Shape: an VML shape object
1029
		// silently: Boolean?: if true, regenerate a picture
1030
		if(this == shape.getParent()){
1031
			if(this.rawNode == shape.rawNode.parentNode){
1032
				this.rawNode.removeChild(shape.rawNode);
1033
			}
1034
			//dojox.gfx.Group.superclass.remove.apply(this, arguments);
1035
			//this.inherited(arguments);
1036
			dojox.gfx.shape.Container.remove.apply(this, arguments);
1037
		}
1038
		return this;	// self
1039
	},
1040
	clear: function(){
1041
		// summary: removes all shapes from a group/surface
1042
		var r = this.rawNode;
1043
		while(r.firstChild != r.lastChild){
1044
			if(r.firstChild != this.bgNode){
1045
				r.removeChild(r.firstChild);
1046
			}
1047
			if(r.lastChild != this.bgNode){
1048
				r.removeChild(r.lastChild);
1049
			}
1050
		}
1051
		//return this.inherited(arguments);	// self
1052
		return dojox.gfx.shape.Container.clear.apply(this, arguments);
1053
	},
1054
	_moveChildToFront: dojox.gfx.shape.Container._moveChildToFront,
1055
	_moveChildToBack:  dojox.gfx.shape.Container._moveChildToBack
1056
};
1057
 
1058
dojo.mixin(dojox.gfx.shape.Creator, {
1059
	// summary: VML shape creators
1060
	createGroup: function(){
1061
		// summary: creates a VML group shape
1062
		var g = this.createObject(dojox.gfx.Group, null);	// dojox.gfx.Group
1063
		// create a background rectangle, which is required to show all other shapes
1064
		var r = g.rawNode.ownerDocument.createElement("v:rect");
1065
		r.style.left = r.style.top = 0;
1066
		r.style.width  = g.rawNode.style.width;
1067
		r.style.height = g.rawNode.style.height;
1068
		r.filled = r.stroked = "f";
1069
		g.rawNode.appendChild(r);
1070
		g.bgNode = r;
1071
		return g;	// dojox.gfx.Group
1072
	},
1073
	createImage: function(image){
1074
		// summary: creates a VML image shape
1075
		// image: Object: an image object (see dojox.gfx.defaultImage)
1076
		if(!this.rawNode) return null;
1077
		var shape = new dojox.gfx.Image(), node = this.rawNode.ownerDocument.createElement('div');
1078
		node.style.position = "absolute";
1079
		node.style.width  = this.rawNode.style.width;
1080
		node.style.height = this.rawNode.style.height;
1081
		node.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=0, M21=0, M22=1, Dx=0, Dy=0)";
1082
		var img  = this.rawNode.ownerDocument.createElement('img');
1083
		node.appendChild(img);
1084
		shape.setRawNode(node);
1085
		this.rawNode.appendChild(node);
1086
		shape.setShape(image);
1087
		this.add(shape);
1088
		return shape;	// dojox.gfx.Image
1089
	},
1090
	createObject: function(shapeType, rawShape) {
1091
		// summary: creates an instance of the passed shapeType class
1092
		// shapeType: Function: a class constructor to create an instance of
1093
		// rawShape: Object: properties to be passed in to the classes "setShape" method
1094
		// overrideSize: Boolean: set the size explicitly, if true
1095
		if(!this.rawNode) return null;
1096
		var shape = new shapeType(),
1097
			node = this.rawNode.ownerDocument.createElement('v:' + shapeType.nodeType);
1098
		shape.setRawNode(node);
1099
		this.rawNode.appendChild(node);
1100
		switch(shapeType){
1101
			case dojox.gfx.Group:
1102
			case dojox.gfx.Line:
1103
			case dojox.gfx.Polyline:
1104
			case dojox.gfx.Text:
1105
			case dojox.gfx.Path:
1106
			case dojox.gfx.TextPath:
1107
				this._overrideSize(node);
1108
		}
1109
		shape.setShape(rawShape);
1110
		this.add(shape);
1111
		return shape;	// dojox.gfx.Shape
1112
	},
1113
	_overrideSize: function(node){
1114
		var p = this;
1115
		for(; p && !(p instanceof dojox.gfx.Surface); p = p.parent);
1116
		node.style.width  = p.width;
1117
		node.style.height = p.height;
1118
		node.coordsize = p.width + " " + p.height;
1119
	}
1120
});
1121
 
1122
dojo.extend(dojox.gfx.Group, dojox.gfx.vml.Container);
1123
dojo.extend(dojox.gfx.Group, dojox.gfx.shape.Creator);
1124
 
1125
dojo.extend(dojox.gfx.Surface, dojox.gfx.vml.Container);
1126
dojo.extend(dojox.gfx.Surface, dojox.gfx.shape.Creator);
1127
 
1128
}