1 /**
  2  * It manages all the Connectors on a diagram
  3  * 
  4  * @constructor
  5  * @this {ConnectorManager}
  6  **/
  7 function ConnectorManager(){
  8     /**An {Array} of {Connector}s. Keeps all Connectors from canvas*/
  9     this.connectors = [];
 10     
 11     /**An {Array} of {ConnectionPoint}s. Keeps all ConnectionPoints from canvas*/
 12     this.connectionPoints = []; 
 13     
 14     /**Used to generate unique IDs for ConnectionPoint*/
 15     this.connectionPointCurrentId = 0; //
 16 
 17     /**An {Array} of {Glue}s. Keeps all Glues from canvas*/
 18     this.glues = [];
 19 
 20     /** Tells in what mode are we:
 21      * 0 = disabled
 22      * 1 = choosing first location (creation)
 23      * 2 = choosing second location (creation)
 24      * 3 = dragging connector
 25      */
 26     this.connectionMode = ConnectorManager.MODE_DISABLED;
 27 }
 28 
 29 
 30 /**Creates a {ConnectorManager} out of JSON parsed object
 31  *@param {JSONObject} o - the JSON parsed object
 32  *@return {ConnectorManager} a newly constructed ConnectorManager
 33  *@author Alex Gheorghiu <alex@scriptoid.com>
 34  **/
 35 ConnectorManager.load = function(o){
 36     var newConnectionManager = new ConnectorManager(); //empty constructor
 37 
 38     var localLog = '';
 39 
 40     //1 - load connectors
 41     localLog += '\nCONNECTORS';
 42     newConnectionManager.connectors = Connector.loadArray(o.connectors);    
 43 //    newConnectionManager.connectorSelectedIndex = o.connectorSelectedIndex;
 44     for(var i=0;i<newConnectionManager.connectors.length;i++){
 45         localLog += '\n' + newConnectionManager.connectors[i].toString();
 46     }
 47 
 48     //2 - load connection points
 49     localLog += '\nCONNECTION POINTS';
 50     newConnectionManager.connectionPoints = ConnectionPoint.loadArray(o.connectionPoints);
 51     for(var i=0;i<newConnectionManager.connectionPoints.length; i++){
 52         localLog += "\n" +  newConnectionManager.connectionPoints[i].toString();
 53     }
 54     //alert(str);
 55     
 56 //    newConnectionManager.connectionPointSelectedIndex = o.connectionPointSelectedIndex;
 57     newConnectionManager.connectionPointCurrentId = o.connectionPointCurrentId;
 58 
 59 
 60     //3 - load glues
 61     localLog += '\nGLUES';
 62     newConnectionManager.glues = Glue.loadArray(o.glues);
 63     
 64     //localLog += 'Connection manager has ' + newConnectionManager.glues.length + " glues";
 65     for(var i=0;i<newConnectionManager.glues.length; i++){
 66         localLog += "\n" +  newConnectionManager.glues[i].toString();
 67     }
 68     
 69     //alert(localLog);
 70     
 71     newConnectionManager.connectionMode = o.connectionMode;
 72 
 73     return newConnectionManager ;
 74 }
 75 
 76 ConnectorManager.prototype = {
 77     
 78     /**
 79      *Performs a deeps equals
 80      *@param {ConnectorManager} anotherConnectionManager - the other object to compare against
 81      *@author Alex Gheorghiu <alex@scriptoid.com>
 82      **/
 83     equals : function(anotherConnectionManager){
 84         if(!anotherConnectionManager instanceof ConnectorManager){
 85             return false;
 86         }
 87 
 88         //test Connectors
 89         for(var i=0; i<this.connectors.length; i++){
 90             if(!this.connectors[i].equals(anotherConnectionManager.connectors[i])){
 91                 return false;
 92             }
 93         }
 94 
 95         //test ConnectionPoints
 96         for(var i=0; i<this.connectionPoints.length; i++){
 97             if(!this.connectionPoints[i].equals(anotherConnectionManager.connectionPoints[i])){
 98                 return false;
 99             }
100         }
101 
102         //test Glues
103         for(var i=0; i<this.glues.length; i++){
104             if(!this.glues[i].equals(anotherConnectionManager.glues[i])){
105                 return false;
106             }
107         }
108 
109 
110 
111         return this.connectionPointCurrentId == anotherConnectionManager.connectionPointCurrentId
112         && this.connectionMode == anotherConnectionManager.connectionMode;
113     },
114 
115 
116     /**Export the entire ConnectionManager to SVG format*/
117     toSVG : function(){
118         var r = '';
119         
120         for(var i=0; i<this.connectors.length; i++){
121             r += this.connectors[i].toSVG();
122         }
123 
124         return r;
125     },
126 
127     /****************************************************************/
128     /**************************Connector*****************************/
129     /****************************************************************/
130     
131     /** 
132      * Creates a new connector. Also store the connector into the pool of connectors.
133      * 
134      * @param {Point} startPoint
135      * @param {Point} endPoint
136      * @param {String} type of Connector. It can be either 'straight' or 'jagged'
137      * @return {Number} the id of the newly created Connector
138      */
139     connectorCreate:function(startPoint,endPoint,type){
140         //get a new id for Connector
141         var id = stack.currentId++;
142 
143         //create and save connector
144         this.connectors.push(new Connector(startPoint,endPoint,type, id));
145 
146         //create ConnectionPoints for Connector
147         this.connectionPointCreate(id, startPoint, ConnectionPoint.TYPE_CONNECTOR);
148         this.connectionPointCreate(id, endPoint, ConnectionPoint.TYPE_CONNECTOR);
149 
150         //log
151         var c = this.connectorGetById(id);
152         //alert('log:connectorCreate: connector has ' + c.turningPoints.length);
153 
154         return id;
155     },
156 
157     /**
158      * Remove a connector
159      * 
160      * @param {Connector} connector - the connector you want to remove
161      */
162     connectorRemove:function(connector){
163         for(var i=0; i<this.connectors.length; i++){
164             if(this.connectors[i] == connector){
165                 this.connectors.splice(i,1);
166                 this.glueRemoveAllBySecondId(this.connectionPointGetAllByParent(connector.id)[0].id);
167                 this.glueRemoveAllBySecondId(this.connectionPointGetAllByParent(connector.id)[1].id);
168                 this.connectionPointRemoveAllByParent(connector.id);
169                 if(this.connectorSelectedIndex == i){
170                     this.connectorSelectedIndex =-1;
171                 }
172                 break;
173             }//end if
174         }//end for
175     },
176 
177 
178     /**
179      *Remove a connector by Id
180      *@param {Number} connectorId - the {Connector}'s id
181      */
182     connectorRemoveById:function(connectorId){
183         for(var i=0; i<this.connectors.length; i++){
184             if(this.connectors[i].id == connectorId){
185                 this.connectorRemove(this.connectors[i]);
186                 break;
187             }//end if
188         }//end for
189     },
190 
191 
192     /**
193      *Remove a connector by Id and also it will remove the
194      *ConnectionPoints and Glues
195      *@param {Number} connectorId - the {Connector}'s id
196      */
197     connectorRemoveByIdCascade:function(connectorId){
198         var cCPs  = this.connectionPointGetAllByParent(connectorId); //Connector's ConnectionPoints
199 
200         //remove all Glues
201         for(var i=0;i<cCPs.length; i++){
202             this.glueRemoveAllBySecondId(cCPs[i].id);
203         }
204 
205         //remove connection points
206         this.connectionPointRemoveAllByParent(connectorId);
207         
208         //remove the connector
209         this.connectorRemoveById(connectorId);
210     },
211 
212 
213 
214     /**Paints all connectors and highlight the selected one
215      *@param {Context} context - a reference to HTML5's canvas
216      *@param {Number} highlightedConnectorId - the id of the highlighted connector
217      *@author Zack
218      *@author Alex
219      *TODO: maybe all painting should not be made in managers
220      **/
221     connectorPaint:function(context, highlightedConnectorId){
222         for(var i=0; i<this.connectors.length; i++){
223             //PAINT A CONNECTOR
224             //detect if we have a currenly selected Connector and "highlight" its ConnectionPoints
225             if(this.connectors[i].id == highlightedConnectorId){
226                 //paint a lime line underneath the connector
227                 context.save();
228                 if(this.connectors[i].style.lineWidth == null){
229                     this.connectors[i].style.lineWidth = 1;
230                 }
231                 var oldStyle = this.connectors[i].style.clone();
232                 var oldWidth=this.connectors[i].style.lineWidth;
233                 this.connectors[i].style = new Style();
234                 this.connectors[i].style.lineWidth = parseInt(oldWidth)+2;
235                 this.connectors[i].style.strokeStyle = "lime";
236                 this.connectors[i].paint(context);
237                 this.connectors[i].style = oldStyle;
238                 context.restore();
239                 this.connectionPointPaint(context, this.connectors[i].id)
240             }
241 
242             //paint the connector
243             context.save();
244             this.connectors[i].paint(context);
245             context.restore();
246 
247             //(if selected) activate the handlers for the connector
248             if(i == this.connectorSelectedIndex){
249                 HandleManager.figureSet(this.connectors[i]);
250                 HandleManager.paint(context);
251             }
252         }
253     },
254 
255     /**
256      * Disconnects all Connectors (actually one) that have a ConnectionPoint
257      * @param {Number} conectionPointId - connectionPoint to disconnect
258      * should only ever be called using the conPoint of a Connector, but could work either way
259      */
260     connectorDisconnect:function(conectionPointId){
261         if(this.connectionPointHasGlues(conectionPointId)){
262             this.glueRemove(conectionPointId);
263         }
264     },
265 
266 
267     /** Simplifies the connection of 2 ConnectionPoints
268      * @param {ConnectionPoint} connectionPoint1 - connectionPoint on a figure/connector
269      * @param {ConnectionPoint} connectionPoint2 - connectionPoint on a figure/connector
270      *
271      * TODO: Is it mandatory that one ConnectionPoint is from a figure an the another one from a Connector?
272      */
273     connectorConnect:function(connectionPoint1, connectionPoint2){
274         //connect the two figures, they were highlighted, so now unhighlight them
275         connectionPoint1.color = ConnectionPoint.CONNECTED_COLOR;
276         connectionPoint2.color = ConnectionPoint.CONNECTED_COLOR;
277 
278         //select the figure we jusfigurest connected to
279         var figurePoint;
280         var nonFigurePoint;
281 
282         //find out which one is from a connector
283         //set the connectors point to the figures point, has a "snap to" effect
284         if(connectionPoint1.type == ConnectionPoint.TYPE_FIGURE){
285             figurePoint = connectionPoint1;
286             nonFigurePoint = connectionPoint2;
287         }
288         else{
289             figurePoint = connectionPoint2;
290             nonFigurePoint = connectionPoint1;
291         }
292 
293         //make connector's point be identical with Figure's point
294         nonFigurePoint.point.x = figurePoint.point.x;
295         nonFigurePoint.point.y = figurePoint.point.y;
296 
297         //are these connectionPoints already connected, if not connect them now?
298         if(!this.connectionPointIsConnected(nonFigurePoint.id,figurePoint.id)){
299             this.glueCreate(nonFigurePoint.id,figurePoint.id);
300         }
301         
302         //if we are now connected at two places, make the line jagged.
303         var connector = this.connectorGetById(nonFigurePoint.parentId);
304 
305         if(connector != null){
306             if(this.connectionPointHasGlues(this.connectionPointGetAllByParent(connector.id)[0].id) //is Connector's first ConnectionPoint glued
307                 && this.connectionPointHasGlues(this.connectionPointGetAllByParent(connector.id)[1].id) //is Connector's second ConnectionPoint glued
308                 && connector.type == Connector.TYPE_JAGGED) //is Connector's type jagged
309                 {
310                 //TODO: it seems that Connector does not have a parameter....!?
311                 alert('Problem connector has ' + connector.turningPoints.length + " points");
312                 //connector.jagged();
313                 connector.jaggedReloaded();
314                 connector.redraw();
315             }
316         }
317         
318     },
319 
320 
321     /** Selects a connector using x and y coordinates, same as figure and handle
322      * @param {Number} x - the x coordinate
323      * @param {Number} y - the y coordinate
324      */
325     connectorSelectXY:function(x,y){
326         //try to pick the new selected connector
327         this.connectorSelectedIndex = -1;
328         for(var i=0; i<this.connectors.length; i++){
329             if(this.connectors[i].contains(x,y)){
330                 this.connectorSelectedIndex = i;
331                 break;
332             }
333         }
334     },
335 
336 
337     /** Returns the currently selected connector
338      * or null if none available
339      */
340     connectorGetSelected:function(){
341         if(this.connectorSelectedIndex!=-1){
342             return this.connectors[this.connectorSelectedIndex];
343         }
344         return null;
345     },
346 
347 
348     /**Get a Connector by its id
349      *@param {Number} connectorId
350      *@return {Connetor} if founded or null if none finded
351      **/
352     connectorGetById:function(connectorId){
353         for(var i=0; i<this.connectors.length; i++){
354             if(this.connectors[i].id == connectorId){
355                 return this.connectors[i];
356             }
357         }
358         return null;
359     },
360 
361 
362     /**Returns the id of the connector the mouse is over.
363      *It actually return the first connector we found.
364      *@param {Number} x - the x coord
365      *@param {Number} y - the y coord
366      *@return {Number} - the id of the connector or -1 if no connector found
367      *
368      *TODO: Note: We are picking the Connector the most far from user as if
369      *we iterate from 0 to connectors.lenght we are going from back to front, similar
370      *to painting
371      */
372     connectorGetByXY:function(x,y){
373         var id = -1;
374         for(var i=0; i<this.connectors.length; i++){
375             if(this.connectors[i].contains(x,y)){
376                 id = this.connectors[i].id;
377                 break;
378             }
379         }
380         return id;
381     },
382 
383     
384     /**Adjust a connector by a ConnectionPoint id
385      * The ConnectionPoint should be already changed/transformed
386      * Usually the connector changed it's start or end ConnectionPoint and now we need to update it's turning
387      * points (intermediate points)
388      *@param {Number} cpId - the id of the {ConnectionPoint} of the {Connector}
389      *@param {Number} x - not used
390      *@param {Number} y - not used
391      **/
392     connectorAdjustByConnectionPoint: function(cpId, x, y){
393         var cp = this.connectionPointGetById(cpId); //ConnectionPoint
394         Log.debug("connectorAdjustByConnectionPoint() - Cp is :" + cp);
395         var con = this.connectorGetById(cp.parentId); //Connector
396         var conCps = this.connectionPointGetAllByParent(con.id); //Conector's ConnectionPoints
397         
398         if(con.type == Connector.TYPE_STRAIGHT){
399             /**For STRAIGHT is very simple just update the tunrning points to the start and end connection points*/
400             var start = conCps[0].point.clone();
401             var end = conCps[1].point.clone();
402             con.turningPoints = [start, end];
403         }
404         else if(con.type == Connector.TYPE_JAGGED){
405             var ESCAPE_DISTANCE = 20;
406 //            //update connection points
407 //            if(conCps[0].id == cpId){ //start point about to change
408 //                conCps[0].point.x = x;
409 //                conCps[0].point.y = y;
410 //            } else if(conCps[1].id == cpId){ //end point about to change
411 //                conCps[1].point.x = x;
412 //                conCps[1].point.y = y;
413 //            } else{
414 //                throw Exception("ConnectorManager:connectorAdjustByConnectionPoint() - this should not happen");
415 //            }
416 
417             //update turning points
418             var start = conCps[0].point.clone();
419             var startFigure = null;
420             var startExitPoint = null;
421             var end = conCps[1].point.clone();
422             var endFigure = null;
423             var endExitPoint = null;
424 
425 
426             //START FIGURE - Do we have one?
427             var glue  = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(conCps[0].id)[0];//the (only) Glue tied to ConnectionPoint
428             if(glue != null){ //only if there is a Figure glued
429                 /**To find the exit point we will go N, E, S and W and move away from the Figure's
430                  *bound by 10 pixels.
431                  *Then we will pick the shortest distance from start to startExitPoint*/
432                 var fCp = this.connectionPointGetById(glue.id1);
433                 startFigure = stack.figureGetById(fCp.parentId);
434                 var fb = startFigure.getBounds();
435                 var tempExit = null;
436 
437                 //north
438                 tempExit = new Point(start.x, fb[1] - FIGURE_ESCAPE_DISTANCE);
439                 startExitPoint = tempExit;
440 
441                 //east
442                 tempExit = new Point(fb[2] + FIGURE_ESCAPE_DISTANCE, start.y);
443                 if(Util.distance(start, tempExit) < Util.distance(start, startExitPoint)){
444                     startExitPoint = tempExit;
445                 }
446 
447                 //south
448                 tempExit = new Point(start.x, fb[3] + FIGURE_ESCAPE_DISTANCE);
449                 if(Util.distance(start, tempExit) < Util.distance(start, startExitPoint)){
450                     startExitPoint = tempExit;
451                 }
452                 
453                 //west
454                 tempExit = new Point(fb[0] - FIGURE_ESCAPE_DISTANCE, start.y);
455                 if(Util.distance(start, tempExit) < Util.distance(start, startExitPoint)){
456                     startExitPoint = tempExit;
457                 }                
458             }
459 
460             //END FIGURE
461             var glues = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(conCps[1].id);
462             if(glues.length > 0){ //only if there is a Figure glued
463                 glue  = glues[0];//there will only be one for this
464                 Log.debug("connectorAdjustByConnectionPoint() - glue is " + glue);
465                 
466                 /*To find the exit point we will go N, E, S and W and move away from the Figure's
467                  *bound by 10 pixels.
468                  *Then we will pick the shortest distance from start to startExitPoint*/                
469                 fCp = this.connectionPointGetById(glue.id1);
470                 endFigure = stack.figureGetById(fCp.parentId);
471                 Log.debug("connectorAdjustByConnectionPoint() - figure is " + endFigure);
472                 fb = endFigure.getBounds();
473                 tempExit = null;
474 
475                 //north
476                 tempExit = new Point(end.x, fb[1] - FIGURE_ESCAPE_DISTANCE);
477                 endExitPoint = tempExit;
478 
479                 //east
480                 tempExit = new Point(fb[2] + FIGURE_ESCAPE_DISTANCE, end.y);
481                 if(Util.distance(end, tempExit) < Util.distance(end, endExitPoint)){
482                     endExitPoint = tempExit;
483                 }
484 
485                 //south
486                 tempExit = new Point(end.x, fb[3] + FIGURE_ESCAPE_DISTANCE);
487                 if(Util.distance(end, tempExit) < Util.distance(end, endExitPoint)){
488                     endExitPoint = tempExit;
489                 }
490 
491                 //west
492                 tempExit = new Point(fb[0] - FIGURE_ESCAPE_DISTANCE, end.y);
493                 if(Util.distance(end, tempExit) < Util.distance(end, endExitPoint)){
494                     endExitPoint = tempExit;
495                 }
496             } else{
497                 Log.debug("connectorAdjustByConnectionPoint() - There is NO figure at the end");
498             }
499 
500             
501             var points = [start, end];
502             var index = 0; //initially set to start
503 
504             //add start exit point
505             if(startExitPoint != null){
506                 Log.debug("ConnectionManage:connectorAdjustByConnectionPoint() - startExitPoint:  " + startExitPoint)
507                 points.splice(index + 1,0, startExitPoint);
508                 index++;
509             }
510 
511             //add end exit point
512             if(endExitPoint != null){
513                 Log.debug("ConnectionManage:connectorAdjustByConnectionPoint() - endExitPoint:  " + endExitPoint)
514                 points.splice(index + 1, 0, endExitPoint);
515                 //endIndex = endIndex + 1;
516             }
517             //Log.info('Index: ' + index);
518             con.turningPoints = points;
519             
520             //NOW THINK :D            
521 
522              //S0 - solution
523             if(points[index].x == points[index+1].x || points[index].y == points[index+1].y){
524                 //we have the S0 solution we do not have to add any new turning point
525                 //Log.info("S0 solution - no additional turning points");
526                 var intersection = false;
527                 if(startFigure != null){
528                     if(Util.lineIntersectsRectangle(points[index], points[index+1], startFigure.getBounds())){
529                         intersection = true;
530                     }
531                 }
532 
533                 if(endFigure != null){
534                     if(Util.lineIntersectsRectangle(points[index], points[index+1], endFigure.getBounds())){
535                         intersection = true;
536                     }
537                 }
538 
539                 if(!intersection){
540                     //ok , there is no intersection so we have a solution
541                     con.updateMiddleText();
542                     return;
543                 }
544             }
545 
546             //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
547             var impure = []; //vector of possible solutions (valid connector that might intersect a figure)
548             
549             //S1 - solution
550             var s1 = null;
551             
552             var s1_1 = new Point(points[index+1].x, points[index].y); //first possible solution
553             var p1_1 = Point.cloneArray(points);
554             p1_1.splice(index+1, 0, s1_1);
555             if(this._validPath(p1_1)){ //if valid collect to impure solutions
556                 impure.push(p1_1);
557             }
558             
559             var s1_2 = new Point(points[index].x, points[index+1].y); //second possible solution
560             var p1_2 = Point.cloneArray(points);
561             p1_2.splice(index+1, 0, s1_2);
562             if(this._validPath(p1_2)){ //if valid collect to impure solutions
563                 impure.push(p1_2);
564             }
565             
566             var score1 = this._scorePath( p1_1, true );
567             var score2 = this._scorePath( p1_2, true );
568             
569             //Log.info("Score 1_1: " + score1 + " score 2_1: " + score2);
570 
571             if(score1 >= 0 &&  score1 >= score2){
572                 s1 = s1_1;
573             }
574             else if(score2 >= 0 &&  score2 >= score1){
575                 s1 = s1_2;
576             }
577             
578 
579             
580             if(s1 != null){
581                 //Log.info("s1 : " + s1);
582                 /**Test so that the connector will not intersect any of the figures*/
583                 var intersection = false;
584                 var s1_points = Point.cloneArray(points);
585                 s1_points.splice(index + 1, 0, s1);
586                 //Log.info("s1_points has " + s1_points.length + " points");
587                 //
588                 //we will try to avoid useless intersection tests
589                 var startIndex = 1;
590                 var endIndex = s1_points.length-2;
591                 if(startFigure == null){
592                     startIndex = 0;
593                 }
594                 if(endFigure == null){
595                     endIndex = s1_points.length-1;
596                 }
597                 for(var p=startIndex; p < endIndex; p++){
598                     if(startFigure != null){
599                         if(Util.lineIntersectsRectangle(s1_points[p], s1_points[p+1], startFigure.getBounds())){
600                             intersection = true;
601                             break;
602                         }
603                     }
604 
605                     if(endFigure != null){
606                         if(Util.lineIntersectsRectangle(s1_points[p], s1_points[p+1], endFigure.getBounds())){
607                             intersection = true;
608                             break;
609                         }
610                     }
611                 }
612                 
613                 if(!intersection){ //we will consider we have a solution ONLY if there is no intersection
614                     con.turningPoints = s1_points;
615                     //Log.info("S1 : " + s1_points);
616                     con.updateMiddleText();
617                     return;
618                 }
619 
620                 //Log.info("con.turningPoints has " + con.turningPoints.length + " points");
621             }
622             else{
623                 //Log.info("S2 here we came");
624             }
625 
626 
627             //S2 - solution
628             var p2Pure = []; //vector of pure solutions (valid connectors that do not intersects any figure)
629             
630 
631 
632             //I - variant
633             var s2_I_1 = new Point( (points[index].x + points[index+1].x) / 2,  points[index].y);
634             var s2_I_2 = new Point( (points[index].x + points[index+1].x) / 2,  points[index+1].y);
635 
636             var p2_I = Point.cloneArray(points);
637             p2_I.splice(index + 1, 0, s2_I_1, s2_I_2);
638 
639             //Log.info("p2_I: " + p2_I);
640             if(this._validPath(p2_I)){
641                 impure.push(p2_I);
642                 var I_intersect = false;
643                 for(var j=1;j<p2_I.length-2; j++){
644                     if( (startFigure != null && Util.lineIntersectsRectangle(p2_I[j], p2_I[j+1], startFigure.getBounds()))
645                         ||
646                         (endFigure != null && Util.lineIntersectsRectangle(p2_I[j], p2_I[j+1], endFigure.getBounds()))
647                     )
648                     {
649                         I_intersect = true;
650                         break;
651                     }
652                 }
653                 if(!I_intersect){
654                     p2Pure.push(p2_I);
655                 }
656                 
657                 Log.debug("S2 I valid");
658             }
659             else{
660                 Log.debug("S2 I not valid");
661             }
662             
663 
664             //II - variant
665             var s2_II_1 = new Point( points[index].x, (points[index].y + points[index+1].y)/2 );
666             var s2_II_2 = new Point( points[index+1].x, (points[index].y + points[index+1].y)/2);
667 
668             var p2_II = Point.cloneArray(points);
669             p2_II.splice(index + 1, 0, s2_II_1, s2_II_2);
670 
671             //Log.info("p2_II: " + p2_II);
672             if(this._validPath(p2_II)){
673                 impure.push(p2_II);
674                 var II_intersect = false;
675                 for(var j=1;j<p2_II.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5)
676                     if( (startFigure != null && Util.lineIntersectsRectangle(p2_II[j], p2_II[j+1], startFigure.getBounds()))
677                         ||
678                         (endFigure != null && Util.lineIntersectsRectangle(p2_II[j], p2_II[j+1], endFigure.getBounds()))
679                     )
680                     {
681                         II_intersect = true;
682                         break;
683                     }
684                 }
685                 if(!II_intersect){
686                     p2Pure.push(p2_II);
687                 }
688                 Log.debug("S2 II valid");
689             }
690             else{
691                 Log.debug("S2 II not valid");
692             }
693 
694 
695             //III - variant (east)
696             //find the amount (stored in delta) of pixels we need to move up so no intersection with a figure will be present
697             //!See:  /documents/specs/connected_figures_deltas.jpg file
698             var eastExits = [];
699             if(startFigure != null){
700                 eastExits.push(startFigure.getBounds()[2] + 20);
701             }
702 
703             if(endFigure != null){
704                 eastExits.push(endFigure.getBounds()[2] + 20);
705             }
706 
707             if(eastExits.length != 0){
708                 var eastExit = Util.max(eastExits); //max east
709 
710                 var s2_III_1 = new Point(eastExit, points[index].y);
711                 var s2_III_2 = new Point(eastExit, points[index+1].y);
712 
713                 var p2_III =  Point.cloneArray(points);
714                 p2_III.splice(index + 1, 0, s2_III_1, s2_III_2);
715 
716                 //Log.info("p2_III: " + p2_III);
717                 if(this._validPath(p2_III)){
718                     impure.push(p2_III);
719                     
720                     Log.debug("S2 III valid");
721 
722                     var III_intersect = false;
723                     for(var j=1;j<p2_III.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5)
724                         if( (startFigure != null && Util.lineIntersectsRectangle(p2_III[j], p2_III[j+1], startFigure.getBounds()))
725                             ||
726                             (endFigure != null && Util.lineIntersectsRectangle(p2_III[j], p2_III[j+1], endFigure.getBounds()))
727                         )
728                         {
729                             III_intersect = true;
730                             break;
731                         }
732                     }
733                     if(!III_intersect){
734                         p2Pure.push(p2_III);
735                     }                    
736                 }
737                 else{
738                     Log.debug("S2 III not valid");
739                 }
740             }
741             
742 
743             //IV (North)
744             Log.debug("Start S2 IV solution");
745             var northExits = [];
746             if(startFigure != null){
747                 northExits.push(startFigure.getBounds()[1] - 20);
748             }
749 
750             if(endFigure != null){
751                 northExits.push(endFigure.getBounds()[1] - 20);
752             }
753 
754             Log.debug("S2 IV test North exit points");
755             if(northExits.length != 0){
756                 Log.debug("S2 IV we have  North points");
757                 var northExit = Util.min(northExits); //northest point
758 
759                 var s2_IV_1 = new Point(points[index].x, northExit);
760                 var s2_IV_2 = new Point(points[index+1].x, northExit);
761 
762                 var p2_IV =  Point.cloneArray(points);
763                 p2_IV.splice(index + 1, 0, s2_IV_1, s2_IV_2);
764 
765                 //Log.info("p2_IV: " + p2_IV);
766                 if(this._validPath(p2_IV)){
767                     impure.push(p2_IV);
768                     Log.debug("S2 IV valid");
769 
770                     var IV_intersect = false;
771                     for(var j=1;j<p2_IV.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5)
772                         if( (startFigure != null && Util.lineIntersectsRectangle(p2_IV[j], p2_IV[j+1], startFigure.getBounds()))
773                             ||
774                             (endFigure != null && Util.lineIntersectsRectangle(p2_IV[j], p2_IV[j+1], endFigure.getBounds()))
775                         )
776                         {
777                             IV_intersect = true;
778                             break;
779                         }
780                     }
781                     if(!IV_intersect){
782                         p2Pure.push(p2_IV);
783                         Log.debug("S2 IV pure");
784                     }
785                     else{
786                         Log.debug("S2 IV NOT pure");
787                     }
788                 }
789                 else{
790                     Log.debug("S2 IV not valid");
791                 }
792             } else{
793                 Log.debug("No North (S2 IV) points");
794             }
795 
796 
797             
798             //V (West)
799             var westExits = [];
800             if(startFigure != null){
801                 westExits.push(startFigure.getBounds()[0] - 20);
802             }
803 
804             if(endFigure != null){
805                 eastExits.push(endFigure.getBounds()[0] - 20);
806             }
807 
808             if(westExits.length != 0){
809                 var westExit = Util.min(westExits); //westest point
810 
811                 var s2_V_1 = new Point(westExit, points[index].y);
812                 var s2_V_2 = new Point(westExit, points[index+1].y);
813 
814                 var p2_V =  Point.cloneArray(points);
815                 p2_V.splice(index + 1, 0, s2_V_1, s2_V_2);
816 
817                 //Log.info("p2_V: " + p2_V);
818                 if(this._validPath(p2_V)){
819                     impure.push(p2_V);
820                     Log.debug("S2 V valid");
821 
822                     var V_intersect = false;
823                     for(var j=1;j<p2_V.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5)
824                         if( (startFigure != null && Util.lineIntersectsRectangle(p2_V[j], p2_V[j+1], startFigure.getBounds()))
825                             ||
826                             (endFigure != null && Util.lineIntersectsRectangle(p2_V[j], p2_V[j+1], endFigure.getBounds()))
827                         )
828                         {
829                             V_intersect = true;
830                             break;
831                         }
832                     }
833                     if(!V_intersect){
834                         p2Pure.push(p2_V);
835                     }
836                 }
837                 else{
838                     Log.debug("S2 V not valid");
839                 }
840             }
841 
842 
843             //VI (South)
844             //find the amount (stored in delta) of pixels we need to move up so no intersection with a figure will be present
845             var southExits = [];
846             if(startFigure != null){
847                 southExits.push(startFigure.getBounds()[3] + 20);
848             }
849 
850             if(endFigure != null){
851                 southExits.push(endFigure.getBounds()[3] + 20);
852             }
853 
854             if(southExits.length != 0){
855                 var southExit = Util.max(southExits); //southest point
856 
857                 var s2_VI_1 = new Point(points[index].x, southExit);
858                 var s2_VI_2 = new Point(points[index+1].x, southExit);
859 
860                 var p2_VI =  Point.cloneArray(points);
861                 p2_VI.splice(index + 1, 0, s2_VI_1, s2_VI_2);
862 
863                 //Log.info("p2_VI: " + p2_VI);
864                 if(this._validPath(p2_VI)){
865                     impure.push(p2_VI);
866                     Log.debug("S2 VI valid");
867 
868                     var VI_intersect = false;
869                     for(var j=1;j<p2_VI.length-2; j++){ //we will not seach for intersections with the extremities of the connector (ex: 0 to 1 and 4 to 5)
870                         if( (startFigure != null && Util.lineIntersectsRectangle(p2_VI[j], p2_VI[j+1], startFigure.getBounds()))
871                             ||
872                             (endFigure != null && Util.lineIntersectsRectangle(p2_VI[j], p2_VI[j+1], endFigure.getBounds()))
873                         )
874                         {
875                             VI_intersect = true;
876                             break;
877                         }
878                     }
879                     if(!VI_intersect){
880                         p2Pure.push(p2_VI);
881                     }
882                 }
883                 else{
884                     Log.debug("S2 VI not valid");
885                 }
886             }
887 
888             //sort solutions by their length
889             if(p2Pure.length > 1){ //sort only when we have at least 1
890                 var sorted = false;
891                 while(!sorted){
892                     sorted = true;
893                     for(k=0;k<p2Pure.length-1; k++){
894                         if(Util.getPolylineLength(p2Pure[k]) > Util.getPolylineLength(p2Pure[k+1])){
895                             var temp = p2Pure[k];
896                             p2Pure[k] = p2Pure[k+1]
897                             p2Pure[k + 1] = temp;
898                             sorted = false;
899                         }
900                     }
901                 }
902             }
903             
904             Log.info("Pure solution : " + p2Pure.length +  " Impure solutions : " + impure.length);
905 
906             //pick a solution
907             if(p2Pure.length > 0){
908                 con.turningPoints = p2Pure[0];
909             }
910             else{
911                 //TODO: maybe sort impure solutions based too ?!
912                 con.turningPoints = impure[0];
913             }
914 
915 //            //this is the solution
916 //            con.turningPoints = points;
917         }
918 
919         con.updateMiddleText();
920     },
921 
922     /**Score a ortogonal path made out of Points
923      *Iterates over a set of points (minimum 3)
924      *For each 3 points if the 3rd one is after the 2nd
925      *  on the same line we add +1 if the 3rd is up or down rlated to the 2nd we do not do anything
926      *  and if the 3rd goes back we imediatelly retun -1
927      *@param {Array} v - an array of {Point}s
928      *@param {Boolean} smooth - if true the smoothest path will have a greater score, if false the most jagged
929      *  will have a bigger score.
930      *@return {Number} - -1 if the path is wrong (goes back) or something >= 0 if is fine
931      *  The bigger the number the smooth the path is
932      *@author Alex Gheorghiu <alex@scriptoid.com>
933      **/
934     _scorePath:function(v, smooth){
935         var n = v.length;
936         if(n < 3){
937             return -1;
938         }
939 
940         var score = 0;
941         for(var i=1;i<n-1;i++){
942             if(v[i-1].x == v[i].x && v[i].x == v[i+1].x){ //on the same vertical
943                 if(signum(v[i+1].y - v[i].y) == signum(v[i].y - v[i-1].y)){ //same direction
944                     if(smooth){
945                         score++;
946                     }                    
947                 }
948                 else{ //going back - no good
949                     return -1;
950                 }
951             }
952             else if(v[i-1].y == v[i].y && v[i].y == v[i+1].y){ //on the same horizontal
953                 if(signum(v[i+1].x - v[i].x) == signum(v[i].x - v[i-1].x)){ //same direction
954                     if(smooth){
955                         score++;
956                     }
957                 }
958                 else{ //going back - no good
959                     return -1;
960                 }
961             }
962             else{ //not on same vertical nor horizontal
963                 if(!smooth){
964                     score++;
965                 }
966                 //do nothing; is like adding 0
967             }
968         }
969 
970         return score;
971     },
972 
973 
974     /**
975      *Tests if a vector of points is a valid path (not going back)
976      *@param {Array} v - an {Array} of {Point}s
977      *@return {Boolean} - true if path is valid, false otherwise
978      *@author Alex <alex@scriptoid.com>
979      **/
980     _validPath:function(v){
981         var n = v.length;
982         if(n < 3){
983             return false;
984         }
985 
986         for(var i=1;i<n-1;i++){
987             if(v[i-1].x == v[i].x && v[i].x == v[i+1].x){ //on the same vertical
988                 if(signum(v[i+1].y - v[i].y) != signum(v[i].y - v[i-1].y)){ //going back
989                     return false;
990                 }
991             }
992             else if(v[i-1].y == v[i].y && v[i].y == v[i+1].y){ //on the same horizontal
993                 if(signum(v[i+1].x - v[i].x) != signum(v[i].x - v[i-1].x)){ //going back
994                     return false;
995                 }
996             }
997         }
998 
999         return true;
1000     },
1001     
1002 
1003     /**Reset this ConnectionManager*/
1004     reset:function(){
1005         this.connectors = [];
1006         this.connectorSelectedIndex = -1;
1007         this.connectionPoints = [];
1008         this.connectionPointSelectedIndex = -1;
1009         this.connectionPointCurrentId = 0;
1010     },
1011     
1012     /** Fired from onMouseDown in index.html
1013      * @return {Boolean} - should we check for handle after this
1014      * @param {Number} x - the mouse coord
1015      * @param {Number} y - the mouse coord
1016      * @deprecated
1017      */
1018     mouseDown_deprecated:function(x,y){
1019 
1020         //get the currently selected connection point and connector
1021         var activeConnector = this.connectorGetSelected();
1022 
1023         this.connectionPointSelectXY(x, y, ConnectionPoint.TYPE_CONNECTOR);
1024         var activeConnectionPoint = this.connectionPointGetSelected();
1025         var overConnectionPoint;
1026 
1027 
1028         //if we have an active connector, then we want to get any point that is under that connector
1029         if(activeConnector != null){
1030             overConnectionPoint = this.connectionPointOver(x, y, -activeConnector.id);
1031             if(this.connectionMode != ConnectorManager.MODE_SECOND){ //not choosing second location
1032                 if(this.connectionPointOver(x,y,activeConnector.id) != null){
1033                     activeConnector.activeConnectionPointId = this.connectionPointOver(x,y,activeConnector.id).id;
1034                 }
1035                 else{
1036                     activeConnector.activeConnectionPointId=-1;
1037                 }
1038             }
1039         }
1040         else{//we have no active connector, so just get connectionpoint of any figure
1041             overConnectionPoint = this.connectionPointOver(x, y);
1042         }
1043 
1044 
1045         if(this.connectionMode == ConnectorManager.MODE_FIRST){ //choosing first location upon creating connector
1046             var conId = this.connectorCreate(new Point(x,y),new Point(x,y), connectorType);
1047 
1048             //select this last added connector (we know it's the last but somehow it's not quite right)
1049             //TODO: make the selection  based on the conId
1050             this.connectorSelectedIndex = this.connectors.length-1;
1051 
1052             //set the new connector as our active connector
1053             activeConnector = this.connectorGetSelected();
1054 
1055             //set the activeConnectionPointId to the id of the end connectionpoint
1056             activeConnector.activeConnectionPointId = this.connectionPointGetAllByParent(activeConnector.id)[1].id;
1057 
1058             alert('connection manager:mouseDown: connector has ' + activeConnector.turningPoints.length  + ' turning points');
1059             //if we are over something when we create the connector, connect them
1060             if(overConnectionPoint){
1061                 this.connectorConnect(this.connectionPointGetAllByParent(activeConnector.id)[0], overConnectionPoint);
1062             }
1063             alert('connection manager 2:mouseDown: connector has ' + activeConnector.turningPoints.length  + ' turning points');
1064             setUpEditPanel(activeConnector);
1065             activeConnectionPoint = null;
1066             this.connectionMode = ConnectorManager.MODE_SECOND;
1067         }
1068 
1069         //we have chosen our first point, now lets choose our second point
1070         
1071         else if(activeConnector!=null){
1072             drag = true;
1073 
1074             //select the handle we are near (if any)
1075             HandleManager.handleSelectXY(x,y);
1076 
1077             //make sure no figure is selected, it shouldnt be anyway
1078             stack.figureSelectedIndex=-1;
1079 
1080             //if we are over a connection point, or we are not connected at all, we can move
1081             var con=this.connectionPointOver(x,y,activeConnector.id);
1082             //we are in move mode
1083             this.connectionMode=ConnectorManager.MODE_DRAG;
1084         }
1085         stack.figureSelectedIndex = -1;
1086         return true;
1087     },
1088 
1089 
1090     /** Fired from onMouseUp in index.html when we know we are draggin a Connector
1091      * @param {Number} x - the mouse coord
1092      * @param {Number} y - the mouse coord
1093      * @deprecated
1094      */
1095     mouseUp_deprecated:function(x,y){
1096 
1097         //get our active ConnectionPoint and Connector
1098         var activeConnector = this.connectorGetSelected();
1099         var overConnectionPoint = this.connectionPointOverByType(x,y,ConnectionPoint.TYPE_FIGURE);
1100         var activeConnectionPoint = this.connectionPointGetSelected();
1101 
1102         //if we are moving and have moved away from our connection point, disconnect
1103         //we don't want to do this on move, as if the user changes their mind, it will redraw their connector
1104         //overwriting any changes to it.
1105         if(this.connectionMode == ConnectorManager.MODE_DRAG){
1106             if(activeConnectionPoint!=null && (overConnectionPoint==null || (this.connectionPointHasGlues(activeConnectionPoint.id) && !this.connectionPointIsConnected(activeConnectionPoint.id,overConnectionPoint.id))))
1107             {
1108                 this.connectorDisconnect(activeConnectionPoint.id);
1109             }
1110         }
1111         else if(this.connectionMode == ConnectorManager.MODE_SECOND){
1112 
1113             //if we are over something, connect them, the connectionPoint is already at the mouse position
1114             if(overConnectionPoint && overConnectionPoint.type==ConnectionPoint.TYPE_FIGURE){
1115                 this.connectorConnect(overConnectionPoint, this.connectionPointGetAllByParent(activeConnector.id)[1]);
1116                 activeConnectionPoint = null;
1117             }
1118             activeConnectionPoint=null;
1119 
1120             //we have finished, so set our mode back to 0
1121             this.connectionMode = ConnectorManager.MODE_DISABLED;
1122         }
1123 
1124         //if we have an overPoint, and we are moving, connect
1125         if(overConnectionPoint && this.connectionMode == ConnectorManager.MODE_DRAG){
1126             //we are over a connection point, and we are not already connected to something, i.e. we have not moved away from our current point
1127             if(overConnectionPoint && activeConnectionPoint!=overConnectionPoint && activeConnector.activeConnectionPointId!=-1){
1128                 var glue = this.glueGetByConnectionPointId(activeConnector.activeConnectionPointId)[0];
1129                 if(!glue || (glue.id1!=overConnectionPoint.id && glue.id2!=overConnectionPoint.id)){
1130                     this.connectorConnect(this.connectionPointGet(activeConnector.activeConnectionPointId),overConnectionPoint);
1131                     this.connectionPointSelectedIndex = -1;
1132                 }
1133             }
1134             if (this.connectorGetMouseOver(x,y)!=activeConnector){
1135                 this.connectorSelectedIndex=-1;
1136             }
1137         }
1138 
1139         //if we are not in "click" mode (i.e. when creating a new connector) then we are no longer in move mode
1140         if(this.connectionMode!=ConnectorManager.MODE_SECOND){
1141             this.connectionMode=ConnectorManager.MODE_DISABLED;
1142         }
1143     },
1144 
1145     /** Fired from onMouseMove in index.html
1146      * @param {Number} x - the mouse coord
1147      * @param {Number} y - the mouse coord
1148      * @return {Boolean} - true if should we redraw after this action, false otherwise
1149      * @deprecated
1150      */
1151     mouseMove_deprecated:function(x,y){
1152         var redraw = false;
1153 
1154         //get our active Connector and ConnectionPoints
1155         var activeConnectionPoint = this.connectionPointGetSelected();
1156         var activeConnector = this.connectorGetSelected();
1157         var overConnectionPoint; // ConnectionPoint we are over
1158 
1159         //get the point under our mouse
1160         if(activeConnector){
1161             overConnectionPoint = this.connectionPointOver(x,y,-activeConnector.id);
1162         }
1163         else{
1164             overConnectionPoint = this.connectionPointOver(x,y);
1165         }
1166         
1167         //we have at least made our first point of a new connector, or we are moving an existing one
1168         if(this.connectionMode != ConnectorManager.MODE_DISABLED){
1169 
1170             //we are over a connection point that is not connected, so highlight it
1171             if(overConnectionPoint && this.glueGetByConnectionPointId(overConnectionPoint.id).length==0){
1172                 overConnectionPoint.color = ConnectionPoint.OVER_COLOR;
1173             }
1174             redraw = true;
1175         }
1176 
1177         //we are an already made/glued connector
1178         if(this.connectionMode == ConnectorManager.MODE_DRAG){
1179 
1180             //make the relevant matrix (should work with snap to also)
1181             var MOVE = generateMoveMatrix(activeConnector, x, y);
1182             
1183             //we are dragging from a connectionpoint
1184             if(activeConnector.activeConnectionPointId!=-1){
1185                 activeConnector.adjust(MOVE, this.connectionPointGet(activeConnector.activeConnectionPointId).point);
1186             }
1187             
1188             //we are moving from the middle, so only allow it if we have no connections
1189             else if(!this.connectionPointHasGlues(this.connectionPointGetAllByParent(activeConnector.id)[0].id)&& !this.connectionPointHasGlues(this.connectionPointGetAllByParent(activeConnector.id)[1].id)){
1190                 activeConnector.transform(MOVE);
1191             }
1192             redraw=true;
1193         }
1194 
1195         //we have created our first point of a new connector, so move the end point with our mouse
1196         if(this.connectionMode==ConnectorManager.MODE_SECOND){
1197             var endPoint=activeConnector.turningPoints[activeConnector.turningPoints.length-1];
1198 
1199             //doesnt always work properly
1200             //we are creating a jagged connector, so make sure it moves in right angled turns
1201             //this just creates a simple 1 turn connector, just while creating
1202             if(activeConnector.turningPoints.length==2 && activeConnector.type=='jagged'){
1203                 endPoint=activeConnector.turningPoints.pop();
1204                 activeConnector.turningPoints.push(new Point(endPoint.x,activeConnector.turningPoints[0].y));
1205                 activeConnector.turningPoints.push(endPoint);
1206             }
1207             activeConnector.adjust(Matrix.translationMatrix(x-endPoint.x,y-endPoint.y), endPoint)
1208             redraw = true;
1209         }
1210         return redraw;
1211     },
1212 
1213     /****************************************************************/
1214     /**************************ConnectionPoint*****************************/
1215     /****************************************************************/
1216 
1217     /**Returns the selected connection point
1218      *@return a {ConnectionPoint} that is stored at position selectedConnectionPointIndex
1219      *or null if selectedConnectionPointIndex is not set (equal to -1)
1220      **/
1221     connectionPointGetSelected:function(){
1222         if(this.connectionPointSelectedIndex == -1){
1223             return null
1224         }
1225         return this.connectionPoints[this.connectionPointSelectedIndex];
1226     },
1227 
1228     /** Returns a connection point id based on an x and y and the ConnectionPoint.RADIUS
1229      * It will pick the first one that matches the criteria
1230      *@param {Number} x - the x coordinates of the point
1231      *@param {Number} y - the y coordinates of the point
1232      *@param {Boolean} type - the type of connector to select. Can be 'connector'(ConnectionPoint.TYPE_CONNECTOR)
1233      *  or 'figure' (ConnectionPoint.TYPE_FIGURE)
1234      *@author Alex Gheorghiu <alex@scriptoid.com>
1235      */
1236     connectionPointGetByXY:function(x,y, type){
1237         var id = -1;
1238 
1239         for(var i=0; i<this.connectionPoints.length; i++){
1240             if( this.connectionPoints[i].contains(x,y) && this.connectionPoints[i].type == type ){
1241                 id = this.connectionPoints[i].id;
1242                 break;
1243             }
1244         }
1245 
1246         return id;
1247     },
1248 
1249     /** Creates a new ConnectionPoint. Beside creating the ConnectionPoint it will also
1250      * inject the id and store the ConnectionPoint
1251      * 
1252      * @param {Number} parentId - the id of the parent ( Figure or Connector )this ConnectionPoint will belong to
1253      * @param {Point} point - the location
1254      * @param {String} type - the type of parent. It can be 'figure' (ConnectionPoint.TYPE_FIGURE)
1255      *      or 'connector' {ConnectionPoint.TYPE_CONNECTOR}
1256      * @return {ConnectionPoint} with a proper id set
1257      *
1258      * @author Alex Gheorghiu <alex@scriptoid.com>
1259      */
1260     connectionPointCreate:function(parentId, point, type){
1261         var conPoint = new ConnectionPoint(parentId, point.clone(), this.connectionPointCurrentId++, type);
1262         this.connectionPoints.push(conPoint);
1263         
1264         return conPoint;
1265     },
1266 
1267 
1268     /** Removes the connectionPoints associated with a parent (it can be either Figure or Connector)     
1269      * @param {Number} parentId - the figure id
1270      * @author Alex Gheorghiu <alex@scriptoid.com>
1271      */
1272     connectionPointRemoveAllByParent:function(parentId){
1273         for(var i=0; i<this.connectionPoints.length; i++){
1274             if(this.connectionPoints[i].parentId == parentId){
1275                 this.connectionPoints.splice(i,1);
1276                 i--;
1277             }
1278         }
1279     },
1280 
1281 
1282     /** Returns a ConnectionPoint based on its id
1283      * or null if none finded
1284      *@param {Number} connectionPointId - the id
1285      *@return {ConnectionPoint}
1286      */
1287     connectionPointGetById:function(connectionPointId){
1288         for(var i=0; i<this.connectionPoints.length; i++){
1289             if(this.connectionPoints[i].id == connectionPointId){
1290                 return this.connectionPoints[i];
1291             }
1292         }
1293         return null;
1294     },
1295     
1296     
1297     /** Returns a ConnectionPoint based on its id
1298      * or null if none finded
1299      *@param {Number} parentId - the id of the parent
1300      *@param {Number} x - the x of the point
1301      *@param {Number} y - the y of the point
1302      *@return {ConnectionPoint}
1303      */
1304     connectionPointGetByParentAndCoordinates:function(parentId, x, y){
1305         for(var i=0; i<this.connectionPoints.length; i++){
1306             if(this.connectionPoints[i].parentId == parentId 
1307                 && this.connectionPoints[i].point.x == x
1308                 && this.connectionPoints[i].point.y == y
1309 
1310                 )
1311                 {
1312                 return this.connectionPoints[i];
1313             }
1314         }
1315         return null;
1316     },
1317 
1318 
1319     /** Returns a subset of whole ConnectionPoints that belong to a figure or a connector
1320      *@param {Number} parentId - the figure or connector's id whom subset we want
1321      *@return {Array}{ConnectionPoint}s
1322      *@author Alex Gheorghiu <alex@scriptoid.com>
1323      */
1324     connectionPointGetAllByParent:function(parentId){
1325         var collectedPoints = [];
1326        
1327         for(var connectionPoint in this.connectionPoints){
1328             if(this.connectionPoints[connectionPoint].parentId == parentId){
1329                 collectedPoints.push(this.connectionPoints[connectionPoint]);
1330             }
1331         }
1332         return collectedPoints;
1333     },
1334 
1335     /**All ConnectionPoints we are over - but filtered by the parent type
1336      *@param {Number} x - the x coordinate 
1337      *@param {Number} y - the x coordinate
1338      *@return {Array} of {ConnectionPoint}s
1339      *@author Alex Gheorghiu <alex@scriptoid.com>
1340      *
1341      *TODO: either documentation or the implementation is weird
1342      *@deprecated
1343      **/
1344     connectionPointOverByType:function(x,y){
1345         var collectedPoints = [];        
1346         for(var i=0; i<this.connectionPoints.length; i++){
1347             if( this.connectionPoints[i].contains(x,y)){
1348                 collectedPoints.push(this.connectionPoints[i]);
1349             }
1350         }
1351         return collectedPoints;
1352     },
1353 
1354     
1355     /** Get the connectionPoint the mouse is over
1356      * @param {Number} x - coordinate
1357      * @param {Number} y - coordinate
1358      * @param {Number} parentFigureId - the subset to obtain (optional)
1359      * If the parentFigureId is null we will get extend our search to all the ConnectionPoints on the canvas
1360      * If the parentFigureId is positive we will limit our search to that shape (Figure or Connector)
1361      * If the parentFigureId is negative we will search on all Canvas but except that shape (Figure or Connector)
1362      * @return {ConnectionPoint} that fit the criteria or null if none present
1363      * @author Alex Gheorghiu <alex@scriptoid.com>
1364      */
1365     connectionPointOver:function(x, y, parentFigureId){
1366         var foundedConnectionPoint = null;
1367 
1368         if(typeof(parentFigureId) == 'number'){ //we have a Figure id specified
1369             if(parentFigureId < 0 ){ //search whole canvas except a figure
1370                 for(var canvasConnectionPoint in this.connectionPoints){
1371                     if(this.connectionPoints[canvasConnectionPoint].parentId != -parentFigureId && this.connectionPoints[canvasConnectionPoint].contains(x,y)){
1372                         this.connectionPoints[canvasConnectionPoint].color = ConnectionPoint.OVER_COLOR;
1373                         foundedConnectionPoint = this.connectionPoints[canvasConnectionPoint];
1374                     }
1375                 }
1376             }
1377             else{ //search only a  figure
1378                 var figureConnectionPoints = this.connectionPointGetAllByParent(parentFigureId);
1379                 for(var figureConnectionPoint in figureConnectionPoints){
1380                     if(figureConnectionPoints[figureConnectionPoint].contains(x,y)){
1381                         figureConnectionPoints[figureConnectionPoint].color = ConnectionPoint.OVER_COLOR;
1382                         foundedConnectionPoint = figureConnectionPoints[figureConnectionPoint];
1383                     }
1384                 }
1385             }
1386         }
1387         else{ //search whole canvas
1388             for(var connectionPoint in this.connectionPoints){
1389                 if(this.connectionPoints[connectionPoint].contains(x,y)){
1390                     this.connectionPoints[connectionPoint].color = ConnectionPoint.OVER_COLOR;
1391                     foundedConnectionPoint = this.connectionPoints[connectionPoint];
1392                 }
1393             }
1394         }
1395         
1396 
1397         return foundedConnectionPoint;
1398     },
1399     
1400 
1401     /**Paints ALL ConnectionPoints that are attached to a shape (Figure or Connector)
1402      *@param {Context} context - the HTML5 canvas' context
1403      *@param {Number} parentFigureId - the the parent figure's ID
1404      */
1405     connectionPointPaint:function(context, parentFigureId){
1406         var figureConnectionPoints = this.connectionPointGetAllByParent(parentFigureId);
1407         
1408         for(var conPoint in figureConnectionPoints){
1409             figureConnectionPoints[conPoint].paint(context);
1410         }
1411     },
1412 
1413 
1414     /**
1415      * Transform all ConnectionPoints of a  Figure
1416      * @param {Number} fId - the the parent figure's ID
1417      * @param {Matrix} matrix - the transformation matrix
1418      **/
1419     connectionPointTransform:function(fId, matrix){
1420         var fCps = this.connectionPointGetAllByParent(fId);
1421         //Log.info("ConnectionManager: connectionPointTransform()....1");
1422         //get all shape's connection points
1423         for(var i = 0 ; i < fCps.length; i++){
1424             //transform figure's connection points (no go for the other side)
1425             fCps[i].transform(matrix);
1426             //Log.info("\tConnectionManager: connectionPointTransform()....2");
1427 
1428             //get all glues for current connection point
1429             var glues = this.glueGetByFirstConnectionPointId(fCps[i].id);
1430             //Log.info("\tConnectionManager: connectionPointTransform()" + fCps[i].id + " glues = " + glues.length);
1431             for(var j=0; j<glues.length; j++){
1432                 //get the ConnectionPoint from other side of the glue (from the connector)
1433                 var conCp = this.connectionPointGetById( glues[j].id2);
1434                 //Log.info("\t\tConnectionManager: connectionPointTransform() - connector's point " + conCp);
1435                 conCp.transform(matrix);
1436 
1437                 //get attached connector
1438 //                var con = this.connectorGetById(conCp.parentId);
1439 
1440                 //adjust attached Connector through the ConnectionPoint
1441 //                con.adjust(matrix, conCp.point.clone());
1442                 this.connectorAdjustByConnectionPoint(glues[j].id2 /*, x, y*/)
1443             //Log.info("ConnectionManager: connectionPointTransform()...");
1444             }
1445         }
1446     //alert("look ma!");
1447     },
1448 
1449 
1450     /**
1451      * See if two {ConnectionPoint}s are connected
1452      *@param id1 - a {ConnectionPoint}'s id
1453      *@param id2 - another {ConnectionPoint}'s id
1454      *@author Alex Gheorghiu <alex@scriptoid.com>
1455      **/
1456     connectionPointIsConnected:function(id1,id2){
1457         for (var i=0; i < this.glues.length; i++){
1458             if( (id1 == this.glues[i].id1 && id2 == this.glues[i].id2)
1459                 || (id1 == this.glues[i].id2 && id2 == this.glues[i].id1) ){
1460                 return true;
1461             }
1462         }
1463         return false;
1464     },
1465 
1466     /**
1467      *TODO: This function needs a serious review
1468      *as it seems is no longer used?!?
1469      *@deprecated
1470      *@author Zack*/
1471     connectionPointUnhighlightAll:function(x,y){
1472         for(var i=0; i<this.connectionPoints.length; i++){
1473             var oppositeType = this.connectionPoints[i].type;
1474             if(oppositeType == ConnectionPoint.TYPE_FIGURE){
1475                 oppositeType = ConnectionPoint.TYPE_CONNECTOR
1476             }
1477             else{
1478                 oppositeType = ConnectionPoint.TYPE_FIGURE
1479             }
1480 
1481             var found = false;//we use this like an else if, as we need internal if's to keep code clean
1482             //
1483             //if this is connected, and our point is ontop of its connected point
1484             if(this.connectionPointHasGlues(this.connectionPoints[i].id)){
1485                 var point = this.connectionPointOverByType(this.connectionPoints[i].point.x, this.connectionPoints[i].point.y, oppositeType);
1486                 if(point && (point.id == this.glueGetByConnectionPointId(this.connectionPoints[i].id)[0].id1 ||
1487                     point.id == this.glueGetByConnectionPointId(this.connectionPoints[i].id)[0].id2)){
1488                     this.connectionPoints[i].color = ConnectionPoint.CONNECTED_COLOR;
1489                     found = true;
1490                 }
1491 
1492             }
1493 
1494             //we are either not connected, or are currently dragging a connected point away from its respective point, and are near another point
1495             if(!found && this.connectionPointOverByType(x, y, ConnectionPoint.TYPE_FIGURE)==this.connectionPoints[i]){
1496                 this.connectionPoints[i].color = ConnectionPoint.OVER_COLOR;
1497             }
1498 
1499             //we are not near a point
1500             else if(!found){
1501                 this.connectionPoints[i].color = ConnectionPoint.NORMAL_COLOR;
1502             }
1503         }
1504 
1505         //now make our active connetion point of our selected connector do the same
1506         if(this.connectionPointOverByType(x, y, ConnectionPoint.TYPE_FIGURE) && this.connectorGetSelected()){
1507             if(this.connectorGetSelected().activeConnectionPointId!=-1){
1508                 var point = this.connectionPointOverByType(x, y, ConnectionPoint.TYPE_FIGURE);
1509                 var glues = this.glueGetByConnectionPointId(this.connectorGetSelected().activeConnectionPointId)
1510                 if(glues.length == 0 || (glues[0].id1 != point.id && glues[0].id2 != point.id)){
1511                     this.connectionPointGet(this.connectorGetSelected().activeConnectionPointId).color = ConnectionPoint.OVER_COLOR;
1512                 }
1513             }
1514         }
1515     },
1516     
1517     /**
1518      *See if a {ConnectionPoint} has {Glue}s attached to it
1519      *@param {Number} conectionPointId - the {ConnectionPoint}'s id
1520      *@return true - if we have {Glue}s with it or false if not
1521      *@author Zack Newsham <zack_newsham@yahoo.co.uk>
1522      *@author Alex Gheorghiu <alex@scriptoid.com>
1523      **/
1524     connectionPointHasGlues:function(conectionPointId){
1525         for (var i=0; i<this.glues.length; i++){
1526             if(conectionPointId == this.glues[i].id1 || conectionPointId == this.glues[i].id2){
1527                 return true;
1528             }
1529         }
1530         return false;
1531     },
1532 
1533     /****************************************************************/
1534     /**************************Glue*****************************/
1535     /****************************************************************/
1536 
1537     /** Returns all {Glue}s that have the first Id equals with a certain id value
1538      *@param {Number} pointId - {Figure}'s id
1539      *@return {Array}{Glue}s
1540      *@author Alex Gheorghiu <alex@scriptoid.com>
1541      */
1542     glueGetByFirstConnectionPointId:function(pointId){
1543         var collectedGlues = [];
1544         for(var i=0; i<this.glues.length; i++){
1545             if(this.glues[i].id1 == pointId){
1546                 collectedGlues.push(this.glues[i]);
1547             }
1548         }
1549         return collectedGlues;
1550     },
1551 
1552     /** Returns all {Glue}s that have the second Id equals with a certain id value
1553      *@param {Number} pointId - {ConnectionPoint}'s id
1554      *@return {Array}{Glue}s
1555      *@author Alex Gheorghiu <alex@scriptoid.com>
1556      */
1557     glueGetBySecondConnectionPointId:function(pointId){
1558         var collectedGlues = [];
1559         for(var i=0; i<this.glues.length; i++){
1560             if(this.glues[i].id2 == pointId){
1561                 collectedGlues.push(this.glues[i]);
1562             }
1563         }
1564         return collectedGlues;
1565     },
1566 
1567 
1568 
1569     /**Creates a new {Glue} and store it into the glue database. Use this instead
1570      *of creating the Glues by simply "new" operator
1571      *
1572      *@param {Number} firstId - the id of the first {ConnectionPoint} - usually the {Figure}'s {ConnectionPoint} id
1573      *@param {Number} secondId - the id of the second {ConnectionPoint} - usually the {Connector}'s {ConnectionPoint} id
1574      *@return {Glue} - the newly created Glue
1575      *@author Alex Gheorghiu <alex@scriptoid.com>
1576      **/
1577     glueCreate:function(firstId, secondId){
1578         var glue = new Glue(firstId, secondId);
1579 
1580         this.glues.push(glue);
1581 
1582         return glue;
1583     },
1584 
1585 
1586     /**Removes all the  {Glue}s  based on it's two IDs
1587      *@param {Number} id1 - the id of the first shape (usually the Figure)
1588      *@param {Number} id2 - the id of the second shape (usually the Connector)
1589      *
1590      *@author Alex Gheorghiu <alex@scriptoid.com>
1591      **/
1592     glueRemoveByIds:function(id1, id2){
1593         for (var i=0; i<this.glues.length; i++){
1594             if(id1 == this.glues[i].id1 && id2 == this.glues[i].id2){
1595                 this.glues.splice(i,1);
1596             }
1597         }
1598     },
1599     
1600     
1601     /**Removes all the  {Glue}s  based on first Id (usually the Figure)
1602      *@param {Number} id - the id of the first shape (usually the Figure)
1603      *@author Alex Gheorghiu <alex@scriptoid.com>
1604      **/
1605     glueRemoveAllByFirstId:function(id){
1606         for (var i=0; i<this.glues.length; i++){
1607             if(id == this.glues[i].id1){
1608                 this.glues.splice(i,1);
1609             }
1610         }
1611     },
1612 
1613 
1614     /**Removes all the  {Glue}s  based on first Id (usually the Connector)
1615      *@param {Number} id - the id of the second shape (usually the Connector)
1616      *@author Alex Gheorghiu <alex@scriptoid.com>
1617      **/
1618     glueRemoveAllBySecondId:function(id){
1619         for (var i=0; i<this.glues.length; i++){
1620             if(id == this.glues[i].id2){
1621                 this.glues.splice(i,1);
1622             }
1623         }
1624     },
1625 
1626     /** Returns all {Glue}s that have the first Id equals with a certain id value
1627      *@param {Number} firstId - first id (usually {Figure}'s id)
1628      *@param {Number} secondId - second id (usually {Connector}'s id)
1629      *@return {Array}{Glue}s
1630      *@author Alex Gheorghiu <alex@scriptoid.com>
1631      */
1632     glueGetAllByIds:function(firstId, secondId){
1633         var collectedGlues = [];
1634         for(var i=0; i<this.glues.length; i++){
1635             if(this.glues[i].id1 == firstId && this.glues[i].id2 == secondId){
1636                 collectedGlues.push(this.glues[i]);
1637             }
1638         }
1639         return collectedGlues;
1640     }
1641 }
1642