summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/drawing/tools
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/drawing/tools')
-rw-r--r--js/dojo/dojox/drawing/tools/Arrow.js87
-rw-r--r--js/dojo/dojox/drawing/tools/Ellipse.js83
-rw-r--r--js/dojo/dojox/drawing/tools/Line.js122
-rw-r--r--js/dojo/dojox/drawing/tools/Path.js207
-rw-r--r--js/dojo/dojox/drawing/tools/Pencil.js87
-rw-r--r--js/dojo/dojox/drawing/tools/Rect.js83
-rw-r--r--js/dojo/dojox/drawing/tools/TextBlock.js808
-rw-r--r--js/dojo/dojox/drawing/tools/custom/Axes.js577
-rw-r--r--js/dojo/dojox/drawing/tools/custom/Equation.js30
-rw-r--r--js/dojo/dojox/drawing/tools/custom/Vector.js392
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 = {
+ "&lt;":"<",
+ "&gt;":">",
+ "&amp;":"&"
+ };
+ 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(/&nbsp;/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(/&nbsp;/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");
+});