1 /**This is the main JavaScript module.
  2  *We move it here so it will not clutter the index.php with a lot of JavaScript
  3  *
  4  *ALL VARIABLES DEFINED HERE WILL BE VISIBLE IN ALL OTHER MODULES AND INSIDE INDEX.PHP
  5  *SO TAKE CARE!
  6  **/
  7 
  8 /**
  9  *Thinking to use name spaces more
 10  *TODO: avoid colision
 11  *@see http://michaux.ca/articles/javascript-namespacing
 12  *@see http://www.dustindiaz.com/namespace-your-javascript/
 13  **/
 14 var testNS = {};
 15 testNS.state = 1;
 16 
 17 /**Activate or deactivate the undo feature**/
 18 var doUndo = true; 
 19 
 20 /**Usually an instance of a Command (see /lib/commands/*.js)*/
 21 var currentMoveUndo = null; 
 22 
 23 var CONNECTOR_MANAGER = new ConnectorManager();
 24 var GRIDWIDTH = 20;
 25 var fillColor=null;
 26 var strokeColor='#000000';
 27 var currentText=null;
 28 var FIGURE_ESCAPE_DISTANCE = 20; /**the distance by which the connectors will escape Figure's bounds*/
 29 
 30 /*It will store a reference to the function that will create a figure( ex: figureForKids:buildFigure3()) will be stored into this
 31  *variable so upon click on canvas this function will create the object*/
 32 var createFigureFunction = null;
 33 
 34 /**A variable that will tell us if we are in IE*/
 35 var IE = false;
 36 
 37 /**A variable that tells us if CTRL is pressed*/
 38 var CNTRL_PRESSED = false;
 39 
 40 /**A variable that tells us if SHIFT is pressed*/
 41 var SHIFT_PRESSED = false;
 42 
 43 /**Current connector. It is null if no connector selected*/
 44 var connector = null;
 45 
 46 /**Connector type*/
 47 var connectorType = '';
 48 
 49 document.onselectstart=stopselection;
 50 
 51 
 52 
 53 /**Supposelly stop any selection from happening*/
 54 function stopselection(ev){
 55     if(!ev){
 56         ev = window.event;
 57     }
 58     //If we are selecting text within anything with the className text, we allow it
 59     //This gives us the option of using textareas, inputs and any other item we
 60     //want to allow text selection in.
 61     if((IE && ev.srcElement.className == "text")  /*IE code*/
 62         || (!IE && ev.target.className == "text") /*Non IE code*/){
 63         return true;
 64     }
 65     return false;
 66 
 67 }
 68 var stack  = new Stack();
 69 
 70 
 71 /**keeps track if the MLB is pressed*/    
 72 var mousePressed = false; 
 73 
 74 /**the default application state*/
 75 var STATE_NONE = 0; 
 76 
 77 /**we have figure to be created**/
 78 var STATE_FIGURE_CREATE = 1; 
 79 
 80 /**we selected a figure (for further editing for example)*/
 81 var STATE_FIGURE_SELECTED = 2; 
 82 
 83 /**we are selecting the start of a connector*/
 84 var STATE_CONNECTOR_PICK_FIRST = 4; 
 85 
 86 /**we are selecting the end of a connector*/
 87 var STATE_CONNECTOR_PICK_SECOND = 8; 
 88 
 89 /**we selected a connector (for further editing for example)*/
 90 var STATE_CONNECTOR_SELECTED = 16;
 91 
 92 /**move a connection point of a connector*/
 93 var STATE_CONNECTOR_MOVE_POINT = 32;
 94 
 95 /**we are dragging the mouse over a group of figures.*/
 96 var STATE_SELECTING_MULTIPLE = 64;
 97 
 98 /**we have a group selected (either temporary or permanent)*/
 99 var STATE_GROUP_SELECTED = 128;
100 
101 /**Keeps current state*/
102 var state = STATE_NONE;
103 
104 /**The (current) selection area*/
105 var selectionArea = new Polygon(); 
106 selectionArea.points.push(new Point(0,0));
107 selectionArea.points.push(new Point(0,0));
108 selectionArea.points.push(new Point(0,0));
109 selectionArea.points.push(new Point(0,0));
110 selectionArea.style.strokeStyle = 'grey';
111 selectionArea.style.lineWidth = '1';
112 
113 /**Toggle grid visible or not*/
114 var gridVisible = false;
115 
116 /**Makes figure snap to grid*/
117 var snapTo = false;
118 
119 /**Keeps last coodinates while dragging*/
120 var lastClick = [];
121 
122 /**Default line width*/
123 var defaultLineWidth = 2;
124 
125 /**Current selected figure id ( -1 if none selected)*/
126 var selectedFigureId = -1;
127 
128 /**Current selected group (-1 if none selected)*/
129 var selectedGroupId = -1;
130 
131 /**Current selecte connector (-1 if none selected)*/
132 var selectedConnectorId = -1;
133 
134 /**Currently selected ConnectionPoint (if -1 none is selected)*/
135 var selectedConnectionPointId = -1;
136 
137 /**Set on true while we drag*/
138 var dragging = false;
139 
140 /**Holds a wrapper around canvas object*/
141 var canvasProps = null; //
142 
143 
144 
145 /**Return current canvas.
146  * Current canvas will ALWAYS have the 'a' as DOM id
147  * @author Alex Gheorghiu <alex@scriptoid.com>
148  **/
149 function getCanvas(){
150     var canvas = document.getElementById("a");
151     return canvas;
152 }
153 
154 
155 /**Return the 2D context of current canvas
156  * @author Alex Gheorghiu <alex@scriptoid.com>
157  **/
158 function getContext(){
159     var canvas = getCanvas();
160     if(canvas.getContext){
161         return canvas.getContext("2d");
162     }
163     else{
164         alert('You need Safari or Firefox 1.5+ to see this demo.');
165     }
166 }
167 
168 /**Keeps current figure set id*/
169 var currentSetId = 'basic'; 
170 
171 /**
172  * Reveals the figure set named by 'name' and hide previously displayed set
173  * @param {String} id - the (div) id value of the set
174  * @author Alex
175  **/
176 function setFigureSet(id){
177     //alert(name);
178     var div = document.getElementById(id);
179     if(div != null){
180         if(currentSetId != null){
181             var currentFigureSet = document.getElementById(currentSetId);
182             currentFigureSet.style.display = 'none';
183         }
184         div.style.display = 'block';
185         currentSetId = id;
186     }
187 }
188 
189 
190 /**Update an object (Figure or Connector)
191  *@param {Number} figureId - the id of the updating object
192  *@param {String} property - (or an {Array} of {String}s). The 'id' under which the property is stored
193  *TODO: is there any case where we are using property as an array ?
194  *@param {String} newValue - the new value of the property
195  *@author Zack, Alex
196  **/
197 function updateFigure(figureId, property, newValue){
198     Log.info("updateFigure() figureId: " + figureId + " property: " + property + ' new value: ' + newValue);
199     
200     /*Try to guess the object type*/
201     var objType = null;
202     var obj = stack.figureGetById(figureId); //try to find it inside {Figure}s
203     
204     if(obj){ //try to find it inside {Connector}s
205         objType = History.OBJECT_FIGURE;        
206     }
207     else{ //no in Figures
208         obj = CONNECTOR_MANAGER.connectorGetById(figureId); //search in Connectors
209         Log.info("updateFigure(): it's a connector 1");
210         
211         if(obj){ //see if it's a Canvas
212             objType = History.OBJECT_CONNECTOR;            
213         }
214         else{ //no in connectors
215             if(figureId == "canvasProps"){
216                 obj = canvasProps;
217                 Log.info("updateFigure(): it's the canvas");
218             }
219         }
220     }
221 
222     
223 
224     var objSave = obj; //keep a reference to initial shape
225 
226     /*Example of property 'primitives.1.str' */
227     var props = property.split(".");
228     //Log.info("Splitted props: " + props);
229 
230 
231     /*Going down the object's hierarchy down to the property's parent
232      *Example:
233      *  for props = ['primitives','1','str'] 
234      *  figure
235      *      |_primitives
236      *          |_1 (it's a Text)
237      *             |_str
238      */
239     //Log.info("Object before descend: " +  obj.oType);
240     var figure = obj; //TODO: Why this variable when we already have objSave?
241     for(var i = 0; i<props.length-1; i++){
242         obj = obj[props[i]];
243     }
244 
245     //the property name
246     var propName = props[props.length -1];
247     Log.info("updateFigure(): last property: " + propName);
248     Log.info("updateFigure(): last object in hierarchy: " + obj.oType);
249 
250     
251     
252     /*if last prop is null */
253     if(obj[propName] === undefined){
254         /*If something is complicated enough to need a function,
255          *  likelyhood is it will need access to its parent figure.
256          *So we will let the parent to do the update as it likes if it has
257          * a method of form set<property_name> in place
258          */
259         var propSet = "set" + props[props.length -1];
260         if(propSet in obj){ //@see https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special_Operators/in_Operator
261             if(doUndo && obj["get"+propName]() != newValue){
262                 var undo = new PropertyCommand(figureId, objType, property, obj["get"+propName](), newValue)
263                 History.addUndo(undo);
264             }
265             obj[propSet](figure,newValue);
266         }
267     }
268     else{
269         if(obj[propName] != newValue){ //try to change it ONLY if new value is different than the last one
270             if(doUndo && obj[propName] != newValue){
271                 var undo = new PropertyCommand(figureId, objType, property, obj[propName], newValue)
272                 History.addUndo(undo);
273             }
274             obj[propName] = newValue;
275         }
276     }
277 
278     //connector's text special case
279     if(objSave instanceof Connector && propName == 'str'){
280         Log.info("updateFigure(): it's a connector 2");
281         objSave.updateMiddleText();
282     }
283 
284     draw();
285 }
286 
287 
288 /**Setup the editor panel for a special shape. 
289  *@param shape - can be either Connector or Figure. If null is provided the
290  *editor panel will be disabled
291  **/
292 function setUpEditPanel(shape){
293     //var propertiesPanel = canvas.edit; //access the edit div
294     var propertiesPanel = document.getElementById("edit");
295     propertiesPanel.innerHTML = "";
296     if(shape == null){
297         //do nothing
298     }
299     else{
300         switch(shape.oType){
301             case 'Group':
302                 //do nothing. We do not want to offer this to groups
303                 break;
304             case 'CanvasProps':
305                 Builder.constructCanvasPropertiesPanel(propertiesPanel, shape);
306                 break;
307             default:
308                 Builder.contructPropertiesPanel(propertiesPanel, shape);
309         }
310     }
311 }
312 
313 
314 /**Setup the creation function (that -later, upon calling - will create the actual {Figure}
315  * Note: It will also set the current state to STATE_FIGURE_CREATE
316  * @param  {Function} fFunction - the function used to create the figure
317  **/
318 function createFigure(fFunction){
319     //alert("createFigure() - You ask me to create a figure? How dare you?");
320     createFigureFunction = fFunction;
321 
322     selectedFigureId = -1;
323     selectedConnectorId = -1;
324     selectedConnectionPointId = -1;
325     state = STATE_FIGURE_CREATE;
326     draw();
327 
328 }
329 
330 /**Activate snapToGrip  option*/
331 function snapToGrid(){
332     if(gridVisible == false && snapTo == false){
333         showGrid();
334     }
335     snapTo =! snapTo;
336     document.getElementById("snapCheckbox").checked=snapTo;
337 }
338 
339 
340 /**Makes grid visible or invisible, depedinding of previous value*/
341 function showGrid(){
342     var canvas = getCanvas();
343     gridVisible = !gridVisible;
344 
345     if(gridVisible){
346         canvas.style.backgroundImage="url(assets/images/gridTile1.png)";
347     }
348     else {
349         canvas.style.backgroundImage="";
350         document.getElementById("snapCheckbox").checked = false;
351     }
352     document.getElementById("gridCheckbox").checked = gridVisible;
353 }
354 
355 
356 /**Click is disabled because we need to handle mouse down and mouse up....etc etc etc*/
357 function onClick(ev){
358     var coords = getCanvasXY(ev);
359     var x = coords[0];
360     var y = coords[1];
361 
362 //here is the problem....how do we know we clicked on canvas
363 /*var fig=stack.figures[stack.figureGetMouseOver(x,y,null)];
364     if(CNTRL_PRESSED && fig!=null){
365         TEMPORARY_GROUP.addPrimitive();
366         stack.figureRemove(fig);
367         stack.figureAdd(TEMPORARY_GROUP);
368     }
369     else if(stack.figureGetMouseOver(x,y,null)!=null){
370         TEMPORARY_GROUP.primitives=[];
371         TEMPORARY_GROUP.addPrimitive(fig);
372         stack.figureRemove(fig);
373     }*/
374 //draw();
375 }
376 
377 
378 /**Receives the ASCII character code but not the keyboard code
379  *@param {Event} ev - the event generated when kay is pressed
380  *@see <a href="http://www.quirksmode.org/js/keys.html">http://www.quirksmode.org/js/keys.html</a>
381  **/
382 function onKeyPress(ev){
383     if(!ev){ //get event for IE
384         ev = window.event;
385     }
386 
387     //ignore texts
388     if((IE && ev.srcElement.className == "text") //IE code. In IE the ev has srcElement member
389         || (!IE && ev.target.className == "text")/*non IE code. In normal JS the ev has the target member*/){
390         return;
391     }
392 
393     switch(ev.charCode){
394         case KEY.NUMPAD4: //Numpad 4
395             if(CNTRL_PRESSED && stack.figureGetSelected()){ //clone a figure
396                 /*TODO: right now the newly created figure belongs to the Group with Id = -1 (supposelly no group)
397                  *So this might not be a good ideea
398                  **/
399 
400                 /*Creates a clone of currently selected figure and
401                  * move it a little right (10 pixels) and down (10 pixels)
402                  * */
403                 var createdFigure = stack.figureGetSelected().clone();
404                 stack.figureAdd(createdFigure);
405                 stack.figureSelect(stack.figures.length-1);
406                 createdFigure.transform(Matrix.translationMatrix(10,10));
407                 getCanvas().style.cursor="default";
408             }
409             break;
410     }//end switch
411 
412 
413     draw();
414     return false;
415 }
416 
417 
418 /**
419  *Receives the key code of keyboard but not the ASCII
420  *Treats the key pressed event
421  *@param {Event} ev - the event generated when key is down
422  *@see <a href="http://www.quirksmode.org/js/keys.html">http://www.quirksmode.org/js/keys.html</a>
423  **/
424 function onKeyDown(ev){
425     
426     Log.info("main.js->onKeyDown()->function call. Event = " + ev.type + " IE = " + IE );
427     
428     //1 - OLD IE browsers
429     if(typeof ev == 'undefined' || ev == null){
430         ev = window.event;
431     }
432     
433     //2 - avoid text elements (if you are on a text area and you press the arrow you do not want the figures to move on canvas)
434     if((IE && ev.srcElement.className == "text")  /*IE code*/
435         || (!IE && ev.target.className == "text") /*Non IE code*/){
436         return true;
437     }
438     
439     
440     //3 - "enhance" event TODO: I'm not sure this is really necessary
441     ev.KEY = ev.keyCode;
442         
443     
444     Log.info("e.keyCode = " + ev.keyCode + " ev.KEY = " + ev.KEY);
445     
446     
447     switch(ev.KEY){
448         case KEY.ESCAPE: //Esc
449             //alert('So do you want to escape me');
450             //cancel any figure creation
451             createFigureFunction = null;
452 
453             if(selectedFigureId != -1 || selectedConnectionPointId != -1 || selectedConnectorId!=-1){
454                 redraw = true;
455             }
456             
457             //deselect any figure
458             selectedFigureId = -1;
459             selectedConnectionPointId = -1;
460             selectedConnectorId = -1;
461 
462             
463             state = STATE_NONE;
464             break;
465 
466         case KEY.DELETE: //Delete
467             //delete any Figure or Group
468 //            alert('Delete pressed' + this);
469             switch(state){
470 
471                 case STATE_FIGURE_SELECTED: //delete a figure ONLY when the figure is selected
472                     if(selectedFigureId > -1){
473                         //remove figure
474 
475                         if(!ev.noAddUndo && doUndo){//only add an action, if we are not currently undo/redoing an action
476                             var undo = new DeleteCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.figureGetById(selectedFigureId),ev)
477 
478                             History.addUndo(undo);
479                         }
480                         
481                         stack.figureRemoveById(selectedFigureId);
482                         
483                         
484                         //remove glues
485                         var xCPs = CONNECTOR_MANAGER.connectionPointGetAllByParent(selectedFigureId);
486                         for(var k=0; k<xCPs.length; k++){
487                             CONNECTOR_MANAGER.glueRemoveAllByFirstId(xCPs[k].id);
488                             
489                         }
490                         
491                         //remove connection points
492                         CONNECTOR_MANAGER.connectionPointRemoveAllByParent(selectedFigureId);
493                         selectedFigureId = -1;
494                         setUpEditPanel(canvasProps);
495                         state = STATE_NONE;
496                         redraw = true;
497                         
498 //                        alert('Delete done');
499                     }
500                     break;
501 
502                 case STATE_CONNECTOR_SELECTED:
503                     if(selectedConnectorId != -1){
504                         var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId);
505                         if(!ev.noAddUndo && doUndo){//only add an action, if we are not currently undo/redoing an action
506                             var undo = new DeleteCommand(selectedConnectorId, History.OBJECT_CONNECTOR, null, con, null);
507                             History.addUndo(undo);
508                         }
509                         CONNECTOR_MANAGER.connectorRemoveByIdCascade(selectedConnectorId);
510                         selectedConnectorId = -1;
511                         setUpEditPanel(canvasProps);
512                         state = STATE_NONE;
513                         redraw = true;
514                     }
515                     break;
516                 case STATE_GROUP_SELECTED:
517                     var figures = stack.figureGetByGroupId(selectedGroupId);
518                     if(!ev.noAddUndo && doUndo){//only add an action, if we are not currently undo/redoing an action
519                         var undo = new DeleteCommand(selectedGroupId, History.OBJECT_GROUP, null, figures, stack.groupGetById(selectedGroupId).permanent)
520 
521                         History.addUndo(undo);
522                     }
523                     var figures = stack.figureGetByGroupId(selectedGroupId);
524                     stack.groupDestroy(selectedGroupId);
525                     for(var i = 0; i < figures.length; i++){
526                         stack.figureRemoveById(figures[i].id);
527                     }
528                     selectedGroupId = -1;
529                     state = STATE_NONE;
530 
531                     break;
532             }
533             break;
534 
535         case KEY.SHIFT: //Shift
536             SHIFT_PRESSED = true;
537             break;
538 
539         case KEY.CTRL: //Ctrl
540         case KEY.COMMAND:
541         case KEY.COMMAND_FIREFOX:
542             CNTRL_PRESSED = true;
543             break;
544 
545         case KEY.LEFT://Arrow Left
546             action("left");
547             return false;
548             break;
549 
550         case KEY.UP://Arrow Up
551             action("up");
552             return false;
553             break;
554 
555         case KEY.RIGHT://Arrow Right
556             action("right");
557             return false;
558             break;
559 
560         case KEY.DOWN://Arrow Down
561             action("down");
562             return false;
563             break;
564 
565         case KEY.Z:
566             if(CNTRL_PRESSED){
567                 action('undo');
568             }
569             break;
570 
571         case KEY.Y:
572             if(CNTRL_PRESSED){
573                 action('redo');
574             }
575             break;
576             
577         case KEY.G:
578             if(CNTRL_PRESSED){
579                 action('group');
580             }
581             break;
582 
583         case KEY.U:
584             if(CNTRL_PRESSED){
585                 action('ungroup');
586             }
587             break;
588 
589         case KEY.S:
590             if(CNTRL_PRESSED){
591                 //Log.info("CTRL-S pressed  ");
592                 save();
593             }
594             break;
595     }
596     draw();
597     return false;
598 }
599 
600 
601 /**
602  *Treats the key up event
603  *@param {Event} ev - the event generated when key is up
604  **/
605 function onKeyUp(ev){
606     if(!ev){ //IE special code
607         ev = window.event;
608     }
609     switch(ev.keyCode){
610         case KEY.SHIFT: //Shift
611             SHIFT_PRESSED = false;
612             break;
613 
614         case KEY.ALT: //Alt
615             CNTRL_PRESSED = false;
616             break;
617 
618         case KEY.CTRL: //Ctrl
619             CNTRL_PRESSED = false;
620             break;
621     }
622     return false;
623 }
624 
625 
626 /**
627  *Treats the mouse down event
628  *@param {Event} ev - the event generated when key is up
629  **/
630 function onMouseDown(ev){
631     var coords = getCanvasXY(ev);
632     var HTMLCanvas = getCanvas();
633     var x = coords[0];
634     var y = coords[1];
635     lastClick = [x,y];
636     //alert('lastClick: ' + lastClick + ' state: ' + state);
637 
638     mousePressed = true;
639     //alert("onMouseDown() + state " + state + " none state is " + STATE_NONE);
640     
641     switch(state){
642         case STATE_NONE:
643             //alert("onMouseDown() - STATE_NONE");
644             snapMonitor = [0,0];
645             
646             //FIGURE & GROUP
647             var fId = stack.figureGetByXY(x, y);
648             if(fId != -1){ //select figure
649                 if(stack.figureGetById(fId).groupId != -1){ //if the figure belongs to a group then select that group
650                     selectedGroupId = stack.figureGetById(fId).groupId;
651                     var grp = stack.groupGetById(selectedGroupId);
652                     state = STATE_GROUP_SELECTED;
653                     if(doUndo){
654                         currentMoveUndo = new MatrixCommand(selectedGroupId, History.OBJECT_GROUP, History.MATRIX, Matrix.translationMatrix(grp.getBounds()[0],grp.getBounds()[1]), null);
655                     }
656                 }
657                 else{ //ok, we will select only the figure
658                     selectedFigureId = fId;
659                     var f = stack.figureGetById(fId);
660                     setUpEditPanel(f);
661                     state = STATE_FIGURE_SELECTED;
662                     if(doUndo){
663                         currentMoveUndo = new MatrixCommand(fId, History.OBJECT_FIGURE, History.MATRIX, Matrix.translationMatrix(f.getBounds()[0],f.getBounds()[1]), null);
664                     }
665                 }
666             }
667             else{
668                 //Log.info("onMouseDown() + STATE_NONE - deselect all figures");
669                 setUpEditPanel(canvasProps);
670                 HandleManager.clear();
671                 selectedFigureId = -1;
672                 
673                 state = STATE_SELECTING_MULTIPLE;
674                 selectionArea.points[0] = new Point(x,y);
675                 selectionArea.points[1] = new Point(x,y);
676                 selectionArea.points[2] = new Point(x,y);
677                 selectionArea.points[3] = new Point(x,y);//the selectionArea has no size until we start dragging the mouse
678             
679             }
680             //END FIGURE
681 
682             //CONNECTOR
683             var cId = CONNECTOR_MANAGER.connectorGetByXY(x, y);
684             if(cId != -1){
685                 selectedConnectorId = cId;
686                 state = STATE_CONNECTOR_SELECTED;
687                 var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId);
688                 setUpEditPanel(con);
689                 Log.info('onMouseDown() + STATE_NONE  - change to STATE_CONNECTOR_SELECTED');
690                 repaint = true;
691             } else {
692                 selectedConnectorId = -1;
693                 Log.info('onMouseDown() + STATE_NONE - deselect any connector');
694                 repaint = true;
695             }
696             //CONNECTOR END
697             break;
698 
699 
700         case STATE_FIGURE_CREATE:
701             snapMonitor = [0,0];
702             
703             //treat new figure
704             //do we need to create a figure on the canvas?
705             if(createFigureFunction){
706                 Log.info("onMouseDown() + STATE_FIGURE_CREATE--> new state STATE_FIGURE_SELECTED");
707                 var createdFigure = createFigureFunction(x,y);
708                 createdFigure.style.lineWidth = defaultLineWidth;
709                 HTMLCanvas.style.cursor = 'default';
710 
711                 stack.figureAdd(createdFigure);
712 
713                 selectedFigureId = createdFigure.id;
714                 if(!ev.noAddUndo && doUndo){//only add an action, if we are not currently undo/redoing an action
715                     var undo = new CreateCommand(createdFigure.id, History.OBJECT_FIGURE, null, createdFigure.id, [createFigureFunction,ev]);
716                     History.addUndo(undo);
717                 }
718                 selectedConnectorId = -1;
719                 state = STATE_FIGURE_SELECTED;
720                 createFigureFunction = null;
721 
722                 //move figure in position
723                 //createdFigure.transform(Matrix.translationMatrix(x-createdFigure.rotationCoords[0].x, y-createdFigure.rotationCoords[0].y))
724 
725                 setUpEditPanel(createdFigure);                
726                 mousePressed = false;
727                 repaint = true;
728             }
729             break;
730 
731         case STATE_FIGURE_SELECTED:
732             snapMonitor = [0,0];
733             var fId = stack.figureGetByXY(x, y);
734             if(fId != -1 && stack.figureGetById(fId).groupId != -1){
735                 state = STATE_GROUP_SELECTED;
736                 selectedGroupId = stack.figureGetById(fId).groupId;
737                 selectedFigureId = -1;
738                 var g = stack.groupGetById(selectedFigureId);
739                 redraw = true;
740                 if(doUndo){
741                     currentMoveUndo = new MatrixCommand(selectedFigureId, History.OBJECT_FIGURE, History.MATRIX, Matrix.translationMatrix(g.getBounds()[0],g.getBounds()[1]), null);
742                 }
743                 HandleManager.figureSet(g);
744                 state = STATE_GROUP_SELECTED;
745                 break;
746             }
747             if(fId != -1 && fId != selectedFigureId){ //select figure, if we havent already got it selected
748                 selectedFigureId = fId;
749                 HandleManager.clear();
750                 var f = stack.figureGetById(fId);
751                 setUpEditPanel(f);
752                 redraw = true;
753                 if(doUndo){
754                     currentMoveUndo = new MatrixCommand(fId, History.OBJECT_FIGURE, History.MATRIX, Matrix.translationMatrix(f.getBounds()[0],f.getBounds()[1]), null);
755                 }
756             }
757             else if(fId == selectedFigureId && fId != -1){
758                 var f = stack.figureGetById(fId);
759                 if(doUndo){
760                     currentMoveUndo = new MatrixCommand(fId, History.OBJECT_FIGURE, History.MATRIX, Matrix.translationMatrix(f.getBounds()[0],f.getBounds()[1]), null);
761                 }
762             }
763             else if(HandleManager.handleGet(x, y) != null){ //select handle
764                 Log.info("onMouseDown() + STATE_FIGURE_SELECTED - handle selected");
765                 HandleManager.handleSelectXY(x,y);
766                 var oldRot = [HandleManager.figure.rotationCoords[0].clone(),HandleManager.figure.rotationCoords[1].clone()];
767                 var angle = Util.getAngle(HandleManager.figure.rotationCoords[0], HandleManager.figure.rotationCoords[1],0.00001);
768                 var trans = Matrix.translationMatrix(-HandleManager.figure.rotationCoords[0].x,-HandleManager.figure.rotationCoords[0].y);
769                 if(angle!= 0){
770                     HandleManager.figure.transform(trans);
771                     HandleManager.figure.transform(Matrix.rotationMatrix(-angle));
772                     trans[0][2] = -trans[0][2];
773                     trans[1][2] = -trans[1][2];
774                     HandleManager.figure.transform(trans);
775                     trans[0][2] = -trans[0][2];
776                     trans[1][2] = -trans[1][2];
777                 }
778                 if(doUndo){
779                     currentMoveUndo = new MatrixCommand(HandleManager.figure.id,History.OBJECT_FIGURE,History.MATRIX, oldRot, HandleManager.figure.getBounds());
780                 }
781                 if(angle!= 0){
782                     HandleManager.figure.transform(trans);
783                     HandleManager.figure.transform(Matrix.rotationMatrix(angle));
784                     trans[0][2] = -trans[0][2];
785                     trans[1][2] = -trans[1][2];
786                     HandleManager.figure.transform(trans);
787                 }
788             }
789             else if(fId != selectedFigureId){
790                 Log.info("onMouseDown() + STATE_FIGURE_SELECTED --> deselect any figure");
791                 selectedFigureId = -1;
792                 setUpEditPanel(canvasProps);
793 
794                 state = STATE_NONE;
795                 redraw = true;
796                 currentMoveUndo = null;
797                 state = STATE_SELECTING_MULTIPLE;
798                 selectionArea.points[0] = new Point(x,y);
799                 selectionArea.points[1] = new Point(x,y);
800                 selectionArea.points[2] = new Point(x,y);
801                 selectionArea.points[3] = new Point(x,y);//the selectionArea has no size until we start dragging the mouse
802             
803             }
804             break;
805 
806         case STATE_GROUP_SELECTED:
807             //GROUPS
808             //if selected group is temporary and we pressed outside of it's border we will destroy it
809             var selectedGroup = stack.groupGetById(selectedGroupId);
810             if(!selectedGroup.contains(x,y) && HandleManager.handleGet(x,y) == null){
811                 if(selectedGroup.permanent == false && doUndo){
812                     History.addUndo(new GroupCommand(selectedGroupId,History.OBJECT_GROUP, false, stack.figureGetIdsByGroupId(selectedGroupId),false));
813                     stack.groupDestroy(selectedGroupId);
814                 }
815                 selectedGroupId = -1;
816                 state = STATE_NONE;
817                 break;
818             }
819 
820 
821 
822             var fId = stack.figureGetByXY(x, y);
823             var gId = selectedGroupId;
824             if(fId != -1 && stack.figureGetById(fId).groupId != -1){
825                 //we have selected a figure, could be part of our group or a different group, but is in a group
826                 gId = stack.figureGetById(fId).groupId;
827             }
828             else if(HandleManager.handleGet(x,y) != null){
829                 //we are dragging a handle, so set gid to -1, to skip a future if
830                 gId = -1;
831             }
832             else if(fId != -1){
833                 //we have an figureId, but no groupId, we are in the wrong place!
834                 state = STATE_FIGURE_SELECTED;
835                 break;
836             }
837 
838             
839             if(gId != -1){
840                 // we have a group, set the selectedGroupId, and create an undo object
841                 selectedGroupId = gId;
842                 //HandleManager.clear();
843                 var g = stack.groupGetById(gId);
844                 redraw = true;
845                 if(doUndo){
846                     currentMoveUndo = new MatrixCommand(gId, History.OBJECT_GROUP, History.MATRIX, Matrix.translationMatrix(g.getBounds()[0],g.getBounds()[1]), null);
847                 }
848                 state = STATE_GROUP_SELECTED;
849             }
850             else if(HandleManager.handleGet(x, y) != null){
851                 //select a handle
852                 Log.info("onMouseDown() + STATE_FIGURE_SELECTED - handle selected");
853                 HandleManager.handleSelectXY(x,y);
854 
855                 var oldRot = [HandleManager.figure.rotationCoords[0].clone(),HandleManager.figure.rotationCoords[1].clone()];
856                 var angle = Util.getAngle(HandleManager.figure.rotationCoords[0], HandleManager.figure.rotationCoords[1],0.00001);
857                 var trans = Matrix.translationMatrix(-HandleManager.figure.rotationCoords[0].x,-HandleManager.figure.rotationCoords[0].y);
858                 if(angle!= 0){
859                     HandleManager.figure.transform(trans);
860                     HandleManager.figure.transform(Matrix.rotationMatrix(-angle));
861                     trans[0][2] = -trans[0][2];
862                     trans[1][2] = -trans[1][2];
863                     HandleManager.figure.transform(trans);
864                     trans[0][2] = -trans[0][2];
865                     trans[1][2] = -trans[1][2];
866                 }
867                 if(doUndo){
868                     currentMoveUndo = new MatrixCommand(HandleManager.figure.id,History.OBJECT_GROUP,History.MATRIX, oldRot, HandleManager.figure.getBounds());
869                 }
870                 if(angle!= 0){
871                     HandleManager.figure.transform(trans);
872                     HandleManager.figure.transform(Matrix.rotationMatrix(angle));
873                     trans[0][2] = -trans[0][2];
874                     trans[1][2] = -trans[1][2];
875                     HandleManager.figure.transform(trans);
876                 }
877             }
878             break;
879 
880         
881         case STATE_CONNECTOR_PICK_FIRST:
882             //moved so it can be called from undo action
883             connectorPickFirst(x,y,ev);
884             break;
885 
886         case STATE_CONNECTOR_PICK_SECOND:
887             state  = STATE_NONE;
888             break;
889 
890 
891         case STATE_CONNECTOR_SELECTED:
892             var cps = CONNECTOR_MANAGER.connectionPointGetAllByParent(selectedConnectorId);
893             var start = cps[0];
894             var end = cps[1];
895             if(start.point.near(x, y, 3)){
896                 var g = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(start.id);
897                 
898                 Log.info("Picked the start point");
899                 selectedConnectionPointId = start.id;
900                 if(g.length != 0 && doUndo == true){//CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints[0]
901                     currentMoveUndo = new MatrixCommand(selectedConnectionPointId, History.OBJECT_CONNECTION_POINT , [g[0].id1,g[0].id2], Matrix.translationMatrix(start.point.x,start.point.y),null);
902                 }
903                 else if(doUndo == true) {
904                     currentMoveUndo = new MatrixCommand(selectedConnectionPointId, History.OBJECT_CONNECTION_POINT , null, Matrix.translationMatrix(start.point.x,start.point.y),null);
905                 }
906                 state = STATE_CONNECTOR_MOVE_POINT;
907                 HTMLCanvas.style.cursor = 'move';
908             }
909             else if(end.point.near(x, y, 3)){
910                 var g = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(end.id);
911 
912                 Log.info("Picked the end point");
913                 selectedConnectionPointId = end.id;
914                 if(g.length != 0 && doUndo == true){//CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints[CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints.length - 1]
915                     currentMoveUndo = new MatrixCommand(selectedConnectionPointId, History.OBJECT_CONNECTION_POINT, [g[0].id1,g[0].id2], Matrix.translationMatrix(end.point.x,end.point.y),null);
916                 }
917                 else if(doUndo == true){
918                     currentMoveUndo = new MatrixCommand(selectedConnectionPointId, History.OBJECT_CONNECTION_POINT, null, Matrix.translationMatrix(end.point.x,end.point.y),null);
919                 }
920                 state = STATE_CONNECTOR_MOVE_POINT;
921                 HTMLCanvas.style.cursor = 'move';
922             }
923             else{ //no connection point selection
924 
925                 var newCId = selectedConnectorId;
926                 if(HandleManager.handleGet(x,y) == null){//we only get a new connector, if we are not currently
927                     //over the current connectors handles
928                     newCId = CONNECTOR_MANAGER.connectorGetByXY(x, y); //did we picked another connector?
929                 }
930                 if(newCId == -1){
931                     Log.info('No other connector selected. Deselect all connectors');
932                     selectedConnectorId = -1;
933                     state = STATE_NONE;
934                     setUpEditPanel(canvasProps);
935                     repaint = true;
936 
937                 //START: Quick Select FIGURE
938                 //                    var fId = stack.figureGetByXY(x, y);
939                 //                    if(fId != -1){ //select figure
940                 //                        Log.info("onMouseDown() + STATE_CONNECTOR_SELECTED - quick select a figure, new state (STATE_FIGURE_SELECTED)");
941                 //                        selectedFigureId = fId;
942                 //                        var f = stack.figureGetById(fId);
943                 //                        setUpEditPanel(f);
944                 //                        mousePressed = false;
945                 //                        state = STATE_FIGURE_SELECTED;
946                 //                    }
947                 //END: Quick Select FIGURE
948                 }
949                 else if(newCId == selectedConnectorId){ //did we picked the same connector?
950                     //do nothing - it's the same connector
951                     Log.info("onMouseDown(): Nothing, it's the same connector");
952                 } else{
953                     Log.info('onMouseDown(): Select another connector');
954                     selectedConnectorId = newCId;
955                     setUpEditPanel(CONNECTOR_MANAGER.connectorGetById(selectedConnectorId));
956                     state = STATE_CONNECTOR_SELECTED;
957                     repaint = true;
958                 }
959                 if(HandleManager.handleGet(x, y) != null){ //select handle
960                     Log.info("onMouseDown() + STATE_FIGURE_SELECTED - handle selected");
961                     HandleManager.handleSelectXY(x,y);
962                     
963                 }
964 
965             //canvas.style.cursor = 'default';
966             //state  = STATE_NONE;
967             }
968 
969             break;
970 
971             
972         default:
973     //alert("onMouseDown() - switch default - state is " + state);
974     }
975 
976     draw();
977 
978     return false;
979 }
980 
981 
982 /**
983  *Treats the mouse up event
984  *@param {Event} ev - the event generated when key is up
985  **/
986 function onMouseUp(ev){
987     var coords = getCanvasXY(ev);
988     x = coords[0];
989     y = coords[1];
990 
991     lastClick = [];
992     switch(state){
993 
994         case STATE_NONE:
995             if(HandleManager.handleGetSelected()){
996                 HandleManager.clear();
997             }
998             break;
999 
1000         case STATE_FIGURE_SELECTED:
1001             if(currentMoveUndo != null && HandleManager.handleGetSelected() == null){//we are moving
1002                 var f = stack.figureGetById(selectedFigureId);
1003                 if(f.getBounds()[0] != currentMoveUndo.previousValue[0][2] 
1004                     || f.getBounds()[1] != currentMoveUndo.previousValue[1][2])
1005                     {
1006                     currentMoveUndo.currentValue = [Matrix.translationMatrix(f.getBounds()[0]-currentMoveUndo.previousValue[0][2],f.getBounds()[1]-currentMoveUndo.previousValue[1][2])];
1007                     currentMoveUndo.previousValue = [Matrix.translationMatrix(currentMoveUndo.previousValue[0][2]-f.getBounds()[0],currentMoveUndo.previousValue[1][2]-f.getBounds()[1])];
1008 
1009                     History.addUndo(currentMoveUndo);
1010                     currentMoveUndo = null;
1011                 }
1012             }
1013             //lots of things that can happen here, so quite complicated
1014             if(HandleManager.handleGetSelected() != null){ //deselect current handle
1015                 var figure = HandleManager.figure;
1016                 var bounds = figure.getBounds();
1017 
1018                 //get the angle of the original shape, prior to any action
1019                 var oldRotCoords = currentMoveUndo.previousValue;
1020                 var newRotCoords = figure.rotationCoords;
1021 
1022                 var oldAngle = Util.getAngle(oldRotCoords[0], oldRotCoords[1],0.001);//exact angle will return .001 different sometimes, this rounds.
1023                 var newAngle = Util.getAngle(newRotCoords[0], newRotCoords[1],0.001);
1024 
1025                 
1026                 if(oldAngle != newAngle){//if action is rotation, opposite action is easy, translate, rotate, translate back.
1027                     currentMoveUndo.previousValue = [Matrix.translationMatrix(-oldRotCoords[0].x,-oldRotCoords[0].y),Matrix.rotationMatrix(oldAngle-newAngle),Matrix.translationMatrix(oldRotCoords[0].x,oldRotCoords[0].y)];
1028                     currentMoveUndo.currentValue = [Matrix.translationMatrix(-oldRotCoords[0].x,-oldRotCoords[0].y),Matrix.rotationMatrix(newAngle-oldAngle),Matrix.translationMatrix(oldRotCoords[0].x,oldRotCoords[0].y)];
1029                 }
1030                 else{
1031                     //if not, difficult
1032 
1033                     //get the exact angle for rotating, if we arent rotating, we must be scaling
1034                     oldAngle = Util.getAngle(oldRotCoords[0], oldRotCoords[1]);
1035                     newAngle = Util.getAngle(newRotCoords[0], newRotCoords[1]);
1036                     var scaleX = 1;
1037                     var scaleY = 1;
1038                     var translationForwardMatrix = Matrix.translationMatrix(0, 0);
1039                     var translationBackMatrix = Matrix.translationMatrix(0, 0);
1040                     
1041                     var oldBounds = currentMoveUndo.currentValue;//we save bounds (pre rotated to be at an angle of 0) here upon starting to drag a handle
1042 
1043 
1044                     //rotate the current figure back to the 0 axis, to get correct representation of width and height
1045                     var trans = Matrix.translationMatrix(-HandleManager.figure.rotationCoords[0].x,-HandleManager.figure.rotationCoords[0].y);
1046                     
1047                     HandleManager.figure.transform(trans);
1048                     trans[0][2] = -trans[0][2];
1049                     trans[1][2] = -trans[1][2];
1050                     HandleManager.figure.transform(Matrix.rotationMatrix(-newAngle));
1051                     HandleManager.figure.transform(trans);
1052                     trans[0][2] = -trans[0][2];
1053                     trans[1][2] = -trans[1][2];
1054 
1055                     var newBounds = HandleManager.figure.getBounds();
1056 
1057                     //rotate the figure back into its normal position
1058                     HandleManager.figure.transform(trans);
1059                     trans[0][2] = -trans[0][2];
1060                     trans[1][2] = -trans[1][2];
1061                     HandleManager.figure.transform(Matrix.rotationMatrix(newAngle));
1062                     HandleManager.figure.transform(trans);
1063 
1064                     //we need a record of the non translated, rotated bounds, in order to scale correctly later
1065                     HandleManager.figure.transform(Matrix.rotationMatrix(-newAngle));
1066                     var untranslatedBounds = HandleManager.figure.getBounds();
1067                     HandleManager.figure.transform(Matrix.rotationMatrix(newAngle));
1068 
1069                     //get the old and new widths and heights;
1070                     var oldWidth = (oldBounds[2]-oldBounds[0])/2;
1071                     var newWidth = (newBounds[2]-newBounds[0])/2;
1072                     var oldHeight = (oldBounds[3]-oldBounds[1])/2;
1073                     var newHeight = (newBounds[3]-newBounds[1])/2;
1074 
1075                     //set the scale proprtions
1076                     scaleX = oldWidth/newWidth;
1077                     scaleY = oldHeight/newHeight;
1078                     var handle = HandleManager.handleGetSelected();
1079 
1080                     //if we are scaling the left/top we need to move the right/bottom to the 0 position
1081                     if(handle.type == 'w' || handle.type == 'nw' || handle.type == 'sw'){
1082                         translationForwardMatrix[0][2] = -untranslatedBounds[2];
1083                         translationBackMatrix[0][2] = untranslatedBounds[2];
1084                     }
1085                     else {
1086                         translationForwardMatrix[0][2] = -untranslatedBounds[0];
1087                         translationBackMatrix[0][2] = untranslatedBounds[0];
1088                     }
1089                     if(handle.type == 'n' || handle.type == 'nw' || handle.type == 'ne'){
1090                         translationForwardMatrix[1][2] = -untranslatedBounds[3];
1091                         translationBackMatrix[1][2] = untranslatedBounds[3];
1092                     }
1093                     else {
1094                         translationForwardMatrix[1][2] = -untranslatedBounds[1];
1095                         translationBackMatrix[1][2] = untranslatedBounds[1];
1096                     }
1097                     if(doUndo){
1098                         //save the set of transformations
1099                         currentMoveUndo.previousValue = [Matrix.rotationMatrix(-newAngle),translationForwardMatrix,Matrix.scaleMatrix(scaleX,scaleY),translationBackMatrix,Matrix.rotationMatrix(newAngle)];
1100                         currentMoveUndo.currentValue = [Matrix.rotationMatrix(-newAngle),translationForwardMatrix,Matrix.scaleMatrix(1/scaleX,1/scaleY),translationBackMatrix,Matrix.rotationMatrix(newAngle)];
1101                     }
1102                 }
1103                 if(doUndo && currentMoveUndo != null){
1104                     History.addUndo(currentMoveUndo);
1105                 }
1106                 HandleManager.clear();
1107                 redraw = true;
1108             }
1109             break;
1110 
1111         case STATE_GROUP_SELECTED:
1112             //GROUPS
1113             if(currentMoveUndo != null && HandleManager.handleGetSelected() == null){
1114                 var f = stack.groupGetById(selectedGroupId);
1115                 if(f.getBounds()[0] != currentMoveUndo.previousValue[0][2]
1116                     || f.getBounds()[1] != currentMoveUndo.previousValue[1][2])
1117                     {
1118                     currentMoveUndo.currentValue = [Matrix.translationMatrix(f.getBounds()[0] - currentMoveUndo.previousValue[0][2], f.getBounds()[1] - currentMoveUndo.previousValue[1][2])];
1119                     currentMoveUndo.previousValue = [Matrix.translationMatrix(currentMoveUndo.previousValue[0][2] - f.getBounds()[0], currentMoveUndo.previousValue[1][2] - f.getBounds()[1])];                    
1120                 }
1121             }
1122             //lots of things that can happen here, so quite complicated
1123             if(HandleManager.handleGetSelected() != null){ //deselect current handle
1124                 var figure = HandleManager.figure;
1125                 var bounds = figure.getBounds();
1126 
1127                 //get the angle of the original shape, prior to any action
1128                 var oldRotCoords = currentMoveUndo.previousValue;
1129                 var newRotCoords = figure.rotationCoords;
1130 
1131                 var oldAngle = Util.getAngle(oldRotCoords[0], oldRotCoords[1],0.001); //exact angle will return .001 different sometimes, this rounds.
1132                 var newAngle = Util.getAngle(newRotCoords[0], newRotCoords[1],0.001);
1133 
1134 
1135                 if(oldAngle != newAngle && currentMoveUndo != null && doUndo){//if action is rotation, opposite action is easy, translate, rotate, translate back.
1136                     currentMoveUndo.previousValue = [Matrix.translationMatrix(-oldRotCoords[0].x,-oldRotCoords[0].y),Matrix.rotationMatrix(oldAngle-newAngle),Matrix.translationMatrix(oldRotCoords[0].x,oldRotCoords[0].y)];
1137                     currentMoveUndo.currentValue = [Matrix.translationMatrix(-oldRotCoords[0].x,-oldRotCoords[0].y),Matrix.rotationMatrix(newAngle-oldAngle),Matrix.translationMatrix(oldRotCoords[0].x,oldRotCoords[0].y)];
1138                 }
1139                 else{//if not, difficult
1140 
1141                     //get the exact angle for rotating, if we arent rotating, we must be scaling
1142                     oldAngle = Util.getAngle(oldRotCoords[0], oldRotCoords[1]);
1143                     newAngle = Util.getAngle(newRotCoords[0], newRotCoords[1]);
1144                     var scaleX = 1;
1145                     var scaleY = 1;
1146                     var translationForwardMatrix = Matrix.translationMatrix(0, 0);
1147                     var translationBackMatrix = Matrix.translationMatrix(0, 0);
1148 
1149                     var oldBounds = currentMoveUndo.currentValue;//we save bounds (pre rotated to be at an angle of 0) here upon starting to drag a handle
1150 
1151 
1152                     //rotate the current figure back to the 0 axis, to get correct representation of width and height
1153                     var trans = Matrix.translationMatrix(-HandleManager.figure.rotationCoords[0].x,-HandleManager.figure.rotationCoords[0].y);
1154 
1155                     HandleManager.figure.transform(trans);
1156                     trans[0][2] = -trans[0][2];
1157                     trans[1][2] = -trans[1][2];
1158                     HandleManager.figure.transform(Matrix.rotationMatrix(-newAngle));
1159                     HandleManager.figure.transform(trans);
1160                     trans[0][2] = -trans[0][2];
1161                     trans[1][2] = -trans[1][2];
1162 
1163                     var newBounds = HandleManager.figure.getBounds();
1164 
1165                     //rotate the figure back into its normal position
1166                     HandleManager.figure.transform(trans);
1167                     trans[0][2] = -trans[0][2];
1168                     trans[1][2] = -trans[1][2];
1169                     HandleManager.figure.transform(Matrix.rotationMatrix(newAngle));
1170                     HandleManager.figure.transform(trans);
1171 
1172                     //we need a record of the non translated, rotated bounds, in order to scale correctly later
1173                     HandleManager.figure.transform(Matrix.rotationMatrix(-newAngle));
1174                     var untranslatedBounds = HandleManager.figure.getBounds();
1175                     HandleManager.figure.transform(Matrix.rotationMatrix(newAngle));
1176 
1177                     //get the old and new widths and heights;
1178                     var oldWidth = (oldBounds[2]-oldBounds[0])/2;
1179                     var newWidth = (newBounds[2]-newBounds[0])/2;
1180                     var oldHeight = (oldBounds[3]-oldBounds[1])/2;
1181                     var newHeight = (newBounds[3]-newBounds[1])/2;
1182 
1183                     //set the scale proprtions
1184                     scaleX = oldWidth/newWidth;
1185                     scaleY = oldHeight/newHeight;
1186                     var handle = HandleManager.handleGetSelected();
1187 
1188                     //if we are scaling the left/top we need to move the right/bottom to the 0 position
1189                     if(handle.type == 'w' || handle.type == 'nw' || handle.type == 'sw'){
1190                         translationForwardMatrix[0][2] = -untranslatedBounds[2];
1191                         translationBackMatrix[0][2] = untranslatedBounds[2];
1192                     }
1193                     else {
1194                         translationForwardMatrix[0][2] = -untranslatedBounds[0];
1195                         translationBackMatrix[0][2] = untranslatedBounds[0];
1196                     }
1197                     if(handle.type == 'n' || handle.type == 'nw' || handle.type == 'ne'){
1198                         translationForwardMatrix[1][2] = -untranslatedBounds[3];
1199                         translationBackMatrix[1][2] = untranslatedBounds[3];
1200                     }
1201                     else {
1202                         translationForwardMatrix[1][2] = -untranslatedBounds[1];
1203                         translationBackMatrix[1][2] = untranslatedBounds[1];
1204                     }
1205 
1206                     //save the set of transformations
1207                     if(doUndo && currentMoveUndo != null){
1208                         currentMoveUndo.previousValue = [Matrix.rotationMatrix(-newAngle),translationForwardMatrix,Matrix.scaleMatrix(scaleX,scaleY),translationBackMatrix,Matrix.rotationMatrix(newAngle)];
1209                         currentMoveUndo.currentValue = [Matrix.rotationMatrix(-newAngle),translationForwardMatrix,Matrix.scaleMatrix(1/scaleX,1/scaleY),translationBackMatrix,Matrix.rotationMatrix(newAngle)];
1210                     }
1211                 }
1212             }
1213             HandleManager.handleSelectedIndex = -1;
1214             if(doUndo && currentMoveUndo != null){
1215                 History.addUndo(currentMoveUndo);
1216             }
1217             currentMoveUndo = null;
1218             break;
1219 
1220         case STATE_SELECTING_MULTIPLE:
1221             state = STATE_NONE;
1222             var figuresToAdd = [];
1223             for(var i = 0; i < stack.figures.length; i++){
1224                 if(stack.figures[i].groupId == -1){ //we only want ungrouped items
1225                     var points = stack.figures[i].getPoints();
1226                     if(points.length == 0){ //if no  point at least to add bounds TODO: lame 'catch all' condition
1227                         points.push( new Point(stack.figures[i].getBounds()[0], stack.figures[i].getBounds()[1]) ); //top left
1228                         points.push( new Point(stack.figures[i].getBounds()[2], stack.figures[i].getBounds()[3]) ); //bottom right
1229                         points.push( new Point(stack.figures[i].getBounds()[0], stack.figures[i].getBounds()[3]) ); //bottom left
1230                         points.push( new Point(stack.figures[i].getBounds()[2], stack.figures[i].getBounds()[1]) ); //top right
1231                     }
1232                         
1233                     for(var a = 0; a < points.length; a++){
1234                         if( Util.isPointInside(points[a], selectionArea.getPoints()) ){
1235                             figuresToAdd.push(stack.figures[i].id);
1236                             break;
1237                         }
1238                     }
1239                 } //end if
1240             } //end for
1241                 
1242             if(figuresToAdd.length > 1){ //if we selected one figure then we can create a group
1243                 selectedGroupId = stack.groupCreate(figuresToAdd);
1244                 state = STATE_GROUP_SELECTED;
1245             }
1246             else if (figuresToAdd.length == 1){ // if we only select one figure, then it is not a group
1247                 selectedFigureId = figuresToAdd[0];
1248                 state = STATE_FIGURE_SELECTED;
1249             }
1250             break;
1251             
1252             
1253         case STATE_CONNECTOR_PICK_SECOND:
1254             //get current connector
1255             var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId);
1256 
1257             //get it's CPs
1258             var conCps = CONNECTOR_MANAGER.connectionPointGetAllByParent(con.id);
1259 
1260             //snap to a figure?
1261             var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP
1262             if(fCpId != -1){ //we are over a figure's cp
1263                 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId);
1264                 Log.info("Second ConnectionPoint is: " + fCp);
1265 
1266                 conCps[1].point.x = fCp.point.x;
1267                 conCps[1].point.y = fCp.point.y;
1268 
1269                 con.turningPoints[con.turningPoints.length - 1].x = fCp.point.x;
1270                 con.turningPoints[con.turningPoints.length - 1].y = fCp.point.y;
1271 
1272                 fCp.color = ConnectionPoint.NORMAL_COLOR; //change back the color
1273                 conCps[1].color = ConnectionPoint.NORMAL_COLOR; //change back the color
1274 
1275                 var g = CONNECTOR_MANAGER.glueCreate(fCp.id, conCps[1].id);
1276 
1277                 if(con.type == Connector.TYPE_JAGGED){
1278                     //TODO: add code
1279                     CONNECTOR_MANAGER.connectorAdjustByConnectionPoint(conCps[1].id);
1280                 }
1281                 if(!ev.noAddUndo && doUndo == true){//if this is a new action, not a "redone" action add a new Undo
1282                     History.addUndo(new ConnectCommand([g.id1,g.id2], History.OBJECT_GLUE, null, g.id1, null));
1283                 }
1284                 else if(doUndo == true){//otherwise, an undo already exists, and we must incrememnt the ponter
1285                     History.CURRENT_POINTER ++;
1286                 }
1287 
1288             }
1289             else{ //not over a figure's CP
1290                 conCps[1].point.x = x;
1291                 conCps[1].point.y = y;
1292 
1293                 con.turningPoints[con.turningPoints.length - 1].x = x;
1294                 con.turningPoints[con.turningPoints.length - 1].y = y;
1295 
1296                 if(con.type == Connector.TYPE_JAGGED){
1297                     CONNECTOR_MANAGER.connectorAdjustByConnectionPoint(conCps[1].id);
1298                 }
1299             }
1300             if(!ev.noAddUndo){
1301                 currentMoveUndo.currentValue = [currentMoveUndo.currentValue, ev];
1302             }
1303 
1304             state = STATE_CONNECTOR_SELECTED;
1305             setUpEditPanel(con);
1306             redraw = true;
1307             break;
1308 
1309         
1310 
1311         case STATE_CONNECTOR_MOVE_POINT:
1312             //selected ConnectionPoint
1313             var selCp = CONNECTOR_MANAGER.connectionPointGetById(selectedConnectionPointId);
1314             
1315             //selected ConnectionPoints (that belong to selected Connector)
1316             var conCps = CONNECTOR_MANAGER.connectionPointGetAllByParent(selCp.parentId);
1317 
1318             var conPoint = selCp;
1319             var matrix = Matrix.translationMatrix(conPoint.point.x - currentMoveUndo.previousValue[0][2], 
1320                                                     conPoint.point.y - currentMoveUndo.previousValue[1][2]);
1321             if(doUndo){
1322                 currentMoveUndo.currentValue = [matrix];
1323                 currentMoveUndo.previousValue = [Matrix.translationMatrix(currentMoveUndo.previousValue[0][2] - conPoint.point.x,
1324                                                                             currentMoveUndo.previousValue[1][2] - conPoint.point.y)];
1325                 History.addUndo(currentMoveUndo);
1326             }
1327             
1328             //change back color
1329             conCps[0].color = ConnectionPoint.NORMAL_COLOR;
1330             conCps[1].color = ConnectionPoint.NORMAL_COLOR;
1331 
1332 
1333             //Test to see if the CP was UNGLUED to another figure
1334             var glues = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(selectedConnectionPointId);
1335             if(glues.length == 1){ //ok it is connected and it should be ony one Glue
1336                 //pick first one
1337                 var fCpId = glues[0].id1; //Figure's ConnectionPoint id
1338                 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId); //Figure's ConnectionPoint
1339                 if(!fCp.point.near(x,y, 3)){ //if we dragged the CP beyound the range of figure's cp
1340                     CONNECTOR_MANAGER.glueRemoveByIds(fCpId , selectedConnectionPointId);
1341                     Log.info('Glue removed');
1342                 }
1343             }
1344 
1345             //Test to see if the CP was GLUED to another figure
1346             //see if over a figure's CP
1347             var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP
1348             if(fCpId != -1){ //we are over a figure's cp
1349                 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId);
1350                 Log.info("onMouseUp() + STATE_CONNECTOR_MOVE_POINT : fCP is " + fCp + " and fCpId is " +  fCpId);
1351                 //var fig = stack.figureSelectById(fCp.parentId);
1352 
1353                 var con = CONNECTOR_MANAGER.connectorGetById(selCp.parentId);
1354 
1355                 /*Test to see if we droped into the same figure (just moved inside figure CP's range)
1356                  *This means that no unglue event occured so we only need to snap back*/
1357                 var existingGlues = CONNECTOR_MANAGER.glueGetAllByIds(fCpId, selectedConnectionPointId);
1358                 if(existingGlues.length > 0){ //same figure
1359                     Log.info("Snap back to old figure (not a new glue)");
1360                     //ok we are still linked to old Figure...so snap back to it
1361                     if(conCps[0].id == selectedConnectionPointId){//start cp
1362                         conCps[0].point.x = fCp.point.x;
1363                         conCps[0].point.y = fCp.point.y;
1364 
1365                         con.turningPoints[0].x = fCp.point.x;
1366                         con.turningPoints[0].y = fCp.point.y;
1367                     } else{ //end cp
1368                         conCps[1].point.x = fCp.point.x;
1369                         conCps[1].point.y = fCp.point.y;
1370 
1371                         con.turningPoints[con.turningPoints.length - 1].x = fCp.point.x;
1372                         con.turningPoints[con.turningPoints.length - 1].y = fCp.point.y;
1373                     }
1374                 }
1375                 else{ //new figure
1376                     Log.info("Snap back to new figure (plus add a new glue)");
1377                     //ok we are still linked to old Figure...so snap back to it
1378                     if(conCps[0].id == selectedConnectionPointId){//start cp
1379                         conCps[0].point.x = fCp.point.x;
1380                         conCps[0].point.y = fCp.point.y;
1381 
1382                         con.turningPoints[0].x = fCp.point.x;
1383                         con.turningPoints[0].y = fCp.point.y;
1384                     } else{ //end cp
1385                         conCps[1].point.x = fCp.point.x;
1386                         conCps[1].point.y = fCp.point.y;
1387 
1388                         con.turningPoints[con.turningPoints.length - 1].x = fCp.point.x;
1389                         con.turningPoints[con.turningPoints.length - 1].y = fCp.point.y;
1390                     }
1391 
1392                     //add a glue
1393                     var g = CONNECTOR_MANAGER.glueCreate(fCpId, selectedConnectionPointId)
1394                     if(!ev.noAddUndo && doUndo == true){//if this is a new action, not a "redone" action add a new Undo
1395                         currentMoveUndo = new ConnectCommand([g.id1,g.id2], History.OBJECT_GLUE, null, g.id1, null);
1396                         History.addUndo(currentMoveUndo);
1397                     }
1398                     else if(doUndo == true){//otherwise, an undo already exists, and we must incrememnt the ponter
1399                         History.CURRENT_POINTER ++;
1400                     }
1401                 }
1402             }
1403             else{
1404             //update if we moved away
1405             //CONNECTOR_MANAGER.connectorAdjustByConnectionPoint(selectedConnectionPointId);
1406             }
1407             CONNECTOR_MANAGER.connectorAdjustByConnectionPoint(selectedConnectionPointId);
1408             
1409             
1410             state = STATE_CONNECTOR_SELECTED; //back to selected connector
1411             selectedConnectionPointId = -1; //but deselect the connection point
1412             redraw = true;
1413             break;
1414             
1415             
1416         case STATE_CONNECTOR_SELECTED:
1417             if(currentMoveUndo){
1418                 var turns = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints;
1419                 var newTurns = [turns.length];
1420                 for(var i = 0; i < turns.length; i ++){
1421                     newTurns[i] = turns[i].clone();
1422                 }
1423                 currentMoveUndo.currentValue = newTurns;
1424                 History.addUndo(currentMoveUndo);
1425                 state = STATE_NONE;
1426                 selectedConnectorId = -1;
1427             }
1428             break;
1429     }
1430     currentMoveUndo = null;
1431     mousePressed = false;
1432     draw();
1433 }
1434 
1435  /**Remembers last move. Initially it's null but once set it's a [x,y] array*/
1436 var lastMove = null;
1437 
1438 /**It will accumulate the changes on either X or Y coordinates for snap effect.
1439  *As we need to escape the "gravity/attraction" of the grid system we need to "accumulate" more changes
1440  *and if those changes become greater than a certain threshold we will initiate a snap action
1441  *Zack : "Because of the snap to grid function we need to move more than a certain amount of pixels
1442  *so we will not be snapped back to the old location"
1443  *Initially it's [0,0] but once more and more changes got added a snap effect will be triggered
1444  *and some of it's elements will be reset to 0.
1445  *So snapMonitor = [sumOfChagesOnX, sumOfChangesOnY]
1446  **/
1447 var snapMonitor = [0,0];
1448 
1449 
1450 /**Treats the mouse move event
1451  *@param {Event} ev - the event generated when key is up
1452  **/
1453 function onMouseMove(ev){
1454 //    //resize canvas.
1455 //    if(lastMousePosition != null){
1456 //        resize(ev);
1457 //    }
1458     var redraw = false;
1459     var coords = getCanvasXY(ev);
1460     var x = coords[0];
1461     var y = coords[1];
1462     var canvas = getCanvas();
1463 
1464     /*change cursor
1465      *More here: http://www.javascriptkit.com/dhtmltutors/csscursors.shtml
1466      */
1467     Log.debug("onMouseMoveCanvas: test if over a figure: " + x + "," + y);
1468 
1469 
1470     switch(state){
1471 
1472         case STATE_NONE:
1473             if(stack.figureIsOver(x,y)){ //over a figure or dragging it
1474                 canvas.style.cursor = 'move';
1475                 Log.debug('onMouseMove() - STATE_NONE - mouse cursor = move');
1476             }
1477             
1478             else{ //default cursor
1479                 canvas.style.cursor = 'default';
1480                 Log.debug('onMouseMove() - STATE_NONE - mouse cursor = default');
1481             }
1482             //END CURSOR STYLE
1483 
1484             //draw figure
1485 
1486             
1487             break;
1488 
1489 
1490         case STATE_SELECTING_MULTIPLE:
1491             selectionArea.points[1].x = x; //top right
1492 
1493             selectionArea.points[2].x = x; //bottom right
1494             selectionArea.points[2].y = y;
1495                 
1496             selectionArea.points[3].y = y;//bottom left
1497             redraw = true;
1498             break;
1499 
1500             
1501         case STATE_FIGURE_CREATE:
1502             if(createFigureFunction){ //creating a new figure
1503                 canvas.style.cursor = 'crosshair';
1504             }
1505             break;
1506 
1507             
1508         case STATE_FIGURE_SELECTED:
1509             if(stack.figureIsOver(x,y)){ //over a figure or dragging it
1510                 canvas.style.cursor = 'move';
1511             //Log.info('onMouseMove() - STATE_FIGURE_SELECTED - mouse cursor = move');
1512             }
1513             else if(HandleManager.handleGet(x,y) != null){ //over a handle?. Handles should appear only for selected figures
1514                 canvas.style.cursor = HandleManager.handleGet(x,y).getCursor();
1515             //Log.info('onMouseMove() - STATE_FIGURE_SELECTED - mouse cursor = ' + canvas.style.cursor);
1516             }
1517             else{ //default cursor
1518                 canvas.style.cursor = 'default';
1519             //Log.info('onMouseMove() - STATE_FIGURE_SELECTED - mouse cursor = default');
1520             }
1521 
1522             /*move figure only if no handle is selected*/
1523             if(mousePressed == true && lastMove != null && selectedFigureId != -1
1524                 && HandleManager.handleGetSelected() == null ){
1525                 //Log.info("onMouseMove() + STATE_FIGURE_SELECTED - move selected figure");
1526                 var fig = stack.figureGetById(selectedFigureId);
1527                 var moveMatrix = generateMoveMatrix(fig, x, y);
1528                 //                if(!Matrix.equals(moveMatrix, Matrix.IDENTITY)){
1529                 fig.transform(moveMatrix);
1530                 redraw = true;
1531             //                }
1532 
1533             /*
1534                 draw();
1535                 return;
1536                         */
1537             }
1538 
1539             /*if we have a handle action*/
1540             if(mousePressed==true && lastMove != null && HandleManager.handleGetSelected()!=null){
1541                 //Log.info("onMouseMove() + STATE_FIGURE_SELECTED - trigger a handler action");
1542                 var handle = HandleManager.handleGetSelected();
1543                 //alert('Handle action');
1544                 handle.action(lastMove,x,y);
1545                 redraw = true;
1546             }
1547             
1548             break;
1549 
1550 
1551         case STATE_GROUP_SELECTED:
1552             //GROUPS
1553             if(stack.figureIsOver(x,y)){ //over a figure or dragging it
1554                 canvas.style.cursor = 'move';
1555                 Log.debug('onMouseMove() - STATE_GROUP_SELECTED - mouse cursor = move');
1556             }
1557             else if(HandleManager.handleGet(x,y) != null){ //over a handle?. Handles should appear only for selected figures
1558                 canvas.style.cursor = HandleManager.handleGet(x,y).getCursor();
1559             }
1560             else{ //default cursor
1561                 canvas.style.cursor = 'default';
1562                 Log.debug('onMouseMove() - STATE_GROUP_SELECTED - mouse cursor = default');
1563             }
1564 
1565             /*move group only if no handle is selected*/
1566             if(mousePressed == true && lastMove != null && selectedGroupId != -1
1567                 && HandleManager.handleGetSelected() == null ){
1568                 //Log.info("onMouseMove() + STATE_FIGURE_SELECTED - move selected figure");
1569                 var g = stack.groupGetById(selectedGroupId);
1570                 var moveMatrix = generateMoveMatrix(g, x, y);
1571                 g.transform(moveMatrix);
1572                 redraw = true;
1573             }
1574 
1575             /*if we have a handle action*/
1576             if(mousePressed==true && lastMove != null && HandleManager.handleGetSelected()!=null){
1577                 //Log.info("onMouseMove() + STATE_FIGURE_SELECTED - trigger a handler action");
1578                 var handle = HandleManager.handleGetSelected();
1579                 //alert('Handle action');
1580                 handle.action(lastMove,x,y);
1581                 redraw = true;
1582             }
1583 
1584             break;
1585 
1586             
1587         case STATE_CONNECTOR_PICK_FIRST:
1588             //change FCP (figure connection points) color
1589             var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP
1590 
1591             if(fCpId != -1){ //we are over a figure's CP
1592                 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId);
1593                 fCp.color = ConnectionPoint.OVER_COLOR;
1594                 //                canvas.style.cursor = 'crosshair';
1595                 selectedConnectionPointId = fCpId;
1596             }
1597             else{ //change back old connection point to normal color
1598                 if(selectedConnectionPointId != -1){
1599                     var oldCp = CONNECTOR_MANAGER.connectionPointGetById(selectedConnectionPointId);
1600                     oldCp.color = ConnectionPoint.NORMAL_COLOR;
1601                     //                    canvas.style.cursor = 'normal';
1602                     selectedConnectionPointId = -1;
1603                 }
1604             }
1605             redraw = true;
1606             break;
1607 
1608 
1609         case STATE_CONNECTOR_PICK_SECOND:
1610             //moved to allow undo to access it
1611             connectorPickSecond(x,y,ev);
1612             redraw = true;
1613             break;
1614 
1615 
1616         case STATE_CONNECTOR_SELECTED:
1617             //alert('Move but we have a connector');
1618             //change cursor to move if over a connector's CP
1619             //var connector = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId);
1620             var cps = CONNECTOR_MANAGER.connectionPointGetAllByParent(selectedConnectorId);
1621             var start = cps[0];
1622             var end = cps[1];
1623             if(start.point.near(x, y, 3) || end.point.near(x, y, 3)){
1624                 canvas.style.cursor = 'move';
1625             }
1626             else if(HandleManager.handleGet(x,y) != null){ //over a handle?. Handles should appear only for selected figures
1627                 canvas.style.cursor = HandleManager.handleGet(x,y).getCursor();
1628             }
1629             else{
1630                 canvas.style.cursor = 'default';
1631             }
1632             
1633             /*if we have a handle action*/
1634             if(mousePressed==true && lastMove != null && HandleManager.handleGetSelected()!=null){
1635                 Log.info("onMouseMove() + STATE_FIGURE_SELECTED - trigger a handler action");
1636                 var handle = HandleManager.handleGetSelected();
1637                 //alert('Handle action');
1638                 
1639                 /*We need completely new copies of the turningPoints in order to restore them,
1640                  *this is simpler than keeping track of the handle used, the direction in which the handle edits
1641                  *and the turningPoints it edits*/
1642                 
1643                 //store old turning points
1644                 var turns = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints;
1645                 var oldTurns = [turns.length];
1646                 for(var i = 0; i < turns.length; i++){
1647                     oldTurns[i] = turns[i].clone();
1648                 }
1649                 
1650                 
1651                 //DO the handle action
1652                 handle.action(lastMove,x,y);
1653                 
1654                 //store new turning points
1655                 turns = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints;
1656                 var newTurns = [turns.length];
1657                 for(var i = 0; i < turns.length; i++){
1658                     newTurns[i] = turns[i].clone();
1659                 }
1660                                 
1661                 
1662                 //see if old turning points are the same as the new turning points
1663                 var difference = false;
1664                 for(var k=0;k<newTurns.length; k++){
1665                     if(! newTurns[k].equals(oldTurns[k])){
1666                         difference = true;
1667                     }
1668                 }
1669                 
1670                 //store the Command in History
1671                 if(difference && doUndo){
1672                     currentMoveUndo = new ConnectorHandleCommand(selectedConnectorId, History.OBJECT_CONNECTOR, null, oldTurns, newTurns);
1673                     History.addUndo(currentMoveUndo);
1674                 }
1675                     
1676                 redraw = true;
1677             }
1678             break;
1679 
1680 
1681         case STATE_CONNECTOR_MOVE_POINT:
1682             if(mousePressed){ //only if we are dragging
1683                 var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId);
1684                 var cps = CONNECTOR_MANAGER.connectionPointGetAllByParent(selectedConnectorId);
1685                 
1686                 //update cursor if over a figure's cp
1687                 var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x,y, ConnectionPoint.TYPE_FIGURE);
1688                 if(fCpId != -1){
1689                     canvas.style.cursor = 'default';
1690                     if(cps[0].id == selectedConnectionPointId){
1691                         cps[0].color = ConnectionPoint.OVER_COLOR;
1692                     }
1693                     else{
1694                         cps[1].color = ConnectionPoint.OVER_COLOR;
1695                     }
1696                 }
1697                 else{
1698                     canvas.style.cursor = 'move';
1699                     if(cps[0].id == selectedConnectionPointId){
1700                         cps[0].color = ConnectionPoint.NORMAL_COLOR;
1701                     }
1702                     else{
1703                         cps[1].color = ConnectionPoint.NORMAL_COLOR;
1704                     }
1705                 }
1706 
1707                 /*update connector - but not unglue/glue it (Unglue and glue is handle in onMouseUp)
1708                  *as we want the glue-unglue to produce only when mouse is released*/
1709                 
1710 
1711                 if(cps[0].id == selectedConnectionPointId){ //start
1712                     //alert('start');
1713                     cps[0].point.x = x;
1714                     cps[0].point.y = y;
1715                     con.turningPoints[0].x = x;
1716                     con.turningPoints[0].y = y;
1717                 } else{ //end
1718                     //alert('end');
1719                     cps[1].point.x = x;
1720                     cps[1].point.y = y;
1721                     con.turningPoints[con.turningPoints.length - 1].x = x;
1722                     con.turningPoints[con.turningPoints.length - 1].y = y;
1723                 }
1724                 redraw = true;
1725             }
1726             break;
1727     }
1728 
1729 
1730     lastMove = [x, y];
1731     
1732     if(redraw){
1733         draw();
1734     }
1735     return false;
1736 }
1737 
1738 
1739 /**Pick the first connector we can get at (x,y) position
1740  *@param {Number} x - the x position 
1741  *@param {Number} y - the y position 
1742  *@param {Event} ev - the event triggered
1743  **/
1744 function connectorPickFirst(x, y, ev){
1745     //create connector
1746     var conId = CONNECTOR_MANAGER.connectorCreate(new Point(x, y),new Point(x+10,y+10) /*fake cp*/, connectorType);
1747     selectedConnectorId = conId;
1748     var con = CONNECTOR_MANAGER.connectorGetById(conId);
1749     if(ev != null && !ev.noAddUndo && doUndo){
1750         currentMoveUndo = new CreateCommand(selectedConnectorId, History.OBJECT_CONNECTOR, connectorType, null, ev)
1751         History.addUndo(currentMoveUndo);
1752     }
1753 
1754     //TRY TO GLUE IT
1755     //1.get CP of the connector
1756     var conCps = CONNECTOR_MANAGER.connectionPointGetAllByParent(conId);
1757 
1758     //see if we can snap to a figure
1759     var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP
1760     if(fCpId != -1){
1761         var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId);
1762 
1763         //update connector' cp
1764         conCps[0].point.x = fCp.point.x;
1765         conCps[0].point.y = fCp.point.y;
1766 
1767         //update connector's turning point
1768         con.turningPoints[0].x = fCp.point.x;
1769         con.turningPoints[0].y = fCp.point.y;
1770 
1771         var g = CONNECTOR_MANAGER.glueCreate(fCp.id, conCps[0].id);
1772     //alert('First glue ' + g);
1773     //lets create an undo with composite id
1774 
1775     /*we have already added an undo object, this is not needed
1776          *if(ev != null && doUndo){//if this is a new action, not a "redone" action add a new Undo
1777             History.addUndo(new Action([g.id1,g.id2], History.OBJECT_GLUE, History.ACTION_CONNECT, null, g.id1, null));
1778         }
1779         else if(doUndo){//otherwise, an undo already exists, and we must incrememnt the ponter
1780             //History.CURRENT_POINTER ++;
1781         }*/
1782     }
1783     state = STATE_CONNECTOR_PICK_SECOND;
1784 }
1785 
1786 /**Pick the second connector we can get at (x,y) position
1787  *@param {Number} x - the x position 
1788  *@param {Number} y - the y position 
1789  *@param {Event} ev - the event triggered
1790  **/
1791 function connectorPickSecond(x, y, ev){
1792     //current connector
1793     var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId) //it should be the last one
1794     var cps = CONNECTOR_MANAGER.connectionPointGetAllByParent(con.id);
1795 
1796     //change FCP (figure connection points) color
1797     var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP
1798 
1799     if(fCpId != -1){
1800         var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId);
1801         fCp.color = ConnectionPoint.OVER_COLOR;
1802         cps[1].color = ConnectionPoint.OVER_COLOR;
1803         selectedConnectionPointId = fCpId;
1804     }
1805     else{//change back old connection point to normal color
1806         if(selectedConnectionPointId != -1){
1807             var oldCp = CONNECTOR_MANAGER.connectionPointGetById(selectedConnectionPointId);
1808             oldCp.color = ConnectionPoint.NORMAL_COLOR;
1809             cps[1].color = ConnectionPoint.NORMAL_COLOR;
1810             selectedConnectionPointId = -1;
1811         }
1812     }
1813 
1814     //update point
1815     if(con.type == Connector.TYPE_STRAIGHT){
1816         //update last connection point
1817         cps[1].point.x = x;
1818         cps[1].point.y = y;
1819 
1820         //update last turning point
1821         con.turningPoints[1].x = x;
1822         con.turningPoints[1].y = y;
1823     }
1824     else if(con.type == Connector.TYPE_JAGGED){
1825 
1826         //WE WILL DRAW A FAKE CONNECTOR - that will be adjusted once mouse released
1827 
1828         //WE WILL FULLY RECONSTRUCT THE turningPoints vector
1829         var lastIndex = con.turningPoints.length - 1;
1830 
1831         //update last connection point
1832         cps[1].point.x = x;
1833         cps[1].point.y = y;
1834 
1835 
1836         //update last turning point
1837         con.turningPoints[lastIndex].x = x;
1838         con.turningPoints[lastIndex].y = y;
1839 
1840         //now add 2 more intermediate points to make it look jagged
1841         var first = con.turningPoints[0];
1842         var last = con.turningPoints[lastIndex];
1843 
1844         //
1845         var tp1 = new Point( (first.x + last.x)/2, first.y);
1846         var tp2 = new Point( (first.x + last.x)/2, last.y);
1847 
1848         con.turningPoints = [first, tp1, tp2, last];
1849 
1850     //Log.info("Now the jagged con has " + con.turningPoints.length + " points" + con.turningPoints);
1851     }
1852 }
1853 
1854 
1855 /** Creates a moving matrix taking into consideration the snapTo option
1856  * The strange stuff is that Dia (http://projects.gnome.org/dia/) is using a top/left align
1857  * but OpenOffice's Draw is using something similar to Diagramo
1858  * @return {Matrix} - translation matrix
1859  * @param {Object} fig - could be a figure, or a Connector
1860  * @param {Number} x - mouse position
1861  * @param {Number} y - mouse position
1862  * @author Zack Newsham
1863  * @author Alex Gheorghiu
1864  */
1865 function generateMoveMatrix(fig, x,y){
1866     //    Log.group("generateMoveMatrix");
1867     var dx = x - lastMove[0];
1868     var dy = y - lastMove[1];
1869     //    Log.info("generateMoveMatrix() - delta  " + dx + ',  ' + dy);
1870     
1871     var moveMatrix = null;
1872         
1873     if(snapTo){ //snap effect
1874         moveMatrix = [
1875         [1, 0, 0],
1876         [0, 1, 0],
1877         [0, 0, 1]
1878         ];
1879                     
1880         //        Log.info("generateMoveMatrix() - old snapMonitor : " + snapMonitor);
1881         //        Log.info("generateMoveMatrix() - dx : " + dx + " dy: " + dy);
1882         snapMonitor[0] += dx;
1883         snapMonitor[1] += dy;
1884         //        Log.info("generateMoveMatrix() - new snapMonitor : " + snapMonitor);
1885         //        Log.info("generateMoveMatrix() - figure bounds : " + fig.getBounds());
1886 
1887         //HORIZONTAL
1888         if(dx > 0){ //dragged to right
1889             var nextGridX = Math.ceil( (fig.getBounds()[2] + snapMonitor[0]) / GRIDWIDTH ) * GRIDWIDTH;
1890             if((snapMonitor[0] + fig.getBounds()[2]) % GRIDWIDTH >= GRIDWIDTH/2 ){                
1891                 moveMatrix[0][2] = nextGridX - fig.getBounds()[2];
1892                 //                Log.info("generateMoveMatrix() - drag right, nextGridX: " + nextGridX + " x-adjust: " + (nextGridX - fig.getBounds()[2]) );
1893                 snapMonitor[0] -= nextGridX - fig.getBounds()[2];
1894             }
1895         }
1896         else if(dx < 0){ //dragged to left
1897             var previousGridX = Math.floor( (fig.getBounds()[0] + snapMonitor[0]) / GRIDWIDTH ) * GRIDWIDTH;
1898 
1899             if( fig.getBounds()[0] + snapMonitor[0] >= previousGridX
1900                 && fig.getBounds()[0] + snapMonitor[0] <= previousGridX + GRIDWIDTH/2
1901                 ){
1902                 moveMatrix[0][2] = -(fig.getBounds()[0] - previousGridX);
1903                 //                Log.info("generateMoveMatrix() - drag left, previousGridX: " + previousGridX + " x-adjust: " + (-(fig.getBounds()[0] - previousGridX)) );
1904                 snapMonitor[0] += fig.getBounds()[0] - previousGridX;
1905             }
1906         }
1907 
1908         //VERTICAL
1909         if(dy > 0){ //dragged to bottom
1910             var nextGridY = Math.ceil( (fig.getBounds()[3] + snapMonitor[1]) / GRIDWIDTH ) * GRIDWIDTH;
1911             if( (fig.getBounds()[3] + snapMonitor[1]) % GRIDWIDTH > GRIDWIDTH/2 ){
1912                 //                Log.info("generateMoveMatrix() - drag bottom");
1913                 moveMatrix[1][2] = nextGridY - fig.getBounds()[3];
1914                 snapMonitor[1] -= nextGridY - fig.getBounds()[3];
1915             }
1916         }
1917         else if (dy < 0){ //drag to top
1918             var previousGridY = Math.floor( (fig.getBounds()[1] + snapMonitor[1]) / GRIDWIDTH ) * GRIDWIDTH;
1919             if(fig.getBounds()[1] + snapMonitor[1] >= previousGridY
1920                 && fig.getBounds()[1] + snapMonitor[1] <= previousGridY + GRIDWIDTH/2
1921                 ){
1922                 //                Log.info("generateMoveMatrix() - drag top");
1923                 moveMatrix[1][2] = -(fig.getBounds()[1] - previousGridY);
1924                 snapMonitor[1] += fig.getBounds()[1] - previousGridY;
1925             }
1926         }
1927 
1928     //        Log.info("generateMoveMatrix() - 'trimmed' snapMonitor : " + snapMonitor);
1929         
1930     } else{ //normal move
1931         moveMatrix = [
1932         [1, 0, dx],
1933         [0, 1, dy],
1934         [0, 0, 1]
1935         ];
1936     }
1937 
1938     Log.groupEnd();
1939     return moveMatrix;
1940 }
1941 
1942 
1943 /**Computes the bounds of the canvas*/
1944 function getCanvasBounds(){
1945     var canvasMinX = $("#a").offset().left;
1946     var canvasMaxX = canvasMinX + $("#a").width();
1947     var canvasMinY = $("#a").offset().top;
1948     var canvasMaxY = canvasMinX + $("#a").height();
1949 
1950     return [canvasMinX, canvasMinY, canvasMaxX, canvasMaxY];
1951 }
1952 
1953 /**Computes the (x,y) coordinates of an event in page
1954  *@param {Event} ev - the event
1955  **/
1956 function getBodyXY(ev){
1957     return [ev.pageX,ev.pageY];//TODO: add scroll
1958 }
1959 
1960 
1961 /**
1962  *Extracts the X and Y from an event (for canvas)
1963  *@param {Event} ev - the event
1964  **/
1965 function getCanvasXY(ev){
1966     var position = [];
1967     var canvasBounds = getCanvasBounds();
1968     var tempPageX = null;
1969     var tempPageY = null;
1970     
1971     if(ev.touches){ //iPad 
1972         if(ev.touches.length > 0){
1973             tempPageX = ev.touches[0].pageX;
1974             tempPageY = ev.touches[0].pageY;
1975         }
1976     }
1977     else{ //normal Desktop
1978         tempPageX = ev.pageX;
1979         tempPageY = ev.pageY;                
1980     }
1981     
1982     if(tempPageX >= canvasBounds[0] && tempPageX <= canvasBounds[2]
1983         && tempPageY >= canvasBounds[1] && tempPageY <= canvasBounds[3])
1984     {
1985         //alert('Inside canvas');
1986         position = [tempPageX - $("#a").offset().left, tempPageY - $("#a").offset().top];
1987         //alert("getXT : " + position);
1988     }
1989 
1990     return position;
1991 }
1992 
1993 
1994 
1995 /**Cleans up the canvas*/
1996 function reset(){
1997     var canvas = getCanvas();
1998     var ctx = getContext();
1999     ctx.beginPath();
2000     ctx.clearRect(0,0,canvas.width,canvas.height)
2001     ctx.closePath();
2002     ctx.stroke();
2003 //canvas.width=canvas.width;
2004 }
2005 
2006 
2007 /**Draws all the stuff on the canvas*/
2008 function draw(){
2009 
2010     var ctx = getContext();
2011 
2012     //alert('Paint 1')
2013     reset();
2014     //alert('Paint 2')
2015     stack.paint(ctx);
2016     
2017     minimap.updateMinimap();
2018 //alert('Paint 3')
2019 }
2020 
2021 
2022 
2023 
2024 /**
2025  *Dispatch actions. Detect the action needed and trigger it.
2026  *@param {String} action - the action name
2027  **/
2028 function action(action){
2029     redraw = false;
2030     switch(action){
2031         
2032         case 'undo':
2033             Log.info("main.js->action()->Undo. Nr of actions in the stack: " + History.COMMANDS.length);
2034             History.undo();
2035             redraw = true;
2036             break;
2037             
2038         case 'redo':
2039             Log.info("main.js->action()->Redo. Nr of actions in the stack: " + History.COMMANDS.length);
2040             History.redo();
2041             redraw = true;
2042             break;
2043             
2044         case 'group':
2045             /*After we pressed Ctrl-G any temporary group will became permanent*/
2046             if(selectedGroupId != -1){
2047                 var group = stack.groupGetById(selectedGroupId);
2048                 group.permanent = true;
2049                 if(doUndo){
2050                     History.addUndo( new GroupCommand(selectedGroupId, History.OBJECT_GROUP, true, false, stack.figureGetIdsByGroupId(selectedGroupId)) );
2051                 }
2052             }
2053             redraw = true;
2054             break;
2055         
2056         case 'ungroup':
2057             if(selectedGroupId != -1){
2058                 if(doUndo){
2059                     History.addUndo(new GroupCommand(selectedGroupId, History.OBJECT_GROUP, true, stack.figureGetIdsByGroupId(selectedGroupId), false));
2060                 }
2061 
2062                 stack.groupDestroy(selectedGroupId);
2063                 selectedGroupId = -1;
2064                 state = STATE_NONE;
2065                 redraw = true;
2066             }
2067             break;
2068        
2069         case 'connector-jagged':
2070             selectedFigureId = -1;
2071             state  = STATE_CONNECTOR_PICK_FIRST;
2072             connectorType = Connector.TYPE_JAGGED;
2073             redraw = true;
2074             break;
2075 
2076         case 'connector-straight':
2077             selectedFigureId = -1;            
2078             state  = STATE_CONNECTOR_PICK_FIRST;
2079             connectorType = Connector.TYPE_STRAIGHT;
2080             redraw = true;
2081             break;
2082 
2083         case 'rotate90':
2084         case 'rotate90A':
2085             if(selectedFigureId){
2086                 //alert("Selected figure index: " + stack.figureSelectedIndex);
2087                 var figure = stack.figureGetById(selectedFigureId);
2088                 var bounds = figure.getBounds();
2089                 var dx = bounds[0] + (bounds[2] - bounds[0]) / 2
2090                 var dy = bounds[1] + (bounds[3] - bounds[1]) / 2
2091                 //alert(dx + '  ' + dy);
2092                 //alert("Selected figure is: " + figure);
2093                 var TRANSLATE = [
2094                 [1, 0, dx * -1],
2095                 [0, 1, dy * -1],
2096                 [0, 0, 1]
2097                 ]
2098                 /*
2099                 var dx = bounds[0] + (bounds[3] - bounds[1]) / 2
2100                 var dy = bounds[1] + (bounds[2] - bounds[0]) / 2
2101                  */
2102                 //TODO: figure out why we have to -1 off the dx, we have to do this regardless of rotation angle
2103                 var TRANSLATEBACK = [
2104                 [1, 0, dx],
2105                 [0, 1, dy],
2106                 [0, 0, 1]
2107                 ]
2108                 figure.transform(TRANSLATE);
2109                 if(action=="rotate90"){
2110                     figure.transform(R90);
2111                 }
2112                 else{
2113                     figure.transform(R90A);
2114                 }
2115                 figure.transform(TRANSLATEBACK);
2116 
2117                 redraw = true;
2118             }
2119             break;
2120 
2121         case 'up':
2122             if(selectedFigureId != -1){
2123                 //alert("Selected figure index: " + stack.figureSelectedIndex);
2124                 var figure = stack.figureGetById(selectedFigureId);
2125                 TRANSLATE = [
2126                 [1, 0, 0],
2127                 [0, 1, -1],
2128                 [0, 0, 1]
2129                 ]
2130                 figure.transform(TRANSLATE);
2131                 redraw = true;
2132             }
2133             break;
2134 
2135         case 'down':
2136             if(selectedFigureId != -1){
2137                 //alert("Selected figure index: " + stack.figureSelectedIndex);
2138                 var figure = stack.figureGetById(selectedFigureId);
2139                 TRANSLATE = [
2140                 [1, 0, 0],
2141                 [0, 1, 1],
2142                 [0, 0, 1]
2143                 ]
2144                 figure.transform(TRANSLATE);
2145                 redraw = true;
2146             }
2147             break;
2148 
2149         case 'right':
2150             if(selectedFigureId != -1){
2151                 //alert("Selected figure index: " + stack.figureSelectedIndex);
2152                 var figure = stack.figureGetById(selectedFigureId);
2153                 TRANSLATE = [
2154                 [1, 0, 1],
2155                 [0, 1, 0],
2156                 [0, 0, 1]
2157                 ]
2158                 figure.transform(TRANSLATE);
2159                 redraw = true;
2160             }
2161             break;
2162 
2163         case 'left':
2164             if(selectedFigureId != -1){
2165                 //alert("Selected figure index: " + stack.figureSelectedIndex);
2166                 var figure = stack.figureGetById(selectedFigureId);
2167                 var TRANSLATE = [
2168                 [1, 0, -1],
2169                 [0, 1, 0],
2170                 [0, 0, 1]
2171                 ]
2172                 figure.transform(TRANSLATE);
2173                 redraw = true;
2174             }
2175             break;
2176 
2177         case 'grow':
2178             if(selectedFigureId != -1){
2179                 //alert("Selected figure index: " + stack.figureSelectedIndex);
2180                 var figure = stack.figureGetById(selectedFigureId);
2181                 var bounds = figure.getBounds();
2182                 var dx = bounds[0] + (bounds[2] - bounds[0]) / 2
2183                 var dy = bounds[1] + (bounds[3] - bounds[1]) / 2
2184                 //alert(dx + '  ' + dy);
2185                 //alert("Selected figure is: " + figure);
2186                 var TRANSLATE = [
2187                 [1, 0, dx * -1],
2188                 [0, 1, dy * -1],
2189                 [0, 0, 1]
2190                 ]
2191                 /*
2192                 var dx = bounds[0] + (bounds[3] - bounds[1]) / 2
2193                 var dy = bounds[1] + (bounds[2] - bounds[0]) / 2
2194                  */
2195                 var TRANSLATEBACK = [
2196                 [1, 0, dx],
2197                 [0, 1, dy],
2198                 [0, 0, 1]
2199                 ]
2200 
2201                 var GROW = [
2202                 [1 + 0.2, 0,   0],
2203                 [0,   1 + 0.2, 0],
2204                 [0,   0,   1]
2205                 ]
2206                 figure.transform(TRANSLATE);
2207                 figure.transform(GROW);
2208                 figure.transform(TRANSLATEBACK);
2209                 redraw = true;
2210             }
2211             break;
2212 
2213         case 'shrink':
2214             if(selectedFigureId != -1){
2215                 //alert("Selected figure index: " + stack.figureSelectedIndex);
2216                 var figure = stack.figureGetById(selectedFigureId);
2217                 var bounds = figure.getBounds();
2218                 var dx = bounds[0] + (bounds[2] - bounds[0]) / 2
2219                 var dy = bounds[1] + (bounds[3] - bounds[1]) / 2
2220                 //alert(dx + '  ' + dy);
2221                 //alert("Selected figure is: " + figure);
2222                 var TRANSLATE = [
2223                 [1, 0, dx * -1],
2224                 [0, 1, dy * -1],
2225                 [0, 0, 1]
2226                 ]
2227                 /*
2228                 var dx = bounds[0] + (bounds[3] - bounds[1]) / 2
2229                 var dy = bounds[1] + (bounds[2] - bounds[0]) / 2
2230                  */
2231                 var TRANSLATEBACK = [
2232                 [1, 0, dx],
2233                 [0, 1, dy],
2234                 [0, 0, 1]
2235                 ]
2236                 var SHRINK = [
2237                 [1 - 0.2, 0,   0],
2238                 [0,   1 - 0.2, 0],
2239                 [0,   0,   1]
2240                 ]
2241                 figure.transform(TRANSLATE);
2242                 figure.transform(SHRINK);
2243                 figure.transform(TRANSLATEBACK);
2244                 redraw = true;
2245             }
2246             break;
2247             
2248         case 'duplicate':
2249             if(selectedFigureId != -1){
2250                 var createdFigure = stack.figureGetById(selectedFigureId).clone();
2251                 createdFigure.transform(Matrix.translationMatrix(10,10));
2252                 stack.figureAdd(createdFigure);
2253                 stack.figureSelect(stack.figures.length-1);
2254                 createdFigure=null;
2255                 getCanvas().style.cursor="default";
2256                 redraw = true;
2257             }
2258             break;
2259 
2260         case 'back':
2261             if(selectedFigureId != -1){
2262                 History.addUndo(new ZOrderCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.idToIndex[selectedFigureId], 0));
2263                 stack.setPosition(selectedFigureId, 0);
2264                 redraw = true;
2265             }
2266             break;
2267 
2268         case 'front':
2269             if(selectedFigureId != -1){
2270                 History.addUndo(new ZOrderCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.idToIndex[selectedFigureId], stack.figures.length-1));
2271                 stack.setPosition(selectedFigureId, stack.figures.length-1);
2272                 redraw = true;
2273             }            
2274             break;
2275 
2276         case 'moveback':
2277             if(selectedFigureId != -1){
2278                 History.addUndo(new ZOrderCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.idToIndex[selectedFigureId], stack.idToIndex[selectedFigureId] - 1));
2279                 stack.swapToPosition(selectedFigureId, stack.idToIndex[selectedFigureId] - 1);
2280                 redraw = true;
2281             }
2282             break;
2283 
2284         case 'moveforward':
2285             if(selectedFigureId != -1){
2286                 History.addUndo(new ZOrderCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.idToIndex[selectedFigureId], stack.idToIndex[selectedFigureId] + 1));
2287                 stack.swapToPosition(selectedFigureId, stack.idToIndex[selectedFigureId] + 1);
2288                 redraw = true;
2289             }            
2290             break;
2291 
2292     }//end switch
2293 
2294     if(redraw){
2295         draw();
2296     }
2297 }
2298 
2299 /**Stores last mouse position. Null initially.*/
2300 var lastMousePosition = null;
2301 
2302 
2303 //function startResize(ev){
2304 //    lastMousePosition = getBodyXY(ev);
2305 //    currentMoveUndo = new Action(null, null, History.ACTION_CANVAS_RESIZE, null, lastMousePosition, null);
2306 //}
2307 //
2308 //
2309 //function stopResize(ev){
2310 //    if(lastMousePosition != null){
2311 //        currentMoveUndo.currentValue = [lastMousePosition[0],lastMousePosition[1]];
2312 //        History.addUndo(currentMoveUndo);
2313 //        currentMoveUndo = null;
2314 //        lastMousePosition = null;
2315 //    }
2316 //}
2317 
2318 
2319 //function resize(ev){
2320 //    if(lastMousePosition != null){
2321 //        var currentMousePosition
2322 //        if(ev instanceof Array){
2323 //            //we are undoing this
2324 //            currentMousePosition = ev;
2325 //        }
2326 //        else{
2327 //            currentMousePosition = getBodyXY(ev);
2328 //        }
2329 //        var width = canvas.getwidth() - (lastMousePosition[0] - currentMousePosition[0]);
2330 //        var height = canvas.getheight() - (lastMousePosition[1] - currentMousePosition[1]);
2331 //        canvas.setwidth(canvas,width);
2332 //        canvas.setheight(canvas, height);
2333 //        setUpEditPanel(canvas);
2334 //        lastMousePosition = currentMousePosition;
2335 //        /*if(canvas.width >= document.body.scrollWidth-370){
2336 //        }
2337 //        else {
2338 //            //document.getElementById("container").style.width = "";
2339 //        }*/
2340 //        draw();
2341 //    }
2342 //}
2343 
2344 
2345 /*======================APPLE=====================================*/
2346 /**Triggered when an touch is initiated (iPad/iPhone).
2347  *Simply forward to onMouseDown
2348  *@param {Event} event - the event triggered
2349  **/
2350 function touchStart(event){
2351     event.preventDefault();
2352 
2353     onMouseDown(event);                                                
2354 }
2355 
2356 
2357 /**Triggered while touching and moving is in progress (iPad/iPhone).
2358  *Simply forward to onMouseMove
2359  *@param {Event} event - the event triggered
2360  **/
2361 function touchMove(event){
2362     event.preventDefault();
2363 
2364     onMouseMove(event);                
2365 }
2366 
2367 
2368 /**Triggered when touch ends (iPad/iPhone).
2369  *Simply forward to onMouseUp
2370  *@param {Event} event - the event triggered
2371  **/
2372 function touchEnd(event){
2373     onMouseUp(event);
2374 }
2375 
2376 /**Triggered when touch is canceled (iPad/iPhone).
2377  *@param {Event} event - the event triggered
2378  **/
2379 function touchCancel(event){
2380     //nothing
2381 }
2382 /*======================END APPLE=================================*/
2383 
2384