text: define(["dojo", "../util/oo", "../defaults"], 
  function(dojo, oo, defaults){
  
  //dojox.drawing.manager.Mouse = 
  return oo.declare(
  	function(/* Object */options){
  		this.util = options.util;
  		this.keys = options.keys;
  		this.id = options.id || this.util.uid("mouse");
  		this.currentNodeId = "";
  		this.registered = {};
  	},
  	
  	{
  		// summary:
  		//		Master object (instance) that tracks mouse
  		//		events. A new instance is created for each
  		//		Drawing object.
  		// description:
  		//		You could connect to any method or event in this
  		//		class, but it is designed to have the object
  		//		'registered'. All objects with the current event
  		//		will be called directly.
  		//
  		//		Custom events are used often. In addition to
  		//		standard events onDown, onUp, onDrag, etc, if
  		//		a certain object is clicked upon (or dragged, etc),
  		//		that object's drawingType will create the custom event,
  		//		such as onAnchorDown, or onStencilDown.
  
  
  		// doublClickSpeed: Number
  		//		Milliseconds between clicks to
  		//		register as for onDoubleClick
  		doublClickSpeed:400,
  		
  		// private properties
  		
  		_lastx:0,
  		_lasty:0,
  		__reg:0,
  		_downOnCanvas:false,
  		
  		/*=====
  		CustomEventMethod: {
  			// summary:
  			//		The custom event methods that an Object that has
  			//		registered with manager.Mouse can receive.
  			//		Can contain any or all of the following methods
  			//		and they will be called on mouse events. All events
  			//		will be sent a EventObject event object.
  			//
  			//		NOTE:
  			//		Events happen anywhere in the document unless
  			//		otherwise noted.
  
  			// onMove: Function
  			//		Fires on mousemove when mouse is up
  			// onDown: Function
  			//		Fires on mousedown *on the canvas*
  			// onDrag: Function
  			//		Fires on mousemove when mouse is down
  			// onUp: Function
  			//		Fires on mouseup, anywhere in the document
  			// onStencilDown: Function
  			//		Fired on mousedown on a Stencil
  			// onStencilDrag: Function
  			//		Fired when mouse moves and mose is down on a Stencil
  			// onStencilUp: Function
  			//		Fired on mouseup off of a Stencil
  			// on[Custom]Up|Down|Move: Function
  			//		Custom events can bet set and fired by setting a
  			//		different drawingType on a Stencil, or by calling
  			//		setEventMode(customEventName)
  		},
  		EventObject: function(){
  			// summary:
  			//		The custom event object that is sent to registered objects
  			//		and their respective methods.
  			//
  			//		NOTE: Most event objects are the same with the exception
  			//		of the onDown events, which have fewer.
  
  			// id: String
  			//		Id of the focused object (included in onDown)
  			// pageX: Number
  			//		The X coordinate of the mouse from the left side of
  			//		the document. (included in onDown)
  			// pageY: Number
  			//		The Y coordinate of the mouse from the top of
  			//		the document. (included in onDown)
  			// x: Number
  			//		The X coordinate of the mouse from the left side
  			//		of the canvas (included in onDown)
  			// y: Number
  			//		The Y coordinate of the mouse from the top
  			//		of the canvas (included in onDown)
  
  			// last:	Object
  			//		The x and y coordinates of the last mousemove
  			//		relative to the canvas (not included in onDown)
  			// move: Object
  			//		The x and y amounts the mouse moved since the last event
  			//		(not included in onDown)
  			// orgX:	Number
  			//		The left side of the canvas from the side of the document (not included in onDown)
  			// orgY:	Number
  			//		The top of the canvas from the top of the document (not included in onDown)
  			// scroll: Object
  			//		The 'top' and 'left' scroll amounts of the canvas. (not included in onDown)
  			// start:	Object
  			//		The x and y coordinates of the mousedown event (not included in onDown)
  			// withinCanvas: Boolean
  			//		Whether the event happened within the Canvas or not (not included in onDown)
  		},
  		=====*/
  			
  		init: function(/* HTMLNode*/node){
  			// summary:
  			//		Internal. Initializes mouse.
  
  			this.container = node;
  			this.setCanvas();
  			var c;
  			var _isDown = false;
  			dojo.connect(this.container, "rightclick", this, function(evt){
  				console.warn("RIGHTCLICK")
  			});
  			
  			dojo.connect(document.body, "mousedown", this, function(evt){
  				//evt.preventDefault();
  				//dojo.stopEvent(evt);
  			});
  			
  			dojo.connect(this.container, "mousedown", this, function(evt){
  				this.down(evt);
  				// Right click shouldn't trigger drag
  				if(evt.button != dojo.mouseButtons.RIGHT){
  					_isDown = true;
  					c = dojo.connect(document, "mousemove", this, "drag");
  				}
  			});
  			dojo.connect(document, "mouseup", this, function(evt){
  				dojo.disconnect(c);
  				_isDown = false;
  				this.up(evt);
  			});
  			dojo.connect(document, "mousemove", this, function(evt){
  				if(!_isDown){
  					this.move(evt);
  				}
  			});
  			dojo.connect(this.keys, "onEsc", this, function(evt){
  				this._dragged = false;
  			});
  		},
  		
  		setCanvas: function(){
  			// summary:
  			//		Internal. Sets canvas position
  			var pos = dojo.coords(this.container.parentNode);
  			this.origin = dojo.clone(pos);
  		},
  		
  		scrollOffset: function(){
  			// summary:
  			// 	Gets scroll offset of canvas
  			return {
  				top:this.container.parentNode.scrollTop,
  				left:this.container.parentNode.scrollLeft
  			}; // Object
  		},
  
  		resize: function(width,height){
  			if(this.origin){
  				this.origin.w=width;
  				this.origin.h=height;
  			}
  		},
  
  		register: function(/* CustomEventMethod*/scope){
  			// summary:
  			//		All objects (Stencils) should register here if they
  			//		use mouse events. When registering, the object will
  			//		be called if it has that method.
  			//		See: CustomEventMethod and EventObject
  			// scope:
  			//		The object to be called
  			// returns: handle
  			//		Keep the handle to be used for disconnection.
  
  			var handle = scope.id || "reg_"+(this.__reg++);
  			if(!this.registered[handle]){ this.registered[handle] = scope; }
  			return handle; // String
  		},
  		unregister: function(handle){
  			// summary:
  			//		Disconnects object. Mouse events are no longer
  			//		called for it.
  			if(!this.registered[handle]){ return; }
  			delete this.registered[handle];
  		},
  		
  		_broadcastEvent:function(strEvt, obj){
  			// summary:
  			//		Fire events to all registered objects.
  
  			//console.log("mouse.broadcast:", strEvt, obj)
  			for(var nm in this.registered){
  				if(this.registered[nm][strEvt]) this.registered[nm][strEvt](obj);
  			}
  		},
  		
  		onDown: function(obj){
  			// summary:
  			//		Create on[xx]Down event and send to broadcaster.
  			//		Could be connected to.
  			
  			//console.info("onDown:", this.eventName("down"))
  			this._broadcastEvent(this.eventName("down"), obj);
  		},
  		
  		onDrag: function(obj){
  			// summary:
  			//		Create on[xx]Drag event and send to broadcaster.
  			//		Could be connected to.
  
  			var nm = this.eventName("drag");
  			if(this._selected && nm == "onDrag"){
  				nm = "onStencilDrag"
  			}
  			this._broadcastEvent(nm, obj);
  		},
  		
  		onMove: function(obj){
  			// summary:
  			//		Create onMove event and send to broadcaster.
  			//		Could be connected to.
  			//		Note: onMove never uses a custom event
  			//		Note: onMove is currently not enabled in the app.
  
  			this._broadcastEvent("onMove", obj);
  		},
  		
  		overName: function(obj,evt){
  			var nm = obj.id.split(".");
  			evt = evt.charAt(0).toUpperCase() + evt.substring(1);
  			if(nm[0] == "dojox" && (defaults.clickable || !defaults.clickMode)){
  				return "onStencil"+evt;
  			}else{
  				return "on"+evt;
  			}
  			
  		},
  		
  		onOver: function(obj){
  			this._broadcastEvent(this.overName(obj,"over"), obj);
  		},
  		
  		onOut: function(obj){
  			this._broadcastEvent(this.overName(obj,"out"), obj);
  		},
  		
  		onUp: function(obj){
  			// summary:
  			//		Create on[xx]Up event and send to broadcaster.
  			//		Could be connected to.
  			
  			// 	blocking first click-off (deselect), largely for TextBlock
  			// 	TODO: should have param to make this optional?
  			var nm = this.eventName("up");
  			
  			if(nm == "onStencilUp"){
  				this._selected  = true;
  			}else if(this._selected && nm == "onUp"){ //////////////////////////////////////////
  				nm = "onStencilUp";
  				this._selected = false;
  			}
  			
  			console.info("Up Event:", this.id, nm, "id:", obj.id);
  			this._broadcastEvent(nm, obj);
  			
  			// Silverlight double-click handled in Silverlight class
  			if(dojox.gfx.renderer == "silverlight"){ return; }
  			
  			// Check Double Click
  			// If a double click is detected, the onDoubleClick event fires,
  			// but does not replace the normal event. They both fire.
  			this._clickTime = new Date().getTime();
  			if(this._lastClickTime){
  				if(this._clickTime-this._lastClickTime<this.doublClickSpeed){
  					var dnm = this.eventName("doubleClick");
  					console.warn("DOUBLE CLICK", dnm, obj);
  					this._broadcastEvent(dnm, obj);
  				}else{
  					//console.log("    slow:", this._clickTime-this._lastClickTime)
  				}
  			}
  			this._lastClickTime = this._clickTime;
  			
  		},
  		
  		zoom: 1,
  		setZoom: function(zoom){
  			// summary:
  			//		Internal. Sets the mouse zoom percentage to
  			//		that of the canvas
  			this.zoom = 1/zoom;
  		},
  		
  		setEventMode: function(mode){
  			// summary:
  			//		Sets the mouse mode s that custom events can be called.
  			//		Also can 'disable' events by using a bogus mode:
  			// |	mouse.setEventMode("DISABLED")
  			//		(unless any object subscribes to this event,
  			//		it is effectively disabled)
  
  			this.mode = mode ? "on" + mode.charAt(0).toUpperCase() + mode.substring(1) :  "";
  		},
  		
  		eventName: function(name){
  			// summary:
  			//		Internal. Determine the event name
  
  			name = name.charAt(0).toUpperCase() + name.substring(1);
  			if(this.mode){
  				if(this.mode == "onPathEdit"){
  					return "on"+name;
  				}
  				if(this.mode == "onUI"){
  					//return "on"+name;
  				}
  				return this.mode + name;
  			}else{
  				//Allow a mode where stencils aren't clickable
  				if(!defaults.clickable && defaults.clickMode){return "on"+name;}
  				var dt = !this.drawingType || this.drawingType=="surface" || this.drawingType=="canvas" ? "" : this.drawingType;
  				var t = !dt ? "" : dt.charAt(0).toUpperCase() + dt.substring(1);
  				return "on"+t+name;
  			}
  		},
  		
  		up: function(evt){
  			// summary:
  			//		Internal. Create onUp event
  
  			this.onUp(this.create(evt));
  		},
  		
  		down: function(evt){
  			// summary:
  			//		Internal. Create onDown event
  
  			this._downOnCanvas = true;
  			var sc = this.scrollOffset();
  			var dim = this._getXY(evt);
  			this._lastpagex = dim.x;
  			this._lastpagey = dim.y;
  			var o = this.origin;
  			var x = dim.x - o.x + sc.left;
  			var y = dim.y - o.y + sc.top;
  			
  			var withinCanvas = x>=0 && y>=0 && x<=o.w && y<=o.h;
  			x*= this.zoom;
  			y*= this.zoom;
  			
  			o.startx = x;
  			o.starty = y;
  			this._lastx = x;
  			this._lasty = y;
  			
  			this.drawingType = this.util.attr(evt, "drawingType") || "";
  			var id = this._getId(evt);
  			//console.log("DOWN:", this.id, id, withinCanvas);
  			//console.log("this.drawingType:", this.drawingType);
  			
  			if(evt.button == dojo.mouseButtons.RIGHT && this.id == "mse"){
  				//Allow right click events to bubble for context menus
  			}else{
  				evt.preventDefault();
  				dojo.stopEvent(evt);
  			}
  			this.onDown({
  				mid:this.id,
  				x:x,
  				y:y,
  				pageX:dim.x,
  				pageY:dim.y,
  				withinCanvas:withinCanvas,
  				id:id
  			});
  			
  		},
  		over: function(obj){
  			// summary:
  			//		Internal.
  
  			this.onOver(obj);
  		},
  		out: function(obj){
  			// summary:
  			//		Internal.
  
  			this.onOut(obj);
  		},
  		move: function(evt){
  			// summary:
  			//		Internal.
  
  			var obj = this.create(evt);
  			if(this.id=="MUI"){
  				//console.log("obj.id:", obj.id, "was:", this.currentNodeId)
  			}
  			if(obj.id != this.currentNodeId){
  				// TODO: I wonder if an ID is good enough
  				//	that would avoid the mixin
  				var outObj = {};
  				for(var nm in obj){
  					outObj[nm] = obj[nm];
  				}
  				outObj.id = this.currentNodeId;
  				this.currentNodeId && this.out(outObj);
  				obj.id && this.over(obj);
  				this.currentNodeId = obj.id;
  			}
  			this.onMove(obj);
  		},
  		drag: function(evt){
  			// summary:
  			//		Internal. Create onDrag event
  			this.onDrag(this.create(evt, true));
  		},
  		create: function(evt, squelchErrors){
  			// summary:
  			//		Internal. Create EventObject
  
  			var sc = this.scrollOffset();
  			var dim = this._getXY(evt);
  			
  			var pagex = dim.x;
  			var pagey = dim.y;
  			
  			var o = this.origin;
  			var x = dim.x - o.x + sc.left;
  			var y = dim.y - o.y + sc.top;
  
  			var withinCanvas = x>=0 && y>=0 && x<=o.w && y<=o.h;
  			x*= this.zoom;
  			y*= this.zoom;
  			
  			var id = withinCanvas ? this._getId(evt, squelchErrors) : "";
  			var ret = {
  				mid:this.id,
  				x:x,
  				y:y,
  				pageX:dim.x,
  				pageY:dim.y,
  				page:{
  					x:dim.x,
  					y:dim.y
  				},
  				orgX:o.x,
  				orgY:o.y,
  				last:{
  					x: this._lastx,
  					y: this._lasty
  				},
  				start:{
  					x: this.origin.startx, //+ sc.left,
  					y: this.origin.starty //+ sc.top
  				},
  				move:{
  					x:pagex - this._lastpagex,
  					y:pagey - this._lastpagey
  				},
  				scroll:sc,
  				id:id,
  				withinCanvas:withinCanvas
  			};
  			
  			//console.warn("MSE LAST:", x-this._lastx, y-this._lasty)
  			this._lastx = x;
  			this._lasty = y;
  			this._lastpagex = pagex;
  			this._lastpagey = pagey;
  			dojo.stopEvent(evt);
  			return ret; //Object
  		},
  		_getId: function(evt, squelchErrors){
  			// summary:
  			//		Internal. Gets ID of focused node.
  			return this.util.attr(evt, "id", null, squelchErrors); // String
  		},
  		_getXY: function(evt){
  			// summary:
  			//		Internal. Gets mouse coords to page.
  			return {x:evt.pageX, y:evt.pageY}; // Object
  		},
  		
  		setCursor: function(cursor,/* HTMLNode*/node){
  			// summary:
  			//		Sets the cursor for  a given node.  If no
  			//		node is specified the containing node is used.
  			if(!node){
  				dojo.style(this.container, "cursor", cursor);
  			}else{
  				dojo.style(node, "cursor", cursor);
  			}
  		}
  	}
  );
  });
  