blob: e9ff53551e9c5f6af8ddb7a7a50a4f994f4a7f35 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2004 Actuate Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Actuate Corporation - Initial implementation.
*****************************************************************************/
BirtDndManager = function( ) {
//An item is dragging
this.isDragging = false;
};
BirtDndManager.prototype = {
__dropTargetManager: null, //PRIVATE - extending classes must use accessor method
__mousemoveHandlerFunction: null,
__dropHandlerFunction: null,
currentDropTarget: null, //Element that is the current valid drop target
currentDragElement: null, //Element that is currently being dragged
/**
Extending classes should use this method to set the drop target manager if
custom drop behavior is desired
@param {DropTargetManager}
*/
setDropTargetManager: function( dTargetManager)
{
this.__dropTargetManager = dTargetManager;
},
/**
Drop targets call this method to register as accepting "dragType" items
*/
addAssociation: function(dragType, targetHtmlId, target)
{
if(this.__dropTargetManager == null)
{
throw new WRError("BirtDndManager", "DropTargetManager not set");
}
this.__dropTargetManager.addAssociation(dragType, targetHtmlId, target);
},
/**
Delete an association when the corresponding html element is removed
@param dragType
@param dropTargetId id attribute of html element to remove
*/
deleteAssociation: function(dragType, htmlId)
{
if(this.__dropTargetManager == null)
{
throw new WRError("BirtDndManager", "DropTargetManager not set");
}
this.__dropTargetManager.deleteAssociation(dragType, htmlId);
},
/**
startDrag redefines itself the first time it is called based on the desired type of drag drop behavior.<b>
*/
startDrag: function(element, event, dragType)
{
//If there is no drop target manager, define startDrag as default
if(this.__dropTargetManager == null)
{
this.startDrag = this.__getDefaultStartDrag();
}
else
{
this.startDrag = this.__getCustomStartDrag();
}
this.startDrag(element, event, dragType);
},
/**
Default start drag function handles elements that drag but do not have drop targets
@returns {function} default start drag function
*/
__getDefaultStartDrag: function()
{
return function(element, event, dragType)
{
debug("default startDrag");
if(!this.isDragging)
{
this.eventMouseout = this.elementMouseout.bindAsEventListener(this);
this.__mousemoveHandlerFunction = this.__moveElement.bindAsEventListener(this);
this.__dropElementFunction = this.__dropElement.bindAsEventListener(this);
var zIndex = this.__activateDragMask();
this.__elementSetUp(element, event, zIndex);
this.__startDragObservers(this.__mousemoveHandlerFunction, this.__dropElementFunction);
}
this.isDragging = true;
}
},
/**
Custom start drag function handles elements that have drop targets
@returns {function} custome start drag function
*/
__getCustomStartDrag: function()
{
return function(element, event, dragType)
{
debug("custom startDrag");
if(!this.isDragging)
{
if(dragType == null) //There are no drop targets
{
this.__mousemoveHandlerFunction = this.__moveElement.bindAsEventListener(this);
this.__dropElementFunction = this.__dropElement.bindAsEventListener(this);
}
else
{
this.__dropTargetManager.setUpForDrag(dragType);
this.__mousemoveHandlerFunction = this.__moveElementWithTarget.bindAsEventListener(this);
this.__dropElementFunction = this.__dropElementWithTarget.bindAsEventListener(this);
}
this.eventMouseout = this.elementMouseout.bindAsEventListener(this);
var zIndex = this.__activateDragMask();
this.__elementSetUp(element, event, zIndex);
this.__startDragObservers(this.__mousemoveHandlerFunction, this.__dropElementFunction);
}
this.isDragging = true;
}
},
__activateDragMask: function()
{
return Mask.show();
},
__deactivateDragMask: function()
{
Mask.hide();
},
/**
Set up an element to be dragged
*/
__elementSetUp: function(elem, event, zIndex)
{
//TODO if removing selections is kept, move to utility funciton
//remove any existing selections
if(document.selection)
{
document.selection.empty();
}
else if(window.getSelection)
{
var selection = window.getSelection();
if(selection)
{
selection.removeAllRanges();
}
}
this.currentDragElement = elem;
this.currentDragElement.oldZIndex = this.currentDragElement.style.zIndex;
this.currentDragElement.style.zIndex = zIndex;
this.currentDropTarget = null;
//Location of mouse
var mouseX = Event.pointerX(event);
var mouseY = Event.pointerY(event);
//Distance from the edge of the element to edge of browser
var offsets = Position.cumulativeOffset(elem);
//Distance from the edge of the element of mouse
elem.offsetX = (mouseX - offsets[0]);
elem.offsetY = (mouseY - offsets[1]);
//Used for revert effect
elem.originalTop = parseInt(elem.style.top || '0');
elem.originalLeft = parseInt(elem.style.left || '0');
},
/**
Start drag event observing
*/
__startDragObservers: function(mousemoveHandlerFunction, dropHandlerFunction)
{
Event.observe(document, "mousemove", mousemoveHandlerFunction, false);
Event.observe(document, "mouseup", dropHandlerFunction, false);
},
/**
Move element
*/
__moveElement: function(event)
{
var xPos = Event.pointerX(event);
var yPos = Event.pointerY(event);
this.__moveCurrentDragElement(xPos, yPos);
},
/**
Move element and use DropTargetManager to check if it can be dropped
*/
__moveElementWithTarget: function(event)
{
var xPos = Event.pointerX(event);
var yPos = Event.pointerY(event);
this.__moveCurrentDragElement(xPos, yPos);
var oldDropTarget = this.currentDropTarget; //is the state changing?
var dropTarget = this.__dropTargetManager.getDropTarget(xPos, yPos);
//Change indicator to drop allowed
if(dropTarget)
{
this.currentDropTarget = dropTarget;
if(!oldDropTarget)
{
var nodes = this.currentDragElement.childNodes;
this.toggleDropIndicator(true, nodes);
}
}
else //Change indicator to drop forbidden
{
this.currentDropTarget = null;
if(oldDropTarget)
{
var nodes = this.currentDragElement.childNodes;
this.toggleDropIndicator(false, nodes);
}
}
},
__moveCurrentDragElement: function(x, y)
{
var offsets = Position.cumulativeOffset(this.currentDragElement);
//subtract the element's current left, top coordinates from the offsets
offsets[0] -= parseInt(this.currentDragElement.style.left || '0');
offsets[1] -= parseInt(this.currentDragElement.style.top || '0');
var style = this.currentDragElement.style;
//take current mouse position, subtract difference in drag object position, s
style.left = (x - offsets[0] - this.currentDragElement.offsetX) + "px";
style.top = (y - offsets[1] - this.currentDragElement.offsetY) + "px";
},
/**
Check if the mouse button is no longer pressed, if not, call
eventDropped. (IE specific)<b>
*/
__mouseStillDown: function(event)
{
/**
Check that the mouse button is still down
(used to detect roll off the screen in IE)
*/
//debug("check mouse down");
//TODO refine this Check that element is really moving
if(!event.which && event.button == "0")
{
this.eventDropped(event);
return true;
}
return false;
},
/**
Handle mouseout of drag element
*/
elementMouseout: function(event)
{
//debug("mouseout");
var target = Event.element( event );
if(target.tagName.toLowerCase() == "iframe")
{
Event.stop(event);
this.eventDropped(event);
}
},
__dropElement: function(event)
{
this.isDragging = false;
Event.stopObserving(document, "mousemove", this.__mousemoveHandlerFunction);
Event.stopObserving(document, "mouseup", this.__dropElementFunction);
Event.stopObserving(document, "mouseout", this.eventMouseout);
this.__deactivateDragMask();
this.currentDragElement.style.zIndex = this.currentDragElement.oldZIndex;
},
__dropElementWithTarget: function(event)
{
this.__dropElement(event);
this.__dropTargetManager.dropElementWithTarget(event, this.currentDragElement, this.currentDropTarget);
},
/**
Toggle drop indicator to show if item can be dropped
@param accept {boolean} Indicates if item is accepted
@param nodes {Array} Potential imageHolder nodes //TODO once indicator is decided on can be made more efficient
*/
toggleDropIndicator: function(accept, nodes)
{
for(var j = 0 ; j < nodes.length ; j++)
{
if(nodes[j].imageHolder)
{
accept ? nodes[j].childNodes[0].style.display = "none" : nodes[j].childNodes[0].style.display = "block";
accept ? nodes[j].childNodes[1].style.display = "block" : nodes[j].childNodes[1].style.display = "none";
break;
}
}
}
}