Location: A review of cardiac cellular electrophysiology models @ f6a8f9030738 / dojo-presentation / js / dojo / dojox / layout / dnd / PlottedDnd.js

Author:
David Nickerson <nickerso@users.sourceforge.net>
Date:
2009-07-16 02:00:03+12:00
Desc:
the starting point for the HH tutorial example
Permanent Source URI:
https://models.fieldml.org/workspace/a1/rawfile/f6a8f90307388eb4b040ee3566b84d88b59247f7/dojo-presentation/js/dojo/dojox/layout/dnd/PlottedDnd.js

dojo.provide("dojox.layout.dnd.PlottedDnd");

dojo.require("dojo.dnd.Source");
dojo.require("dojo.dnd.Manager");
dojo.require("dojox.layout.dnd.Avatar");

dojo.declare("dojox.layout.dnd.PlottedDnd", [dojo.dnd.Source], {
	// summary:
	//		dnd source handling plotted zone to show the dropping area
	GC_OFFSET_X: dojo.dnd.manager().OFFSET_X,
	GC_OFFSET_Y: dojo.dnd.manager().OFFSET_Y,
	
	constructor: function(/*Node*/node, /*Object*/params){
		
		this.childBoxes 		= null;
		this.dropIndicator		= new dojox.layout.dnd.DropIndicator("dndDropIndicator", "div");
		this.withHandles 		= params.withHandles;
		this.handleClasses	 	= params.handleClasses;
		this.opacity 			= params.opacity;
		this.allowAutoScroll	= params.allowAutoScroll;//MODIF MYS
		this.dom                = params.dom;
		this.singular			= true;
		this.skipForm 			= true;
		this._over 				= false;
		this.defaultHandleClass = "GcDndHandle";
		this.isDropped			= false;
		this._timer				= null;
		//Initialize the params to calculate offset
  		this.isOffset = (params.isOffset)?true:false;
  		this.offsetDrag = (params.offsetDrag) ? params.offsetDrag : {x:0,y:0};
		this.hideSource = params.hideSource ? params.hideSource : true;  
		this._drop = this.dropIndicator.create();
		
	},
	
	_calculateCoords : function(/*Boolean*/height){
		// summary: Calculate each position of children
		dojo.forEach(this.node.childNodes, dojo.hitch(this, function(child){
				child.coords = {
					xy: dojo.coords(child, true),
					w: child.offsetWidth / 2,
					h: child.offsetHeight / 2
				};
				if(height){
					child.coords.mh = dojo.marginBox(child).h;
				}
		}));
	},
	
	_legalMouseDown: function(/*Event*/e){
		// summary: Checks if user clicked on "approved" items.
		if(!this.withHandles){ return true; }
		for(var node = (e.target); node && node != this.node; node = node.parentNode)
			if(dojo.hasClass(node, this.defaultHandleClass))
				return true;	
		return false;	// Boolean
	},
	
	setDndItemSelectable: function(/*Node*/node, /*Boolean*/isSelectable) {
		// summary: set an item as selectable	
		for(var _node = node; _node && node != this.node; _node = _node.parentNode) {
			if (dojo.hasClass(_node,"dojoDndItem")) {
				dojo.setSelectable(_node, isSelectable);
				return;
			}
		}	
	},
	
	getDraggedWidget: function(/*Node*/node) {
		// summary: Return one or more widget selected during the drag.
		var _node = node;
		while (_node && _node.nodeName.toLowerCase()!="body" && !dojo.hasClass(_node,"dojoDndItem")) {
			_node = _node.parentNode;
		}
		return (_node)? dijit.byNode(_node) : null;
	},
	
	isAccepted: function(/*Node*/ node) {
		// summary: test if this node can be accepted	
		var _dndType = (node) ? node.getAttribute("dndtype") : null;
		if(_dndType && _dndType in this.accept)
			return true;
		else 
			return false;
	},
	
	onDndStart:function(/*Object*/source, /*Array*/nodes, /*Object*/copy){
		// summary: Called to initiate the DnD operation.
		if (source == this)
			this.firstIndicator = true;
		else
			this.firstIndicator = false;
			
		this._calculateCoords(true);
		//this.isDropped = true;
		if (nodes[0].coords) {
			this._drop.style.height = nodes[0].coords.mh + "px";
		}else{
			var m= dojo.dnd.manager();
			this._drop.style.height = m.avatar.node.clientHeight+"px";
		}
		this.dndNodes = nodes;
		dojox.layout.dnd.PlottedDnd.superclass.onDndStart.call(this,source, nodes, copy);
		if(source==this)
			if(this.hideSource)
				for(var i=0; i<nodes.length; i++)
				 	dojo.style(nodes[i], "display", "none");
	},

	onDndCancel:function(){
		// summary: Called to cancel the DnD operation.	
		var m = dojo.dnd.manager();
		if(m.source == this && this.hideSource){
			var nodes = this.getSelectedNodes();
			for(var i=0; i<nodes.length; i++)
                if(nodes[i]) 
                  	dojo.style(nodes[i], "display", "");	
		}
		dojox.layout.dnd.PlottedDnd.superclass.onDndCancel.call(this);
		this.deleteDashedZone();
	},
	
	onDndDrop: function(source,nodes,copy,target) {
	// summary: Called to finish the DnD operation	
		try {
			if (!this.isAccepted(nodes[0])) 
				this.onDndCancel();
			else {
				if (source == this && this._over && this.dropObject) 
					this.current = this.dropObject.c;
				dojox.layout.dnd.PlottedDnd.superclass.onDndDrop.call(this, source, nodes, copy, target);
				this._calculateCoords(true);
			}
		}
		catch (error) {
			console.info(error);
		}
	},
			
	onMouseDown: function(/*Event*/e) {
	// summary: Event processor for onmousedown.	
		if (this.current == null)
			this.selection = {};
		else{
			if (this.current == this.anchor)
				this.anchor = null;
		}
		if (this.current !== null) {
			this.current.coords = {
				xy: dojo.coords(this.current, true),
				w: this.current.offsetWidth / 2,
				h: this.current.offsetHeight / 2,
				mh: dojo.marginBox(this.current).h
			};
			this._drop.style.height = this.current.coords.mh + "px";
			
			if (this.isOffset) {
				if (this.offsetDrag.x == 0 && this.offsetDrag.y == 0) {
					var NoOffsetDrag = true;
					var coords = dojo.coords(this._getChildByEvent(e));
					this.offsetDrag.x = coords.x - e.pageX;
					this.offsetDrag.y = coords.y - e.clientY;
				}
				if (this.offsetDrag.y < 16 && this.current != null) 
					this.offsetDrag.y = this.GC_OFFSET_Y;
				var m = dojo.dnd.manager();
				m.OFFSET_X = this.offsetDrag.x;
				m.OFFSET_Y = this.offsetDrag.y;
				if (NoOffsetDrag) {
					this.offsetDrag.x = 0;
					this.offsetDrag.y = 0;
				}
			}
		}
		if (dojo.dnd.isFormElement(e)) {			
			this.setDndItemSelectable(e.target,true);
		}
		else {
			this.containerSource = true;
			var _draggedWidget = this.getDraggedWidget(e.target);
			if (_draggedWidget && _draggedWidget.dragRestriction)
				dragRestriction = true;
			else
				dojox.layout.dnd.PlottedDnd.superclass.onMouseDown.call(this,e);
		}
	},

	onMouseUp: function(/*Event*/e) {
	// summary: Event processor for onmouseup.	
		dojox.layout.dnd.PlottedDnd.superclass.onMouseUp.call(this,e);
		this.containerSource = false;
		if (!dojo.isIE && this.mouseDown)
			this.setDndItemSelectable(e.target,true);
		var m = dojo.dnd.manager();
		m.OFFSET_X = this.GC_OFFSET_X;
		m.OFFSET_Y = this.GC_OFFSET_Y;
	},
	
	onMouseMove: function(e) {
	// summary: Event processor for onmousemove	
		var m = dojo.dnd.manager();
		if (this.isDragging) {
			var before = false;
			if (this.current != null || (this.current == null && !this.dropObject)) {
				if (this.isAccepted(m.nodes[0]) || this.containerSource)
					before = this.setIndicatorPosition(e);
				
			}
			if (this.current != this.targetAnchor || before != this.before) {
				this._markTargetAnchor(before);
				m.canDrop(!this.current || m.source != this || !(this.current.id in this.selection));
			}

			if (this.allowAutoScroll)
				this._checkAutoScroll(e);
		}
		else {
			if (this.mouseDown && this.isSource) {
				var nodes = this.getSelectedNodes();
				if (nodes.length) {
					m.startDrag(this, nodes, this.copyState(dojo.dnd.getCopyKeyState(e)));
				}
			}

			if (this.allowAutoScroll) 
				this._stopAutoScroll();
		}		
	},
	
	_markTargetAnchor: function(/*Boolean*/before){
	// summary: Assigns a class to the current target anchor based on "before" status	
		if(this.current == this.targetAnchor && this.before == before){ return; }
		this.targetAnchor = this.current;
		this.targetBox = null;
		this.before = before;
	},
	
	_unmarkTargetAnchor: function(){
	// summary: Removes a class of the current target anchor based on "before" status.	
		if(!this.targetAnchor){ return; }
		this.targetAnchor = null;
		this.targetBox = null;
		this.before = true;
	},
	
	setIndicatorPosition: function(/*Event*/e) {
	// summary: set the position of the drop indicator
		var before = false;
		if(this.current){
			if (!this.current.coords || this.allowAutoScroll) {
				this.current.coords = {
					xy: dojo.coords(this.current, true),
					w: this.current.offsetWidth / 2,
					h: this.current.offsetHeight / 2
				};
			}
			if (this.horizontal) {
				before = (e.pageX - this.current.coords.xy.x) < this.current.coords.w;
			}
			else {
				before = (e.pageY - this.current.coords.xy.y) < this.current.coords.h;
			}
			this.insertDashedZone(before);	
		}
		else {
			if (!this.dropObject /*|| dojo.isIE*/) this.insertDashedZone(false);
		}
		return before;
	},
	

	onOverEvent:function(){
		this._over = true;
		dojox.layout.dnd.PlottedDnd.superclass.onOverEvent.call(this);
		if (this.isDragging) {
			var m = dojo.dnd.manager();
			if (!this.current && !this.dropObject && this.getSelectedNodes()[0] && this.isAccepted(m.nodes[0]))
				this.insertDashedZone(false);
		}
	},
	
	onOutEvent: function() {
		this._over = false;
		this.containerSource = false;
		dojox.layout.dnd.PlottedDnd.superclass.onOutEvent.call(this);
		if (this.dropObject) this.deleteDashedZone();
	},
	
	deleteDashedZone: function() {
	// summary: hide the dashed zone
		this._drop.style.display = "none";
			var next = this._drop.nextSibling;
			while (next != null) {
				next.coords.xy.y -= parseInt(this._drop.style.height);
				next = next.nextSibling;
			}
		delete this.dropObject;
	},
	
	insertDashedZone: function(/*Boolean*/before) {
	// summary: Insert the dashed zone at the right place 
		if (this.dropObject) {
			if (before == this.dropObject.b && ((this.current && this.dropObject.c == this.current.id) || (!this.current && !this.dropObject.c)))
				return;
			else
				this.deleteDashedZone();
		}
		this.dropObject =  {n: this._drop, c: (this.current)? this.current.id : null, b: before};
		if (this.current) {
			dojo.place(this._drop, this.current, (before) ? "before" : "after");
			if (!this.firstIndicator) {
				var next = this._drop.nextSibling;
				while (next != null) {
					next.coords.xy.y += parseInt(this._drop.style.height);
					next = next.nextSibling;
				}
			}else{
				this.firstIndicator = false;
			}
		}
		else 
			this.node.appendChild(this._drop);
		this._drop.style.display = "";
	},
	
	insertNodes: function(/*Boolean*/addSelected, /*Array*/data, /*Boolean*/before, /*Node*/anchor){
	// summary: Inserts new data items (see Dojo Container's insertNodes method for details).	
		if (this.dropObject){
			dojo.style(this.dropObject.n,"display","none");
			dojox.layout.dnd.PlottedDnd.superclass.insertNodes.call(this,true,data,true,this.dropObject.n);
			this.deleteDashedZone();
		}
		else {
			 return dojox.layout.dnd.PlottedDnd.superclass.insertNodes.call(this,addSelected,data,before,anchor);
		}
		var _widget = dijit.byId(data[0].getAttribute("widgetId"));
		if (_widget) {
			dojox.layout.dnd._setGcDndHandle(_widget, this.withHandles, this.handleClasses);
			if(this.hideSource)
				dojo.style(_widget.domNode, "display", "");
		}
	},
	
	_checkAutoScroll: function(e){
		if (this._timer)
			clearTimeout(this._timer);
		this._stopAutoScroll();
		var node = this.dom;
		var y = this._sumAncestorProperties(node,"offsetTop");
		//Down
		if( (e.pageY - node.offsetTop +30 ) > node.clientHeight ){
			autoScrollActive = true;
			this._autoScrollDown(node);
		} else if ( (node.scrollTop > 0) && (e.pageY - y) < 30){
			//Up
			autoScrollActive = true;
			this._autoScrollUp(node);
		}
	},

	_autoScrollUp: function(node){
		if( autoScrollActive && node.scrollTop > 0) {
			node.scrollTop -= 30;
			this._timer = setTimeout(dojo.hitch(this, function(){this._autoScrollUp(node);}),"100");
		}
	},

	_autoScrollDown: function(node){
		if( autoScrollActive && (node.scrollTop < (node.scrollHeight-node.clientHeight))){
			node.scrollTop += 30;
			this._timer = setTimeout(dojo.hitch(this, function(){this._autoScrollDown(node);}),"100");
		}
	},

	_stopAutoScroll: function(){
		this.autoScrollActive = false;
	},

	_sumAncestorProperties: function(node, prop){
		//	summary
		//	Returns the sum of the passed property on all ancestors of node.
		node = dojo.byId(node);
		if(!node){ return 0; }
		
		var retVal = 0;
		while(node){
			var val = node[prop];
			if(val){
				retVal += val - 0;
				if(node==dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
			}
			node = node.parentNode;
		}
		return retVal;	//	integer
	}
	
});

dojox.layout.dnd._setGcDndHandle = function(service,withHandles,handleClasses, first) {
	if(!first){
		dojo.query(".GcDndHandle", service.domNode).removeClass("GcDndHandle");
	}
	if(!withHandles){
		dojo.addClass(service.domNode, "GcDndHandle");
	}else{
		var _hasHandle = false;
		for(var i = handleClasses.length - 1; i >= 0; i--){
			var _node = dojo.query("." + handleClasses[i], service.domNode)[0];
			if(_node){
				_hasHandle = true;
				if(handleClasses[i] != "GcDndHandle"){
					var _gripNode = dojo.query(".GcDndHandle", service.domNode);
					if(_gripNode.length == 0){ 
						dojo.removeClass(service.domNode, "GcDndHandle");
					}else{				
						_gripNode.removeClass("GcDndHandle");
					}
					dojo.addClass(_node, "GcDndHandle");
				}
			}
		}
		if(!_hasHandle){ 
			dojo.addClass(service.domNode, "GcDndHandle");
		}
	}
};

dojo.declare("dojox.layout.dnd.DropIndicator", null, {
	// summary: An empty widget to show at the user the drop zone of the widget.
	constructor: function(/*String*/cn, /*String*/tag) {
		this.tag = tag || "div";
		this.style = cn || null;
	},

	isInserted : function(){
		return (this.node.parentNode && this.node.parentNode.nodeType==1);
	},

	create : function(/*Node*//*nodeRef*/){
		if(this.node && this.isInserted()) return this.node;
		var h= "90px"; 
		var el = document.createElement(this.tag);
		if(this.style){
			el.className = this.style;
			el.style.height = h;
		}else{
			with(el.style) {
				position = "relative";
				border = "1px dashed #F60";
				margin = "2px";
				height = h;
			}
		}
		this.node = el;
		return el;
	},
	
	destroy : function(){
		if(!this.node || !this.isInserted()) return;
		this.node.parentNode.removeChild(this.node);
		this.node = null;
	}
});

dojo.extend(dojo.dnd.Manager, {
	
	canDrop: function(flag){
		var canDropFlag = this.target && flag;
		if(this.canDropFlag != canDropFlag){
			this.canDropFlag = canDropFlag;
			if (this.avatar) this.avatar.update();
		}
		
	},
	
	makeAvatar: function(){
	//summary: Makes the avatar, it is separate to be overwritten dynamically, if needed.
		if (this.source.declaredClass == "dojox.layout.dnd.PlottedDnd")
		   return new dojox.layout.dnd.Avatar(this,this.source.opacity);
		else
			return new dojo.dnd.Avatar(this);
	}
});

if (dojo.isIE){
	dojox.layout.dnd.handdleIE = [
		dojo.subscribe("/dnd/start", null, function(){
			IEonselectstart = document.body.onselectstart;
			document.body.onselectstart = function (e) {return false;};
		}),
		dojo.subscribe("/dnd/cancel", null, function(){
			document.body.onselectstart = IEonselectstart;
		}),
		dojo.subscribe("/dnd/drop", null, function(){
			document.body.onselectstart = IEonselectstart;
		})
	];
	dojo.addOnWindowUnload(function(){
		dojo.forEach(dojox.layout.dnd.handdleIE, dojo.unsubscribe);
	});
}