123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /*
- Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
- Available via Academic Free License >= 2.1 OR the modified BSD license.
- see: http://dojotoolkit.org/license for details
- */
- 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(mixin){
- var self=this;
- this.annCounter=1;
- this.shapes=[];
- this.image=null;
- this.imageSrc=null;
- this.size={ w:0, h:0 };
- this.surface=null;
- this.group=null;
- //node should have tabindex set, otherwise keyboard action does not work
- this.node=null;
- this.zoomFactor=1; // multiplier for zooming.
-
- this.tools=null; // toolbar reference.
- this.obj={}; // lookup table for shapes. Not keen on this solution.
- dojo.mixin(this,mixin);
- // 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._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 || e.keyCode===122){ //ctrl+z
- self.undo();
- prevent = true;
- }
- else if(e.keyCode===89 || e.keyCode===121){ //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){
- //in IE, when clicking into the drawing canvas, the node does not get focused,
- //do it manually here to force it, otherwise the keydown event listener is
- //never triggered in IE.
- if(dojox.gfx.renderer=='vml'){
- self.node.focus();
- }
- var o=self._fromEvt(e);
- self._startPoint={ x:e.pageX, y:e.pageY };
- self._ctr=dojo.position(self.node);
- // figure out the coordinates taking scroll into account
- var scroll={x:self.node.scrollLeft,y:self.node.scrollTop};
- //var win = dojo.window.get(self.node.ownerDocument);
- //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"){
- if(!self.isSelected(o)){
- self.select(o);
- self._sameShapeSelected=false;
- }else{
- self._sameShapeSelected=true;
- }
- }
- 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:dx, dy:dy});
- self._c.setBinding({dx:dx/self.zoomFactor, dy: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)
- }
- if(rect.width && rect.height){
- 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.initUndoStack();
- };
- var p=ta.Figure.prototype;
- p.initUndoStack=function(){
- this.history=new ta.UndoStack(this);
- };
- p.setTool=function(/*dojox.sketch._Plugin*/t){
- this._ctool=t;
- };
- //gridSize: int
- // if it is greater than 0, all new shapes placed on the drawing will have coordinates
- // snapped to the gridSize. For example, if gridSize is set to 10, all coordinates
- // (only including coordinates which specifies the x/y position of shape are affected
- // by this parameter) will be dividable by 10
- p.gridSize=0;
- p._calCol=function(v){
- return this.gridSize?(Math.round(v/this.gridSize)*this.gridSize):v;
- };
- p._delete=function(arr,noundo){
- for(var i=0; i<arr.length; i++){
- //var before=arr[i].serialize();
- arr[i].setMode(ta.Annotation.Modes.View);
- arr[i].destroy(noundo);
- this.remove(arr[i]);
- this._remove(arr[i]);
- if(!noundo){
- arr[i].onRemove();
- }
- }
- arr.splice(0,arr.length);
- };
- 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.backgroundRect=this.surface.createRect({ x:0, y:0, width:this.size.w, height:this.size.h })
- // .setFill("white");
- this.group=this.surface.createGroup();
- this._cons=[];
- var es=this.surface.getEventSource();
- this._cons.push(
- // kill any dragging events.
- // for FF
- dojo.connect(es, "ondraggesture", dojo.stopEvent),
- dojo.connect(es, "ondragenter", dojo.stopEvent),
- dojo.connect(es, "ondragover", dojo.stopEvent),
- dojo.connect(es, "ondragexit", dojo.stopEvent),
- dojo.connect(es, "ondragstart", dojo.stopEvent),
- // for IE
- dojo.connect(es, "onselectstart", dojo.stopEvent),
- // hook up the drag system.
- dojo.connect(es, 'onmousedown', this._md),
- dojo.connect(es, 'onmousemove', this._mm),
- dojo.connect(es, 'onmouseup', this._mu),
- // misc hooks
- dojo.connect(es, 'onclick', this, 'onClick'),
- dojo.connect(es, 'ondblclick', this._dblclick),
- dojo.connect(node, 'onkeydown', this._keydown));
-
- this.image=this.group.createImage({ width:this.imageSize.w, height:this.imageSize.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=[];
- //TODO: how to destroy a surface properly?
- dojo.empty(this.node);
- //this.node.removeChild(this.surface.getEventSource());
- this.group=this.surface=null;
- this.obj={};
- this.shapes=[];
- };
- p.nextKey=function(){ return "annotation-"+this.annCounter++; };
- 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));
- for(var i=0; i<this.shapes.length; i++){
- this.shapes[i].zoom(this.zoomFactor);
- }
- };
- 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.offsetWidth-5)/this.size.w;
- var hF=(this.node.parentNode.offsetHeight-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","");
- }else if(key&&key.indexOf("-labelShape")>-1){
- key=key.replace("-labelShape","");
- }
- return this.obj[key];
- };
- p._keyFromEvt=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 key;
- };
- p._fromEvt=function(e){
- return this._get(this._keyFromEvt(e));
- };
- 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.getAnnotator=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.imageSize={
- w:parseFloat(img.getAttribute('width'),10),
- h:parseFloat(img.getAttribute('height'),10)
- };
- 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.onClick=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;
- })();
- }
|