1 /**
  2  *  Copyright 2010 Scriptoid s.r.l
  3  **/
  4 
  5 /**
  6  *Handles are created on-the-fly for a figure. They are completelly managed by the HandleManager
  7  *Each handle is responsable for an action. The {Handle} does not need to keep a reference to the parent shape
  8  *as the HandleManager will do that.
  9  *@constructor
 10  *@this {Handle}
 11  *@param {String} type - the type of handle
 12  **/
 13 function Handle(type){
 14     /**Type of Handle*/
 15     this.type = type;
 16 
 17     /*These are stupidly initialized to 0 but they should not be present at all...
 18      *anyway they got set to the proper values in HandleManager::handleGetAll() function*/
 19     
 20     /**The center of the circle (x coordinates)*/
 21     this.x = 0;
 22     
 23     /**The center of the circle (y coordinates)*/
 24     this.y = 0;
 25     
 26     /**Used by Connector handles, to not display redundant handles (i.e. when they are on the same line)*/
 27     this.visible = true;
 28 }
 29 
 30 /**It's a (static) vector of handle types
 31  * Note: R - stand for rotation
 32  * Note: More handles might be added in the future : like handles to increase the number of edges for a hexagone
 33  * Those handles will be specific for a figure
 34  **/
 35 Handle.types = ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'r' ]; //DO NOT CHANGE THE ORDER OF THESE VALUES
 36 
 37 /**It's a (static) vector of connector types*/
 38 Handle.connectorTypes = ['ns', 'ew'];
 39 
 40 /**Creates a {Handle} out of JSON parsed object
 41  *@param {JSONObject} o - the JSON parsed object
 42  *@return {Handle} a newly constructed Handle
 43  *@author Alex Gheorghiu <alex@scriptoid.com>
 44  **/
 45 Handle.load = function(o){
 46     var newHandle = new Handle(o.type);
 47     newHandle.x = o.x;
 48     newHandle.y = o.y;
 49     newHandle.visible = o.visible;
 50     return newHandle;
 51 }
 52 
 53 /**Creates an array of handles from an array of {JSONObject}s
 54  *@param {Array} v - the array of JSONObjects
 55  *@return an {Array} of {Handle}s
 56  *@author Alex Gheorghiu <alex@scriptoid.com>
 57  **/
 58 Handle.loadArray = function(v){
 59     var newHandles = [];
 60     for(var i=0; i< v.length; i++){
 61         newHandles.push(Handle.load(v[i]));
 62     }
 63     return newHandles;
 64 }
 65 
 66 /**Default handle radius*/
 67 Handle.RADIUS = 3;
 68 
 69 Handle.prototype = {
 70     /**Compares to another Handle
 71      *@param {Handle} group -  - the other glue
 72      *@return {Boolean} - true if equals, false otherwise
 73      **/
 74     equals : function(anotherHandle){
 75         if(!anotherHandle instanceof Handle){
 76             return false;
 77         }
 78 
 79         return this.type == anotherHandle.type
 80             && this.x == anotherHandle.x
 81             && this.y == anotherHandle.y
 82             && this.visible == anotherHandle.visible;
 83     },
 84 
 85     
 86     /**Handle actions for Figure
 87      *
 88      *@param {Array } lastMove - an array that will hold [x,y] of last x & y coordinates
 89      *@param {Number} newX - new X coordinate
 90      *@param {Number} newY - new Y coordinate
 91      **/
 92     actionFigure: function(lastMove, newX, newY){
 93         //see if we have a resize and prepare the figure by moving it back to Origin and "unrotate" it
 94         if(this.type != 'r'){ //if not "rotate" (figure), "updown", "leftright" (connector)
 95             //find the angle by which the figure was rotated (for any figure this is initally 0 so if != than 0 we have a rotation)
 96             var angle = Util.getAngle(HandleManager.figure.rotationCoords[0], HandleManager.figure.rotationCoords[1]);
 97 
 98             //save initial figure's center
 99             var oldCenter = HandleManager.figure.rotationCoords[0].clone();
100 
101             //move the figure to origine and "unrotate" it
102             HandleManager.figure.transform(Matrix.translationMatrix(-oldCenter.x,-oldCenter.y));
103             HandleManager.figure.transform(Matrix.rotationMatrix(-angle));
104             HandleManager.figure.transform(Matrix.translationMatrix(oldCenter.x,oldCenter.y));
105 
106             //move the new [x,y] to the "un-rotated" and "un-translated" space
107             var p = new Point(newX,newY);
108             p.transform(Matrix.translationMatrix(-oldCenter.x,-oldCenter.y));
109             p.transform(Matrix.rotationMatrix(-angle));
110             p.transform(Matrix.translationMatrix(oldCenter.x,oldCenter.y));
111             newX = p.x;
112             newY = p.y;
113 
114             var handlerPoint=new Point(this.x,this.y) //Handler's center point (used to draw it's circle)
115             //rotate that as well.
116             handlerPoint.transform(Matrix.translationMatrix(-oldCenter.x,-oldCenter.y));
117             handlerPoint.transform(Matrix.rotationMatrix(-angle));
118             handlerPoint.transform(Matrix.translationMatrix(oldCenter.x,oldCenter.y));
119         }
120         
121         var figBounds=HandleManager.figure.getBounds();
122         
123         var transX = 0; //the amount of translation on Ox
124         var transY = 0; //the amount of translation on Oy
125         var scaleX = 1; //the scale percentage on Ox
126         var scaleY = 1; //the scale percentage on Oy
127         var arc = false;
128         
129         switch(this.type){
130             case 'n':
131                 /*move the xOy coodinates at the bottom of the figure and then scale*/
132                 transY = figBounds[3];
133                 if(newY < figBounds[3]-5){ //North must not get too close to South
134                     scaleY = (figBounds[3]-newY)/(figBounds[3]-handlerPoint.y);
135                 }
136                 break;
137 
138             case 's':
139                 /*move the xOy coodinates at the top of the figure (superfluous as we are there already) and then scale*/
140                 transY = figBounds[1];
141                 if(newY > figBounds[1]+5){ //South must not get too close to North
142                     scaleY = (newY-figBounds[1])/(handlerPoint.y-figBounds[1]);
143                 }
144                 break;
145 
146             case 'w':
147                 /*move the xOy coordinates at the right of the figure and then scale*/
148                 transX = figBounds[2];
149                 if(newX < figBounds[2]-5){ //West(newX) must not get too close to East(figBounds[2])
150                     scaleX = (figBounds[2]-newX)/(figBounds[2]-handlerPoint.x);
151                 }
152                 break;
153 
154             case 'e':
155                 /*move the xOy coodinates at the left of the figure (superfluous as we are there already) and then scale*/
156                 transX = figBounds[0];
157                 if(newX > figBounds[0]+5){
158                     scaleX = (newX-figBounds[0])/(handlerPoint.x-figBounds[0]);
159                 }
160                 break;
161 
162             case 'nw':
163                 /*You can think as a combined North and West action*/
164                 transX = figBounds[2];
165                 transY = figBounds[3];
166                 if(newX<figBounds[2]-5 && newY<figBounds[3]-5){
167                     scaleY = (figBounds[3]-newY) /(figBounds[3]-handlerPoint.y);
168                     scaleX = (figBounds[2]-newX) / (figBounds[2]-handlerPoint.x);
169                 }
170                 break;
171 
172             case 'ne':
173                 transX = figBounds[0]
174                 transY = figBounds[3];
175                 if(newX>figBounds[0]+5 && newY<figBounds[3]-5){
176                     scaleX = (newX-figBounds[0])/(handlerPoint.x-figBounds[0]);
177                     scaleY = (figBounds[3]-newY)/(figBounds[3]-handlerPoint.y);
178                 }
179                 break;
180 
181             case 'sw':
182                 transX = figBounds[2]
183                 transY = figBounds[1];
184                 if(newX<figBounds[2]-5 && newY>figBounds[1]+5){
185                     scaleX = (figBounds[2]-newX)/((figBounds[2]-handlerPoint.x));
186                     scaleY = (newY-figBounds[1])/(handlerPoint.y-figBounds[1]);
187                 }
188                 break;
189 
190             case 'se':
191                 transX = figBounds[0];
192                 transY = figBounds[1];
193                 if(newX>figBounds[0]+5 && newY>figBounds[1]+5){
194                     scaleY= (newY-figBounds[1]) / (handlerPoint.y-figBounds[1]);
195                     scaleX= (newX-figBounds[0]) / (handlerPoint.x-figBounds[0]);
196                 }
197                 break;
198 
199             case 'r':
200                 //rotationCoords[0] is always the center of the shape, we clone it as when we do -rotationCoords[0].x, it is set to 0.
201                 var center = HandleManager.figure.rotationCoords[0].clone();
202                 var endAngle = Util.getAngle(HandleManager.figure.rotationCoords[0],new Point(newX,newY));
203                 var startAngle = Util.getAngle(HandleManager.figure.rotationCoords[0],HandleManager.figure.rotationCoords[1]);//new Point(lastMove[0],lastMove[1])
204                 var rotAngle = endAngle - startAngle;
205 
206 
207                 HandleManager.figure.transform(Matrix.translationMatrix(-center.x,-center.y))
208                 HandleManager.figure.transform(Matrix.rotationMatrix(rotAngle))
209                 HandleManager.figure.transform(Matrix.translationMatrix(center.x,center.y));
210                 break;    
211         }
212         
213         /*By default the NW, NE, SW and SE are scalling keeping the ratio
214          *but you can use SHIFT to cause a free (no keep ratio) change
215          *So, if no SHIFT pressed we force a "keep ration" resize
216          **/
217         if(!SHIFT_PRESSED && transX != 0 && transY != 0){//keep ratios, only affects ne/nw resize
218 
219             //if we are scaling along the x axis (West or East resize), with an arc(behaves like corner) then scale relative to x movement
220             //TODO: what's the reason for this?
221             if(this.getCursor()=="w-resize" || this.getCursor()=="e-resize"){
222                 scaleY = scaleX;
223             }
224             else { //for everything else, scale based on y
225                 scaleX = scaleY;
226             }
227         }
228 
229         if(this.type!='r'){
230             HandleManager.figure.transform(Matrix.translationMatrix(-transX, -transY));
231             HandleManager.figure.transform(Matrix.scaleMatrix(scaleX, scaleY))
232             HandleManager.figure.transform(Matrix.translationMatrix(transX, transY));
233 
234 
235             //rotate the figure back to its original coordinates
236             HandleManager.figure.transform(Matrix.translationMatrix(-oldCenter.x,-oldCenter.y));
237             HandleManager.figure.transform(Matrix.rotationMatrix(angle));
238             HandleManager.figure.transform(Matrix.translationMatrix(oldCenter.x,oldCenter.y));
239         }
240     },
241 
242     /**
243      *Handle actions for Connector
244      *
245      *@param {Array } lastMove - an array that will hold [x,y] of last x & y coordinates
246      *@param {Number} newX - new X coordinate
247      *@param {Number} newY - new Y coordinate
248      **/
249     actionConnector: function(lastMove, newX, newY){
250         switch(this.type){
251             case 'v':
252                 var index;
253                 //find the two turning points this handle is in between
254                 for(var i = 1; i < HandleManager.figure.turningPoints.length-1; i++){
255                     if(HandleManager.figure.turningPoints[i-1].y == HandleManager.figure.turningPoints[i].y 
256                         && HandleManager.figure.turningPoints[i].y == this.y 
257                         && Math.min(HandleManager.figure.turningPoints[i].x, HandleManager.figure.turningPoints[i-1].x) <= this.x 
258                         && Math.max(HandleManager.figure.turningPoints[i].x, HandleManager.figure.turningPoints[i-1].x) >= this.x)
259                     {
260                         index = i;
261                     }
262                 }
263                 //Pick turning points neighbours and translate them on Oy
264                 HandleManager.figure.turningPoints[index-1].transform( Matrix.translationMatrix(0, newY - lastMove[1]) );
265                 HandleManager.figure.turningPoints[index].transform( Matrix.translationMatrix(0, newY - lastMove[1]) );
266                 break;
267 
268             case 'h':
269                 var index;
270                 //find the two turning points this handle is in between
271                 for(var i = 1; i < HandleManager.figure.turningPoints.length-1; i++){
272                     if(HandleManager.figure.turningPoints[i-1].x == HandleManager.figure.turningPoints[i].x 
273                         && HandleManager.figure.turningPoints[i].x == this.x 
274                         && Math.min(HandleManager.figure.turningPoints[i].y, HandleManager.figure.turningPoints[i-1].y) <= this.y 
275                         && Math.max(HandleManager.figure.turningPoints[i].y, HandleManager.figure.turningPoints[i-1].y) >= this.y)
276                     {
277                         index = i;
278                     }
279                 }
280                 //Pick turning points neighbours and translate them on Ox
281                 HandleManager.figure.turningPoints[index-1].transform( Matrix.translationMatrix(newX-lastMove[0],0) );
282                 HandleManager.figure.turningPoints[index].transform( Matrix.translationMatrix(newX-lastMove[0],0) );
283                 break;
284         }
285         HandleManager.figure.updateMiddleText();
286     },
287 
288     /**Handle an action. Simply dispatch to the correct handler
289      *@param {Array } lastMove - an array that will hold [x,y] of last x & y coordinates
290      *@param {Number} newX - new X coordinate
291      *@param {Number} newY - new Y coordinate
292      **/
293     action: function(lastMove, newX, newY){
294         if(lastMove == null || lastMove.length != 2){
295             throw new Exception('Handle:action() Last move is wrong');
296         }
297         if(HandleManager.figure instanceof Connector){
298             this.actionConnector(lastMove, newX, newY);
299         }
300         else{
301             this.actionFigure(lastMove, newX, newY);
302         }
303     },
304 
305 
306     /**This is the method you have to call to paint a handler
307      * All handles will be circles...so we avoid to much of the computing for rectangle handles
308      * They will have a filling color (green) and a stoke (black)
309      * @param {Context} context - the 2D context
310      **/
311     paint : function(context){
312 
313         context.beginPath();
314         context.arc(this.x, this.y, Handle.RADIUS, 0, Math.PI*2, false);
315         context.fillStyle = "rgb(0,255,0)";
316         context.closePath();
317         context.fill();
318         
319         context.beginPath();
320         context.arc(this.x, this.y, Handle.RADIUS, 0, Math.PI*2, false);
321         context.strokeStyle = "rgb(0,0,0)";
322         context.closePath();
323         context.stroke();
324 
325         
326         if(this.type == 'r'){
327             var line = new Line(new Point(this.x,this.y), new Point(HandleManager.handles[1].x,HandleManager.handles[1].y))
328             line.style.dashLength = 3;
329             line.style.strokeStyle="grey";
330             line.paint(context);
331         }
332     },
333 
334 
335     /**See if the handle contains a point
336      *@param {Number} x - the x coordinate of the point
337      *@param {Number} y - the y coordinate of the point
338      **/
339     contains:function(x,y){
340         var p=new Point(this.x,this.y);
341         return p.near(x,y, Handle.RADIUS);
342     },
343 
344     
345     /**
346      *Get a handle bounds
347      **/
348     getBounds : function(){
349         return [this.x - Handle.RADIUS, this.y - Handle.RADIUS, this.x + Handle.RADIUS,this.y + Handle.RADIUS];
350     },
351 
352 
353     /** 
354      *Transform the Handle through a matrix
355      *@param {Matrix} matrix - the matrix that will perform the transformation
356      **/
357     transform: function(matrix){
358         var p=new Point(this.x,this.y)
359         p.transform(matrix);
360         this.x=p.x;
361         this.y=p.y;
362     },
363     
364 
365     /**Get the specific cursor for this handle. Cursor is ONLY a visual clue for
366      *  the user to know how to move his mouse.
367      *
368      *Behaviour:
369      * If North handle is in the North we have 'Up/Down arrow" cursor
370      * If North handle is in the West (so it has "Left/Right arrow") (or anything different that North)
371      *  we have 'Left/Right arrow' cursor but the figure will expand as follows:
372      *  - rotate back to initial position
373      *  - expand North
374      *  - rotate back to current position
375      *  - repaint
376      * @see <a href="http://www.w3schools.com/css/pr_class_cursor.asp">http://www.w3schools.com/css/pr_class_cursor.asp</a> for cusor values
377      * @author Zack Newsham <zack_newsham@yahoo.co.uk>
378      * @author Alex Gheorghiu <alex@scriptoid.com>
379      **/
380     
381     getCursor:function(){
382         if(HandleManager.figure instanceof Connector){
383             if(this.visible == false){
384                 return "";
385             }
386             if(this.type == 'v'){
387                 return 'ns-resize';
388             }
389             else{
390                 return 'ew-resize';
391             }
392         } //end if Connector
393         else{
394             if(this.visible == false){
395                 return "";
396             }
397             if(this.type == 'r'){
398                 return 'move';
399             }
400         
401             var figureBounds = HandleManager.figure.getBounds(); //get figure's bounds
402             var figureCenter = new Point(figureBounds[0] + ((figureBounds[2]-figureBounds[0])/2),
403                 (figureBounds[1] + ((figureBounds[3] - figureBounds[1])/2)) ); //get figure's center
404 
405             //find north
406             var closestToNorthIndex = -1; //keeps the index of closest handle to North
407             var minAngleToNorth = 2 * Math.PI; //keeps the smallest (angular) distante to North
408             var myIndex = -1;
409 
410             for(var i=0; i<HandleManager.handles.length-1; i++){
411                 var handleCenter = new Point(HandleManager.handles[i].x, HandleManager.handles[i].y);
412                 var angle = Util.getAngle(figureCenter,handleCenter); //get the angle between those 2 points 0=n
413 
414                 if(angle <= Math.PI){ //quadrant I or II
415                     if(angle < minAngleToNorth){
416                         minAngleToNorth = angle;
417                         closestToNorthIndex = i;
418                     }
419                 }
420                 else{ //quadrant III or IV
421                     if(2 * Math.PI - angle < minAngleToNorth){
422                         minAngleToNorth = 2 * Math.PI - angle
423                         closestToNorthIndex = i;
424                     }
425                 }
426             }
427 
428            //alert("closest to North is : " + closestToNorthIndex);
429            for(var k=0; k<8; k++){ //there will always be 8 resize handlers
430                //we do not use modulo 9 as we want to ignore the "rotate" handle
431                if(HandleManager.handles[(closestToNorthIndex + k) % 8] == this){
432                     return Handle.types[k]+"-resize";
433                }
434            }
435         } //end if Figure
436 
437         return "";
438     }
439 }
440 
441 
442 /**HandleManager will act like a Singleton (even not defined as one)
443  * You will attach a Figure to it and he will be in charge with the figure manipulation
444  * @constructor
445  * @this {HandleManager}
446  **/
447 function HandleManager(){
448 }
449 
450 /**The shape (figure or connector) that the HandleManager will manage*/
451 HandleManager.figure = null;
452 
453 /**An {Array} with current handles*/
454 HandleManager.handles = [];
455 
456 /**An {Array} with connector handles*/
457 HandleManager.connectorHandles = [];
458 
459 /**Selection rectangle (the rectangle upon the Handles will stay in case of a Figure/Group)*/
460 HandleManager.selectRect = null;
461 
462 /**Currently selected handle*/
463 HandleManager.handleSelectedIndex = -1;
464 
465 /**Get selected handle or null if no handler selected*/
466 HandleManager.handleGetSelected = function(){
467     if(HandleManager.handleSelectedIndex!=-1){
468         return HandleManager.handles[HandleManager.handleSelectedIndex];
469     }
470     return null;
471 }
472 
473 /**Use this method to set a new shape (Figure or Connetor)  to this manager.
474  * Every time a new figure is set, old handles will dissapear (got erased by new figure's handles)
475  **/
476 HandleManager.figureSet = function(shape){
477     HandleManager.figure = shape;
478 
479     //1. clear old/previous handles
480     HandleManager.handles = [];
481 
482     //2. setup/add handles for this figure
483     if(shape instanceof Connector) {
484         HandleManager.selectRect = null;
485         
486         //we don't want to affect the start or end points
487         for(var i=1; i<shape.turningPoints.length-2; i++){
488             var h;
489             var previousAngle = Util.getAngle(HandleManager.figure.turningPoints[i-1], HandleManager.figure.turningPoints[i], 0.00001);
490             var currentAngle = Util.getAngle(HandleManager.figure.turningPoints[i], HandleManager.figure.turningPoints[i+1], 0.00001);
491             var nextAngle = Util.getAngle(HandleManager.figure.turningPoints[i+1],HandleManager.figure.turningPoints[i+2], 0.00001);
492 
493             if(previousAngle != currentAngle && currentAngle != nextAngle){
494                 if(shape.turningPoints[i].x == shape.turningPoints[i+1].x){ //same vertical
495                     h = new Handle("h");
496                     h.x = HandleManager.figure.turningPoints[i].x;
497                     h.y = (HandleManager.figure.turningPoints[i].y + HandleManager.figure.turningPoints[i+1].y) / 2;
498 
499                 }
500                 else{ // same horizontal
501                     h = new Handle("v");
502                     h.x = (HandleManager.figure.turningPoints[i].x +  HandleManager.figure.turningPoints[i+1].x) / 2;
503                     h.y = HandleManager.figure.turningPoints[i].y;
504                 }
505                 h.visible = true;
506                 HandleManager.handles.push(h);
507             }
508         }
509     }
510     else if(shape instanceof Figure || shape instanceof Group){
511         //find Figure's angle
512         var angle = Util.getAngle(HandleManager.figure.rotationCoords[0], HandleManager.figure.rotationCoords[1]);
513 
514         //rotate it back to "normal" space (from current space)
515         HandleManager.figure.transform(Matrix.rotationMatrix(-angle), false);
516         HandleManager.selectRect = new Polygon();
517 
518         //construct bounds of the Figure in "normal" space
519         var bounds = HandleManager.figure.getBounds();
520         HandleManager.selectRect.points = [];
521         HandleManager.selectRect.addPoint(new Point(bounds[0] - 10, bounds[1] - 10)); //top left
522         HandleManager.selectRect.addPoint(new Point(bounds[2]+ 10, bounds[1] - 10)); //top right
523         HandleManager.selectRect.addPoint(new Point(bounds[2] + 10, bounds[3] + 10)); //bottom right
524         HandleManager.selectRect.addPoint(new Point(bounds[0] - 10, bounds[3] + 10)); //bottom left
525 
526         bounds = HandleManager.selectRect.getBounds();
527 
528         //update current handles
529         var handle = new Handle("nw"); //NW
530         handle.x = bounds[0];
531         handle.y = bounds[1];
532         HandleManager.handles.push(handle);
533 
534         handle = new Handle("n"); //N
535         handle.x = bounds[0]+(bounds[2]-bounds[0])/2;
536         handle.y = bounds[1];
537         HandleManager.handles.push(handle);
538 
539         handle = new Handle("ne"); //NE
540         handle.x = bounds[2];
541         handle.y = bounds[1];
542         HandleManager.handles.push(handle);
543 
544         handle = new Handle("e"); //E
545         handle.x = bounds[2];
546         handle.y = bounds[1]+(bounds[3]-bounds[1])/2;
547         HandleManager.handles.push(handle);
548 
549         handle = new Handle("se"); //SE
550         handle.x = bounds[2];
551         handle.y = bounds[3];
552         HandleManager.handles.push(handle);
553 
554         handle = new Handle("s"); //S
555         handle.x = bounds[0]+(bounds[2]-bounds[0])/2;
556         handle.y = bounds[3];
557         HandleManager.handles.push(handle);
558 
559         handle = new Handle("sw"); //SW
560         handle.x = bounds[0];
561         handle.y = bounds[3];
562         HandleManager.handles.push(handle);
563 
564         handle = new Handle("w"); //W
565         handle.x = bounds[0];
566         handle.y = bounds[1]+(bounds[3]-bounds[1])/2;
567         HandleManager.handles.push(handle);
568 
569 
570         handle = new Handle("r"); //Rotation
571         handle.x = bounds[0]+(bounds[2]-bounds[0])/2;
572         handle.y = bounds[1]-20;
573         HandleManager.handles.push(handle);
574 
575 
576         HandleManager.selectRect.transform(Matrix.rotationMatrix(angle));
577 
578         //rotate figure from "normal" space to current space
579         HandleManager.figure.transform(Matrix.rotationMatrix(angle),false);
580         if(shape instanceof Figure){
581             if(shape.primitives[0] instanceof Text && shape.primitives.length == 1){
582                 for(var i = 0; i < HandleManager.handles.length-1; i++){
583                     HandleManager.handles[i].visible = false;
584                 }
585             }
586         }
587         //now transform the handles from "normal" space too
588         for(var i=0; i<HandleManager.handles.length; i++){
589             HandleManager.handles[i].transform(Matrix.rotationMatrix(angle));
590         }
591     }
592 }
593 
594 /**Returns all handles for a shape (figure or connector).
595  *It does not mean that the HandleManager keeps records of all Handles for a
596  *Figure but more likely they are computed on-the-fly
597  *@return an {Array} of {Handle} that you can further use to manage the figure
598  **/
599 HandleManager.handleGetAll = function(){
600     return HandleManager.handles;
601 }
602 
603 /**Returns the handle from a certain coordinates
604  *@param {Number} x - the value on Ox
605  *@param {Number} y - the value on Oy
606  ***/
607 HandleManager.handleGet=function(x,y){
608     for(var i=0; i<HandleManager.handles.length; i++){
609         if(HandleManager.handles[i].contains(x,y)){
610             return HandleManager.handles[i];
611         }
612     }
613     return null;
614 }
615 
616 /**
617  *Select the handle from a certain coodinates
618  *@param {Number} x - the value on Ox
619  *@param {Number} y - the value on Oy
620  **/
621 HandleManager.handleSelectXY = function(x,y){
622     HandleManager.handleSelectedIndex=-1;
623     for (var i=0; i<HandleManager.handles.length; i++){
624         if(HandleManager.handles[i].contains(x,y)){
625             HandleManager.handleSelectedIndex = i;
626         }
627     }
628 }
629 
630 /**
631  *Clear HandleManager
632  **/
633 HandleManager.clear = function(){
634     HandleManager.handleSelectedIndex = -1;
635     HandleManager.figure = null;
636     HandleManager.handles = [];
637 }
638 
639 /**Paint the Handles, actually the HandleManager will delegate each paint to
640  *the proper Handle to paint
641  *@param {Context} context - the 2D context
642  **/
643 HandleManager.paint = function(context){
644     var handles = HandleManager.handleGetAll(); //calling this sets the coordinates
645 
646     //paint first the selection rectanle
647     context.save();
648 
649     //paint selection rectagle (if present - only for Figure and Group)
650     if(HandleManager.selectRect != null){
651         //alert("Handle manager paint!");
652         HandleManager.selectRect.style.strokeStyle="grey";
653         HandleManager.selectRect.paint(context);
654     }
655 
656     //now paint handles
657     for(var i=0; i<handles.length; i++){
658         if(handles[i].visible == true){
659             handles[i].paint(context);
660         }
661     }
662     context.restore()
663 }
664