summaryrefslogtreecommitdiff
path: root/includes/js/dojox/sketch
diff options
context:
space:
mode:
authormensonge <mensonge@b3834d28-1941-0410-a4f8-b48e95affb8f>2008-11-13 09:49:11 +0000
committermensonge <mensonge@b3834d28-1941-0410-a4f8-b48e95affb8f>2008-11-13 09:49:11 +0000
commite44a7e37b6c7b5961adaffc62b9042b8d442938e (patch)
tree95b67c356e93163467db2451f2b8cce84ed5d582 /includes/js/dojox/sketch
parenta62b9742ee5e28bcec6872d88f50f25b820914f6 (diff)
downloadsemanticscuttle-e44a7e37b6c7b5961adaffc62b9042b8d442938e.tar.gz
semanticscuttle-e44a7e37b6c7b5961adaffc62b9042b8d442938e.tar.bz2
New feature: basic Ajax suggestion for tags and implementation of Dojo toolkit
git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@151 b3834d28-1941-0410-a4f8-b48e95affb8f
Diffstat (limited to 'includes/js/dojox/sketch')
-rw-r--r--includes/js/dojox/sketch/Anchor.js61
-rw-r--r--includes/js/dojox/sketch/Annotation.js223
-rw-r--r--includes/js/dojox/sketch/DoubleArrowAnnotation.js200
-rw-r--r--includes/js/dojox/sketch/Figure.js493
-rw-r--r--includes/js/dojox/sketch/LeadAnnotation.js141
-rw-r--r--includes/js/dojox/sketch/PreexistingAnnotation.js121
-rw-r--r--includes/js/dojox/sketch/README58
-rw-r--r--includes/js/dojox/sketch/SingleArrowAnnotation.js183
-rw-r--r--includes/js/dojox/sketch/Slider.js31
-rw-r--r--includes/js/dojox/sketch/Toolbar.js96
-rw-r--r--includes/js/dojox/sketch/UnderlineAnnotation.js82
-rw-r--r--includes/js/dojox/sketch/UndoStack.js104
-rw-r--r--includes/js/dojox/sketch/_Plugin.js83
-rw-r--r--includes/js/dojox/sketch/resources/images/icons.gifbin0 -> 600 bytes
-rw-r--r--includes/js/dojox/sketch/resources/sketch.css17
-rw-r--r--includes/js/dojox/sketch/resources/sketch.css.commented.css17
-rw-r--r--includes/js/dojox/sketch/tests/annotation.svg1
-rw-r--r--includes/js/dojox/sketch/tests/images/figure2.gifbin0 -> 40349 bytes
-rw-r--r--includes/js/dojox/sketch/tests/images/testsBodyBg.gifbin0 -> 753 bytes
-rw-r--r--includes/js/dojox/sketch/tests/test_full.html66
20 files changed, 1977 insertions, 0 deletions
diff --git a/includes/js/dojox/sketch/Anchor.js b/includes/js/dojox/sketch/Anchor.js
new file mode 100644
index 0000000..de72ac0
--- /dev/null
+++ b/includes/js/dojox/sketch/Anchor.js
@@ -0,0 +1,61 @@
+if(!dojo._hasResource["dojox.sketch.Anchor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.Anchor"] = true;
+dojo.provide("dojox.sketch.Anchor");
+dojo.require("dojox.gfx");
+
+(function(){
+ var ta=dojox.sketch;
+ ta.Anchor=function(an, id, isControl){
+ var self=this;
+ var size=4; // .5 * size of anchor.
+ var rect=null;
+
+ this.type=function(){ return "Anchor"; };
+ this.annotation=an;
+
+ this.id=id;
+ this._key="anchor-" + ta.Anchor.count++;
+ this.shape=null;
+ this.isControl=(isControl!=null)?isControl:true;
+
+ this.beginEdit=function(){
+ this.annotation.beginEdit(ta.CommandTypes.Modify);
+ };
+ this.endEdit=function(){
+ this.annotation.endEdit();
+ };
+ this.doChange=function(pt){
+ if(this.isControl) this.shape.applyTransform(pt);
+ else{
+ an.transform.dx+=pt.dx;
+ an.transform.dy+=pt.dy;
+ }
+ };
+ this.setBinding=function(pt){
+ an[id]={ x: an[id].x+pt.dx, y:an[id].y+pt.dy };
+ an.draw();
+ an.drawBBox();
+ };
+ this.setUndo=function(){ an.setUndo(); };
+
+ this.enable=function(){
+ if(!an.shape) return;
+ an.figure._add(this);
+ rect={ x:an[id].x-size, y:an[id].y-size, width:size*2, height:size*2 };
+ this.shape=an.shape.createRect(rect)
+ .setStroke({ color:"black", width:1 })
+ .setFill([255,255,255,0.35]);
+ this.shape.getEventSource().setAttribute("id", self._key);
+ this.shape.getEventSource().setAttribute("shape-rendering", "crispEdges");
+ };
+ this.disable=function(){
+ an.figure._remove(this);
+ if(an.shape) an.shape.remove(this.shape);
+ this.shape=null;
+ rect=null;
+ };
+ };
+ ta.Anchor.count=0;
+})();
+
+}
diff --git a/includes/js/dojox/sketch/Annotation.js b/includes/js/dojox/sketch/Annotation.js
new file mode 100644
index 0000000..ea260ca
--- /dev/null
+++ b/includes/js/dojox/sketch/Annotation.js
@@ -0,0 +1,223 @@
+if(!dojo._hasResource["dojox.sketch.Annotation"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.Annotation"] = true;
+dojo.provide("dojox.sketch.Annotation");
+dojo.require("dojox.sketch.Anchor");
+dojo.require("dojox.sketch._Plugin");
+
+(function(){
+ var ta=dojox.sketch;
+ dojo.declare("dojox.sketch.AnnotationTool", ta._Plugin, {
+// constructor: function(){
+//// console.log('this.shape',this.shape);
+//// this.annotation=ta.tools[this.shape];
+// },
+ onMouseMove: function(e,rect){
+ if(this._cshape){
+ this._cshape.setShape(rect);
+ } else {
+ this._cshape=this.figure.surface.createRect(rect)
+ .setStroke({color:"#999", width:1, style:"ShortDot"})
+ .setFill([255,255,255,0.7]);
+ this._cshape.getEventSource().setAttribute("shape-rendering","crispEdges");
+ }
+ },
+ onMouseUp: function(e){
+ var f=this.figure;
+ if(!(f._startPoint.x==e.pageX&&f._startPoint.y==e.pageY)){
+ if(this._cshape){
+ // The minimum number of pixels one has to travel before a shape
+ // gets drawn.
+ var limit=40;
+ if(Math.max(
+ limit,
+ Math.abs(f._absEnd.x-f._start.x),
+ Math.abs(f._absEnd.y-f._start.y)
+ )>limit){
+ this._create(f._start, f._end);
+ }
+ }
+ }
+ if(this._cshape) f.surface.remove(this._cshape);
+ },
+ _create: function(start,end){
+ // create a new shape, needs to be accessible from the
+ // dragging functions.
+ var f=this.figure;
+ var _=f.nextKey();
+ var a=new (this.annotation)(f, "annotation-"+_);
+ a.transform={dx:start.x/f.zoomFactor, dy:start.y/f.zoomFactor};
+ a.end={ x:end.x/f.zoomFactor, y:end.y/f.zoomFactor };
+ if(a.control){
+ a.control={ x:Math.round((end.x/2)/f.zoomFactor),y:Math.round((end.y/2)/f.zoomFactor) };
+ }
+ f.onBeforeCreateShape(a);
+ a.initialize();
+ f.select(a);
+ f.onCreateShape(a);
+ f.history.add(ta.CommandTypes.Create,a);
+ }
+ });
+ ta.Annotation=function(figure, id){
+ // for editing stuff.
+ this.id=this._key=id;
+ this.figure=figure;
+ this.mode=ta.Annotation.Modes.View;
+ this.shape=null; // dojox.gfx.Group
+ this.boundingBox=null; // rect for boundaries
+ this.hasAnchors=true;
+ this.anchors={}; // ta.Anchor
+ this._properties={
+ 'stroke':{ color:"blue", width:2 },
+ 'fill': "blue",
+ 'label': ""
+ };
+
+ if(this.figure) this.figure.add(this);
+ };
+ var p=ta.Annotation.prototype;
+ p.constructor=ta.Annotation;
+ p.type=function(){ return ''; };
+ p.getType=function(){ return ta.Annotation; };
+
+ p.remove=function(){
+ this.figure.history.add(ta.CommandTypes.Delete, this, this.serialize());
+ };
+ p.property=function(name,/*?*/value){
+ var r;
+ name=name.toLowerCase();
+ if(this._properties[name]!==undefined){
+ r=this._properties[name];
+ }
+ if(arguments.length>1){
+ this._properties[name]=value;
+ }
+ if(r!=value){
+ this.onPropertyChange(name,r);
+ }
+ return r;
+ };
+ p.onPropertyChange=function(name,oldvalue){};
+ p.onCreate=function(){
+ this.figure.history.add(ta.CommandTypes.Create,this);
+ }
+ p.onDblClick=function(event){
+ var l=prompt('Set new text:',this.property('label'));
+ if(l!==false){
+ this.beginEdit(ta.CommandTypes.Modify);
+ this.property('label',l);
+ this.draw();
+ this.endEdit();
+ }
+ }
+ p.initialize=function(){ };
+ p.destroy=function(){ };
+ p.draw=function(){ };
+ p.apply=function(obj){ };
+ p.serialize=function(){ };
+ p.getBBox=function(){ };
+ p.beginEdit=function(type){
+ this._type=type||ta.CommandTypes.Move;
+ this._prevState=this.serialize();
+ };
+ p.endEdit=function(){
+ var newstep=true;
+ if(this._type==ta.CommandTypes.Move){
+ var f=this.figure;
+ if(f._absEnd.x==f._start.x&&f._absEnd.y==f._start.y){
+ newstep=false;
+ }
+ }
+ if(newstep){
+ this.figure.history.add(this._type,this,this._prevState);
+ }
+ this._type=this._prevState='';
+ };
+ p.calculate={
+ slope:function(p1, p2){
+ if(!(p1.x-p2.x)) return 0;
+ return ((p1.y-p2.y)/(p1.x-p2.x));
+ },
+ dx:function(p1, p2, dy){
+ var s=this.slope(p1,p2);
+ if(s==0) return s;
+ return dy/s;
+ },
+ dy:function(p1, p2, dx){ return this.slope(p1,p2)*dx; }
+ };
+ p.drawBBox=function(){
+ var r=this.getBBox();
+ if(!this.boundingBox){
+ this.boundingBox=this.shape.createRect(r)
+ .moveToBack()
+ .setStroke({color:"#999", width:1, style:"Dash"})
+ .setFill([238,238,238,0.3]);
+ this.boundingBox.getEventSource().setAttribute("id",this.id+"-boundingBox");
+ this.boundingBox.getEventSource().setAttribute("shape-rendering","crispEdges");
+ this.figure._add(this);
+ } else this.boundingBox.setShape(r);
+ };
+ p.setBinding=function(pt){
+ this.transform.dx+=pt.dx;
+ this.transform.dy+=pt.dy;
+ this.draw();
+ };
+ p.doChange=function(pt){ };
+ p.getTextBox=function(){
+ return dojox.gfx._base._getTextBox(this.property('label'),ta.Annotation.labelFont);
+ };
+ p.setMode=function(m){
+ if(this.mode==m) return;
+ this.mode=m;
+ var method="disable";
+ if(m==ta.Annotation.Modes.Edit) method="enable";
+ if(method=="enable"){
+ // draw the bounding box
+ this.drawBBox();
+ this.figure._add(this);
+ } else {
+ if(this.boundingBox){
+ if(this.shape) this.shape.remove(this.boundingBox);
+ this.boundingBox=null;
+ }
+ }
+ for(var p in this.anchors){ this.anchors[p][method](); }
+ };
+// p.writeProperties=function(){
+// var ps=this._properties;
+// return "<!CDATA[properties:"+dojo.toJson(ps)+"]]>";
+// };
+ p.writeCommonAttrs=function(){
+ return 'id="' + this.id + '" dojoxsketch:type="' + this.type() + '"'
+ + ' transform="translate('+ this.transform.dx + "," + this.transform.dy + ')"'
+ + (this.data?(' ><![CDATA[data:'+dojo.toJson(this.data)+']]'):'');
+ };
+ p.readCommonAttrs=function(obj){
+ var i=0,cs=obj.childNodes,c;
+ while((c=cs[i++])){
+ if(c.nodeType==4){ //CDATA
+ if(c.nodeValue.substr(0,11)=='properties:'){
+ this._properties=dojo.fromJson(c.nodeValue.substr(11));
+ }else if(c.nodeValue.substr(0,5)=='data:'){
+ this.data=dojo.fromJson(c.nodeValue.substr(5));
+ }else{
+ console.error('unknown CDATA node in node ',obj);
+ }
+ }
+ }
+
+ if(obj.getAttribute('transform')){
+ var t=obj.getAttribute('transform').replace("translate(","");
+ var pt=t.split(",");
+ this.transform.dx=parseFloat(pt[0],10);
+ this.transform.dy=parseFloat(pt[1],10);
+ }
+ };
+ ta.Annotation.Modes={ View:0, Edit:1 };
+ ta.Annotation.labelFont={family:"Arial", size:"16px", weight:"bold"};
+ ta.Annotation.register=function(name){
+ var cls=ta[name+'Annotation'];
+ ta.registerTool(name, function(p){dojo.mixin(p,{shape: name,annotation:cls});return new ta.AnnotationTool(p)});
+ };
+})();
+
+}
diff --git a/includes/js/dojox/sketch/DoubleArrowAnnotation.js b/includes/js/dojox/sketch/DoubleArrowAnnotation.js
new file mode 100644
index 0000000..0817f0a
--- /dev/null
+++ b/includes/js/dojox/sketch/DoubleArrowAnnotation.js
@@ -0,0 +1,200 @@
+if(!dojo._hasResource["dojox.sketch.DoubleArrowAnnotation"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.DoubleArrowAnnotation"] = true;
+dojo.provide("dojox.sketch.DoubleArrowAnnotation");
+
+dojo.require("dojox.sketch.Annotation");
+dojo.require("dojox.sketch.Anchor");
+
+(function(){
+ var ta=dojox.sketch;
+ ta.DoubleArrowAnnotation=function(figure, id){
+ ta.Annotation.call(this, figure, id);
+ this.transform={ dx:0, dy:0 };
+ this.start={x:0, y:0};
+ this.control={x:100, y:-50};
+ this.end={x:200, y:0};
+ this.textPosition={ x:0, y:0 };
+ this.textOffset=6;
+ this.textYOffset=10;
+ this.textAlign="middle";
+ this.startRotation=0;
+ this.endRotation=0;
+
+// this.property('label',this.id);
+ this.labelShape=null;
+ this.pathShape=null;
+ this.startArrow=null;
+ this.startArrowGroup=null;
+ this.endArrow=null;
+ this.endArrowGroup=null;
+
+ this.anchors.start=new ta.Anchor(this, "start");
+ this.anchors.control=new ta.Anchor(this, "control");
+ this.anchors.end=new ta.Anchor(this, "end");
+ };
+ ta.DoubleArrowAnnotation.prototype=new ta.Annotation;
+ var p=ta.DoubleArrowAnnotation.prototype;
+ p.constructor=ta.DoubleArrowAnnotation;
+
+ p.type=function(){ return 'DoubleArrow'; };
+ p.getType=function(){ return ta.DoubleArrowAnnotation; };
+
+ p._rot=function(){
+ // arrowhead rotation
+ var opp=this.start.y-this.control.y;
+ var adj=this.start.x-this.control.x;
+ if(!adj) adj=1;
+ this.startRotation=Math.atan(opp/adj);
+ opp=this.control.y-this.end.y;
+ adj=this.control.x-this.end.x;
+ if(!adj) adj=1;
+ this.endRotation=Math.atan(opp/adj);
+ };
+ p._pos=function(){
+ // text position
+ var offset=this.textOffset;
+
+ // figure out the pull of the curve and place accordingly
+ if(this.control.y<this.end.y) offset*=-1;
+ else offset+=this.textYOffset;
+ var ab={x:((this.control.x-this.start.x)*.5)+this.start.x, y:((this.control.y-this.start.y)*.5)+this.start.y};
+ var bc={x:((this.end.x-this.control.x)*.5)+this.control.x, y:((this.end.y-this.control.y)*.5)+this.control.y};
+ this.textPosition={x:((bc.x-ab.x)*.5)+ab.x, y:(((bc.y-ab.y)*.5)+ab.y)+offset};
+ };
+
+ p.apply=function(obj){
+ if(!obj) return;
+ if(obj.documentElement) obj=obj.documentElement;
+ this.readCommonAttrs(obj);
+
+ for(var i=0; i<obj.childNodes.length; i++){
+ var c=obj.childNodes[i];
+ if(c.localName=="text") this.property('label',c.childNodes.length?c.childNodes[0].nodeValue:'');
+ else if(c.localName=="path"){
+ // the line
+ var d=c.getAttribute('d').split(" ");
+ var s=d[0].split(",");
+ this.start.x=parseFloat(s[0].substr(1),10);
+ this.start.y=parseFloat(s[1],10);
+ s=d[1].split(",");
+ this.control.x=parseFloat(s[0].substr(1),10);
+ this.control.y=parseFloat(s[1],10);
+ s=d[2].split(",");
+ this.end.x=parseFloat(s[0],10);
+ this.end.y=parseFloat(s[1],10);
+ }
+ }
+ };
+ p.initialize=function(obj){
+ // create, based on passed DOM node if available.
+ var font=(ta.Annotation.labelFont)?ta.Annotation.labelFont:{family:"Times", size:"16px"};
+ this.apply(obj);
+
+ // calculate the other positions
+ this._rot();
+ this._pos();
+
+ // rotation matrix
+ var rot=this.startRotation;
+ if(this.control.x<this.start.x) rot+=Math.PI;
+ var startRot=dojox.gfx.matrix.rotate(rot);
+
+ rot=this.endRotation;
+ if(this.control.x>=this.end.x) rot+=Math.PI;
+ var endRot=dojox.gfx.matrix.rotateAt(rot, this.end.x, this.end.y);
+
+ // draw the shapes
+ this.shape=this.figure.group.createGroup();
+ this.shape.getEventSource().setAttribute("id", this.id);
+ if(this.transform.dx||this.transform.dy) this.shape.setTransform(this.transform);
+ this.pathShape=this.shape.createPath(
+ "M"+this.start.x+" "+this.start.y+"Q"+this.control.x+" "+this.control.y+" "+this.end.x+" "+this.end.y + " l0,0"
+ ).setStroke(this.property('stroke'));
+
+ this.startArrowGroup=this.shape.createGroup().setTransform({ dx:this.start.x, dy:this.start.y });
+ this.startArrowGroup.applyTransform(startRot);
+// console.log('startArrow',this.property('fill'));
+ this.startArrow=this.startArrowGroup.createPath("M0,0 l20,-5 -3,5 3,5 Z").setFill(this.property('fill'));
+
+ this.endArrowGroup=this.shape.createGroup().setTransform(endRot);
+ this.endArrow=this.endArrowGroup.createPath(
+ "M" + this.end.x + "," + this.end.y + " l-20,-5 3,5 -3,5 Z"
+ ).setFill(this.property('fill'));
+ this.labelShape=this.shape.createText({
+ x:this.textPosition.x, y:this.textPosition.y, text:this.property('label'), align:this.textAlign
+ }).setFont(font).setFill(this.property('fill'));
+ };
+ p.destroy=function(){
+ if(!this.shape) return;
+ this.startArrowGroup.remove(this.startArrow);
+ this.endArrowGroup.remove(this.endArrow);
+ this.shape.remove(this.startArrowGroup);
+ this.shape.remove(this.endArrowGroup);
+ this.shape.remove(this.pathShape);
+ this.shape.remove(this.labelShape);
+ this.figure.group.remove(this.shape);
+ this.shape=this.pathShape=this.labelShape=this.startArrowGroup=this.startArrow=this.endArrowGroup=this.endArrow=null;
+ };
+ p.draw=function(obj){
+ this.apply(obj);
+ this._rot();
+ this._pos();
+ // rotation matrix
+ var rot=this.startRotation;
+ if(this.control.x<this.start.x) rot+=Math.PI;
+ var startRot=dojox.gfx.matrix.rotate(rot);
+ rot=this.endRotation;
+ if(this.control.x>=this.end.x) rot+=Math.PI;
+ var endRot=dojox.gfx.matrix.rotateAt(rot, this.end.x, this.end.y);
+
+ this.shape.setTransform(this.transform);
+ this.pathShape.setShape(
+ "M"+this.start.x+" "+this.start.y+" Q"+this.control.x+" "+this.control.y+" "+this.end.x+" "+this.end.y + " l0,0"
+ ).setStroke(this.property('stroke'));
+ this.startArrowGroup.setTransform({ dx:this.start.x, dy:this.start.y });
+ this.startArrowGroup.applyTransform(startRot);
+ this.startArrow.setFill(this.property('fill'));
+
+ this.endArrowGroup.setTransform(endRot);
+ this.endArrow.setShape(
+ "M" + this.end.x + "," + this.end.y + " l-20,-5 3,5 -3,5 Z"
+ ).setFill(this.property('fill'));
+ this.labelShape.setShape({x:this.textPosition.x, y:this.textPosition.y, text:this.property('label')}).setFill(this.property('fill'));
+ };
+ p.getBBox=function(){
+ var x=Math.min(this.start.x, this.control.x, this.end.x);
+ var y=Math.min(this.start.y, this.control.y, this.end.y);
+ var w=Math.max(this.start.x, this.control.x, this.end.x)-x;
+ var h=Math.max(this.start.y, this.control.y, this.end.y)-y;
+ return { x:x, y:y, width:w, height:h };
+ };
+ p.serialize=function(){
+ var s=this.property('stroke');
+ return '<g '+this.writeCommonAttrs()+'>'
+ + '<path style="stroke:'+s.color+';stroke-width:'+s.width+';fill:none;" d="'
+ + "M"+this.start.x+","+this.start.y+" "
+ + "Q"+this.control.x+","+this.control.y+" "
+ + this.end.x+","+this.end.y
+ + '" />'
+ + '<g transform="translate(' + this.start.x + "," + this.start.y + ") "
+ + 'rotate(' + (Math.round((this.startRotation*(180/Math.PI))*Math.pow(10,4))/Math.pow(10,4)) + ')">'
+ + '<path style="fill:'+s.color+';" d="M0,0 l20,-5, -3,5, 3,5 Z" />'
+ + '</g>'
+ + '<g transform="rotate('
+ + (Math.round((this.endRotation*(180/Math.PI))*Math.pow(10,4))/Math.pow(10,4))
+ + ", "+this.end.x+", "+this.end.y
+ + ')">'
+ + '<path style="fill:'+s.color+';" d="M'+this.end.x+","+this.end.y+' l-20,-5, 3,5, -3,5 Z" />'
+ + '</g>'
+ + '<text style="fill:'+s.color+';text-anchor:'+this.textAlign+'" font-weight="bold" '
+ + 'x="' + this.textPosition.x + '" '
+ + 'y="' + this.textPosition.y + '">'
+ + this.property('label')
+ + '</text>'
+ + '</g>';
+ };
+
+ ta.Annotation.register("DoubleArrow");
+})();
+
+}
diff --git a/includes/js/dojox/sketch/Figure.js b/includes/js/dojox/sketch/Figure.js
new file mode 100644
index 0000000..ae573bb
--- /dev/null
+++ b/includes/js/dojox/sketch/Figure.js
@@ -0,0 +1,493 @@
+if(!dojo._hasResource["dojox.sketch.Figure"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.Figure"] = true;
+dojo.provide("dojox.sketch.Figure");
+dojo.experimental("dojox.sketch");
+
+dojo.require("dojox.gfx");
+dojo.require("dojox.sketch.UndoStack");
+
+(function(){
+ var ta=dojox.sketch;
+ ta.tools={};
+ ta.registerTool=function(type, fn){ ta.tools[type]=fn; };
+ ta.Figure = function(){
+ var self=this;
+ var annCounter=1;
+
+ this.shapes=[];
+ this.image=null;
+ this.imageSrc=null;
+ this.size={ w:0, h:0 };
+ this.surface=null;
+ this.group=null;
+ this.node=null;
+
+ this.zoomFactor=1; // multiplier for zooming.
+
+ this.tools=null; // toolbar reference.
+ this.nextKey=function(){ return annCounter++; };
+
+ this.obj={}; // lookup table for shapes. Not keen on this solution.
+
+ this.initUndoStack();
+
+ // what is selected.
+ this.selected=[];
+ this.hasSelections=function(){ return this.selected.length>0 };
+ this.isSelected=function(obj){
+ for(var i=0; i<self.selected.length; i++){
+ if(self.selected[i]==obj) return true;
+ }
+ return false;
+ };
+ this.select=function(obj){
+ // TODO: deal with multiple vs. single selections.
+ if(!self.isSelected(obj)){
+ // force single select
+ self.clearSelections();
+ self.selected=[ obj ];
+ }
+ // force a bbox update, regardless
+ obj.setMode(ta.Annotation.Modes.View);
+ obj.setMode(ta.Annotation.Modes.Edit);
+ };
+ this.deselect=function(obj){
+ var idx=-1;
+ for(var i=0; i<self.selected.length; i++){
+ if(self.selected[i]==obj){
+ idx=i;
+ break;
+ }
+ }
+ if(idx>-1){
+ obj.setMode(ta.Annotation.Modes.View);
+ self.selected.splice(idx,1);
+ }
+ return obj;
+ };
+ this.clearSelections=function(){
+ for(var i=0; i<self.selected.length; i++)
+ self.selected[i].setMode(ta.Annotation.Modes.View);
+ self.selected=[];
+ };
+ this.replaceSelection=function(n, o){
+ if(!self.isSelected(o)){
+ self.select(n);
+ return;
+ }
+ var idx=-1;
+ for(var i=0; i<self.selected.length; i++){
+ if(self.selected[i]==o){
+ idx=i; break;
+ }
+ }
+ if(idx>-1){
+ self.selected.splice(idx,1,n);
+ }
+ };
+
+ // for the drag and drop handlers.
+ this._c=null; // current shape
+ this._ctr=null; // container measurements
+ this._lp=null; // last position
+ this._action=null;
+ this._prevState=null;
+ this._startPoint=null; // test to record a move.
+
+ // if an object isn't selected and we're dragging anyways.
+ this._ctool=null; // hard code it.
+ this._start=null;
+ this._end=null;
+ this._absEnd=null;
+ this._cshape=null;
+
+ this._click=function(e){
+ if(self._c){
+ dojo.stopEvent(e);
+ return;
+ }
+ var o=self._fromEvt(e);
+ if(!o){
+ self.clearSelections();
+ dojo.stopEvent(e);
+ } else if(!o.setMode){
+ // skip me.
+ } else self.select(o);
+ };
+ this._dblclick=function(e){
+ var o=self._fromEvt(e);
+ if(o){
+ self.onDblClickShape(o,e);
+ }
+ };
+
+ this._keydown=function(e){
+ var prevent=false;
+ if(e.ctrlKey){
+ if(e.keyCode==90){ //ctrl+z
+ self.undo();
+ prevent = true;
+ }else if(e.keyCode==89){ //ctrl+y
+ self.redo();
+ prevent = true;
+ }
+ }
+
+ if(e.keyCode==46 || e.keyCode==8){ //delete or backspace
+ self._delete(self.selected);
+ prevent = true;
+ }
+
+ if(prevent){
+ dojo.stopEvent(e);
+ }
+ };
+
+ // drag handlers.
+ this._md=function(e){
+ var o=self._fromEvt(e);
+ self._startPoint={ x:e.pageX, y:e.pageY };
+ var win = dijit.getDocumentWindow(self.node.ownerDocument);
+ // figure out the coordinates within the iframe
+ self._ctr=dojo._abs(self.node);
+ var scroll=dojo.withGlobal(win,dojo._docScroll);
+ self._ctr={x:self._ctr.x-scroll.x, y:self._ctr.y-scroll.y};
+ var X=e.clientX-self._ctr.x, Y=e.clientY-self._ctr.y;
+ self._lp={ x:X, y:Y };
+
+ // capture it separately
+ self._start={ x:X, y:Y };
+ self._end={ x:X, y:Y };
+ self._absEnd={ x:X, y:Y };
+ if(!o){
+ self.clearSelections();
+ self._ctool.onMouseDown(e);
+ }else{
+ if(o.type && o.type()!="Anchor"){
+ self.select(o);
+ }
+ o.beginEdit();
+ self._c=o;
+ }
+ };
+ this._mm=function(e){
+ if(!self._ctr) return;
+ var x=e.clientX-self._ctr.x;
+ var y=e.clientY-self._ctr.y;
+ var dx=x-self._lp.x;
+ var dy=y-self._lp.y;
+ self._absEnd={x:x, y:y};
+ if(self._c){
+ self._c.doChange({dx:Math.round(dx/self.zoomFactor), dy:Math.round(dy/self.zoomFactor)});
+ self._c.setBinding({dx:Math.round(dx/self.zoomFactor), dy:Math.round(dy/self.zoomFactor)});
+ self._lp={x:x, y:y};
+ }
+ else {
+ self._end={x:dx, y:dy};
+ var rect={
+ x:Math.min(self._start.x,self._absEnd.x),
+ y:Math.min(self._start.y,self._absEnd.y),
+ width:Math.abs(self._start.x-self._absEnd.x),
+ height:Math.abs(self._start.y-self._absEnd.y)
+ }
+ self._ctool.onMouseMove(e,rect);
+ }
+ };
+ this._mu=function(e){
+ if(self._c){
+ // record the event.
+ self._c.endEdit();
+ }else{
+ self._ctool.onMouseUp(e);
+ }
+
+ // clear the stuff out.
+ self._c=self._ctr=self._lp=self._action=self._prevState=self._startPoint=null;
+ self._cshape=self._start=self._end=self._absEnd=null;
+ };
+
+ this._delete=function(arr,noundo){
+ for(var i=0; i<arr.length; i++){
+ //var before=arr[i].serialize();
+ if(!noundo){
+ arr[i].remove();
+ }
+ arr[i].setMode(ta.Annotation.Modes.View);
+ arr[i].destroy();
+ self.remove(arr[i]);
+ self._remove(arr[i]);
+ }
+ arr.splice(0,arr.length);
+ };
+ };
+
+ var p=ta.Figure.prototype;
+ p.initUndoStack=function(){
+ this.history=new ta.UndoStack(this);
+ };
+ p.setTool=function(/*dojox.sketch._Plugin*/t){
+ this._ctool=t;
+ };
+ p.onDblClickShape=function(shape,e){
+ if(shape['onDblClick']){
+ shape.onDblClick(e);
+ }
+ };
+
+ p.onCreateShape=function(shape){};
+ p.onBeforeCreateShape=function(shape){};
+ p.initialize=function(node){
+ this.node=node;
+ this.surface=dojox.gfx.createSurface(node, this.size.w, this.size.h);
+ this.surface.createRect({ x:0, y:0, width:this.size.w, height:this.size.h })
+ .setFill("white");
+ this.group=this.surface.createGroup();
+
+ // kill any dragging events.
+ this._cons=[];
+ this._cons.push(dojo.connect(this.node, "ondragstart", dojo, "stopEvent"));
+ this._cons.push(dojo.connect(this.node, "onselectstart", dojo, "stopEvent"));
+
+ // hook up the drag system.
+ this._cons.push(dojo.connect(this.surface.getEventSource(), 'onmousedown', this._md));
+ this._cons.push(dojo.connect(this.surface.getEventSource(), 'onmousemove', this._mm));
+ this._cons.push(dojo.connect(this.surface.getEventSource(), 'onmouseup', this._mu));
+
+ this._cons.push(dojo.connect(this.surface.getEventSource(), 'ondblclick', this._dblclick));
+ this._cons.push(dojo.connect(this.surface.getEventSource().ownerDocument, 'onkeydown', this._keydown));
+
+ // rect hack. Fcuking VML>
+ this.group.createRect({ x:0, y:0, width:this.size.w, height:this.size.h });
+ this.image=this.group.createImage({ width:this.size.w, height:this.size.h, src:this.imageSrc });
+ };
+ p.destroy=function(isLoading){
+ if(!this.node){
+ return;
+ }
+ if(!isLoading){
+ if(this.history) this.history.destroy();
+ if(this._subscribed){
+ dojo.unsubscribe(this._subscribed);
+ delete this._subscribed;
+ }
+ }
+ dojo.forEach(this._cons,dojo.disconnect);
+ this._cons=[];
+
+ this.node.removeChild(this.surface.getEventSource());
+ this.group=this.surface=null;
+ this.obj={};
+ this.shapes=[];
+ };
+ p.draw=function(){ };
+ p.zoom=function(pct){
+ // first get the new dimensions
+ this.zoomFactor=pct/100;
+ var w=this.size.w*this.zoomFactor;
+ var h=this.size.h*this.zoomFactor;
+ this.surface.setDimensions(w, h);
+ // then scale it.
+ this.group.setTransform(dojox.gfx.matrix.scale(this.zoomFactor, this.zoomFactor));
+ if(dojo.isIE){
+ this.image.rawNode.style.width=Math.max(w,this.size.w);
+ this.image.rawNode.style.height=Math.max(h,this.size.h);
+ }
+ //this.rect.setShape({width:w,height:h});
+ };
+ p.getFit=function(){
+ // assume fitting the parent node.
+// var box=dojo.html.getContentBox(this.node.parentNode);
+ //the following should work under IE and FF, not sure about others though
+ var wF=(this.node.parentNode.clientWidth-5)/this.size.w;
+ var hF=(this.node.parentNode.clientHeight-5)/this.size.h;
+ return Math.min(wF, hF)*100;
+ };
+ p.unzoom=function(){
+ // restore original size.
+ this.zoomFactor=1;
+ this.surface.setDimensions(this.size.w, this.size.h);
+ this.group.setTransform();
+ };
+
+ // object registry for drag handling.
+ p._add=function(obj){ this.obj[obj._key]=obj; };
+ p._remove=function(obj){
+ if(this.obj[obj._key]){
+ delete this.obj[obj._key];
+ }
+ };
+ p._get=function(key){
+ if(key&&key.indexOf("bounding")>-1) key=key.replace("-boundingBox","");
+ return this.obj[key];
+ };
+ p._fromEvt=function(e){
+ var key=e.target.id+"";
+ if(key.length==0){
+ // ancestor tree until you get to the end (meaning this.surface)
+ var p=e.target.parentNode;
+ var node=this.surface.getEventSource();
+ while(p && p.id.length==0 && p!=node){
+ p=p.parentNode;
+ }
+ key=p.id;
+ }
+ return this._get(key);
+ };
+
+ p.add=function(annotation){
+ for(var i=0; i<this.shapes.length; i++){
+ if(this.shapes[i]==annotation){ return true; }
+ }
+ this.shapes.push(annotation);
+ return true;
+ };
+ p.remove=function(annotation){
+ var idx=-1;
+ for(var i=0; i<this.shapes.length; i++){
+ if(this.shapes[i]==annotation){
+ idx=i;
+ break;
+ }
+ }
+ if(idx>-1) this.shapes.splice(idx, 1);
+ return annotation;
+ };
+ p.get=function(id){
+ for(var i=0; i<this.shapes.length; i++){
+ if(this.shapes[i].id==id) {
+ return this.shapes[i];
+ }
+ }
+ return null;
+ };
+
+ p.convert=function(ann, t){
+ // convert an existing annotation to a different kind of annotation
+ var ctor=t+"Annotation";
+ if(!ta[ctor]) return;
+ var type=ann.type(), id=ann.id, label=ann.label, mode=ann.mode; tokenId=ann.tokenId;
+ var start, end, control, transform;
+ switch(type){
+ case "Preexisting":
+ case "Lead":{
+ transform={dx:ann.transform.dx, dy:ann.transform.dy };
+ start={x:ann.start.x, y:ann.start.y};
+ end={x:ann.end.x, y:ann.end.y };
+ var cx=end.x-((end.x-start.x)/2);
+ var cy=end.y-((end.y-start.y)/2);
+ control={x:cx, y:cy};
+ break;
+ }
+ case "SingleArrow":
+ case "DoubleArrow":{
+ transform={dx:ann.transform.dx, dy:ann.transform.dy };
+ start={x:ann.start.x, y:ann.start.y};
+ end={x:ann.end.x, y:ann.end.y };
+ control={x:ann.control.x, y:ann.control.y};
+ break;
+ }
+ case "Underline":{
+ transform={dx:ann.transform.dx, dy:ann.transform.dy };
+ start={x:ann.start.x, y:ann.start.y};
+ control={x:start.x+50, y:start.y+50 };
+ end={x:start.x+100, y:start.y+100 };
+ break;
+ }
+ case "Brace":{ }
+ }
+ var n=new ta[ctor](this, id);
+
+ if(n.type()=="Underline"){
+ // special handling, since we never move the start point.
+ n.transform={dx:transform.dx+start.x, dy:transform.dy+start.y };
+ } else {
+ if(n.transform) n.transform=transform;
+ if(n.start) n.start=start;
+ }
+ if(n.end) n.end=end;
+ if(n.control) n.control=control;
+ n.label=label;
+ n.token=dojo.lang.shallowCopy(ann.token);
+ n.initialize();
+
+ this.replaceSelection(n, ann);
+ this._remove(ann);
+ this.remove(ann);
+ ann.destroy();
+
+ // this should do all the things we need it to do for getting it registered.
+ n.setMode(mode);
+ };
+ p.setValue=function(text){
+ var obj=dojox.xml.DomParser.parse(text);
+ var node=this.node;
+ this.load(obj,node);
+ this.zoom(this.zoomFactor*100); //zoom to orignal scale
+ };
+ p.load=function(obj, n){
+ // create from pseudo-DOM
+ if(this.surface){ this.destroy(true); }
+ var node=obj.documentElement; // should be either the document or the docElement
+ this.size={ w:parseFloat(node.getAttribute('width'),10), h:parseFloat(node.getAttribute('height'),10) };
+ var g=node.childrenByName("g")[0];
+ var img=g.childrenByName("image")[0];
+ this.imageSrc=img.getAttribute("xlink:href");
+ this.initialize(n);
+
+ // now let's do the annotations.
+ var ann=g.childrenByName("g");
+ for(var i=0; i<ann.length; i++) this._loadAnnotation(ann[i]);
+ if(this._loadDeferred){
+ this._loadDeferred.callback(this);
+ this._loadDeferred=null;
+ }
+ this.onLoad();
+ };
+ p.onLoad=function(){};
+ p._loadAnnotation=function(obj){
+ var ctor=obj.getAttribute('dojoxsketch:type')+"Annotation";
+ if(ta[ctor]){
+ var a=new ta[ctor](this, obj.id);
+ a.initialize(obj);
+ this.nextKey();
+ a.setMode(ta.Annotation.Modes.View);
+ this._add(a);
+ return a;
+ }
+ return null;
+ };
+
+ p.onUndo=function(){};
+ p.onBeforeUndo=function(){};
+ p.onRedo=function(){};
+ p.onBeforeRedo=function(){};
+ p.undo=function(){
+ if(this.history){
+ this.onBeforeUndo();
+ this.history.undo();
+ this.onUndo();
+ }
+ };
+ p.redo=function(){
+ if(this.history){
+ this.onBeforeRedo();
+ this.history.redo();
+ this.onRedo();
+ }
+ };
+ p.serialize=function(){
+ var s='<svg xmlns="http://www.w3.org/2000/svg" '
+ + 'xmlns:xlink="http://www.w3.org/1999/xlink" '
+ + 'xmlns:dojoxsketch="http://dojotoolkit.org/dojox/sketch" '
+ + 'width="' + this.size.w + '" height="' + this.size.h + '">'
+ + '<g>'
+ + '<image xlink:href="' + this.imageSrc + '" x="0" y="0" width="'
+ + this.size.w + '" height="' + this.size.h + '" />';
+ for(var i=0; i<this.shapes.length; i++) s+= this.shapes[i].serialize();
+ s += '</g></svg>';
+ return s;
+ };
+ p.getValue=p.serialize;
+})();
+
+}
diff --git a/includes/js/dojox/sketch/LeadAnnotation.js b/includes/js/dojox/sketch/LeadAnnotation.js
new file mode 100644
index 0000000..40e571e
--- /dev/null
+++ b/includes/js/dojox/sketch/LeadAnnotation.js
@@ -0,0 +1,141 @@
+if(!dojo._hasResource["dojox.sketch.LeadAnnotation"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.LeadAnnotation"] = true;
+dojo.provide("dojox.sketch.LeadAnnotation");
+dojo.require("dojox.sketch.Annotation");
+dojo.require("dojox.sketch.Anchor");
+
+(function(){
+ var ta=dojox.sketch;
+ ta.LeadAnnotation=function(figure, id){
+ ta.Annotation.call(this, figure, id);
+ this.transform={dx:0, dy:0 };
+ this.start={ x:0, y:0 };
+ this.control={x:100, y:-50};
+ this.end={ x:200, y:0 };
+ this.textPosition={ x:0, y:0 };
+ this.textOffset=4;
+ this.textAlign="middle";
+ this.textYOffset=10;
+
+// this.property('label',this.id);
+ this.pathShape=null;
+ this.labelShape=null;
+
+ this.anchors.start=new ta.Anchor(this, "start");
+ this.anchors.control=new ta.Anchor(this, "control");
+ this.anchors.end=new ta.Anchor(this, "end");
+ };
+ ta.LeadAnnotation.prototype=new ta.Annotation;
+ var p=ta.LeadAnnotation.prototype;
+ p.constructor=ta.LeadAnnotation;
+
+ p.type=function(){ return 'Lead'; }
+ p.getType=function(){ return ta.LeadAnnotation; };
+
+ p._pos=function(){
+ var offset=this.textOffset, x=0, y=0;
+ var slope=this.calculate.slope(this.control, this.end);
+ if(Math.abs(slope)>=1){
+ x=this.end.x+this.calculate.dx(this.control, this.end, offset);
+ if(this.control.y>this.end.y) y=this.end.y-offset;
+ else y=this.end.y+offset+this.textYOffset;
+ } else if(slope==0){
+ x=this.end.x+offset;
+ y=this.end.y+this.textYOffset;
+ } else {
+ if(this.start.x>this.end.x){
+ x=this.end.x-offset;
+ this.textAlign="end";
+ } else {
+ x=this.end.x+offset;
+ this.textAlign="start";
+ }
+ if(this.start.y<this.end.y) y=this.end.y+this.calculate.dy(this.control, this.end, offset)+this.textYOffset;
+ else y=this.end.y+this.calculate.dy(this.control, this.end, -offset);
+ }
+ this.textPosition={ x:x, y:y };
+ };
+ p.apply=function(obj){
+ if(!obj) return;
+ if(obj.documentElement) obj=obj.documentElement;
+ this.readCommonAttrs(obj);
+
+ for(var i=0; i<obj.childNodes.length; i++){
+ var c=obj.childNodes[i];
+ if(c.localName=="text") this.property('label',c.childNodes.length?c.childNodes[0].nodeValue:'');
+ else if(c.localName=="path"){
+ // the line
+ var d=c.getAttribute('d').split(" ");
+ var s=d[0].split(",");
+ this.start.x=parseFloat(s[0].substr(1),10);
+ this.start.y=parseFloat(s[1],10);
+ s=d[1].split(",");
+ this.control.x=parseFloat(s[0].substr(1),10);
+ this.control.y=parseFloat(s[1],10);
+ s=d[2].split(",");
+ this.end.x=parseFloat(s[0],10);
+ this.end.y=parseFloat(s[1],10);
+ }
+ }
+ };
+
+ p.initialize=function(obj){
+ var font=(ta.Annotation.labelFont)?ta.Annotation.labelFont:{family:"Times", size:"16px"};
+ this.apply(obj);
+ this._pos();
+
+ // create either from scratch or based on the passed node
+ this.shape=this.figure.group.createGroup();
+ this.shape.getEventSource().setAttribute("id", this.id);
+ if(this.transform.dx || this.transform.dy) this.shape.setTransform(this.transform);
+ this.pathShape=this.shape.createPath(
+ "M"+this.start.x+","+this.start.y+" Q"+this.control.x+","+this.control.y+" "+this.end.x+","+this.end.y+" l0,0"
+ ).setStroke(this.property('stroke'));
+ this.labelShape=this.shape.createText({
+ x:this.textPosition.x, y:this.textPosition.y, text:this.property('label'), align:this.textAlign
+ }).setFont(font).setFill(this.property('fill'));
+ };
+ p.destroy=function(){
+ if(!this.shape) return;
+ this.shape.remove(this.pathShape);
+ this.shape.remove(this.labelShape);
+ this.figure.group.remove(this.shape);
+ this.shape=this.pathShape=this.labelShape=null;
+ };
+ p.getBBox=function(){
+ var x=Math.min(this.start.x, this.control.x, this.end.x);
+ var y=Math.min(this.start.y, this.control.y, this.end.y);
+ var w=Math.max(this.start.x, this.control.x, this.end.x)-x;
+ var h=Math.max(this.start.y, this.control.y, this.end.y)-y;
+ return { x:x, y:y, width:w, height:h };
+ };
+ p.draw=function(obj){
+ this.apply(obj);
+ this._pos();
+ this.shape.setTransform(this.transform);
+ this.pathShape.setShape(
+ "M"+this.start.x+","+this.start.y+" Q"+this.control.x+","+this.control.y+" "+this.end.x+","+this.end.y+" l0,0"
+ ).setStroke(this.property('stroke'));
+ this.labelShape.setShape({ x:this.textPosition.x, y:this.textPosition.y, text:this.property('label') })
+ .setFill(this.property('fill'));
+ };
+ p.serialize=function(){
+ var stroke=this.property('stroke');
+ return '<g '+this.writeCommonAttrs()+'>'
+ + '<path style="stroke:'+stroke.color+';stroke-width:'+stroke.width+';fill:none;" d="'
+ + "M"+this.start.x+","+this.start.y+" "
+ + "Q"+this.control.x+","+this.control.y+" "
+ + this.end.x+","+this.end.y
+ + '" />'
+ + '<text style="fill:'+stroke.color+';text-anchor:'+this.textAlign+'" font-weight="bold" '
+ + 'x="' + this.textPosition.x + '" '
+ + 'y="' + this.textPosition.y + '">'
+ + this.property('label')
+ + '</text>'
+ + '</g>';
+ };
+
+ ta.Annotation.register("Lead");
+})();
+
+}
diff --git a/includes/js/dojox/sketch/PreexistingAnnotation.js b/includes/js/dojox/sketch/PreexistingAnnotation.js
new file mode 100644
index 0000000..a8c123e
--- /dev/null
+++ b/includes/js/dojox/sketch/PreexistingAnnotation.js
@@ -0,0 +1,121 @@
+if(!dojo._hasResource["dojox.sketch.PreexistingAnnotation"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.PreexistingAnnotation"] = true;
+dojo.provide("dojox.sketch.PreexistingAnnotation");
+
+dojo.require("dojox.sketch.Annotation");
+dojo.require("dojox.sketch.Anchor");
+
+(function(){
+ var ta=dojox.sketch;
+ ta.PreexistingAnnotation=function(figure, id){
+ ta.Annotation.call(this, figure, id);
+ this.transform={dx:0, dy:0};
+ this.start={ x:0, y:0 };
+ this.end={ x:200, y:200 };
+ this.radius=8;
+ this.textPosition={ x:196, y:196 };
+ this.textOffset=4;
+ this.textAlign="end";
+
+ this.property('label',this.id);
+ this.rectShape=null;
+ this.labelShape=null;
+
+ this.anchors.start=new ta.Anchor(this, "start");
+ this.anchors.end=new ta.Anchor(this, "end");
+ };
+ ta.PreexistingAnnotation.prototype=new ta.Annotation;
+ var p=ta.PreexistingAnnotation.prototype;
+ p.constructor=ta.PreexistingAnnotation;
+
+ p.type=function(){ return 'Preexisting' };
+ p.getType=function(){ return ta.PreexistingAnnotation; };
+
+ p._pos=function(){
+ var x=Math.min(this.start.x, this.end.x);
+ var y=Math.min(this.start.y, this.end.y);
+ var w=Math.max(this.start.x, this.end.x);
+ var h=Math.max(this.start.y, this.end.y);
+ this.start={ x:x, y:y };
+ this.end={ x:w, y:h };
+ this.textPosition={ x:this.end.x-this.textOffset, y:this.end.y-this.textOffset };
+ };
+ p.apply=function(obj){
+ if(!obj) return;
+ if(obj.documentElement) obj=obj.documentElement;
+ this.readCommonAttrs(obj);
+
+ for(var i=0; i<obj.childNodes.length; i++){
+ var c=obj.childNodes[i];
+ if(c.localName=="text") this.property('label',c.childNodes.length?c.childNodes[0].nodeValue:'');
+ else if(c.localName=="rect"){
+ if(c.getAttribute('x')!==null) this.start.x=parseFloat(c.getAttribute('x'), 10);
+ if(c.getAttribute('width')!==null) this.end.x=parseFloat(c.getAttribute('width'), 10)+parseFloat(c.getAttribute('x'), 10);
+ if(c.getAttribute('y')!==null) this.start.y=parseFloat(c.getAttribute('y'), 10);
+ if(c.getAttribute('height')!==null) this.end.y=parseFloat(c.getAttribute('height'), 10)+parseFloat(c.getAttribute('y'), 10);
+ if(c.getAttribute('r')!==null) this.radius=parseFloat(c.getAttribute('r'),10);
+ }
+ }
+ };
+ p.initialize=function(obj){
+ var font=(ta.Annotation.labelFont)?ta.Annotation.labelFont:{family:"Times", size:"16px"};
+ this.apply(obj);
+ this._pos();
+
+ // create either from scratch or based on the passed node
+ this.shape=this.figure.group.createGroup();
+ this.shape.getEventSource().setAttribute("id", this.id);
+ if(this.transform.dx || this.transform.dy) this.shape.setTransform(this.transform);
+ this.rectShape=this.shape.createRect({
+ x:this.start.x, y: this.start.y, width: this.end.x-this.start.x, height:this.end.y-this.start.y, r:this.radius
+ }).setStroke({color:this.property('fill'), width:1}).setFill([255,255,255,0.1]);
+ this.rectShape.getEventSource().setAttribute("shape-rendering","crispEdges");
+ this.labelShape=this.shape.createText({
+ x:this.textPosition.x, y:this.textPosition.y, text:this.property('label'), align:this.textAlign
+ }).setFont(font).setFill(this.property('fill'));
+ };
+ p.destroy=function(){
+ if(!this.shape) return;
+ this.shape.remove(this.rectShape);
+ this.shape.remove(this.labelShape);
+ this.figure.group.remove(this.shape);
+ this.shape=this.rectShape=this.labelShape=null;
+ };
+ p.getBBox=function(){
+ var x=Math.min(this.start.x, this.end.x);
+ var y=Math.min(this.start.y, this.end.y);
+ var w=Math.max(this.start.x, this.end.x)-x;
+ var h=Math.max(this.start.y, this.end.y)-y;
+ return { x:x-2, y:y-2, width:w+4, height:h+4 };
+ };
+ p.draw=function(obj){
+ this.apply(obj);
+ this._pos();
+ this.shape.setTransform(this.transform);
+ this.rectShape.setShape({x:this.start.x, y: this.start.y, width: this.end.x-this.start.x, height:this.end.y-this.start.y, r:this.radius})
+ .setStroke({ color:this.property('fill'), width:1 }).setFill([255,255,255,0.1]);
+ this.labelShape.setShape({ x:this.textPosition.x, y:this.textPosition.y, text:this.property('label') }).setFill(this.property('fill'));
+ };
+ p.serialize=function(){
+ var s=this.property('stroke');
+ return '<g '+this.writeCommonAttrs()+'>'
+ + '<rect style="stroke:'+s.color+';stroke-weight:1;fill:none;" '
+ + 'x="' + this.start.x + '" '
+ + 'width="' + (this.end.x-this.start.x) + '" '
+ + 'y="' + this.start.y + '" '
+ + 'height="' + (this.end.y-this.start.y) + '" '
+ + 'rx="' + this.radius + '" '
+ + 'ry="' + this.radius + '" '
+ + ' />'
+ + '<text style="fill:'+s.color+';text-anchor:'+this.textAlign+'" font-weight="bold" '
+ + 'x="' + this.textPosition.x + '" '
+ + 'y="' + this.textPosition.y + '">'
+ + this.property('label')
+ + '</text>'
+ + '</g>';
+ };
+
+ ta.Annotation.register("Preexisting");
+})();
+
+}
diff --git a/includes/js/dojox/sketch/README b/includes/js/dojox/sketch/README
new file mode 100644
index 0000000..85b2264
--- /dev/null
+++ b/includes/js/dojox/sketch/README
@@ -0,0 +1,58 @@
+-------------------------------------------------------------------------------
+dojox.sketch
+-------------------------------------------------------------------------------
+Version 0.1
+Release date: 28/01/2008
+-------------------------------------------------------------------------------
+Project state:
+experimental
+-------------------------------------------------------------------------------
+Credits
+ Contributed by TeamPatent (supported by National Science Foundation grant 638334)
+ Tom Trenka (ttrenka@gmail.com)
+ Heng Liu/LiuCougar (heng@teampatent.com)
+-------------------------------------------------------------------------------
+Project description
+
+A cross-browser drawing editor based on dojox.gfx.
+-------------------------------------------------------------------------------
+Dependencies:
+
+dijit (Toolbar, Button, Slider)
+dojox.gfx
+dojox.xml
+-------------------------------------------------------------------------------
+Documentation
+
+Currently, 5 shapes are supported: line, single arrow line, double arrow line,
+underline text and text. The first 3 shapes can have optinal text associated.
+
+Shapes can be added, deleted, moved and modified. All of these operations can
+be undo-ed or redo-ed.
+
+TODO:
+ * provide UI to change various properties on shapes (fill, stroke, text) and
+allow changing of background image
+ * serialize/unserialize in dojox.gfx to svg (and maybe vml as well?) (or another
+simplier format? such as a json based one, which is easier to parse, and then
+write a convertor to convert the json format to svg or any other format?)
+ * Move mousedown/up/move to each shape (to prepare for the following)
+ * Add shapes for other primitive shapes (needs to decide which primitive
+dojox.gfx.shapes are useful), and add in support for user to group any
+shapes/groups to form a single "compound shape" (need to add support to set
+fill/stroke properties on the entire compound shape, which shallpropagate to
+all children shapes)
+-------------------------------------------------------------------------------
+Installation instructions
+
+Install dijit, dojox.gfx and dojox.xml first
+
+Grab the following from the Dojo SVN Repository:
+http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/sketch.js
+http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/sketch/*
+
+Install into the following directory structure:
+/dojox/sketch/
+
+...which should be at the same level as your Dojo checkout.
+-------------------------------------------------------------------------------
diff --git a/includes/js/dojox/sketch/SingleArrowAnnotation.js b/includes/js/dojox/sketch/SingleArrowAnnotation.js
new file mode 100644
index 0000000..29ad7f0
--- /dev/null
+++ b/includes/js/dojox/sketch/SingleArrowAnnotation.js
@@ -0,0 +1,183 @@
+if(!dojo._hasResource["dojox.sketch.SingleArrowAnnotation"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.SingleArrowAnnotation"] = true;
+dojo.provide("dojox.sketch.SingleArrowAnnotation");
+dojo.require("dojox.sketch.Annotation");
+dojo.require("dojox.sketch.Anchor");
+
+(function(){
+ var ta=dojox.sketch;
+ ta.SingleArrowAnnotation=function(figure, id){
+ ta.Annotation.call(this, figure, id);
+ this.transform={ dx:0, dy:0 };
+ this.start={x:0, y:0};
+ this.control={x:100, y:-50};
+ this.end={x:200, y:0};
+ this.textPosition={ x:0, y:0 };
+ this.textOffset=4;
+ this.textAlign="middle";
+ this.textYOffset=10;
+ this.rotation=0;
+
+// this.property('label',this.id);
+// this.label=this.id;
+ this.pathShape=null;
+ this.arrowhead=null;
+ this.arrowheadGroup=null;
+ this.labelShape=null;
+
+ this.anchors.start=new ta.Anchor(this, "start");
+ this.anchors.control=new ta.Anchor(this, "control");
+ this.anchors.end=new ta.Anchor(this, "end");
+ };
+ ta.SingleArrowAnnotation.prototype=new ta.Annotation;
+ var p=ta.SingleArrowAnnotation.prototype;
+ p.constructor=ta.SingleArrowAnnotation;
+
+ p.type=function(){ return 'SingleArrow'; };
+ p.getType=function(){ return ta.SingleArrowAnnotation; };
+
+ // helper functions
+ p._rot=function(){
+ // arrowhead rotation
+ var opp=this.start.y-this.control.y;
+ var adj=this.start.x-this.control.x;
+ if(!adj) adj=1;
+ this.rotation=Math.atan(opp/adj);
+ };
+ p._pos=function(){
+ // text position
+ var offset=this.textOffset, x=0, y=0;
+ var slope=this.calculate.slope(this.control, this.end);
+ if(Math.abs(slope)>=1){
+ x=this.end.x+this.calculate.dx(this.control, this.end, offset);
+ if(this.control.y>this.end.y) y=this.end.y-offset;
+ else y=this.end.y+offset+this.textYOffset;
+ } else if(slope==0){
+ x=this.end.x+offset;
+ y=this.end.y+this.textYOffset;
+ } else {
+ if(this.start.x>this.end.x){
+ x=this.end.x-offset;
+ this.textAlign="end";
+ } else {
+ x=this.end.x+offset;
+ this.textAlign="start";
+ }
+ if(this.start.y<this.end.y) y=this.end.y+this.calculate.dy(this.control, this.end, offset)+this.textYOffset;
+ else y=this.end.y+this.calculate.dy(this.control, this.end, -offset);
+ }
+ this.textPosition={ x:x, y:y };
+ };
+
+ p.apply=function(obj){
+ if(!obj) return;
+ if(obj.documentElement) obj=obj.documentElement;
+ this.readCommonAttrs(obj);
+
+ for(var i=0; i<obj.childNodes.length; i++){
+ var c=obj.childNodes[i];
+ if(c.localName=="text") this.property('label',c.childNodes.length?c.childNodes[0].nodeValue:'');
+ else if(c.localName=="path"){
+ // the line
+ var d=c.getAttribute('d').split(" ");
+ var s=d[0].split(",");
+ this.start.x=parseFloat(s[0].substr(1),10);
+ this.start.y=parseFloat(s[1],10);
+ s=d[1].split(",");
+ this.control.x=parseFloat(s[0].substr(1),10);
+ this.control.y=parseFloat(s[1],10);
+ s=d[2].split(",");
+ this.end.x=parseFloat(s[0],10);
+ this.end.y=parseFloat(s[1],10);
+ }
+ }
+ };
+ p.initialize=function(obj){
+ // create, based on passed DOM node if available.
+ var font=(ta.Annotation.labelFont)?ta.Annotation.labelFont:{family:"Times", size:"16px"};
+ this.apply(obj);
+
+ // calculate the other positions
+ this._rot();
+ this._pos();
+
+ // rotation matrix
+ var rot=this.rotation;
+ if(this.control.x>=this.end.x&&this.control.x<this.start.x) rot+=Math.PI;
+ var tRot=dojox.gfx.matrix.rotate(rot);
+
+ // draw the shapes
+ this.shape=this.figure.group.createGroup();
+ this.shape.getEventSource().setAttribute("id", this.id);
+ if(this.transform.dx||this.transform.dy) this.shape.setTransform(this.transform);
+ this.pathShape=this.shape.createPath(
+ "M"+this.start.x+","+this.start.y+" Q"+this.control.x+","+this.control.y+" "+this.end.x+","+this.end.y+" l0,0"
+ ).setStroke(this.property('stroke'));
+ this.arrowheadGroup=this.shape.createGroup().setTransform({ dx:this.start.x, dy:this.start.y }).applyTransform(tRot);
+ this.arrowhead=this.arrowheadGroup.createPath("M0,0 l20,-5 -3,5 3,5 Z").setFill(this.property('fill'));
+ this.labelShape=this.shape.createText({
+ x:this.textPosition.x, y:this.textPosition.y, text:this.property('label'), align:this.textAlign
+ }).setFont(font).setFill(this.property('fill'));
+ };
+ p.destroy=function(){
+ if(!this.shape) return;
+ this.arrowheadGroup.remove(this.arrowhead);
+ this.shape.remove(this.arrowheadGroup);
+ this.shape.remove(this.pathShape);
+ this.shape.remove(this.labelShape);
+ this.figure.group.remove(this.shape);
+ this.shape=this.pathShape=this.labelShape=this.arrowheadGroup=this.arrowhead=null;
+ };
+ p.draw=function(obj){
+ this.apply(obj);
+ this._rot();
+ this._pos();
+
+ // rotation matrix
+ var rot=this.rotation;
+ if(this.control.x<this.start.x) rot+=Math.PI;
+ var tRot=dojox.gfx.matrix.rotate(rot);
+
+ this.shape.setTransform(this.transform);
+ this.pathShape.setShape(
+ "M"+this.start.x+","+this.start.y+" Q"+this.control.x+","+this.control.y+" "+this.end.x+","+this.end.y+" l0,0"
+ ).setStroke(this.property('stroke'));
+ this.arrowheadGroup.setTransform({dx:this.start.x,dy:this.start.y}).applyTransform(tRot);
+ this.arrowhead.setFill(this.property('fill'));
+ this.labelShape.setShape({x:this.textPosition.x, y:this.textPosition.y, text:this.property('label'), align:this.textAlign})
+ .setFill(this.property('fill'));
+ };
+ p.getBBox=function(){
+ var x=Math.min(this.start.x, this.control.x, this.end.x);
+ var y=Math.min(this.start.y, this.control.y, this.end.y);
+ var w=Math.max(this.start.x, this.control.x, this.end.x)-x;
+ var h=Math.max(this.start.y, this.control.y, this.end.y)-y;
+ return { x:x, y:y, width:w, height:h };
+ };
+ p.serialize=function(){
+ var s=this.property('stroke');
+ var r=this.rotation*(180/Math.PI);
+ if(this.start.x>this.end.x) r-=180;
+ r=Math.round(r*Math.pow(10,4))/Math.pow(10,4);
+ return '<g '+this.writeCommonAttrs()+'>'
+ + '<path style="stroke:'+s.color+';stroke-width:'+s.width+';fill:none;" d="'
+ + "M"+this.start.x+","+this.start.y+" "
+ + "Q"+this.control.x+","+this.control.y+" "
+ + this.end.x+","+this.end.y
+ + '" />'
+ + '<g transform="translate(' + this.start.x + "," + this.start.y + ") "
+ + 'rotate(' + r + ')">'
+ + '<path style="fill:'+s.color+';" d="M0,0 l20,-5, -3,5, 3,5 Z" />'
+ + '</g>'
+ + '<text style="fill:'+s.color+';text-anchor:'+this.textAlign+'" font-weight="bold" '
+ + 'x="' + this.textPosition.x + '" '
+ + 'y="' + this.textPosition.y + '">'
+ + this.property('label')
+ + '</text>'
+ + '</g>';
+ };
+
+ ta.Annotation.register("SingleArrow");
+})();
+
+}
diff --git a/includes/js/dojox/sketch/Slider.js b/includes/js/dojox/sketch/Slider.js
new file mode 100644
index 0000000..e014e78
--- /dev/null
+++ b/includes/js/dojox/sketch/Slider.js
@@ -0,0 +1,31 @@
+if(!dojo._hasResource["dojox.sketch.Slider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.Slider"] = true;
+dojo.provide("dojox.sketch.Slider");
+
+dojo.require("dijit.form.Slider");
+dojo.declare("dojox.sketch.Slider",dojox.sketch._Plugin,{
+ _initButton: function(){
+ this.slider=new dijit.form.HorizontalSlider({minimum:20,maximum:200,value:20,style:"width:200px;float:right"});
+ this.connect(this.slider,'onChange','_setZoom');
+ this.connect(this.slider.sliderHandle,'ondblclick','_zoomToFit');
+ },
+ _zoomToFit: function(){
+ this.slider.setValue(this.figure.getFit(),true);
+ },
+ _setZoom: function(v){
+ if(this.figure){
+ this.figure.zoom(v);
+ }
+ },
+ setToolbar: function(t){
+ t.addChild(this.slider);
+ if(!t._reset2Zoom){
+ t._reset2Zoom=true;
+ this.connect(t,'reset','_zoomToFit');
+ }
+ }
+});
+
+dojox.sketch.registerTool("Slider", dojox.sketch.Slider);
+
+}
diff --git a/includes/js/dojox/sketch/Toolbar.js b/includes/js/dojox/sketch/Toolbar.js
new file mode 100644
index 0000000..73dc82c
--- /dev/null
+++ b/includes/js/dojox/sketch/Toolbar.js
@@ -0,0 +1,96 @@
+if(!dojo._hasResource["dojox.sketch.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.Toolbar"] = true;
+dojo.provide("dojox.sketch.Toolbar");
+
+dojo.require("dojox.sketch.Annotation");
+dojo.require("dijit.Toolbar");
+dojo.require("dijit.form.Button");
+dojo.require("dijit.form.Slider");
+
+dojo.declare("dojox.sketch.ButtonGroup", null, {
+ constructor: function(){
+ this._childMaps={};
+ this._children=[];
+ },
+ add: function(/*_Plugin*/ plugin){
+ this._childMaps[plugin]=plugin.connect(plugin,'onActivate',dojo.hitch(this,'_resetGroup',plugin));
+ this._children.push(plugin);
+ },
+// remove: function(/*_Plugin*/ plugin){
+// widget.disconnect(this._childMaps[widget.id]);
+// delete this._childMaps[widget.id];
+// this._children.splice(this._children.indexOf(widget.id),1);
+// },
+ _resetGroup: function(p){
+ var cs=this._children;
+ dojo.forEach(cs,function(c){
+ if(p!=c && c['attr']){
+ c.attr('checked',false);
+ }
+ });
+ }
+});
+
+dojo.declare("dojox.sketch.Toolbar", dijit.Toolbar, {
+ figure: null,
+ plugins: null,
+ postCreate: function(){
+ this.inherited(arguments);
+ this.shapeGroup=new dojox.sketch.ButtonGroup;
+
+ this.connect(this.figure,'onLoad','reset');
+ if(!this.plugins){
+ this.plugins=['Slider','Lead','SingleArrow','DoubleArrow','Underline','Preexisting'];
+ }
+ this._plugins=[];
+
+ dojo.forEach(this.plugins,function(obj){
+ var name=dojo.isString(obj)?obj:obj.name;
+ var p=new dojox.sketch.tools[name](obj.args||{});
+ this._plugins.push(p);
+ p.setFigure(this.figure);
+ p.setToolbar(this);
+ if(!this._defaultTool && p.button){
+ this._defaultTool=p;
+ }
+ },this);
+ },
+ destroy: function(){
+ dojo.forEach(this._plugins,function(p){
+ p.destroy();
+ });
+ this.inherited(arguments);
+ delete this._defaultTool;
+ delete this._plugins;
+ },
+ addGroupItem: function(/*_Plugin*/item,group){
+ if(group!='toolsGroup'){
+ console.error('not supported group '+group);
+ return;
+ }
+
+ this.shapeGroup.add(item);
+ },
+ reset: function(){
+ this._defaultTool.activate();
+ },
+ _setShape: function(s){
+ if(!this.figure.surface) return;
+ // now do the action.
+ if(this.figure.hasSelections()){
+ for(var i=0; i<this.figure.selected.length; i++){
+ var before=this.figure.selected[i].serialize();
+ this.figure.convert(this.figure.selected[i], s);
+ this.figure.history.add(ta.CommandTypes.Convert, this.figure.selected[i], before);
+ }
+ }
+ }
+});
+
+dojox.sketch.makeToolbar=function(node,figure){
+ var toolbar=new dojox.sketch.Toolbar({"figure":figure});
+ node.appendChild(toolbar.domNode);
+ return toolbar;
+};
+
+}
diff --git a/includes/js/dojox/sketch/UnderlineAnnotation.js b/includes/js/dojox/sketch/UnderlineAnnotation.js
new file mode 100644
index 0000000..7b67053
--- /dev/null
+++ b/includes/js/dojox/sketch/UnderlineAnnotation.js
@@ -0,0 +1,82 @@
+if(!dojo._hasResource["dojox.sketch.UnderlineAnnotation"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.UnderlineAnnotation"] = true;
+dojo.provide("dojox.sketch.UnderlineAnnotation");
+dojo.require("dojox.sketch.Annotation");
+dojo.require("dojox.sketch.Anchor");
+
+(function(){
+ var ta=dojox.sketch;
+ ta.UnderlineAnnotation=function(figure, id){
+ ta.Annotation.call(this, figure, id);
+ this.transform={dx:0, dy:0};
+ this.start={x:0, y:0};
+ this.property('label',this.id);
+ this.labelShape=null;
+ this.lineShape=null;
+ this.anchors.start=new ta.Anchor(this, "start", false);
+ };
+ ta.UnderlineAnnotation.prototype=new ta.Annotation;
+ var p=ta.UnderlineAnnotation.prototype;
+ p.constructor=ta.UnderlineAnnotation;
+
+ p.type=function(){ return 'Underline'; };
+ p.getType=function(){ return ta.UnderlineAnnotation; };
+
+ p.apply=function(obj){
+ if(!obj) return;
+ if(obj.documentElement) obj=obj.documentElement;
+ this.readCommonAttrs(obj);
+
+ for(var i=0; i<obj.childNodes.length; i++){
+ var c=obj.childNodes[i];
+ if(c.localName=="text") this.property('label',c.childNodes[0].nodeValue);
+ }
+ };
+
+ p.initialize=function(obj){
+ var font=(ta.Annotation.labelFont)?ta.Annotation.labelFont:{family:"Times", size:"16px"};
+ this.apply(obj);
+
+ // create either from scratch or based on the passed node
+ this.shape=this.figure.group.createGroup();
+ this.shape.getEventSource().setAttribute("id", this.id);
+ if(this.transform.dx || this.transform.dy) this.shape.setTransform(this.transform);
+ this.labelShape=this.shape.createText({
+ x:0, y:0, text:this.property('label'), align:"start"
+ }).setFont(font).setFill(this.property('fill'));
+ this.lineShape=this.shape.createLine({ x1:1, x2:this.labelShape.getTextWidth(), y1:2, y2:2 }).setStroke({ color:this.property('fill'), width:1 });
+ this.lineShape.getEventSource().setAttribute("shape-rendering","crispEdges");
+ };
+ p.destroy=function(){
+ if(!this.shape) return;
+ this.shape.remove(this.labelShape);
+ this.shape.remove(this.lineShape);
+ this.figure.group.remove(this.shape);
+ this.shape=this.lineShape=this.labelShape=null;
+ };
+ p.getBBox=function(){
+ var b=this.getTextBox();
+// console.log('getBBox',b,this.getLabel());
+ return { x:0, y:b.h*-1+4, width:b.w+2, height:b.h };
+ };
+ p.draw=function(obj){
+ this.apply(obj);
+ this.shape.setTransform(this.transform);
+ this.labelShape.setShape({ x:0, y:0, text:this.property('label') }).setFill(this.property('fill'));
+ this.lineShape.setShape({ x1:1, x2:this.labelShape.getTextWidth()+1, y1:2, y2:2 }).setStroke({ color:this.property('fill'), width:1 });
+ };
+ p.serialize=function(){
+ var s=this.property('stroke');
+ return '<g '+this.writeCommonAttrs()+'>'
+ + '<line x1="1" x2="'+this.labelShape.getTextWidth()+1+'" y1="5" y2="5" style="stroke:'+s.color+';stroke-weight:'+s.width+'" />'
+ + '<text style="fill:'+this.property('fill')+';" font-weight="bold" '
+ + 'x="0" y="0">'
+ + this.property('label')
+ + '</text>'
+ + '</g>';
+ };
+
+ ta.Annotation.register("Underline");
+})();
+
+}
diff --git a/includes/js/dojox/sketch/UndoStack.js b/includes/js/dojox/sketch/UndoStack.js
new file mode 100644
index 0000000..e711557
--- /dev/null
+++ b/includes/js/dojox/sketch/UndoStack.js
@@ -0,0 +1,104 @@
+if(!dojo._hasResource["dojox.sketch.UndoStack"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch.UndoStack"] = true;
+dojo.provide("dojox.sketch.UndoStack");
+dojo.require("dojox.xml.DomParser");
+
+(function(){
+ var ta=dojox.sketch;
+ ta.CommandTypes={ Create:"Create", Move:"Move", Modify:"Modify", Delete:"Delete", Convert:"Convert"};
+
+ dojo.declare("dojox.sketch.UndoStack",null,{
+ constructor: function(figure){
+ this.figure=figure;
+ this._steps=[];
+ this._undoedSteps=[];
+ },
+ apply: function(state, from, to){
+ // the key here is to neutrally move from one state to another.
+ // we let the individual functions (i.e. undo and redo) actually
+ // determine the from and to; all we do here is implement it.
+
+ // check whether this is a fullText step
+ if(!from && !to && state.fullText){
+ this.figure.setValue(state.fullText);
+ return;
+ }
+
+ var fromText=from.shapeText;
+ var toText=to.shapeText;
+
+ if(fromText.length==0&&toText.length==0){
+ // nothing to reapply?
+ return;
+ }
+ if(fromText.length==0){
+ // We are creating.
+ var o=dojox.xml.DomParser.parse(toText).documentElement;
+ var a=this.figure._loadAnnotation(o);
+ if(a) this.figure._add(a);
+ return;
+ }
+ if(toText.length==0){
+ // we are deleting.
+ var ann=this.figure.get(from.shapeId);
+ this.figure._delete([ann],true);
+ return;
+ }
+
+ // we can simply reinit and draw from the shape itself,
+ // regardless of the actual command.
+ var nann=this.figure.get(to.shapeId);
+ var no=dojox.xml.DomParser.parse(toText).documentElement;
+ nann.draw(no);
+ this.figure.select(nann);
+ return;
+ },
+ // stack methods.
+ add: function(/*String*/cmd, /*ta.Annotation?*/ann, /*String?*/before){
+ var id=ann?ann.id:'';
+ //var bbox=ann?ann.getBBox():{};
+ var after=ann?ann.serialize():"";
+ if(cmd==ta.CommandTypes.Delete) after="";
+
+ /*if(ann){
+ // fix the bbox x/y coords
+ var t=ann.transform;
+ bbox.x+=t.dx;
+ bbox.y+=t.dy;
+ }*/
+ var state={
+ cmdname:cmd,
+ //bbox:bbox,
+// fullText:fullText,
+ before:{
+ shapeId: id,
+ shapeText:before||''
+ },
+ after:{
+ shapeId: id,
+ shapeText:after
+ }
+ };
+ //console.log('dojox.sketch history add',state);
+ this._steps.push(state);
+ this._undoedSteps = [];
+ },
+ destroy: function(){},
+ undo: function(){
+ var state=this._steps.pop();
+ if(state){
+ this._undoedSteps.push(state);
+ this.apply(state,state.after,state.before);
+ }
+ },
+ redo: function(){
+ var state=this._undoedSteps.pop();
+ if(state){
+ this._steps.push(state);
+ this.apply(state,state.before,state.after);
+ }
+ }
+ });
+})();
+
+}
diff --git a/includes/js/dojox/sketch/_Plugin.js b/includes/js/dojox/sketch/_Plugin.js
new file mode 100644
index 0000000..fb7cb93
--- /dev/null
+++ b/includes/js/dojox/sketch/_Plugin.js
@@ -0,0 +1,83 @@
+if(!dojo._hasResource["dojox.sketch._Plugin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.sketch._Plugin"] = true;
+dojo.provide("dojox.sketch._Plugin");
+//dojo.require("dojox.sketch");
+dojo.require("dijit.form.Button");
+
+dojo.declare("dojox.sketch._Plugin", null, {
+ // summary
+ // This represents a "plugin" to the dojox.sketch.Figure, which is basically
+ // a single button on the Toolbar and some associated code
+ constructor: function(/*Object?*/args){
+ if(args){
+ dojo.mixin(this, args);
+ }
+ this._connects=[];
+ },
+
+ figure: null,
+ iconClassPrefix: "dojoxSketchIcon",
+ itemGroup: 'toolsGroup',
+ button: null,
+ queryCommand: null,
+ shape: "",
+ useDefaultCommand: true,
+ buttonClass: dijit.form.ToggleButton,
+ _initButton: function(){
+ if(this.shape.length){
+ //TODO: i18n
+// var label = dojox.sketch.shapes[this.shape];
+ var className = this.iconClassPrefix+" "+this.iconClassPrefix + this.shape.charAt(0).toUpperCase() + this.shape.substr(1);
+ if(!this.button){
+ var props = {
+ label: this.shape,
+ showLabel: false,
+ iconClass: className,
+ dropDown: this.dropDown,
+ tabIndex: "-1"
+ };
+ this.button = new this.buttonClass(props);
+ this.connect(this.button,'onClick','activate');
+ }
+ }
+ },
+ attr: function(name,/*?*/value){
+ if(arguments.length>1){
+ this.button.setAttribute(name,value);
+ }else{
+ this.button.getAttribute(name);
+ }
+ },
+ onActivate: function(){},
+ activate: function(/*?*/e){
+ this.onActivate();
+ this.figure.setTool(this);
+ this.attr('checked',true);
+ },
+ onMouseDown: function(e){},
+ onMouseMove: function(e){},
+ onMouseUp: function(e){},
+ destroy: function(f){
+ dojo.forEach(this._connects,dojo.disconnect);
+ },
+ connect: function(o,f,tf){
+ this._connects.push(dojo.connect(o,f,this,tf));
+ },
+ setFigure: function(/*Widget*/figure){
+ // FIXME: detatch from previous figure!!
+ this.figure = figure;
+
+ // FIXME: prevent creating this if we don't need to (i.e., figure can't handle our command)
+ this._initButton();
+ },
+ setToolbar: function(/*Widget*/toolbar){
+ if(this.button){
+ toolbar.addChild(this.button);
+ }
+ if(this.itemGroup){
+ toolbar.addGroupItem(this,this.itemGroup);
+ }
+ }
+});
+
+}
diff --git a/includes/js/dojox/sketch/resources/images/icons.gif b/includes/js/dojox/sketch/resources/images/icons.gif
new file mode 100644
index 0000000..ab33b0a
--- /dev/null
+++ b/includes/js/dojox/sketch/resources/images/icons.gif
Binary files differ
diff --git a/includes/js/dojox/sketch/resources/sketch.css b/includes/js/dojox/sketch/resources/sketch.css
new file mode 100644
index 0000000..7451395
--- /dev/null
+++ b/includes/js/dojox/sketch/resources/sketch.css
@@ -0,0 +1,17 @@
+.dojoxSketchIcon{
+ background-repeat:no-repeat;
+ height:16px;
+ min-width:16px;
+ text-align:center;
+ width:16px;
+}
+.dojoxSketchIcon { background-image:url(images/icons.gif); }
+.ShowCallouts{ background-position:0px 0px; }
+.PreviousCallout{ background-position:0px -16px; }
+.NextCallout{ background-position:0px -32px; }
+.dojoxSketchIconLead{ background-position:0px -48px; }
+.dojoxSketchIconUnderline{ background-position:0px -64px; }
+.dojoxSketchIconSingleArrow{ background-position:0px -80px; }
+.dojoxSketchIconBrace{ background-position:0px -96px; }
+.dojoxSketchIconDoubleArrow{ background-position:0px -112px; }
+.dojoxSketchIconPreexisting{ background-position:0px -128px; }
diff --git a/includes/js/dojox/sketch/resources/sketch.css.commented.css b/includes/js/dojox/sketch/resources/sketch.css.commented.css
new file mode 100644
index 0000000..7451395
--- /dev/null
+++ b/includes/js/dojox/sketch/resources/sketch.css.commented.css
@@ -0,0 +1,17 @@
+.dojoxSketchIcon{
+ background-repeat:no-repeat;
+ height:16px;
+ min-width:16px;
+ text-align:center;
+ width:16px;
+}
+.dojoxSketchIcon { background-image:url(images/icons.gif); }
+.ShowCallouts{ background-position:0px 0px; }
+.PreviousCallout{ background-position:0px -16px; }
+.NextCallout{ background-position:0px -32px; }
+.dojoxSketchIconLead{ background-position:0px -48px; }
+.dojoxSketchIconUnderline{ background-position:0px -64px; }
+.dojoxSketchIconSingleArrow{ background-position:0px -80px; }
+.dojoxSketchIconBrace{ background-position:0px -96px; }
+.dojoxSketchIconDoubleArrow{ background-position:0px -112px; }
+.dojoxSketchIconPreexisting{ background-position:0px -128px; }
diff --git a/includes/js/dojox/sketch/tests/annotation.svg b/includes/js/dojox/sketch/tests/annotation.svg
new file mode 100644
index 0000000..9638588
--- /dev/null
+++ b/includes/js/dojox/sketch/tests/annotation.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dojoxsketch="http://dojotoolkit.org/dojox/sketch" width="1000" height="1000"><g><image xlink:href="images/figure2.gif" x="0" y="0" width="1000" height="1000" /><g id="ann-1" dojoxsketch:type="Lead" transform="translate(437,169)"><path style="stroke:blue;stroke-width:2;fill:none;" d="M0,0 Q100,-50 200,0" /><text style="fill:blue;text-anchor:start" font-weight="bold" x="204" y="-2">1</text></g><g id="ann-2" dojoxsketch:type="Preexisting" transform="translate(526,408)"><rect style="stroke:blue;stroke-weight:1;fill:none;" x="0" width="104" y="0" height="210" rx="8" ry="8" /><text style="fill:blue;text-anchor:end" font-weight="bold" x="100" y="206">2</text></g><g id="ann-3" dojoxsketch:type="SingleArrow" transform="translate(537,804)"><path style="stroke:blue;stroke-width:2;fill:none;" d="M-44,13 Q-137,38 -162,-23" /><g transform="translate(-44,13) rotate(-195.0464)"><path style="fill:blue;" d="M0,0 l20,-5, -3,5, 3,5 Z" /></g><text style="fill:blue;text-anchor:middle" font-weight="bold" x="-160.36065573770492" y="-27">3</text></g><g id="ann-4" dojoxsketch:type="DoubleArrow" transform="translate(329,-7)"><path style="stroke:blue;stroke-width:2;fill:none;" d="M249,306 Q505,242 426,389" /><g transform="translate(249,306) rotate(-14.0362)"><path style="fill:blue;" d="M0,0 l20,-5, -3,5, 3,5 Z" /></g><g transform="rotate(-61.7458, 426, 389)"><path style="fill:blue;" d="M426,389 l-20,-5, 3,5, -3,5 Z" /></g><text style="fill:blue;text-anchor:middle" font-weight="bold" x="421.25" y="288.75">4</text></g><g id="ann-5" dojoxsketch:type="Underline" transform="translate(821,517)"><line x1="1" x2="91" y1="5" y2="5" style="stroke:blue;stroke-weight:2" /><text style="fill:blue;" font-weight="bold" x="0" y="0">5</text></g></g></svg> \ No newline at end of file
diff --git a/includes/js/dojox/sketch/tests/images/figure2.gif b/includes/js/dojox/sketch/tests/images/figure2.gif
new file mode 100644
index 0000000..7218a4c
--- /dev/null
+++ b/includes/js/dojox/sketch/tests/images/figure2.gif
Binary files differ
diff --git a/includes/js/dojox/sketch/tests/images/testsBodyBg.gif b/includes/js/dojox/sketch/tests/images/testsBodyBg.gif
new file mode 100644
index 0000000..4e0b4a7
--- /dev/null
+++ b/includes/js/dojox/sketch/tests/images/testsBodyBg.gif
Binary files differ
diff --git a/includes/js/dojox/sketch/tests/test_full.html b/includes/js/dojox/sketch/tests/test_full.html
new file mode 100644
index 0000000..b78c232
--- /dev/null
+++ b/includes/js/dojox/sketch/tests/test_full.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+ <head>
+ <title>Annotator/Figure Testing</title>
+<style type="text/css">
+@import "../../../dojo/resources/dojo.css";
+@Import "../../../dijit/themes/tundra/tundra.css";
+</style>
+ <link href="../resources/sketch.css" type="text/css" rel="stylesheet"/>
+ <style type="text/css">
+ body {
+ background:#fff url("images/testsBodyBg.gif") repeat-x top left;
+ padding:1em 3em;
+ }
+ table { margin:0; width:100%;}
+ table tr td { padding:0; }
+ table tr td table { border:0; width:auto;}
+
+ #container{ width:600px; }
+ #toolbar{ width:100%; }
+ #canvas{ width:600px; height:600px; background-color:#fff;border:1px solid #ccc; overflow:auto;position:relative;}
+ </style>
+ <script>var djConfig={ isDebug: false };</script>
+ <script src="../../../dojo/dojo.js"></script>
+ <script src="../Toolbar.js"></script>
+ <script>
+ dojo.require("dojox.sketch");
+ dojo.require("dojox.sketch.Slider");
+ dojo.require("dojox.sketch.LeadAnnotation");
+ dojo.require("dojox.sketch.UnderlineAnnotation");
+ dojo.require("dojox.sketch.SingleArrowAnnotation");
+ dojo.require("dojox.sketch.DoubleArrowAnnotation");
+ dojo.require("dojox.sketch.PreexistingAnnotation");
+ var f, a, t;
+ function init(){
+ var ta=dojox.sketch;
+ f=new ta.Figure();
+ dojo.connect(f, "_mu", function(){ dojo.byId("output").value=f.serialize(); });
+
+ t=ta.makeToolbar(dojo.byId("toolbar"), f);
+
+ // test loading from an SVG file
+ dojo.xhrGet({
+ url:"annotation.svg",
+ preventCache:true,
+ load:function(data, ioArgs){
+ f.load(dojox.xml.DomParser.parse(data), dojo.byId("canvas"));
+ dojo.byId("output").value=f.serialize();
+ }
+ });
+ }
+ dojo.addOnLoad(init);
+ </script>
+ </head>
+ <body class="tundra">
+ <h1>Annotator/Figure Testing Platform</h1>
+ <p>This is a generic test to create a figure from an existing SVG file, to edit that figure, and to test the undo stack. Double click a shape to set new text for it.</p>
+ <div id="container">
+ <div id="toolbar"></div>
+ <div id="canvas"></div>
+ </div>
+ <h2>Serialized output</h2>
+ <textarea id="output" style="width:100%;height:180px;overflow:auto;font-size:0.8em;"></textarea>
+ </body>
+</html>