text: dojo.provide("dojox.mdnd.Moveable");
  
  dojo.declare(
  	"dojox.mdnd.Moveable",
  	null,
  {
  	// summary:
  	//		Allow end-users to track a DOM node into the web page
  
  	// handle: DOMNode
  	//		The node on which the user clicks to drag the main node.
  	handle: null,
  	
  	// skip: Boolean
  	// 		A flag to control a drag action if a form element has been focused.
  	//		If true, the drag action is not executed.
  	skip: true,
  
  	// dragDistance: Integer
  	//		The user clicks on the handle, but the drag action will really begin
  	//		if he tracks the main node to more than 3 pixels.
  	dragDistance: 3,
  	
  	constructor: function(/*Object*/params, /*DOMNode*/node){
  		// summary:
  		// 		Configure parameters and listen to mousedown events from handle
  		//		node.
  		// params:
  		//		Hash of parameters
  		// node:
  		//		The draggable node
  
  		//console.log("dojox.mdnd.Moveable ::: constructor");
  		this.node = dojo.byId(node);
  		
  		this.d = this.node.ownerDocument;
  		
  		if(!params){ params = {}; }
  		this.handle = params.handle ? dojo.byId(params.handle) : null;
  		if(!this.handle){ this.handle = this.node; }
  		this.skip = params.skip;
  		this.events = [
  			dojo.connect(this.handle, "onmousedown", this, "onMouseDown")
  		];
  		if(dojox.mdnd.autoScroll){
  			this.autoScroll = dojox.mdnd.autoScroll;
  		}
  		
  	},
  	
  	isFormElement: function(/*DOMEvent*/ e){
  		// summary:
  		//		identify the type of target node associated with a DOM event.
  		// e:
  		//		a DOM event
  		// returns:
  		//		if true, the target is one of those specific nodes.
  
  		//console.log("dojox.mdnd.Moveable ::: isFormElement");
  		var t = e.target;
  		if(t.nodeType == 3 /*TEXT_NODE*/){
  			t = t.parentNode;
  		}
  		return " a button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0;	// Boolean
  	},
  	
  	onMouseDown: function(/*DOMEvent*/e){
  		// summary:
  		//		Occurs when the user clicks on the handle node.
  		//		Skip the drag action if a specific node is targeted.
  		//		Listens to mouseup and mousemove events on to the HTML document.
  		// e:
  		//		a DOM event
  		// tags:
  		//		callback
  
  		//console.log("dojox.mdnd.Moveable ::: onMouseDown");
  		if(this._isDragging){ return;}
  		var isLeftButton = (e.which || e.button) == 1;
  		if(!isLeftButton){
  			return;
  		}
  		if(this.skip && this.isFormElement(e)){ return; }
  		if(this.autoScroll){
  			this.autoScroll.setAutoScrollNode(this.node);
  			this.autoScroll.setAutoScrollMaxPage();
  		}
  		this.events.push(dojo.connect(this.d, "onmouseup", this, "onMouseUp"));
  		this.events.push(dojo.connect(this.d, "onmousemove", this, "onFirstMove"));
  		this._selectStart = dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent);
  		this._firstX = e.clientX;
  		this._firstY = e.clientY;
  		dojo.stopEvent(e);
  	},
  	
  	onFirstMove: function(/*DOMEvent*/e){
  		// summary:
  		//		Occurs when the user moves the mouse after clicking on the
  		//		handle.
  		//		Determinate when the drag action will have to begin (see
  		//		dragDistance).
  		// e:
  		//		A DOM event
  		// tags:
  		//		callback
  
  		//console.log("dojox.mdnd.Moveable ::: onFirstMove");
  		dojo.stopEvent(e);
  		var d = (this._firstX - e.clientX) * (this._firstX - e.clientX)
  				+ (this._firstY - e.clientY) * (this._firstY - e.clientY);
  		if(d > this.dragDistance * this.dragDistance){
  			this._isDragging = true;
  			dojo.disconnect(this.events.pop());
  			dojo.style(this.node, "width", dojo.contentBox(this.node).w + "px");
  			this.initOffsetDrag(e);
  			this.events.push(dojo.connect(this.d, "onmousemove", this, "onMove"));
  		}
  	},
  	
  	initOffsetDrag: function(/*DOMEvent*/e){
  		// summary:
  		//		Initialize the gap between main node coordinates and the clicked point.
  		//		Call the onDragStart method.
  		// e:
  		//		A DOM event
  
  		//console.log("dojox.mdnd.Moveable ::: initOffsetDrag");
  		this.offsetDrag = { 'l': e.pageX, 't': e.pageY };
  		var s = this.node.style;
  		var position = dojo.position(this.node, true);
  		/*if(s.position == "relative" || s.position == ""){
  			s.position = "absolute"; // enforcing the absolute mode
  		}*/
  		this.offsetDrag.l = position.x - this.offsetDrag.l;
  		this.offsetDrag.t = position.y - this.offsetDrag.t;
  		var coords = {
  			'x': position.x,
  			'y': position.y
  		};
  		this.size = {
  			'w': position.w,
  			'h': position.h
  		};
  		// method to catch
  		this.onDragStart(this.node, coords, this.size);
  	},
  	
  	onMove: function(/*DOMEvent*/e){
  		// summary:
  		//		Occurs when the user moves the mouse.
  		//		Calls the onDrag method.
  		// e:
  		//		a DOM event
  		// tags:
  		//		callback
  
  		//console.log("dojox.mdnd.Moveable ::: onMove");
  		dojo.stopEvent(e);
  		// hack to avoid too many calls to onMove in IE8 causing sometimes slowness
  		if(dojo.isIE == 8 && new Date() - this.date < 20){
  			return;
  		}
  		if(this.autoScroll){
  			this.autoScroll.checkAutoScroll(e);
  		}
  		var coords = {
  			'x': this.offsetDrag.l + e.pageX,
  			'y': this.offsetDrag.t + e.pageY
  		};
  		var s = this.node.style;
  		s.left = coords.x + "px";
  		s.top = coords.y + "px";
  		
  		// method to catch
  		this.onDrag(this.node, coords, this.size, {'x':e.pageX, 'y':e.pageY});
  		if(dojo.isIE == 8){
  			this.date = new Date();
  		}
  	},
  	
  	onMouseUp: function(/*DOMEvent*/e){
  		// summary:
  		//		Occurs when the user releases the mouse
  		//		Calls the onDragEnd method.
  		// e:
  		//		a DOM event
  
  		if (this._isDragging){
  			dojo.stopEvent(e);
  			this._isDragging = false;
  			if(this.autoScroll){
  				this.autoScroll.stopAutoScroll();
  			}
  			delete this.onMove;
  			this.onDragEnd(this.node);
  			this.node.focus();
   		}
  		dojo.disconnect(this.events.pop());
  		dojo.disconnect(this.events.pop());
  	},
  	
  	onDragStart: function(/*DOMNode*/node, /*Object*/coords, /*Object*/size){
  		// summary:
  		//		Stub function.
  		//		Notes : border box model
  		// node:
  		//		a DOM node
  		//	coords:
  		//		absolute position of the main node
  		// size:
  		//		an object encapsulating width an height values
  		// tags:
  		//		callback
  
  	},
  	
  	onDragEnd: function(/*DOMNode*/node){
  		// summary:
  		//		Stub function
  		//		Notes : Coordinates don't contain margins
  		// node:
  		//		a DOM node
  		// tags:
  		//		callback
  
  	},
  	
  	onDrag: function(/*DOMNode*/node, /*Object*/coords, /*Object*/size, /*Object*/mousePosition){
  		// summary:
  		//		Stub function.
  		//		Notes : border box model for size value, margin box model for coordinates
  		// node:
  		//		a DOM node
  		// coords:
  		//		position of the main node (equals to css left/top properties)
  		// size:
  		//		an object encapsulating width and height values
  		// mousePosition:
  		//		coordiantes of mouse
  		// tags:
  		//		callback
  
  	},
  
  	destroy: function(){
  		// summary:
  		//		Delecte associated events
  
  		// console.log("dojox.mdnd.Moveable ::: destroy");
  		dojo.forEach(this.events, dojo.disconnect);
  		this.events = this.node = null;
  	}
  });
  