1 if(!dojo._hasResource["dojox.gfx.silverlight"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojox.gfx.silverlight"] = true;
3 dojo.provide("dojox.gfx.silverlight");
5 dojo.require("dojox.gfx._base");
6 dojo.require("dojox.gfx.shape");
7 dojo.require("dojox.gfx.path");
9 dojo.experimental("dojox.gfx.silverlight");
11 dojox.gfx.silverlight.dasharray = {
15 shortdashdot: [4, 1, 1, 1],
16 shortdashdotdot: [4, 1, 1, 1, 1, 1],
20 dashdot: [4, 3, 1, 3],
21 longdashdot: [8, 3, 1, 3],
22 longdashdotdot: [8, 3, 1, 3, 1, 3]
25 dojox.gfx.silverlight.fontweight = {
30 dojox.gfx.silverlight.caps = {butt: "Flat", round: "Round", square: "Square"};
31 dojox.gfx.silverlight.joins = {bevel: "Bevel", round: "Round"};
33 dojox.gfx.silverlight.fonts = {
34 serif: "Times New Roman",
35 times: "Times New Roman",
36 "sans-serif": "Arial",
38 monotone: "Courier New",
39 courier: "Courier New"
42 dojox.gfx.silverlight.hexColor = function(/*String|Array|dojo.Color*/ color){
43 // summary: converts a color object to a Silverlight hex color string (#aarrggbb)
44 var c = dojox.gfx.normalizeColor(color),
45 t = c.toHex(), a = Math.round(c.a * 255);
46 a = (a < 0 ? 0 : a > 255 ? 255 : a).toString(16);
47 return "#" + (a.length < 2 ? "0" + a : a) + t.slice(1); // String
50 dojo.extend(dojox.gfx.Shape, {
51 // summary: Silverlight-specific implementation of dojox.gfx.Shape methods
53 setFill: function(fill){
54 // summary: sets a fill object (Silverlight)
55 // fill: Object: a fill object
56 // (see dojox.gfx.defaultLinearGradient,
57 // dojox.gfx.defaultRadialGradient,
58 // dojox.gfx.defaultPattern,
61 var p = this.rawNode.getHost().content, r = this.rawNode, f;
64 this.fillStyle = null;
65 this._setFillAttr(null);
68 if(typeof(fill) == "object" && "type" in fill){
72 this.fillStyle = f = dojox.gfx.makeParameters(dojox.gfx.defaultLinearGradient, fill);
73 var lgb = p.createFromXaml("<LinearGradientBrush/>");
74 lgb.mappingMode = "Absolute";
75 lgb.startPoint = f.x1 + "," + f.y1;
76 lgb.endPoint = f.x2 + "," + f.y2;
77 dojo.forEach(f.colors, function(c){
78 var t = p.createFromXaml("<GradientStop/>");
80 t.color = dojox.gfx.silverlight.hexColor(c.color);
81 lgb.gradientStops.add(t);
83 this._setFillAttr(lgb);
86 this.fillStyle = f = dojox.gfx.makeParameters(dojox.gfx.defaultRadialGradient, fill);
87 var rgb = p.createFromXaml("<RadialGradientBrush/>"), w = r.width, h = r.height,
88 l = this.rawNode["Canvas.Left"], t = this.rawNode["Canvas.Top"];
89 rgb.center = (f.cx - l) / w + "," + (f.cy - t) / h;
90 rgb.radiusX = f.r / w;
91 rgb.radiusY = f.r / h;
92 dojo.forEach(f.colors, function(c){
93 var t = p.createFromXaml("<GradientStop/>");
95 t.color = dojox.gfx.silverlight.hexColor(c.color);
96 rgb.gradientStops.add(t);
98 this._setFillAttr(rgb);
101 // don't fill: Silverlight doesn't define TileBrush for some reason
102 this.fillStyle = null;
103 this._setFillAttr(null);
109 this.fillStyle = f = dojox.gfx.normalizeColor(fill);
110 var scb = p.createFromXaml("<SolidColorBrush/>");
111 scb.color = f.toHex();
113 this._setFillAttr(scb);
116 _setFillAttr: function(f){
117 this.rawNode.fill = f;
120 setStroke: function(stroke){
121 // summary: sets a stroke object (Silverlight)
122 // stroke: Object: a stroke object
123 // (see dojox.gfx.defaultStroke)
125 var p = this.rawNode.getHost().content, r = this.rawNode;
128 this.strokeStyle = null;
132 // normalize the stroke
133 if(typeof stroke == "string"){
134 stroke = {color: stroke};
136 var s = this.strokeStyle = dojox.gfx.makeParameters(dojox.gfx.defaultStroke, stroke);
137 s.color = dojox.gfx.normalizeColor(s.color);
138 // generate attributes
140 var scb = p.createFromXaml("<SolidColorBrush/>");
141 scb.color = s.color.toHex();
142 scb.opacity = s.color.a;
144 r.strokeThickness = s.width;
145 r.strokeStartLineCap = r.strokeEndLineCap = r.strokeDashCap =
146 dojox.gfx.silverlight.caps[s.cap];
147 if(typeof s.join == "number"){
148 r.strokeLineJoin = "Miter";
149 r.strokeMiterLimit = s.join;
151 r.strokeLineJoin = dojox.gfx.silverlight.joins[s.join];
153 var da = s.style.toLowerCase();
154 if(da in dojox.gfx.silverlight.dasharray){ da = dojox.gfx.silverlight.dasharray[da]; }
155 if(da instanceof Array){
158 for(var i = 0; i < da.length; ++i){
163 for(var i = 0; i < da.length; i += 2){
166 if(da[i] < 1){ da[i] = 1; }
168 for(var i = 1; i < da.length; i += 2){
173 r.strokeDashArray = da.join(",");
175 r.strokeDashArray = null;
181 _getParentSurface: function(){
182 var surface = this.parent;
183 for(; surface && !(surface instanceof dojox.gfx.Surface); surface = surface.parent);
187 _applyTransform: function() {
188 var tm = this.matrix, r = this.rawNode;
190 var p = this.rawNode.getHost().content,
191 m = p.createFromXaml("<MatrixTransform/>"),
192 mm = p.createFromXaml("<Matrix/>");
200 r.renderTransform = m;
202 r.renderTransform = null;
207 setRawNode: function(rawNode){
209 // assigns and clears the underlying node that will represent this
210 // shape. Once set, transforms, gradients, etc, can be applied.
211 // (no fill & stroke by default)
213 rawNode.stroke = null;
214 this.rawNode = rawNode;
219 _moveToFront: function(){
220 // summary: moves a shape to front of its parent's list of shapes (Silverlight)
221 var c = this.parent.rawNode.children, r = this.rawNode;
226 _moveToBack: function(){
227 // summary: moves a shape to back of its parent's list of shapes (Silverlight)
228 var c = this.parent.rawNode.children, r = this.rawNode;
235 dojo.declare("dojox.gfx.Group", dojox.gfx.Shape, {
236 // summary: a group shape (Silverlight), which can be used
237 // to logically group shapes (e.g, to propagate matricies)
238 constructor: function(){
239 dojox.gfx.silverlight.Container._init.call(this);
241 setRawNode: function(rawNode){
242 // summary: sets a raw Silverlight node to be used by this shape
243 // rawNode: Node: an Silverlight node
244 this.rawNode = rawNode;
247 dojox.gfx.Group.nodeType = "Canvas";
249 dojo.declare("dojox.gfx.Rect", dojox.gfx.shape.Rect, {
250 // summary: a rectangle shape (Silverlight)
251 setShape: function(newShape){
252 // summary: sets a rectangle shape object (Silverlight)
253 // newShape: Object: a rectangle shape object
254 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
256 var r = this.rawNode, n = this.shape;
257 r["Canvas.Left"] = n.x;
258 r["Canvas.Top"] = n.y;
261 r.radiusX = r.radiusY = n.r;
265 dojox.gfx.Rect.nodeType = "Rectangle";
267 dojo.declare("dojox.gfx.Ellipse", dojox.gfx.shape.Ellipse, {
268 // summary: an ellipse shape (Silverlight)
269 setShape: function(newShape){
270 // summary: sets an ellipse shape object (Silverlight)
271 // newShape: Object: an ellipse shape object
272 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
274 var r = this.rawNode, n = this.shape;
275 r["Canvas.Left"] = n.cx - n.rx;
276 r["Canvas.Top"] = n.cy - n.ry;
282 dojox.gfx.Ellipse.nodeType = "Ellipse";
284 dojo.declare("dojox.gfx.Circle", dojox.gfx.shape.Circle, {
285 // summary: a circle shape (Silverlight)
286 setShape: function(newShape){
287 // summary: sets a circle shape object (Silverlight)
288 // newShape: Object: a circle shape object
289 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
291 var r = this.rawNode, n = this.shape;
292 r["Canvas.Left"] = n.cx - n.r;
293 r["Canvas.Top"] = n.cy - n.r;
294 r.width = r.height = 2 * n.r;
298 dojox.gfx.Circle.nodeType = "Ellipse";
300 dojo.declare("dojox.gfx.Line", dojox.gfx.shape.Line, {
301 // summary: a line shape (Silverlight)
302 setShape: function(newShape){
303 // summary: sets a line shape object (Silverlight)
304 // newShape: Object: a line shape object
305 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
307 var r = this.rawNode, n = this.shape;
308 r.x1 = n.x1; r.y1 = n.y1; r.x2 = n.x2; r.y2 = n.y2;
312 dojox.gfx.Line.nodeType = "Line";
314 dojo.declare("dojox.gfx.Polyline", dojox.gfx.shape.Polyline, {
315 // summary: a polyline/polygon shape (Silverlight)
316 setShape: function(points, closed){
317 // summary: sets a polyline/polygon shape object (Silverlight)
318 // points: Object: a polyline/polygon shape object
319 if(points && points instanceof Array){
321 // points: Array: an array of points
322 this.shape = dojox.gfx.makeParameters(this.shape, {points: points});
323 if(closed && this.shape.points.length){
324 this.shape.points.push(this.shape.points[0]);
327 this.shape = dojox.gfx.makeParameters(this.shape, points);
330 var p = this.shape.points, rp = [];
331 for(var i = 0; i < p.length; ++i){
332 if(typeof p[i] == "number"){
333 rp.push(p[i], p[++i]);
335 rp.push(p[i].x, p[i].y);
338 this.rawNode.points = rp.join(",");
342 dojox.gfx.Polyline.nodeType = "Polyline";
344 dojo.declare("dojox.gfx.Image", dojox.gfx.shape.Image, {
345 // summary: an image (Silverlight)
346 setShape: function(newShape){
347 // summary: sets an image shape object (Silverlight)
348 // newShape: Object: an image shape object
349 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
351 var r = this.rawNode, n = this.shape;
352 r["Canvas.Left"] = n.x;
353 r["Canvas.Top"] = n.y;
359 setRawNode: function(rawNode){
361 // assigns and clears the underlying node that will represent this
362 // shape. Once set, transforms, gradients, etc, can be applied.
363 // (no fill & stroke by default)
364 this.rawNode = rawNode;
367 dojox.gfx.Image.nodeType = "Image";
369 dojo.declare("dojox.gfx.Text", dojox.gfx.shape.Text, {
370 // summary: an anchored text (Silverlight)
371 setShape: function(newShape){
372 // summary: sets a text shape object (Silverlight)
373 // newShape: Object: a text shape object
374 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
376 var r = this.rawNode, s = this.shape;
378 r.textDecorations = s.decoration == "underline" ? "Underline" : "None";
379 r["Canvas.Left"] = -10000;
380 r["Canvas.Top"] = -10000;
381 window.setTimeout(dojo.hitch(this, "_delayAlignment"), 0);
384 _delayAlignment: function(){
386 var r = this.rawNode, s = this.shape,
387 w = r.actualWidth, h = r.actualHeight, x = s.x, y = s.y - h * 0.75;
396 var a = this.matrix ? dojox.gfx.matrix.multiplyPoint(this.matrix, x, y) : {x: x, y: y};
397 r["Canvas.Left"] = a.x;
398 r["Canvas.Top"] = a.y;
400 setStroke: function(){
401 // summary: ignore setting a stroke style
404 _setFillAttr: function(f){
405 this.rawNode.foreground = f;
407 setRawNode: function(rawNode){
409 // assigns and clears the underlying node that will represent this
410 // shape. Once set, transforms, gradients, etc, can be applied.
411 // (no fill & stroke by default)
412 this.rawNode = rawNode;
414 _applyTransform: function() {
415 var tm = this.matrix, r = this.rawNode;
417 // the next line is pure magic :-(
418 tm = dojox.gfx.matrix.normalize([1/100, tm, 100]);
419 var p = this.rawNode.getHost().content,
420 m = p.createFromXaml("<MatrixTransform/>"),
421 mm = p.createFromXaml("<Matrix/>");
429 r.renderTransform = m;
431 r.renderTransform = null;
435 getTextWidth: function(){
436 // summary: get the text width in pixels
437 return this.rawNode.actualWidth;
440 dojox.gfx.Text.nodeType = "TextBlock";
442 dojo.declare("dojox.gfx.Path", dojox.gfx.path.Path, {
443 // summary: a path shape (Silverlight)
444 _updateWithSegment: function(segment){
445 // summary: updates the bounding box of path with new segment
446 // segment: Object: a segment
447 dojox.gfx.Path.superclass._updateWithSegment.apply(this, arguments);
448 var p = this.shape.path;
449 if(typeof(p) == "string"){
450 this.rawNode.data = p ? p : null;
453 setShape: function(newShape){
454 // summary: forms a path using a shape (Silverlight)
455 // newShape: Object: an SVG path string or a path object (see dojox.gfx.defaultPath)
456 dojox.gfx.Path.superclass.setShape.apply(this, arguments);
457 var p = this.shape.path;
458 this.rawNode.data = p ? p : null;
462 dojox.gfx.Path.nodeType = "Path";
464 dojo.declare("dojox.gfx.TextPath", dojox.gfx.path.TextPath, {
465 // summary: a textpath shape (Silverlight)
466 _updateWithSegment: function(segment){
467 // summary: updates the bounding box of path with new segment
468 // segment: Object: a segment
470 setShape: function(newShape){
471 // summary: forms a path using a shape (Silverlight)
472 // newShape: Object: an SVG path string or a path object (see dojox.gfx.defaultPath)
474 _setText: function(){
477 dojox.gfx.TextPath.nodeType = "text";
479 dojo.declare("dojox.gfx.Surface", dojox.gfx.shape.Surface, {
480 // summary: a surface object to be used for drawings (Silverlight)
481 constructor: function(){
482 dojox.gfx.silverlight.Container._init.call(this);
484 setDimensions: function(width, height){
485 // summary: sets the width and height of the rawNode
486 // width: String: width of surface, e.g., "100px"
487 // height: String: height of surface, e.g., "100px"
488 this.width = dojox.gfx.normalizedLength(width); // in pixels
489 this.height = dojox.gfx.normalizedLength(height); // in pixels
490 var p = this.rawNode && this.rawNode.getHost();
497 getDimensions: function(){
498 // summary: returns an object with properties "width" and "height"
499 var p = this.rawNode && this.rawNode.getHost();
500 var t = p ? {width: p.content.actualWidth, height: p.content.actualHeight} : null;
501 if(t.width <= 0){ t.width = this.width; }
502 if(t.height <= 0){ t.height = this.height; }
507 dojox.gfx.silverlight.surfaces = {};
509 dojox.gfx.createSurface = function(parentNode, width, height){
510 // summary: creates a surface (Silverlight)
511 // parentNode: Node: a parent node
512 // width: String: width of surface, e.g., "100px"
513 // height: String: height of surface, e.g., "100px"
515 var s = new dojox.gfx.Surface();
516 parentNode = dojo.byId(parentNode);
517 // create an empty canvas
518 var t = parentNode.ownerDocument.createElement("script");
519 t.type = "text/xaml";
520 t.id = dojox.gfx._base._getUniqueId();
521 t.text = "<Canvas xmlns='http://schemas.microsoft.com/client/2007' Name='" + dojox.gfx._base._getUniqueId() + "'/>";
522 document.body.appendChild(t);
524 var pluginName = dojox.gfx._base._getUniqueId();
525 Silverlight.createObject(
529 { // Plugin properties.
530 width: String(width), // Width of rectangular region of plugin in pixels.
531 height: String(height), // Height of rectangular region of plugin in pixels.
532 inplaceInstallPrompt: "false", // Determines whether to display in-place install prompt if invalid version detected.
533 //background: "white", // Background color of plugin.
534 //isWindowless: "false", // Determines whether to display plugin in Windowless mode.
535 background: "transparent", // Background color of plugin.
536 isWindowless: "true", // Determines whether to display plugin in Windowless mode.
537 framerate: "24", // MaxFrameRate property value.
538 version: "1.0" // Silverlight version.
544 s.rawNode = dojo.byId(pluginName).content.root;
545 // register the plugin with its parent node
546 dojox.gfx.silverlight.surfaces[s.rawNode.name] = parentNode;
547 s.width = dojox.gfx.normalizedLength(width); // in pixels
548 s.height = dojox.gfx.normalizedLength(height); // in pixels
549 return s; // dojox.gfx.Surface
554 dojox.gfx.silverlight.Font = {
555 _setFont: function(){
556 // summary: sets a font object (Silverlight)
557 var f = this.fontStyle, r = this.rawNode,
558 fw = dojox.gfx.silverlight.fontweight,
559 fo = dojox.gfx.silverlight.fonts, t = f.family.toLowerCase();
560 r.fontStyle = f.style == "italic" ? "Italic" : "Normal";
561 r.fontWeight = f.weight in fw ? fw[f.weight] : f.weight;
562 r.fontSize = dojox.gfx.normalizedLength(f.size);
563 r.fontFamily = t in fo ? fo[t] : f.family;
567 dojox.gfx.silverlight.Container = {
569 dojox.gfx.shape.Container._init.call(this);
571 add: function(shape){
572 // summary: adds a shape to a group/surface
573 // shape: dojox.gfx.Shape: an VML shape object
574 if(this != shape.getParent()){
575 //dojox.gfx.Group.superclass.add.apply(this, arguments);
576 //this.inherited(arguments);
577 dojox.gfx.shape.Container.add.apply(this, arguments);
578 this.rawNode.children.add(shape.rawNode);
582 remove: function(shape, silently){
583 // summary: remove a shape from a group/surface
584 // shape: dojox.gfx.Shape: an VML shape object
585 // silently: Boolean?: if true, regenerate a picture
586 if(this == shape.getParent()){
587 var parent = shape.rawNode.getParent();
589 parent.children.remove(shape.rawNode);
591 //dojox.gfx.Group.superclass.remove.apply(this, arguments);
592 //this.inherited(arguments);
593 dojox.gfx.shape.Container.remove.apply(this, arguments);
598 // summary: removes all shapes from a group/surface
599 this.rawNode.children.clear();
600 //return this.inherited(arguments); // self
601 return dojox.gfx.shape.Container.clear.apply(this, arguments);
603 _moveChildToFront: dojox.gfx.shape.Container._moveChildToFront,
604 _moveChildToBack: dojox.gfx.shape.Container._moveChildToBack
607 dojo.mixin(dojox.gfx.shape.Creator, {
608 createObject: function(shapeType, rawShape){
609 // summary: creates an instance of the passed shapeType class
610 // shapeType: Function: a class constructor to create an instance of
611 // rawShape: Object: properties to be passed in to the classes "setShape" method
612 if(!this.rawNode){ return null; }
613 var shape = new shapeType();
614 var node = this.rawNode.getHost().content.createFromXaml("<" + shapeType.nodeType + "/>");
615 shape.setRawNode(node);
616 shape.setShape(rawShape);
618 return shape; // dojox.gfx.Shape
622 dojo.extend(dojox.gfx.Text, dojox.gfx.silverlight.Font);
623 //dojo.extend(dojox.gfx.TextPath, dojox.gfx.silverlight.Font);
625 dojo.extend(dojox.gfx.Group, dojox.gfx.silverlight.Container);
626 dojo.extend(dojox.gfx.Group, dojox.gfx.shape.Creator);
628 dojo.extend(dojox.gfx.Surface, dojox.gfx.silverlight.Container);
629 dojo.extend(dojox.gfx.Surface, dojox.gfx.shape.Creator);
632 var surfaces = dojox.gfx.silverlight.surfaces;
633 var mouseFix = function(s, a){
634 var ev = {target: s, currentTarget: s,
635 preventDefault: function(){}, stopPropagation: function(){}};
638 ev.shiftKey = a.shift;
639 var p = a.getPosition(null);
640 ev.x = ev.offsetX = ev.layerX = p.x;
641 ev.y = ev.offsetY = ev.layerY = p.y;
642 // calculate clientX and clientY
643 var parent = surfaces[s.getHost().content.root.name];
644 var t = dojo._abs(parent);
645 ev.clientX = t.x + p.x;
646 ev.clientY = t.y + p.y;
650 var keyFix = function(s, a){
652 keyCode: a.platformKeyCode,
659 onclick: {name: "MouseLeftButtonUp", fix: mouseFix},
660 onmouseenter: {name: "MouseEnter", fix: mouseFix},
661 onmouseleave: {name: "MouseLeave", fix: mouseFix},
662 onmousedown: {name: "MouseLeftButtonDown", fix: mouseFix},
663 onmouseup: {name: "MouseLeftButtonUp", fix: mouseFix},
664 onmousemove: {name: "MouseMove", fix: mouseFix},
665 onkeydown: {name: "KeyDown", fix: keyFix},
666 onkeyup: {name: "KeyUp", fix: keyFix}
668 var eventsProcessing = {
669 connect: function(name, object, method){
670 var token, n = name in eventNames ? eventNames[name] :
671 {name: name, fix: function(){ return {}; }};
672 if(arguments.length > 2){
673 token = this.getEventSource().addEventListener(n.name,
674 function(s, a){ dojo.hitch(object, method)(n.fix(s, a)); });
676 token = this.getEventSource().addEventListener(n.name,
677 function(s, a){ object(n.fix(s, a)); });
679 return {name: n.name, token: token};
681 disconnect: function(token){
682 this.getEventSource().removeEventListener(token.name, token.token);
685 dojo.extend(dojox.gfx.Shape, eventsProcessing);
686 dojo.extend(dojox.gfx.Surface, eventsProcessing);
687 dojox.gfx.equalSources = function(a, b){
688 return a && b && a.equals(b);