diff options
Diffstat (limited to 'js/dojo/dojox/drawing/tools')
| -rw-r--r-- | js/dojo/dojox/drawing/tools/Arrow.js | 87 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/Ellipse.js | 83 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/Line.js | 122 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/Path.js | 207 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/Pencil.js | 87 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/Rect.js | 83 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/TextBlock.js | 808 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/custom/Axes.js | 577 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/custom/Equation.js | 30 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/tools/custom/Vector.js | 392 |
10 files changed, 2476 insertions, 0 deletions
diff --git a/js/dojo/dojox/drawing/tools/Arrow.js b/js/dojo/dojox/drawing/tools/Arrow.js new file mode 100644 index 0000000..d87396d --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Arrow.js @@ -0,0 +1,87 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Arrow", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Arrow"); + +dojox.drawing.tools.Arrow = dojox.drawing.util.oo.declare( + // summary: + // Extends stencil.Line and adds an arrow head + // to the end and or start. + // + dojox.drawing.tools.Line, + function(options){ + // summary: constructor + if(this.arrowStart){ + this.begArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:0, idx2:1}); + } + if(this.arrowEnd){ + this.endArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:1, idx2:0}); + } + if(this.points.length){ + // This is protecting against cases when there are no points + // not sure how that would ever happen + // Render & label here instead of in base because of Arrow annotation + this.render(); + options.label && this.setLabel(options.label); + } + }, + { + draws:true, + type:"dojox.drawing.tools.Arrow", + baseRender:false, + + // arrowStart: Boolean + // Whether or not to place an arrow on start. + arrowStart:false, + // + // arrowEnd: Boolean + // Whether or not to place an arrow on end. + arrowEnd:true, + + labelPosition: function(){ + // summary: + // The custom position used for the label + // + var d = this.data; + var pt = dojox.drawing.util.positioning.label({x:d.x1,y:d.y1},{x:d.x2,y:d.y2}); + return { + x:pt.x, + y:pt.y + } + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this.shape){ return; } + + // if too small, need to reset + var p = this.points; + var len = this.util.distance(p[0].x,p[0].y,p[1].x,p[1].y); + if(len<this.minimumSize){ + this.remove(this.shape, this.hit); + return; + } + + var pt = this.util.snapAngle(obj, this.angleSnap/180); + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:pt.x, y:pt.y} + ]); + + this.renderedOnce = true; + this.onRender(this); + } + } +); + +dojox.drawing.tools.Arrow.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.Arrow", + tooltip:"Arrow Tool", + iconClass:"iconArrow" +}; + +dojox.drawing.register(dojox.drawing.tools.Arrow.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Ellipse.js b/js/dojo/dojox/drawing/tools/Ellipse.js new file mode 100644 index 0000000..927c0de --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Ellipse.js @@ -0,0 +1,83 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Ellipse", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Ellipse"); + +dojox.drawing.tools.Ellipse = dojox.drawing.util.oo.declare( + // summary: + // A drawable Ellipse. + // + dojox.drawing.stencil.Ellipse, + function(){ + // summary: constructor + }, + { + draws:true, + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + var s = obj.start, e = obj; + var x = s.x < e.x ? s.x : e.x, + y = s.y < e.y ? s.y : e.y, + w = s.x < e.x ? e.x-s.x : s.x-e.x, + h = s.y < e.y ? e.y-s.y : s.y-e.y; + + if(this.keys.shift){ w = h = Math.max(w,h); } + if(!this.keys.alt){ // ellipse is normally on center + x+=w/2; y+=h/2; w/=2; h/=2; + } else{ + if(y - h < 0){ h = y; } + if(x - w < 0){ w = x; } + } + + this.points = [ + {x:x-w, y:y-h}, // TL + {x:x+w, y:y-h}, // TR + {x:x+w, y:y+h}, // BR + {x:x-w, y:y+h} // BL + ]; + this.render(); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this._downOnCanvas){ return; } + this._downOnCanvas = false; + //Default shape on single click + if(!this.shape){ + var s = obj.start, e = this.minimumSize*2; + this.data = { + cx: s.x+e, + cy: s.y+e, + rx: e, + ry: e + }; + this.dataToPoints(); + this.render(); + }else{ + // if too small, need to reset + var o = this.pointsToData(); + console.log("Create a default shape here, pt to data: ",o); + if(o.rx*2<this.minimumSize && o.ry*2 < this.minimumSize){ + this.remove(this.shape, this.hit); + return; + } + } + + this.onRender(this); + + } + } +); + +dojox.drawing.tools.Ellipse.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.Ellipse", + tooltip:"Ellipse Tool", + iconClass:"iconEllipse" +}; + +dojox.drawing.register(dojox.drawing.tools.Ellipse.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Line.js b/js/dojo/dojox/drawing/tools/Line.js new file mode 100644 index 0000000..f8d3cde --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Line.js @@ -0,0 +1,122 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Line", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Line"); + +dojox.drawing.tools.Line = dojox.drawing.util.oo.declare( + // summary: + // Class for a drawable Line + dojox.drawing.stencil.Line, + function(){ + // summary: constructor + }, + { + draws:true, + showAngle:true, + onTransformEnd: function(/*manager.Anchor*/anchor){ + // summary: + // Overwrites _Base.onTransformEnd + // + this._toggleSelected(); + if(this.getRadius()<this.minimumSize){ + var p = this.points; + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:p[0].x, y:p[0].y} + ]); + }else{ + var d = this.data; + var obj = {start:{x:d.x1,y:d.y1},x:d.x2,y:d.y2}; + var pt = this.util.snapAngle(obj, this.angleSnap/180); + this.setPoints([ + {x:d.x1, y:d.y1}, + {x:pt.x, y:pt.y} + ]); + + this._isBeingModified = false; + this.onModify(this); + } + }, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + if(this.created){ return; } + var x1 = obj.start.x, + y1 = obj.start.y, + x2 = obj.x, + y2 = obj.y; + + if(this.keys.shift){ + var pt = this.util.snapAngle(obj, 45/180); + x2 = pt.x; + y2 = pt.y; + } + + if(this.keys.alt){ + // FIXME: + // should double the length of the line + // FIXME: + // if alt dragging past ZERO it seems to work + // but select/deselect shows bugs + var dx = x2>x1 ? ((x2-x1)/2) : ((x1-x2)/-2); + var dy = y2>y1 ? ((y2-y1)/2) : ((y1-y2)/-2); + x1 -= dx; + x2 -= dx; + y1 -= dy; + y2 -= dy; + } + + this.setPoints([ + {x:x1, y:y1}, + {x:x2, y:y2} + ]); + this.render(); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this._downOnCanvas){ return; } + this._downOnCanvas = false; + //Default shape on single click + if(!this.shape){ + var s = obj.start, e = this.minimumSize*4; + this.setPoints([ + {x:s.x, y:s.y+e}, + {x:s.x, y:s.y} + ]); + this.render(); + + }else{ + // if too small, need to reset + + if(this.getRadius()<this.minimumSize){ + this.remove(this.shape, this.hit); + return; + } + } + + var pt = this.util.snapAngle(obj, this.angleSnap/180); + var p = this.points; + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:pt.x, y:pt.y} + ]); + + this.renderedOnce = true; + this.onRender(this); + } + } +); + +dojox.drawing.tools.Line.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.Line", + tooltip:"Line Tool", + iconClass:"iconLine" +}; + +dojox.drawing.register(dojox.drawing.tools.Line.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Path.js b/js/dojo/dojox/drawing/tools/Path.js new file mode 100644 index 0000000..c9a01af --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Path.js @@ -0,0 +1,207 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Path", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Path"); + +dojox.drawing.tools.Path = dojox.drawing.util.oo.declare( + // summary: + // Class for a drawable Path + // + dojox.drawing.stencil.Path, + function(){ + // summary: constructor + + this.pathMode = ""; + this.currentPathMode = ""; + this._started = false; + this.oddEvenClicks = 0; + + }, + { + draws:true, + onDown: function(obj){ + if(!this._started){ + this.onStartPath(obj); + } + + }, + + makeSubPath: function(_closePath){ + if(_closePath){ + if(this.currentPathMode=="Q"){ + this.points.push({ + x:this.points[0].x, + y:this.points[0].y + }); + } + this.points.push({t:"Z"}); + this.render(); + } + this.currentPathMode = ""; + this.pathMode = "M"; + }, + + onStartPath: function(obj){ + this._started = true; + this.revertRenderHit = this.renderHit; + this.renderHit = false; + this.closePath = false; + + + this.mouse.setEventMode("PathEdit"); + + this.closePoint = {x:obj.x, y:obj.y}; + + this._kc1 = this.connect(this.keys, "onEsc", this, function(){ + this.onCompletePath(false); + }); + + this._kc2 = this.connect(this.keys, "onKeyUp", this, function(evt){ + + switch(evt.letter){ + case "c": + this.onCompletePath(true); break; + case "l": this.pathMode = "L"; break; + case "m": this.makeSubPath(false); break; + case "q": this.pathMode = "Q"; break; + case "s": this.pathMode = "S"; break; + case "z": this.makeSubPath(true); break; + } + + //console.log("KEY:", evt.letter); + }); + }, + + onCompletePath:function(_closePath){ + this.remove(this.closeGuide, this.guide); + var box = this.getBounds(); + if(box.w<this.minimumSize && box.h<this.minimumSize){ + this.remove(this.hit, this.shape, this.closeGuide); + this._started = false; + this.mouse.setEventMode(""); + this.setPoints([]); + return; + } + + + if(_closePath){ + if(this.currentPathMode=="Q"){ + this.points.push({ + x:this.points[0].x, + y:this.points[0].y + }); + } + this.closePath = true; + } + + + this.renderHit = this.revertRenderHit; + this.renderedOnce = true; + this.onRender(this); + this.disconnect([this._kc1, this._kc2]); + this.mouse.setEventMode(""); + this.render(); + //console.log(this.stringPath); + }, + + onUp: function(/*EventObject*/obj){ + //console.log(" Path UP", obj.mid, "within:", obj.withinCanvas) + + + if(!this._started || !obj.withinCanvas){ return; } + + + if(this.points.length>2 && this.closeRadius>this.util.distance(obj.x, obj.y, this.closePoint.x, this.closePoint.y)){ + this.onCompletePath(true); + + }else { + var p = { + x:obj.x, + y:obj.y + }; + this.oddEvenClicks++; + if(this.currentPathMode != this.pathMode){ + if(this.pathMode=="Q"){ + p.t = "Q"; + this.oddEvenClicks = 0; + }else if(this.pathMode=="L"){ + p.t = "L"; + }else if(this.pathMode=="M"){ + p.t = "M"; + this.closePoint = {x:obj.x, y:obj.y}; + } + this.currentPathMode = this.pathMode; + } + + + this.points.push(p); + if(this.points.length>1){ + this.remove(this.guide); + this.render(); + } + + } + + //console.log(this.stringPath); + }, + createGuide: function(obj){ + if(!this.points.length){ return; } + var realPoints = [].concat(this.points); + + var pt = { + x:obj.x, + y:obj.y + }; + if(this.currentPathMode=="Q" && this.oddEvenClicks % 2){ + // On a Q curve, every other click needs to be a + // straight line - the inbetween Q coords don't render + pt.t = "L"; // this is not permanent + } + + this.points.push(pt); + + this.render(); + this.points = realPoints; + + + var dist = this.util.distance(obj.x, obj.y, this.closePoint.x, this.closePoint.y); + if(this.points.length>1){ + if(dist<this.closeRadius && !this.closeGuide){ + var c = { + cx:this.closePoint.x, + cy:this.closePoint.y, + rx:this.closeRadius, + ry:this.closeRadius + } + this.closeGuide = this.container.createEllipse(c) + .setFill(this.closeColor); + + }else if(dist>this.closeRadius && this.closeGuide){ + this.remove(this.closeGuide); + this.closeGuide = null; + } + } + + }, + + onMove: function(obj){ + if(!this._started){ return; } + this.createGuide(obj); + }, + onDrag: function(obj){ + if(!this._started){ return; } + this.createGuide(obj); + } + } +); + +dojox.drawing.tools.Path.setup = { + // summary: See Base ToolsSetup + // + name:"dojox.drawing.tools.Path", + tooltip:"Path Tool", + iconClass:"iconLine" +}; + +dojox.drawing.register(dojox.drawing.tools.Path.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Pencil.js b/js/dojo/dojox/drawing/tools/Pencil.js new file mode 100644 index 0000000..a0b82a1 --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Pencil.js @@ -0,0 +1,87 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Pencil", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Pencil"); + +dojox.drawing.tools.Pencil = dojox.drawing.util.oo.declare( + // summary: + // Class for a drawable, continous Path + // + dojox.drawing.stencil.Path, + function(){ + // summary: constructor + this._started = false; + }, + { + draws:true, + // minDist: Number + // The distance the mouse must travel before rendering + // a path segment. Lower number is a higher definition + // path but more points. + minDist: 15, // how to make this more dynamic? Settable? + + onDown: function(obj){ + this._started = true; + var p = { + x:obj.x, + y:obj.y + }; + this.points = [p]; + this.lastPoint = p; + this.revertRenderHit = this.renderHit; + this.renderHit = false; + this.closePath = false; + }, + + onDrag: function(obj){ + if( + !this._started + || this.minDist > this.util.distance(obj.x, obj.y, this.lastPoint.x, this.lastPoint.y) + ){ return; } + + var p = { + x:obj.x, + y:obj.y + }; + this.points.push(p); + this.render(); + this.checkClosePoint(this.points[0], obj); + this.lastPoint = p; + }, + + onUp: function(obj){ + if(!this._started){ return; } + if(!this.points || this.points.length<2){ + this._started = false; + this.points = []; + return; + } + var box = this.getBounds(); + if(box.w<this.minimumSize && box.h<this.minimumSize){ + this.remove(this.hit, this.shape, this.closeGuide); + this._started = false; + this.setPoints([]); + return; + } + if(this.checkClosePoint(this.points[0], obj, true)){ + this.closePath = true; + } + this.renderHit = this.revertRenderHit; + this.renderedOnce = true; + this.render(); + this.onRender(this); + + } + } +); + +dojox.drawing.tools.Pencil.setup = { + // summary: See Base ToolsSetup + // + name:"dojox.drawing.tools.Pencil", + tooltip:"Pencil Tool", + iconClass:"iconLine" +}; + +dojox.drawing.register(dojox.drawing.tools.Pencil.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Rect.js b/js/dojo/dojox/drawing/tools/Rect.js new file mode 100644 index 0000000..75ab4aa --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Rect.js @@ -0,0 +1,83 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Rect", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Rect"); + +dojox.drawing.tools.Rect = dojox.drawing.util.oo.declare( + // summary: + // Class for a drawable rectangle + // + dojox.drawing.stencil.Rect, + function(){ + // summary: constructor + }, + { + draws:true, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + var s = obj.start, e = obj; + var x = s.x < e.x ? s.x : e.x, + y = s.y < e.y ? s.y : e.y, + w = s.x < e.x ? e.x-s.x : s.x-e.x, + h = s.y < e.y ? e.y-s.y : s.y-e.y; + + if(this.keys.shift){ w = h = Math.max(w,h); } + + if(this.keys.alt){ + x-=w; y-=h; w*=2; h*=2; + x = Math.max(x, 0); + y = Math.max(y, 0); + } + this.setPoints ([ + {x:x, y:y}, // TL + {x:x+w, y:y}, // TR + {x:x+w, y:y+h}, // BR + {x:x, y:y+h} // BL + ]); + this.render(); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this._downOnCanvas){ return; } + this._downOnCanvas = false; + + //Default shape on single click + if(!this.shape){ + var s = obj.start; + var e = this.minimumSize*4; + this.setPoints([ + {x:s.x, y:s.y}, + {x:s.x+e, y:s.y}, + {x:s.x+e, y:s.y+e}, + {x:s.x, y:s.y+e} + ]); + this.render(); + }else{ + + // if too small, need to reset + var o = this.data; + if(o.width<this.minimumSize && o.height < this.minimumSize){ + this.remove(this.shape, this.hit); + return; + } + } + this.onRender(this); + + } + } +); + +dojox.drawing.tools.Rect.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.Rect", + tooltip:'<span class="drawingTipTitle">Rectangle Tool</span><br/>' + + '<span class="drawingTipDesc">SHIFT - constrain to square</span>', + iconClass:"iconRect" +}; +dojox.drawing.register(dojox.drawing.tools.Rect.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/TextBlock.js b/js/dojo/dojox/drawing/tools/TextBlock.js new file mode 100644 index 0000000..4584e5e --- /dev/null +++ b/js/dojo/dojox/drawing/tools/TextBlock.js @@ -0,0 +1,808 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/TextBlock", ["dijit","dojo","dojox","dojo/require!dojox/drawing/stencil/Text"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.TextBlock"); +dojo.require("dojox.drawing.stencil.Text"); + +(function(){ + var conEdit; + dojo.addOnLoad(function(){ + // In order to use VML in IE, it's necessary to remove the + // DOCTYPE. But this has the side effect that causes a bug + // where contenteditable divs cannot be made dynamically. + // The solution is to include one in the main document + // that can be appended and removed as necessary: + // <div id="conEdit" contenteditable="true"></div> + // + // console.log("Removing conedit"); + conEdit = dojo.byId("conEdit"); + if(!conEdit){ + console.error("A contenteditable div is missing from the main document. See 'dojox.drawing.tools.TextBlock'") + }else{ + conEdit.parentNode.removeChild(conEdit); + } + }); + + dojox.drawing.tools.TextBlock = dojox.drawing.util.oo.declare( + // summary: + // A tool to create text fields on a canvas. + // description: + // Extends stencil.Text by adding an HTML layer that + // can be dragged out to a certain size, and accept + // a text entry. Will wrap text to the width of the + // html field. + // When created programmtically, use 'auto' to shrink + // the width to the size of the text. Use line breaks + // ( \n ) to create new lines. + // + // TODO - disable zoom while showing? + // + // FIXME: + // Handles width: auto, align:middle, etc. but for + // display only, edit is out of whack + // + dojox.drawing.stencil.Text, + function(options){ + // summary: constructor + // + if(options.data){ + var d = options.data; + var text = d.text ? this.typesetter(d.text) : d.text; + var w = !d.width ? this.style.text.minWidth : d.width=="auto" ? "auto" : Math.max(d.width, this.style.text.minWidth); + var h = this._lineHeight; + + if(text && w=="auto"){ + var o = this.measureText(this.cleanText(text, false), w); + w = o.w; + h = o.h; + }else{ + // w = this.style.text.minWidth; + this._text = ""; + } + + this.points = [ + {x:d.x, y:d.y}, + {x:d.x+w, y:d.y}, + {x:d.x+w, y:d.y+h}, + {x:d.x, y:d.y+h} + ]; + + if(d.showEmpty || text){ + this.editMode = true; + + + dojo.disconnect(this._postRenderCon); + this._postRenderCon = null; + this.connect(this, "render", this, "onRender", true); + + if(d.showEmpty){ + this._text = text || ""; + this.edit(); + }else if(text && d.editMode){ + this._text = ""; + this.edit(); + }else if(text){ + this.render(text); + } + setTimeout(dojo.hitch(this, function(){ + this.editMode = false; + }),100) + + }else{ + // Why make it if it won't render... + this.render(); + } + + }else{ + this.connectMouse(); + this._postRenderCon = dojo.connect(this, "render", this, "_onPostRender"); + } + //console.log("TextBlock:", this.id) + }, + { + draws:true, + baseRender:false, + type:"dojox.drawing.tools.TextBlock", + _caretStart: 0, + _caretEnd: 0, + _blockExec: false, + +/*===== +StencilData: { + // summary: + // The data used to create the dojox.gfx Text + // x: Number + // Left point x + // y: Number + // Top point y + // width: ? Number|String + // Optional width of Text. Not required but reccommended. + // for auto-sizing, use 'auto' + // height: ? Number + // Optional height of Text. If not provided, _lineHeight is used. + // text: String + // The string content. If not provided, may auto-delete depending on defaults. +}, +=====*/ + + // selectOnExec: Boolean + // Whether the Stencil is selected when the text field + // is executed or not + selectOnExec:true, + // + // showEmpty: Boolean + // If true and there is no text in the data, the TextBlock + // Is displayed and focused and awaits input. + showEmpty: false, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + if(!this.parentNode){ + this.showParent(obj); + } + var s = this._startdrag, e = obj.page; + this._box.left = (s.x < e.x ? s.x : e.x); + this._box.top = s.y; + this._box.width = (s.x < e.x ? e.x-s.x : s.x-e.x) + this.style.text.pad; + + dojo.style(this.parentNode, this._box.toPx()); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + + if(!this._downOnCanvas){ return; } + this._downOnCanvas = false; + + var c = dojo.connect(this, "render", this, function(){ + dojo.disconnect(c); + this.onRender(this); + + }); + this.editMode = true; + this.showParent(obj); + this.created = true; + this.createTextField(); + this.connectTextField(); + }, + + showParent: function(/*EventObject*/obj){ + // summary: + // Internal. Builds the parent node for the + // contenteditable HTML node. + // + if(this.parentNode){ return; } + var x = obj.pageX || 10; + var y = obj.pageY || 10; + this.parentNode = dojo.doc.createElement("div"); + this.parentNode.id = this.id; + var d = this.style.textMode.create; + this._box = { + left:x, + top:y, + width:obj.width || 1, + height:obj.height && obj.height>8 ? obj.height : this._lineHeight, + border:d.width+"px "+d.style+" "+d.color, + position:"absolute", + zIndex:500, + toPx: function(){ + var o = {}; + for(var nm in this){ + o[nm] = typeof(this[nm])=="number" && nm!="zIndex" ? this[nm] + "px" : this[nm]; + } + return o; + } + }; + + dojo.style(this.parentNode, this._box); + + document.body.appendChild(this.parentNode); + }, + createTextField: function(/*String*/txt){ + // summary: + // Internal. Inserts the contenteditable HTML node + // into its parent node, and styles it. + // + // style parent + var d = this.style.textMode.edit; + this._box.border = d.width+"px "+d.style+" "+d.color; + this._box.height = "auto"; + this._box.width = Math.max(this._box.width, this.style.text.minWidth*this.mouse.zoom); + dojo.style(this.parentNode, this._box.toPx()); + // style input + this.parentNode.appendChild(conEdit); + dojo.style(conEdit, { + height: txt ? "auto" : this._lineHeight+"px", + fontSize:(this.textSize/this.mouse.zoom)+"px", + fontFamily:this.style.text.family + }); + // FIXME: + // In Safari, if the txt ends with '&' it gets stripped + conEdit.innerHTML = txt || ""; + + return conEdit; //HTMLNode + }, + connectTextField: function(){ + // summary: + // Internal. Creates the connections to the + // contenteditable HTML node. + // + if(this._textConnected){ return; } // good ol' IE and its double events + // FIXME: + // Ouch-getting greekPalette by id. At the minimum this should + // be from the plugin manager + var greekPalette = dijit.byId("greekPalette"); + var greekHelp = greekPalette==undefined ? false : true; + if(greekHelp){ + //set it up + dojo.mixin(greekPalette,{ + _pushChangeTo: conEdit, + _textBlock: this + }); + }; + + this._textConnected = true; + this._dropMode = false; + this.mouse.setEventMode("TEXT"); + this.keys.editMode(true); + var kc1, kc2, kc3, kc4, self = this, _autoSet = false, + exec = function(){ + if(self._dropMode){ return; } + dojo.forEach([kc1,kc2,kc3,kc4], function(c){ + dojo.disconnect(c) + }); + self._textConnected = false; + self.keys.editMode(false); + self.mouse.setEventMode(); + self.execText(); + }; + + kc1 = dojo.connect(conEdit, "keyup", this, function(evt){ + // if text is empty, we need a height so the field's height + // doesn't collapse + if(dojo.trim(conEdit.innerHTML) && !_autoSet){ + dojo.style(conEdit, "height", "auto"); _autoSet = true; + }else if(dojo.trim(conEdit.innerHTML).length<2 && _autoSet){ + dojo.style(conEdit, "height", this._lineHeight+"px"); _autoSet = false; + } + + if(!this._blockExec){ + if(evt.keyCode==13 || evt.keyCode==27){ + dojo.stopEvent(evt); + exec(); + } + } else { + if(evt.keyCode==dojo.keys.SPACE){ + dojo.stopEvent(evt); + greekHelp && greekPalette.onCancel(); + } + } + }); + kc2 = dojo.connect(conEdit, "keydown", this, function(evt){ + if(evt.keyCode==13 || evt.keyCode==27){ // TODO: make escape an option + dojo.stopEvent(evt); + } + // if backslash, user is inputting a special character + // This gives popup help. + if(evt.keyCode==220){ + if(!greekHelp){ + console.info("For greek letter assistance instantiate: dojox.drawing.plugins.drawing.GreekPalette"); + return; + } + dojo.stopEvent(evt); + this.getSelection(conEdit); + // Differences in how browsers handle events made it necessary + // to stop the evt and add the backslash here. + this.insertText(conEdit,"\\"); + this._dropMode = true; + this._blockExec = true; + greekPalette.show({ + around:this.parentNode, + orient:{'BL':'TL'} + }); + } + if(!this._dropMode){ + this._blockExec = false; + } else { + // Controls for when we have a character helper and it's active + switch(evt.keyCode){ + case dojo.keys.UP_ARROW: + case dojo.keys.DOWN_ARROW: + case dojo.keys.LEFT_ARROW: + case dojo.keys.RIGHT_ARROW: + dojo.stopEvent(evt); + greekPalette._navigateByArrow(evt); + break; + case dojo.keys.ENTER: + dojo.stopEvent(evt); + greekPalette._onCellClick(evt); + break; + case dojo.keys.BACKSPACE: + case dojo.keys.DELETE: + dojo.stopEvent(evt); + greekPalette.onCancel(); + break; + } + } + + }); + + kc3 = dojo.connect(document, "mouseup", this, function(evt){ + // note: _onAnchor means an anchor has been clicked upon + if(!this._onAnchor && evt.target.id != "conEdit"){ + dojo.stopEvent(evt); + exec(); + }else if(evt.target.id == "conEdit" && conEdit.innerHTML == ""){ + // wonky stuff happens when you click on the + // field when its empty. + conEdit.blur(); + setTimeout(function(){ + conEdit.focus(); + },200) + } + }); + + this.createAnchors(); + + kc4 = dojo.connect(this.mouse, "setZoom", this, function(evt){ + exec(); + }); + + + conEdit.focus(); + + this.onDown = function(){}; + this.onDrag = function(){}; + + setTimeout(dojo.hitch(this, function(){ + // once again for Silverlight: + conEdit.focus(); + + // this is a pretty odd chunk of code here. + // specifcally need to overwrite old onUp + // however, this still gets called. its + // not disconnecting. + this.onUp = function(){ + if(!self._onAnchor && this.parentNode){ + self.disconnectMouse(); + exec(); + self.onUp = function(){} + } + } + }), 500); + }, + + + execText: function(){ + // summary: + // Internal. Method fired when text is executed, + // via mouse-click-off, ESC key or Enter key. + // + var d = dojo.marginBox(this.parentNode); + var w = Math.max(d.w, this.style.text.minWidth); + + var txt = this.cleanText(conEdit.innerHTML, true); + conEdit.innerHTML = ""; + conEdit.blur(); + this.destroyAnchors(); + + // need to convert characters before measuring width. + txt = this.typesetter(txt); + + var o = this.measureText(txt, w); + var sc = this.mouse.scrollOffset(); + var org = this.mouse.origin; + + var x = this._box.left + sc.left - org.x; + var y = this._box.top + sc.top - org.y; + + x *= this.mouse.zoom; + y *= this.mouse.zoom; + w *= this.mouse.zoom; + o.h *= this.mouse.zoom; + + + this.points = [ + {x:x, y:y}, + {x:x+w, y:y}, + {x:x+w, y:y+o.h}, + {x:x, y:y+o.h} + ]; + this.editMode = false; + + + console.log("EXEC TEXT::::", this._postRenderCon); + + if(!o.text){ + this._text = ""; + this._textArray = []; + } + // Only for Combo objects (vectors, rectangle, or ellipse). + this.render(o.text); + this.onChangeText(this.getText()); + }, + + edit: function(){ + // summary: + // Internal? + // Method used to instantiate the contenteditable HTML node. + // + this.editMode = true; + var text = this.getText() || ""; + console.log("EDIT TEXT:",text, " ",text.replace("/n", " ")); + // NOTE: no mouse obj + if(this.parentNode || !this.points){ return; } + var d = this.pointsToData(); + + var sc = this.mouse.scrollOffset(); + var org = this.mouse.origin; + + var obj = { + pageX: (d.x ) / this.mouse.zoom - sc.left + org.x, + pageY: (d.y ) / this.mouse.zoom- sc.top + org.y, + width:d.width / this.mouse.zoom, + height:d.height / this.mouse.zoom + }; + + this.remove(this.shape, this.hit); + this.showParent(obj); + this.createTextField(text.replace("/n", " ")); + this.connectTextField(); + if(text){ + //setTimeout(dojo.hitch(this, function(){ + this.setSelection(conEdit, "end"); + //}), 500) + } + }, + cleanText: function(/*String*/txt, /*Boolean*/removeBreaks){ + // summary: + // Cleans text. Strings HTML chars and double spaces + // and optionally removes line breaks. + var replaceHtmlCodes = function(str){ + var chars = { + "<":"<", + ">":">", + "&":"&" + }; + for(var nm in chars){ + str = str.replace(new RegExp(nm, "gi"), chars[nm]) + } + return str + }; + + if(removeBreaks){ + dojo.forEach(['<br>', '<br/>', '<br />', '\\n', '\\r'], function(br){ + txt = txt.replace(new RegExp(br, 'gi'), " "); + }); + } + txt = txt.replace(/ /g, " "); + txt = replaceHtmlCodes(txt); + txt = dojo.trim(txt); + // remove double spaces, since SVG doesn't show them anyway + txt = txt.replace(/\s{2,}/g, " "); + return txt; //String + }, + + measureText: function(/* String */ str, /* ? Number */width){ + // summary: + // Mechanism for measuring text. + // SVG nor VML have a way of determining the width or + // height of a block of text. This method creates an + // HTML text block and those measurements are used for + // displaying the SVG/VML text. + // arguments: + // str: String + // The text to display and measure. + // width: [optional] Number + // If the width is not provided, it will be assumed + // that the text is one line and the width will be + // measured and the _lineHeight used for th height. + // If width is provided, word-wrap is assumed, and + // line breaks will be inserted into the text at each + // point where a word wraps in the HTML. The height is + // then measured. + // + var r = "(<br\\s*/*>)|(\\n)|(\\r)"; + this.showParent({width:width || "auto", height:"auto"}); + this.createTextField(str); + var txt = ""; + var el = conEdit; + el.innerHTML = "X"; + var h = dojo.marginBox(el).h; + + el.innerHTML = str; + + if(!width || new RegExp(r, "gi").test(str)){ + // has line breaks in text + txt = str.replace(new RegExp(r, "gi"), "\n"); + el.innerHTML = str.replace(new RegExp(r, "gi"), "<br/>"); + + }else if(dojo.marginBox(el).h == h){ + // one line + txt = str; + + }else{ + // text wraps + var ar = str.split(" "); + var strAr = [[]]; + var line = 0; + el.innerHTML = ""; + while(ar.length){ + var word = ar.shift(); + el.innerHTML += word+" "; //urk, always an extra space + if(dojo.marginBox(el).h > h){ + line++; + strAr[line] = []; + el.innerHTML = word+" "; + } + strAr[line].push(word) + } + + dojo.forEach(strAr, function(ar, i){ + strAr[i] = ar.join(" "); + }); + txt = strAr.join("\n"); + + // get the resultant height + el.innerHTML = txt.replace("\n", "<br/>"); + + } + + var dim = dojo.marginBox(el); + + conEdit.parentNode.removeChild(conEdit); + dojo.destroy(this.parentNode); + this.parentNode = null; + + return {h:dim.h, w:dim.w, text:txt}; //Object + }, + + _downOnCanvas:false, + onDown: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDown + // + this._startdrag = { + x: obj.pageX, + y: obj.pageY + }; + dojo.disconnect(this._postRenderCon); + this._postRenderCon = null; + this._downOnCanvas = true; + }, + + createAnchors: function(){ + // summary: + // Internal. Creates HTML nodes at each corner + // of the contenteditable div. These nodes are + // draggable and will resize the div horizontally. + // + this._anchors = {}; + var self = this; + var d = this.style.anchors, + b = d.width, + w = d.size-b*2, + h = d.size-b*2, + p = (d.size)/2*-1 + "px"; + + var s = { + position:"absolute", + width:w+"px", + height:h+"px", + backgroundColor:d.fill, + border:b+"px " + d.style + " "+d.color + }; + if(dojo.isIE){ + s.paddingLeft = w + "px"; + s.fontSize = w + "px" + } + var ss = [ + {top: p, left:p}, + {top:p, right:p}, + {bottom:p, right:p}, + {bottom:p,left:p} + ]; + for(var i=0;i<4;i++){ + var isLeft = (i==0) || (i==3); + var id = this.util.uid(isLeft ? "left_anchor" : "right_anchor"); + + var a = dojo.create("div", {id:id}, this.parentNode); + dojo.style(a, dojo.mixin(dojo.clone(s), ss[i])); + + var md, mm, mu; + var md = dojo.connect(a, "mousedown", this, function(evt){ + isLeft = evt.target.id.indexOf("left")>-1; + self._onAnchor = true; + var orgX = evt.pageX; + var orgW = this._box.width; + dojo.stopEvent(evt); + + + mm = dojo.connect(document, "mousemove", this, function(evt){ + var x = evt.pageX; + if(isLeft){ + this._box.left = x; + this._box.width = orgW + orgX - x; + }else{ + this._box.width = x + orgW - orgX; + } + dojo.style(this.parentNode, this._box.toPx()); + }); + + mu = dojo.connect(document, "mouseup", this, function(evt){ + orgX = this._box.left; + orgW = this._box.width; + dojo.disconnect(mm); + dojo.disconnect(mu); + self._onAnchor = false; + conEdit.focus(); + dojo.stopEvent(evt); + }); + }); + + this._anchors[id] = { + a:a, + cons:[md] + } + } + }, + + destroyAnchors: function(){ + // summary: + // Internal. Destroys HTML anchors. + for(var n in this._anchors){ + dojo.forEach(this._anchors[n].con, dojo.disconnect, dojo); + dojo.destroy(this._anchors[n].a); + }; + }, + + setSavedCaret: function(val){ + // summary: + // Internal, called when caret needs to + // be moved into position after text is added + this._caretStart = this._caretEnd = val; + }, + + getSavedCaret: function(){ + return {start: this._caretStart, end: this._caretEnd} + }, + + insertText: function(node,val){ + // summary: + // Uses saved caret position to insert text + // into position and place caret at the end of + // insertion + // + var t, text = node.innerHTML; + var caret = this.getSavedCaret(); + + text = text.replace(/ /g, " "); + t = text.substr(0,caret.start) + val + text.substr(caret.end); + t = this.cleanText(t,true); + this.setSavedCaret(Math.min(t.length,(caret.end + val.length))); + node.innerHTML = t; + this.setSelection(node,"stored"); + }, + + getSelection: function(node){ + // summary: + // This gets and stores the caret position + // in the contentEditable div (conEdit). + // NOTE: Doesn't work with html nodes inside + // the div. + // + var start, end; + if(dojo.doc.selection){ + //debugger; + var r = dojo.doc.selection.createRange(); + var rs = dojo.body().createTextRange(); + rs.moveToElementText(node); + var re = rs.duplicate(); + rs.moveToBookmark(r.getBookmark()); + re.setEndPoint('EndToStart', rs); + start = this._caretStart = re.text.length; + end = this._caretEnd = re.text.length+r.text.length; + console.warn("Caret start: ",start," end: ",end," length: ",re.text.length," text: ",re.text); + } else { + this._caretStart = dojo.global.getSelection().getRangeAt(node).startOffset; + this._caretEnd = dojo.global.getSelection().getRangeAt(node).endOffset; + console.log("Caret start: ", this._caretStart," end: ", this._caretEnd); + } + }, + + setSelection: function(node, what){ + // summary: + // Used for placing the cursor during edits and character help. + // Takes the values: end, beg, start, all or any numerical value + // (in which case the number will constitute the caret position) + // + console.warn("setSelection:"); + if(dojo.doc.selection){ // IE + //debugger; + var rs = dojo.body().createTextRange(); + rs.moveToElementText(node); + + switch(what){ + case "end": + rs.collapse(false); + break; + case "beg" || "start": + rs.collapse(); + break; + case "all": + rs.collapse(); + rs.moveStart("character", 0); + rs.moveEnd("character",node.text.length); + break; + case "stored": + rs.collapse(); + var dif = this._caretStart-this._caretEnd; + //console.log("start: ",this._caretStart, " end: ",this._caretEnd," dif: ",dif); + rs.moveStart("character",this._caretStart); + rs.moveEnd("character",dif); + break; + }; + rs.select(); + + }else{ + var getAllChildren = function(node, children){ + children = children || []; + for(var i=0;i<node.childNodes.length; i++){ + var n = node.childNodes[i]; + if(n.nodeType==3){ + children.push(n); + }else if(n.tagName && n.tagName.toLowerCase()=="img"){ + children.push(n); + }; + + if(n.childNodes && n.childNodes.length){ + getAllChildren(n, children); + }; + } + return children; + }; + console.log("ff node:", node) + node.focus(); + var selection = dojo.global.getSelection(); + selection.removeAllRanges(); + var r = dojo.doc.createRange(); + var nodes = getAllChildren(node); + switch(what){ + case "end": + console.log("len:", nodes[nodes.length - 1].textContent.length); + r.setStart(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length); + r.setEnd(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length); + break; + case "beg" || "start": + r.setStart(nodes[0], 0); + r.setEnd(nodes[0], 0); + break; + case "all": + r.setStart(nodes[0], 0); + r.setEnd(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length); + break; + case "stored": + console.log("Caret start: ",this._caretStart," caret end: ",this._caretEnd); + r.setStart(nodes[0], this._caretStart); + r.setEnd(nodes[0], this._caretEnd); + } + selection.addRange(r); + console.log("sel ", what, " on ", node); + } + } + } + ); + + dojox.drawing.tools.TextBlock.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.TextBlock", + tooltip:"Text Tool", + iconClass:"iconText" + }; + dojox.drawing.register(dojox.drawing.tools.TextBlock.setup, "tool"); + +})(); + +}); diff --git a/js/dojo/dojox/drawing/tools/custom/Axes.js b/js/dojo/dojox/drawing/tools/custom/Axes.js new file mode 100644 index 0000000..a30bc11 --- /dev/null +++ b/js/dojo/dojox/drawing/tools/custom/Axes.js @@ -0,0 +1,577 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/custom/Axes", ["dijit","dojo","dojox","dojo/require!dojox/drawing/stencil/Path"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.custom.Axes"); +dojo.require("dojox.drawing.stencil.Path"); + + +dojox.drawing.tools.custom.Axes = dojox.drawing.util.oo.declare( + // summary: + // Draws a right-angle Axes (shaped like an L, not a +) + // description: + // This Stencil is created with a Path so that the L shape + // is one continuous piece. Arrow heads are placed at the end + // of each axis. The Axes can be rotated. There are custom + // label methods. + // + dojox.drawing.stencil.Path, + function(options){ + this.closePath = false; + + this.xArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:0, idx2:1}); + this.yArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:2, idx2:1}); + if(options.data){ + //Allows import of z-axis in non-enabled canvas and xy-axis in + //enabled canvas + this.style.zAxisEnabled = options.data.cosphi == 1 ? true : false; + this.setData(options.data); + } + if(this.style.zAxisEnabled){ + // If the z-axis is enabled, all axes will be created with a z-axis on the canvas. + // there is no switching back and forth for the axis, only for vectors. + this.data.cosphi = 1; + var ops = {}; + dojo.mixin(ops,options); + dojo.mixin(ops,{ + container:this.container.createGroup(), + style: this.style, + showAngle: false, + label: null + }); + if(options.data && (!ops.data.radius || !ops.data.angle)){ + ops.data.x2 = ops.data.x4; + ops.data.y2 = ops.data.y4; + } + ops.style.zAxis = true; + this.zAxis = new dojox.drawing.tools.custom.Vector(ops); + this.zAxis.minimumSize = 5; + //console.log("-----constructing axes: ",this.zAxis); + this.connectMult([ + [this, "onChangeStyle", this.zAxis, "onChangeStyle"], + [this, "select", this.zAxis, "select"], + [this, "deselect", this.zAxis, "deselect"], + [this, "onDelete", this.zAxis, "destroy"], + [this, "onDrag", this, "zSet"], + [this, "onTransform", this, "zSet"], + [this.zAxis, "onBeforeRender", this, "zSet"], + [this, "_onPostRender", this.zAxis, "render"] + ]); + + } + + if(this.points && this.points.length){ + this.setPoints = this._postSetPoints; + // render isn't called yet because baseRender is false + // instead it is called here + this.render(); + options.label && this.setLabel(options.label); + options.shadow && this.addShadow(options.shadow); + } + }, + { + draws:true, + type:"dojox.drawing.tools.custom.Axes", + minimumSize:30, + showAngle:true, + closePath:false, + baseRender:false, + zScale:.5, + + zPoint: function(obj){ + // summary: + // Finds the point for the z axis. + obj.radius = this.util.length(obj); + var pt = this.util.pointOnCircle(obj.start.x, obj.start.y, obj.radius*this.zScale, this.style.zAngle); + return {x:pt.x, y:pt.y, skip:true, noAnchor:true}; + }, + + zSet: function(){ + if(!this.zAxis){ return; }; + var c = this.points[1]; + var z = this.points[3]; + var p = [ + {x:c.x, y:c.y}, + {x:z.x, y:z.y} + ]; + var len = this.util.length({start:{x:c.x, y:c.y}, x:z.x, y:z.y}); + len > this.zAxis.minimumSize ? this.zAxis.setPoints(p) : false; + this.zAxis.cosphi = 1; + }, + + createLabels: function(){ + // summary: + // Creates the label for each axis. + // + // NOTE: Not passing style into text because it's changing it + var props = {align:"middle", valign:"middle", util:this.util, annotation:true, container:this.container, mouse:this.mouse, stencil:this}; + this.labelX = new dojox.drawing.annotations.Label(dojo.mixin(props,{ + labelPosition:this.setLabelX + })); + this.labelY = new dojox.drawing.annotations.Label(dojo.mixin(props,{ + labelPosition:this.setLabelY + })); + if(this.style.zAxisEnabled){ + this.labelZ = new dojox.drawing.annotations.Label(dojo.mixin(props,{ + labelPosition:this.setLabelZ + })); + } + + }, + + setLabelX: function(){ + // summary: + // Custom placement for x-axis label + // + var ax = this.points[0]; + var c = this.points[1]; + + var dist = 40; + var offdist = 20; + var pt, px, py, pt2; + + pt = this.util.lineSub(c.x, c.y, ax.x, ax.y, dist); + px = pt.x + (pt.y -ax.y); + py = pt.y + (ax.x - pt.x); + pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist)); + + return { + x: pt2.x, + y: pt2.y, + width:20 + }; + }, + setLabelY: function(){ + // summary: + // Custom placement for y-axis label + // + var c = this.points[1]; + var ay = this.points[2]; + + var dist = 40; + var offdist = 20; + var pt, px, py, pt2; + pt = this.util.lineSub(c.x, c.y, ay.x, ay.y, dist); + px = pt.x + (ay.y - pt.y); + py = pt.y + (pt.x - ay.x); + pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist)); + return { + x: pt2.x, + y: pt2.y, + width:20 + }; + }, + setLabelZ: function(){ + // summary: + // Custom placement for z-axis label + // + var c = this.points[1]; + var z = this.points[3]; + + var dist = 40; + var offdist = 20; + var pt, px, py, pt2; + pt = this.util.lineSub(c.x, c.y, z.x, z.y, dist); + px = pt.x + (pt.y - z.y); + py = pt.y + (z.x - pt.x); + pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist)); + + return { + x:pt2.x, + y:pt2.y, + width:20 + } + }, + setLabel: function(/* ? String*/value){ + // summary: + // Set the text of the labels. The text would be + // broken up into the two labels. + // arguments: + // value: [optional] String + // If no argument is passed, defaults to two labels + // 'x' and 'y'. If an argument is passed, that + // text will be split on the word 'and' to determine + // the two labels. + // + if(this._labelsCreated){ return; } + !this.labelX && this.createLabels(); + var x = "x"; + var y = "y"; + var z = "z"; + if(value){ + // match first "and" or "&" and trim whitespace. + // Non-greedy matches are not supported in older + // browsers such as Netscape Navigator 4 or + // Microsoft Internet Explorer 5.0. + if(this.labelZ){ + var lbls = value.match(/(.*?)(and|&)(.*?)(and|&)(.*)/i); + if(lbls.length>4){ + x = lbls[1].replace(/^\s+/,"").replace(/\s+$/,""); + y = lbls[3].replace(/^\s+/,"").replace(/\s+$/,""); + z = lbls[5].replace(/^\s+/,"").replace(/\s+$/,""); + } + }else{ + var lbls = value.match(/(.*?)(and|&)(.*)/i); + if(lbls.length>2){ + x = lbls[1].replace(/^\s+/,"").replace(/\s+$/,""); + y = lbls[3].replace(/^\s+/,"").replace(/\s+$/,""); + } + } + } + this.labelX.setLabel(x); + this.labelY.setLabel(y); + if(this.labelZ){ + this.labelZ.setLabel(z); + } + this._labelsCreated = true; + }, + getLabel: function(){ + // summary: + // Getter for the labels. returns an object. + // + if(!this.labelX){ return null; } + return { + x:this.labelX.getText(), + y:this.labelY.getText(), + z:this.labelZ?this.labelZ.getText():null + }; // Object + }, + + anchorPositionCheck: function(/*Number*/x, /*Number*/y, /*manager.Anchor*/anchor){ + // summary: + // Gets called from anchor to check if its current + // position is ok. If not, its x or y transform will + // be changed until this passes. + // + var pm = this.container.getParent().getTransform(); + var am = anchor.shape.getTransform(); + + // the xaxis point has changed and is not yet set as a point + // - but the center should be good (except for the transform). + // Now check the yaxis point. + + var p = this.points; + var o = {x:am.dx+anchor.org.x+pm.dx, y:am.dy+anchor.org.y+pm.dy}; + var c = {x:p[1].x+pm.dx, y:p[1].y+pm.dy}; + var ox = c.x - (c.y - o.y); + var oy = c.y - (o.x - c.x); + + return {x:ox, y:oy}; + + }, + + onTransformBegin: function(/*manager.Anchor*/anchor){ + // summary: + // Overwrites _Base.onTransformBegin + // + // called from anchor point up mouse down + this._isBeingModified = true; + }, + + onTransformEnd: function(/*manager.Anchor*/anchor){ + // summary: + // Overwrites _Base.onTransformEnd + // + // Gets called on anchor mouseup + // also gets called by checkBounds - we don't want that. + if(!anchor){ return; } + + // tell anchor to go to prev point if wrong + // called from anchor point up mouse up + + this._isBeingModified = false; + //this.deselect(); + this._toggleSelected(); + console.log("before:", Math.ceil(this.points[1].x), " x ", Math.ceil(this.points[1].y)) + + var o = this.points[0]; + var c = this.points[1]; + var obj = {start:{x:c.x,y:c.y},x:o.x, y:o.y}; + var pt = this.util.constrainAngle(obj, 0, 89); + var zpt = this.style.zAxisEnabled ? this.zPoint(obj) : null; + + if(pt.x==o.x && pt.y == o.y){ + // we're within the constraint, so now we snap + pt = this.util.snapAngle(obj, this.angleSnap/180); + + obj.x = pt.x; + obj.y = pt.y; + var ox = obj.start.x - (obj.start.y - obj.y); + var oy = obj.start.y - (obj.x - obj.start.x); + + if(ox<0 || oy<0){ + console.warn("AXES ERROR LESS THAN ZERO - ABORT"); + return; + } + this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}]; + this.points.push({x:ox, y:oy, noAnchor:true}); + if(zpt){ this.points.push(zpt);} + this.setPoints(this.points); + + //this.select(); + this.onModify(this); + return; + } + + // we're outside of the constraint. Set to the low or high. + this.points[0].x = pt.x + this.points[0].y = pt.y; + o = this.points[0]; + + var ox = c.x - (c.y - o.y); + var oy = c.y - (o.x - c.x); + + this.points[2] = {x:ox, y:oy, noAnchor:true}; + if(zpt){ this.points.push(zpt); } + this.setPoints(this.points); + + // reset handles render + //anchor.reset(this); + + this.labelX.setLabel(); + this.labelY.setLabel(); + if(this.labelZ){ + this.labelZ.setLabel(); + } + + //this.select(); + this.onModify(this); + + }, + + getBounds: function(/*Boolean*/absolute){ + // summary: + // Custom getBounds overwrites _Base.getBounds + // + var px = this.points[0], + pc = this.points[1], + py = this.points[2]; + if(this.style.zAxisEnabled){ var pz = this.points[3]; } + + if(absolute){ + var bounds = { + x:pc.x, + y:pc.y, + x1:pc.x, + y1:pc.y, + x2:px.x, + y2:px.y, + x3:py.x, + y3:py.y + }; + if(this.style.zAxisEnabled){ + bounds.x4 = pz.x; + bounds.y4 = pz.y; + } + return bounds; + } + + var x1 = this.style.zAxisEnabled ? (py.x < pz.x ? py.x : pz.x) : py.x; + y1 = py.y < px.y ? py.y : px.y, + x2 = px.x, + y2 = this.style.zAxisEnabled ? pz.y : pc.y; + + return { + x1:x1, + y1:y1, + x2:x2, + y2:y2, + x:x1, + y:y1, + w:x2-x1, + h:y2-y1 + }; + }, + + _postSetPoints: function(/*Array*/pts){ + // summary: + // Because Axes only has one anchor, + // we substitute a special setPoints method + // + this.points[0] = pts[0]; + if(this.pointsToData){ + this.data = this.pointsToData(); + } + }, + + onTransform: function(/*Number*/anchor){ + // summary: + // Overwrites _Base.onTransform + // + // the xaxis point has changed - the center will not. + // need to find the yaxis point. + var o = this.points[0]; + var c = this.points[1]; + var ox = c.x - (c.y - o.y); + var oy = c.y - (o.x - c.x); + + // 'noAnchor' on a point indicates an anchor should + // not be rendered. This is the Y point being set. + this.points[2] = {x:ox, y:oy, noAnchor:true}; + if(this.style.zAxisEnabled){ + this.points[3] = this.zPoint({start:{x:c.x, y:c.y}, x:o.x, y:o.y}); + } + this.setPoints(this.points); + if(!this._isBeingModified){ + this.onTransformBegin(); + } + this.render(); + }, + + pointsToData: function(){ + //summary: + // Converts points to data. + var p = this.points; + var d = { + x1:p[1].x, + y1:p[1].y, + x2:p[0].x, + y2:p[0].y, + x3:p[2].x, + y3:p[2].y + } + if(this.style.zAxisEnabled){ + d.x4 = p[3].x; + d.y4 = p[3].y; + d.cosphi = 1; + } + return d; + + }, + + getRadius: function(){ + //summary: + // Possibility of z-axis makes bounds unreliable. + // Use these points instead. + var p = this.points; + var line = {start:{x:p[1].x, y:p[1].y}, x:p[0].x, y:p[0].y}; + return this.util.length(line); + }, + + dataToPoints: function(/* ? Object*/o){ + //summary: + // Converts data to points. + o = o || this.data; + if(o.radius || o.angle){ + // instead of using x1,x2,y1,y1, + // it's been set as x,y,angle,radius + var pt = this.util.pointOnCircle(o.x,o.y,o.radius,o.angle), zpt; + var ox = o.x - (o.y - pt.y); + var oy = o.y - (pt.x - o.x); + if((o.cosphi && o.cosphi==1) || this.style.zAxisEnabled){ + this.style.zAxisEnabled = true; + zpt = this.util.pointOnCircle(o.x, o.y, o.radius*this.zScale, this.style.zAngle); + } + this.data = o = { + x1:o.x, + y1:o.y, + x2:pt.x, + y2:pt.y, + x3:ox, + y3:oy + } + if(this.style.zAxisEnabled){ + this.data.x4 = o.x4 = zpt.x; + this.data.y4 = o.y4 = zpt.y; + this.data.cosphi = 1; + } + + } + this.points = [ + {x:o.x2, y:o.y2}, + {x:o.x1, y:o.y1, noAnchor:true}, + {x:o.x3, y:o.y3, noAnchor:true} + ]; + if(this.style.zAxisEnabled){ this.points.push({x:o.x4, y:o.y4, skip:true, noAnchor:true}); } + return this.points; + }, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + var pt = this.util.constrainAngle(obj, 0, 89); + obj.x = pt.x; + obj.y = pt.y; + var ox = obj.start.x - (obj.start.y - obj.y); + var oy = obj.start.y - (obj.x - obj.start.x); + + if(ox<0 || oy<0){ + return; + } + this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}]; + + this.points.push({x:ox, y:oy, noAnchor:true}); + if(this.style.zAxisEnabled){ + var zpt = this.zPoint(obj); + this.points.push(zpt); + } + this.render(); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(!this._downOnCanvas){ return; } + this._downOnCanvas = false; + var p = this.points; + if(!p.length){ + var s = obj.start, d = 100; + this.points = [ + {x:s.x+d, y:s.y+d}, + {x:s.x, y:s.y+d, noAnchor:true}, + {x:s.x, y:s.y, noAnchor:true} + ]; + if(this.style.zAxisEnabled){ + var zpt = this.zPoint({start:{x:s.x, y:s.y+d}, x:s.x+d, y:s.y+d}); + this.points.push(zpt); + } + this.setPoints = this._postSetPoints; + this.pointsToData(); + this.render(); + this.onRender(this); + return; + } + + var len = this.util.distance(p[1].x ,p[1].y ,p[0].x ,p[0].y ); + if(!p || !p.length){ + return; + }else if(len < this.minimumSize){ + this.remove(this.shape, this.hit); + this.xArrow.remove(this.xArrow.shape, this.xArrow.hit); + this.yArrow.remove(this.yArrow.shape, this.yArrow.hit); + if(this.zArrow){ + this.zArrow.remove(this.zArrow.shape, this.zArrow.hit); + } + return; + } + + var o = p[0]; + var c = p[1]; + obj = {start:{x:c.x,y:c.y},x:o.x,y:o.y}; + var pt = this.util.snapAngle(obj, this.angleSnap/180); + obj.x = pt.x; + obj.y = pt.y; + var ox = obj.start.x - (obj.start.y - obj.y); + var oy = obj.start.y - (obj.x - obj.start.x); + + if(ox<0 || oy<0){ + return; + } + this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}]; + + this.points.push({x:ox, y:oy, noAnchor:true}); + if(this.style.zAxisEnabled){ this.points.push(this.zPoint(obj)); } + this.onRender(this); + this.setPoints = this._postSetPoints; + } + } +); + +dojox.drawing.tools.custom.Axes.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.custom.Axes", + tooltip:"Axes Tool", + iconClass:"iconAxes" +}; +dojox.drawing.register(dojox.drawing.tools.custom.Axes.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/custom/Equation.js b/js/dojo/dojox/drawing/tools/custom/Equation.js new file mode 100644 index 0000000..b9b5f40 --- /dev/null +++ b/js/dojo/dojox/drawing/tools/custom/Equation.js @@ -0,0 +1,30 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/custom/Equation", ["dijit","dojo","dojox","dojo/require!dojox/drawing/tools/TextBlock"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.custom.Equation"); +dojo.require("dojox.drawing.tools.TextBlock"); + +dojox.drawing.tools.custom.Equation = dojox.drawing.util.oo.declare( + // summary: + // Essentially the same as the TextBlock tool, but + // allows for a different icon and tooltip title. + // + dojox.drawing.tools.TextBlock, + function(options){ + + }, + { + customType:"equation" + } + +); + +dojox.drawing.tools.custom.Equation.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.custom.Equation", + tooltip:"Equation Tool", + iconClass:"iconEq" +}; +dojox.drawing.register(dojox.drawing.tools.custom.Equation.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/custom/Vector.js b/js/dojo/dojox/drawing/tools/custom/Vector.js new file mode 100644 index 0000000..748af50 --- /dev/null +++ b/js/dojo/dojox/drawing/tools/custom/Vector.js @@ -0,0 +1,392 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/custom/Vector", ["dijit","dojo","dojox","dojo/require!dojox/drawing/tools/Arrow,dojox/drawing/util/positioning"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.custom.Vector"); +dojo.require("dojox.drawing.tools.Arrow"); +dojo.require("dojox.drawing.util.positioning"); + +dojox.drawing.tools.custom.Vector = dojox.drawing.util.oo.declare( + // summary: + // Creates a Vector Stencil. + // description: + // Generally the same as an arrow, except that the arrow + // head is only at the end. There is additionaly functionality + // to allow for a 'zero vector' - one with no length. + // + // + dojox.drawing.tools.Arrow, + function(options){ + this.minimumSize = this.style.arrows.length; + this.addShadow({size:3, mult:2}); + }, + { + draws:true, + type:"dojox.drawing.tools.custom.Vector", + minimumSize:30, + showAngle:true, + + + + changeAxis: function(cosphi){ + // summary: + // Converts a vector to and from the z axis. + // If passed a cosphi value that is used to set + // the axis, otherwise it is the opp of what it is. + cosphi = cosphi!==undefined?cosphi:this.style.zAxis? 0 : 1; + if(cosphi == 0){ + this.style.zAxis = false; + this.data.cosphi = 0; + }else{ + this.style.zAxis = true; + var p = this.points; + var pt = this.zPoint(); + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:pt.x, y:pt.y} + ]); + } + this.render(); + }, + + _createZeroVector: function(shp, d, sty){ + // summary: + // Special creation function for the zero-vector shape + // + var s = shp=="hit" ? this.minimumSize : this.minimumSize/6; + var f = shp=="hit" ? sty.fill : null; + d = { + cx:this.data.x1, + cy:this.data.y1, + rx:s, + ry:s + }; + + this.remove(this[shp]); + this[shp] = this.container.createEllipse(d) + .setStroke(sty) + .setFill(f); + this.util.attr(this[shp], "drawingType", "stencil"); + }, + + _create: function(/*String*/shp, /*StencilData*/d, /*Object*/sty){ + // summary: + // Creates a dojox.gfx.shape based on passed arguments. + // Can be called many times by implementation to create + // multiple shapes in one stencil. + // + this.remove(this[shp]); + this[shp] = this.container.createLine(d) + .setStroke(sty); + this._setNodeAtts(this[shp]); + }, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + if(this.created){ return; } + + var x1 = obj.start.x, + y1 = obj.start.y, + x2 = obj.x, + y2 = obj.y; + + if(this.keys.shift && !this.style.zAxis){ + var pt = this.util.snapAngle(obj, 45/180); + x2 = pt.x; + y2 = pt.y; + } + + if(this.keys.alt){ + // FIXME: + // should double the length of the line + // FIXME: + // if alt dragging past ZERO it seems to work + // but select/deselect shows bugs + var dx = x2>x1 ? ((x2-x1)/2) : ((x1-x2)/-2); + var dy = y2>y1 ? ((y2-y1)/2) : ((y1-y2)/-2); + x1 -= dx; + x2 -= dx; + y1 -= dy; + y2 -= dy; + } + + if(this.style.zAxis){ + var pts = this.zPoint(obj); + x2 = pts.x; + y2 = pts.y; + } + + this.setPoints([ + {x:x1, y:y1}, + {x:x2, y:y2} + ]); + this.render(); + }, + + onTransform: function(/* ? manager.Anchor */anchor){ + // summary: + // Called from anchor point mouse drag + // also called from plugins.Pan.checkBounds + if(!this._isBeingModified){ + this.onTransformBegin(); + } + // this is not needed for anchor moves, but it + // is for stencil move: + + this.setPoints(this.points); + this.render(); + }, + + anchorConstrain: function(x, y){ + // summary: + // Called from anchor point mouse drag + if(!this.style.zAxis){ return null; } + var radians = this.style.zAngle*Math.PI/180; + //Constrain to angle + var test = x<0 ? x>-y : x<-y; + var dx = test ? x : -y/Math.tan(radians); + var dy = !test ? y : -Math.tan(radians)*x; + return {x:dx, y:dy} + }, + + zPoint: function(obj){ + // summary: + // Takes any point and converts it to + // be on the z-axis. + if(obj===undefined){ + if(!this.points[0]){ return null; }; + var d = this.pointsToData(); + obj = { + start:{ + x:d.x1, + y:d.y1 + }, + x:d.x2, + y:d.y2 + }; + } + var radius = this.util.length(obj); + var angle = this.util.angle(obj); + angle<0 ? angle = 360 + angle : angle; + + angle = angle > 135 && angle < 315 ? this.style.zAngle : this.util.oppAngle(this.style.zAngle); + + return this.util.pointOnCircle(obj.start.x, obj.start.y, radius, angle); + }, + + pointsToData: function(p){ + // summary: + // Converts points to data + p = p || this.points; + var cosphi = 0; + var obj = {start:{x:p[0].x, y:p[0].y}, x:p[1].x, y:p[1].y}; + if(this.style.zAxis && (this.util.length(obj)>this.minimumSize)){ + + var angle = this.util.angle(obj); + angle<0 ? angle = 360 + angle : angle; + cosphi = angle > 135 && angle < 315 ? 1 : -1; + } + this.data = { + x1: p[0].x, + y1: p[0].y, + x2: p[1].x, + y2: p[1].y, + cosphi: cosphi + }; + return this.data; + }, + + dataToPoints: function(o){ + //summary: + // Converts data to points. + o = o || this.data; + if(o.radius || o.angle){ + // instead of using x1,x2,y1,y1, + // it's been set as x,y,angle,radius + var cosphi = 0; + var pt = this.util.pointOnCircle(o.x,o.y,o.radius,o.angle); + if(this.style.zAxis || (o.cosphi && o.cosphi!=0)){ + this.style.zAxis = true; + cosphi = o.angle > 135 && o.angle < 315 ? 1 : -1; + } + //console.log(" ---- pts:", pt.x, pt.y); + this.data = o = { + x1:o.x, + y1:o.y, + x2:pt.x, + y2:pt.y, + cosphi:cosphi + } + + } + this.points = [ + {x:o.x1, y:o.y1}, + {x:o.x2, y:o.y2} + ]; + return this.points; + }, + + render: function(){ + // summary: + // Renders the 'hit' object (the shape used for an expanded + // hit area and for highlighting) and the'shape' (the actual + // display object). Additionally checks if Vector should be + // drawn as an arrow or a circle (zero-length) + // + this.onBeforeRender(this); + if(this.getRadius() >= this.minimumSize){ + this._create("hit", this.data, this.style.currentHit); + this._create("shape", this.data, this.style.current); + + }else{ + this.data.cosphi = 0; + this._createZeroVector("hit", this.data, this.style.currentHit); + this._createZeroVector("shape", this.data, this.style.current); + } + }, + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this._downOnCanvas){ return; } + this._downOnCanvas = false; + //Default vector for single click + if(!this.shape){ + var d = 100; + obj.start.x = this.style.zAxis ? obj.start.x + d : obj.start.x; + obj.y = obj.y+d; + this.setPoints([ + {x:obj.start.x, y:obj.start.y}, + {x:obj.x, y:obj.y} + ]); + this.render(); + } + + // When within minimum size this sets zero vector length to zero + if(this.getRadius()<this.minimumSize){ + var p = this.points; + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:p[0].x, y:p[0].y} + ]); + }else{ + //SnapAngle fails for the zero length vector + var p = this.points; + var pt = this.style.zAxis ? this.zPoint(obj) : this.util.snapAngle(obj, this.angleSnap/180); + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:pt.x, y:pt.y} + ]); + } + this.renderedOnce = true; + this.onRender(this); + } + } + +); + +dojox.drawing.tools.custom.Vector.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.custom.Vector", + tooltip:"Vector Tool", + iconClass:"iconVector" +}; + +if(dojox.drawing.defaults.zAxisEnabled){ + dojox.drawing.tools.custom.Vector.setup.secondary = { + // summary: + // Creates a secondary tool for the Vector Stencil. + // description: + // See Toolbar.js makeButtons function. The toolbar + // checks Vector.setup for a secondary tool and requires + // name, label, and funct. Currently it doesn't accept icon + // and only uses text from label for the button. Funct is the + // function that fires when the button is clicked. + // + // Setup and postSetup are optional + // and allow tool specific functions to be added to the + // Toolbar object as if they were written there. + name: "vectorSecondary", + label: "z-axis", + funct: function(button){ + button.selected ? this.zDeselect(button) : this.zSelect(button); + + var stencils = this.drawing.stencils.selectedStencils; + for(var nm in stencils){ + if(stencils[nm].shortType == "vector" && (stencils[nm].style.zAxis != dojox.drawing.defaults.zAxis)){ + var s = stencils[nm]; + s.changeAxis(); + //Reset anchors + if(s.style.zAxis){ s.deselect(); s.select(); } + } + } + + }, + setup: function(){ + // summary: + // All functions, variables and connections defined here + // are treated as if they were added directly to toolbar. + // They are included with the tool because secondary buttons + // are tool specific. + var zAxis = dojox.drawing.defaults.zAxis; + this.zSelect = function(button){ + if(!button.enabled){ return; } + zAxis = true; + dojox.drawing.defaults.zAxis = true; + button.select(); + this.vectorTest(); + this.zSelected = button; + }; + this.zDeselect = function(button){ + if(!button.enabled){ return; } + zAxis = false; + dojox.drawing.defaults.zAxis = false; + button.deselect(); + this.vectorTest(); + this.zSelected = null; + }; + this.vectorTest = function(){ + dojo.forEach(this.buttons, function(b){ + if(b.toolType=="vector" && b.selected){ + this.drawing.currentStencil.style.zAxis = zAxis; + } + },this); + }; + dojo.connect(this, "onRenderStencil", this, function(){ if(this.zSelected){ this.zDeselect(this.zSelected)}}); + var c = dojo.connect(this.drawing, "onSurfaceReady", this, function(){ + dojo.disconnect(c); + dojo.connect(this.drawing.stencils, "onSelect", this, function(stencil){ + if(stencil.shortType == "vector"){ + if(stencil.style.zAxis){ + //If stencil is on the z-axis, update button to reflect that + dojo.forEach(this.buttons, function(b){ + if(b.toolType=="vectorSecondary"){ + this.zSelect(b); + } + },this); + + }else{ + //Update button to not be z-axis + dojo.forEach(this.buttons, function(b){ + if(b.toolType=="vectorSecondary"){ + this.zDeselect(b); + } + },this); + } + }; + }); + }); + }, + postSetup: function(btn){ + // summary: + // Depending on the secondary tool, it may need + // extra functionality for some of the basic functions. + // Post is passed the button so those connections can + // be made. + dojo.connect(btn, "enable", function(){ dojox.drawing.defaults.zAxisEnabled = true; }); + dojo.connect(btn, "disable", function(){ dojox.drawing.defaults.zAxisEnabled = false; }); + } + }; +} +dojox.drawing.register(dojox.drawing.tools.custom.Vector.setup, "tool"); +}); |
