text: /**
   * Copyright (c) 2009
   * Jan-Felix Schwarz
   * Copyright (c) 2010-2011 Intalio, Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   * DEALINGS IN THE SOFTWARE.
   **/
  if (!WAPAMA.Plugins) 
      WAPAMA.Plugins = new Object();
  
  WAPAMA.Plugins.KeysMove = WAPAMA.Plugins.AbstractPlugin.extend({
  
      facade: undefined,
      
      construct: function(facade){
      
          this.facade = facade;
          this.copyElements = [];
          
          //this.facade.registerOnEvent(WAPAMA.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this));
  
  		// SELECT ALL
  		this.facade.offer({
  		keyCodes: [{
  		 		metaKeys: [WAPAMA.CONFIG.META_KEY_META_CTRL],
  				keyCode: 65,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.selectAll.bind(this)
           });
  		 
  		// MOVE LEFT SMALL		
  		this.facade.offer({
  		keyCodes: [{
  		 		metaKeys: [WAPAMA.CONFIG.META_KEY_META_CTRL],
  				keyCode: WAPAMA.CONFIG.KEY_CODE_LEFT,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, WAPAMA.CONFIG.KEY_CODE_LEFT, false)
           });
  		 
  		 // MOVE LEFT
  		 this.facade.offer({
  		 keyCodes: [{
  				keyCode: WAPAMA.CONFIG.KEY_CODE_LEFT,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, WAPAMA.CONFIG.KEY_CODE_LEFT, true)
           });
  		 
  		// MOVE RIGHT SMALL	
  		 this.facade.offer({
  		 keyCodes: [{
  		 		metaKeys: [WAPAMA.CONFIG.META_KEY_META_CTRL],
  				keyCode: WAPAMA.CONFIG.KEY_CODE_RIGHT,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, WAPAMA.CONFIG.KEY_CODE_RIGHT, false)
           });
  		 
  		// MOVE RIGHT	
  		 this.facade.offer({
  		 keyCodes: [{
  				keyCode: WAPAMA.CONFIG.KEY_CODE_RIGHT,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, WAPAMA.CONFIG.KEY_CODE_RIGHT, true)
           });
  		 
  		// MOVE UP SMALL	
  		 this.facade.offer({
  		 keyCodes: [{
  		 		metaKeys: [WAPAMA.CONFIG.META_KEY_META_CTRL],
  				keyCode: WAPAMA.CONFIG.KEY_CODE_UP,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, WAPAMA.CONFIG.KEY_CODE_UP, false)
           });
  		 
  		// MOVE UP	
  		 this.facade.offer({
  		 keyCodes: [{
  				keyCode: WAPAMA.CONFIG.KEY_CODE_UP,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, WAPAMA.CONFIG.KEY_CODE_UP, true)
           });
  		 
  		// MOVE DOWN SMALL	
  		 this.facade.offer({
  		 keyCodes: [{
  		 		metaKeys: [WAPAMA.CONFIG.META_KEY_META_CTRL],
  				keyCode: WAPAMA.CONFIG.KEY_CODE_DOWN,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, WAPAMA.CONFIG.KEY_CODE_DOWN, false)
           });
  		 
  		// MOVE DOWN	
  		 this.facade.offer({
  		 keyCodes: [{
  				keyCode: WAPAMA.CONFIG.KEY_CODE_DOWN,
  				keyAction: WAPAMA.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, WAPAMA.CONFIG.KEY_CODE_DOWN, true)
           });
  		 
           
      },
      
  	/**
  	 * Select all shapes in the editor
  	 *
  	 */
  	selectAll: function(e){
      	Event.stop(e.event);
  		this.facade.setSelection(this.facade.getCanvas().getChildShapes(true))
  	},
  	
  	move: function(key, far, e) {
  		
      	Event.stop(e.event);
  
  		// calculate the distance to move the objects and get the selection.
  		var distance = far? 20 : 5;
  		var selection = this.facade.getSelection();
  		var currentSelection = this.facade.getSelection();
  		var p = {x: 0, y: 0};
  		
  		// switch on the key pressed and populate the point to move by.
  		switch(key) {
  
  			case WAPAMA.CONFIG.KEY_CODE_LEFT:
  				p.x = -1*distance;
  				break;
  			case WAPAMA.CONFIG.KEY_CODE_RIGHT:
  				p.x = distance;
  				break;
  			case WAPAMA.CONFIG.KEY_CODE_UP:
  				p.y = -1*distance;
  				break;
  			case WAPAMA.CONFIG.KEY_CODE_DOWN:
  				p.y = distance;
  				break;
  		}
  		
  		// move each shape in the selection by the point calculated and update it.
  		selection = selection.findAll(function(shape){ 
  			// Check if this shape is docked to an shape in the selection			
  			if(shape instanceof WAPAMA.Core.Node && shape.dockers.length == 1 && selection.include( shape.dockers.first().getDockedShape() )){ 
  				return false 
  			} 
  			
  			// Check if any of the parent shape is included in the selection
  			var s = shape.parent; 
  			do{ 
  				if(selection.include(s)){ 
  					return false
  				}
  			}while(s = s.parent); 
  			
  			// Otherwise, return true
  			return true;
  			
  		});
  		
  		/* Edges must not be movable, if only edges are selected and at least 
  		 * one of them is docked.
  		 */
  		var edgesMovable = true;
  		var onlyEdgesSelected = selection.all(function(shape) {
  			if(shape instanceof WAPAMA.Core.Edge) {
  				if(shape.isDocked()) {
  					edgesMovable = false;
  				}
  				return true;	
  			}
  			return false;
  		});
  		
  		if(onlyEdgesSelected && !edgesMovable) {
  			/* Abort moving shapes */
  			return;
  		}
  		
  		selection = selection.map(function(shape){ 
  			if( shape instanceof WAPAMA.Core.Node ){
  				/*if( shape.dockers.length == 1 ){
  					return shape.dockers.first()
  				} else {*/
  					return shape
  				//}
  			} else if( shape instanceof WAPAMA.Core.Edge ) {
  				
  				var dockers = shape.dockers;
  				
  				if( selection.include( shape.dockers.first().getDockedShape() ) ){
  					dockers = dockers.without( shape.dockers.first() )
  				}
  
  				if( selection.include( shape.dockers.last().getDockedShape() ) ){
  					dockers = dockers.without( shape.dockers.last() )
  				}
  				
  				return dockers	
  							
  			} else {
  				return null
  			}
  		
  		}).flatten().compact();
  		
  		if (selection.size() > 0) {
  			
  			//Stop moving at canvas borders
  			var selectionBounds = [ this.facade.getCanvas().bounds.lowerRight().x,
  			                        this.facade.getCanvas().bounds.lowerRight().y,
  			                        0,
  			                        0 ];
  			selection.each(function(s) {
  				selectionBounds[0] = Math.min(selectionBounds[0], s.bounds.upperLeft().x);
  				selectionBounds[1] = Math.min(selectionBounds[1], s.bounds.upperLeft().y);
  				selectionBounds[2] = Math.max(selectionBounds[2], s.bounds.lowerRight().x);
  				selectionBounds[3] = Math.max(selectionBounds[3], s.bounds.lowerRight().y);
  			});
  			if(selectionBounds[0]+p.x < 0)
  				p.x = -selectionBounds[0];
  			if(selectionBounds[1]+p.y < 0)
  				p.y = -selectionBounds[1];
  			if(selectionBounds[2]+p.x > this.facade.getCanvas().bounds.lowerRight().x)
  				p.x = this.facade.getCanvas().bounds.lowerRight().x - selectionBounds[2];
  			if(selectionBounds[3]+p.y > this.facade.getCanvas().bounds.lowerRight().y)
  				p.y = this.facade.getCanvas().bounds.lowerRight().y - selectionBounds[3];
  			
  			if(p.x!=0 || p.y!=0) {
  				// Instantiate the moveCommand
  				var commands = [new WAPAMA.Core.Command.Move(selection, p, null, currentSelection, this)];
  				// Execute the commands			
  				this.facade.executeCommands(commands);
  			}
  			
  		}
  	},
  	
  	getUndockedCommant: function(shapes){
  
  		var undockEdgeCommand = WAPAMA.Core.Command.extend({
  			construct: function(moveShapes){
  				this.dockers = moveShapes.collect(function(shape){ return shape instanceof WAPAMA.Core.Controls.Docker ? {docker:shape, dockedShape:shape.getDockedShape(), refPoint:shape.referencePoint} : undefined }).compact();
  			},			
  			execute: function(){
  				this.dockers.each(function(el){
  					el.docker.setDockedShape(undefined);
  				})
  			},
  			rollback: function(){
  				this.dockers.each(function(el){
  					el.docker.setDockedShape(el.dockedShape);
  					el.docker.setReferencePoint(el.refPoint);
  					//el.docker.update();
  				})
  			}
  		});
  		
  		command = new undockEdgeCommand( shapes );
  		command.execute();	
  		return command;
  	},
  	
  //    /**
  //     * The key handler for this plugin. Every action from the set of cut, copy,
  //     * paste and delete should be accessible trough simple keyboard shortcuts.
  //     * This method checks whether any event triggers one of those actions.
  //     *
  //     * @param {Object} event The keyboard event that should be analysed for
  //     *     triggering of this plugin.
  //     */
  //    keyHandler: function(event){
  //        //TODO document what event.which is.
  //        
  //        WAPAMA.Log.debug("keysMove.js handles a keyEvent.");
  //        
  //        // assure we have the current event.
  //        if (!event) 
  //            event = window.event;
  //        
  //        // get the currently pressed key and state of control key.
  //        var pressedKey = event.which || event.keyCode;
  //        var ctrlPressed = event.ctrlKey;
  //
  //		// if the key is one of the arrow keys, forward to move and return.
  //		if ([WAPAMA.CONFIG.KEY_CODE_LEFT, WAPAMA.CONFIG.KEY_CODE_RIGHT,
  //			WAPAMA.CONFIG.KEY_CODE_UP, WAPAMA.CONFIG.KEY_CODE_DOWN].include(pressedKey)) {
  //			
  //			this.move(pressedKey, !ctrlPressed);
  //			return;
  //		}
  //		
  //    }
  	
  });
  