blob: bd0c68d2ce98f022f0c2848a854dc9c292a53832 [file] [log] [blame]
/*
Copyright (c) 2004-2005, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
modified BSD license. For more information on Dojo licensing, see:
http://dojotoolkit.org/community/licensing.shtml
*/
dojo.provide("dojo.dnd.HtmlDragAndDrop");
dojo.provide("dojo.dnd.HtmlDragSource");
dojo.provide("dojo.dnd.HtmlDropTarget");
dojo.provide("dojo.dnd.HtmlDragObject");
dojo.require("dojo.dnd.HtmlDragManager");
dojo.require("dojo.animation.*");
dojo.require("dojo.dom");
dojo.require("dojo.style");
dojo.require("dojo.html");
dojo.require("dojo.lang");
dojo.dnd.HtmlDragSource = function(node, type){
node = dojo.byId(node);
this.constrainToContainer = false;
if(node){
this.domNode = node;
this.dragObject = node;
// register us
dojo.dnd.DragSource.call(this);
// set properties that might have been clobbered by the mixin
this.type = type||this.domNode.nodeName.toLowerCase();
}
}
dojo.lang.extend(dojo.dnd.HtmlDragSource, {
dragClass: "", // CSS classname(s) applied to node when it is being dragged
onDragStart: function(){
var dragObj = new dojo.dnd.HtmlDragObject(this.dragObject, this.type, this.dragClass);
if (this.constrainToContainer) {
dragObj.constrainTo(this.constrainingContainer);
}
return dragObj;
},
setDragHandle: function(node){
node = dojo.byId(node);
dojo.dnd.dragManager.unregisterDragSource(this);
this.domNode = node;
dojo.dnd.dragManager.registerDragSource(this);
},
setDragTarget: function(node){
this.dragObject = node;
},
constrainTo: function(container) {
this.constrainToContainer = true;
if (container) {
this.constrainingContainer = container;
} else {
this.constrainingContainer = this.domNode.parentNode;
}
}
});
dojo.dnd.HtmlDragObject = function(node, type, dragClass){
this.domNode = dojo.byId(node);
this.type = type;
if(dragClass) { this.dragClass = dragClass; }
this.constrainToContainer = false;
}
dojo.lang.extend(dojo.dnd.HtmlDragObject, {
dragClass: "",
opacity: 0.5,
// if true, node will not move in X and/or Y direction
disableX: false,
disableY: false,
/**
* Creates a clone of this node and replaces this node with the clone in the
* DOM tree. This is done to prevent the browser from selecting the textual
* content of the node. This node is then set to opaque and drags around as
* the intermediate representation.
*/
onDragStart: function(e){
dojo.html.clearSelection();
this.scrollOffset = {
top: dojo.html.getScrollTop(), // document.documentElement.scrollTop,
left: dojo.html.getScrollLeft() // document.documentElement.scrollLeft
};
this.dragStartPosition = {top: dojo.style.getAbsoluteY(this.domNode, true) + this.scrollOffset.top,
left: dojo.style.getAbsoluteX(this.domNode, true) + this.scrollOffset.left};
this.dragOffset = {top: this.dragStartPosition.top - e.clientY,
left: this.dragStartPosition.left - e.clientX};
this.dragClone = this.domNode.cloneNode(true);
//this.domNode.parentNode.replaceChild(this.dragClone, this.domNode);
if ((this.domNode.parentNode.nodeName.toLowerCase() == 'body') || (dojo.style.getComputedStyle(this.domNode.parentNode,"position") == "static")) {
this.parentPosition = {top: 0, left: 0};
} else {
this.parentPosition = {top: dojo.style.getAbsoluteY(this.domNode.parentNode, true),
left: dojo.style.getAbsoluteX(this.domNode.parentNode,true)};
}
if (this.constrainToContainer) {
this.constraints = this.getConstraints();
}
// set up for dragging
with(this.dragClone.style){
position = "absolute";
top = this.dragOffset.top + e.clientY + "px";
left = this.dragOffset.left + e.clientX + "px";
}
if(this.dragClass) { dojo.html.addClass(this.dragClone, this.dragClass); }
dojo.style.setOpacity(this.dragClone, this.opacity);
dojo.html.body().appendChild(this.dragClone);
},
getConstraints: function() {
if (this.constrainingContainer.nodeName.toLowerCase() == 'body') {
width = dojo.html.getViewportWidth();
height = dojo.html.getViewportHeight();
padLeft = 0;
padTop = 0;
} else {
width = dojo.style.getContentWidth(this.constrainingContainer);
height = dojo.style.getContentHeight(this.constrainingContainer);
padLeft = dojo.style.getPixelValue(this.constrainingContainer, "padding-left", true);
padTop = dojo.style.getPixelValue(this.constrainingContainer, "padding-top", true);
}
return {
minX: padLeft,
minY: padTop,
maxX: padLeft+width - dojo.style.getOuterWidth(this.domNode),
maxY: padTop+height - dojo.style.getOuterHeight(this.domNode)
}
},
updateDragOffset: function() {
var sTop = dojo.html.getScrollTop(); // document.documentElement.scrollTop;
var sLeft = dojo.html.getScrollLeft(); // document.documentElement.scrollLeft;
if(sTop != this.scrollOffset.top) {
var diff = sTop - this.scrollOffset.top;
this.dragOffset.top += diff;
this.scrollOffset.top = sTop;
}
},
/** Moves the node to follow the mouse */
onDragMove: function(e){
this.updateDragOffset();
var x = this.dragOffset.left + e.clientX - this.parentPosition.left;
var y = this.dragOffset.top + e.clientY - this.parentPosition.top;
if (this.constrainToContainer) {
if (x < this.constraints.minX) { x = this.constraints.minX; }
if (y < this.constraints.minY) { y = this.constraints.minY; }
if (x > this.constraints.maxX) { x = this.constraints.maxX; }
if (y > this.constraints.maxY) { y = this.constraints.maxY; }
}
if(!this.disableY) { this.dragClone.style.top = y + "px"; }
if(!this.disableX) { this.dragClone.style.left = x + "px"; }
},
/**
* If the drag operation returned a success we reomve the clone of
* ourself from the original position. If the drag operation returned
* failure we slide back over to where we came from and end the operation
* with a little grace.
*/
onDragEnd: function(e){
switch(e.dragStatus){
case "dropSuccess":
dojo.dom.removeNode(this.dragClone);
this.dragClone = null;
break;
case "dropFailure": // slide back to the start
var startCoords = [dojo.style.getAbsoluteX(this.dragClone),
dojo.style.getAbsoluteY(this.dragClone)];
// offset the end so the effect can be seen
var endCoords = [this.dragStartPosition.left + 1,
this.dragStartPosition.top + 1];
// animate
var line = new dojo.math.curves.Line(startCoords, endCoords);
var anim = new dojo.animation.Animation(line, 300, 0, 0);
var dragObject = this;
dojo.event.connect(anim, "onAnimate", function(e) {
dragObject.dragClone.style.left = e.x + "px";
dragObject.dragClone.style.top = e.y + "px";
});
dojo.event.connect(anim, "onEnd", function (e) {
// pause for a second (not literally) and disappear
dojo.lang.setTimeout(dojo.dom.removeNode, 200,
dragObject.dragClone);
});
anim.play();
break;
}
},
constrainTo: function(container) {
this.constrainToContainer=true;
if (container) {
this.constrainingContainer = container;
} else {
this.constrainingContainer = this.domNode.parentNode;
}
}
});
dojo.dnd.HtmlDropTarget = function(node, types){
if (arguments.length == 0) { return; }
node = dojo.byId(node);
this.domNode = node;
dojo.dnd.DropTarget.call(this);
this.acceptedTypes = types || [];
}
dojo.inherits(dojo.dnd.HtmlDropTarget, dojo.dnd.DropTarget);
dojo.lang.extend(dojo.dnd.HtmlDropTarget, {
onDragOver: function(e){
if(!this.accepts(e.dragObjects)){ return false; }
// cache the positions of the child nodes
this.childBoxes = [];
for (var i = 0, child; i < this.domNode.childNodes.length; i++) {
child = this.domNode.childNodes[i];
if (child.nodeType != dojo.dom.ELEMENT_NODE) { continue; }
var top = dojo.style.getAbsoluteY(child);
var bottom = top + dojo.style.getInnerHeight(child);
var left = dojo.style.getAbsoluteX(child);
var right = left + dojo.style.getInnerWidth(child);
this.childBoxes.push({top: top, bottom: bottom,
left: left, right: right, node: child});
}
// TODO: use dummy node
return true;
},
_getNodeUnderMouse: function(e){
var mousex = e.pageX || e.clientX + dojo.html.body().scrollLeft;
var mousey = e.pageY || e.clientY + dojo.html.body().scrollTop;
// find the child
for (var i = 0, child; i < this.childBoxes.length; i++) {
with (this.childBoxes[i]) {
if (mousex >= left && mousex <= right &&
mousey >= top && mousey <= bottom) { return i; }
}
}
return -1;
},
createDropIndicator: function() {
this.dropIndicator = document.createElement("div");
with (this.dropIndicator.style) {
position = "absolute";
zIndex = 1;
borderTopWidth = "1px";
borderTopColor = "black";
borderTopStyle = "solid";
width = dojo.style.getInnerWidth(this.domNode) + "px";
left = dojo.style.getAbsoluteX(this.domNode) + "px";
}
},
onDragMove: function(e, dragObjects){
var i = this._getNodeUnderMouse(e);
if(!this.dropIndicator){
this.createDropIndicator();
}
if(i < 0) {
if(this.childBoxes.length) {
var before = (dojo.html.gravity(this.childBoxes[0].node, e) & dojo.html.gravity.NORTH);
} else {
var before = true;
}
} else {
var child = this.childBoxes[i];
var before = (dojo.html.gravity(child.node, e) & dojo.html.gravity.NORTH);
}
this.placeIndicator(e, dragObjects, i, before);
if(!dojo.html.hasParent(this.dropIndicator)) {
dojo.html.body().appendChild(this.dropIndicator);
}
},
placeIndicator: function(e, dragObjects, boxIndex, before) {
with(this.dropIndicator.style){
if (boxIndex < 0) {
if (this.childBoxes.length) {
top = (before ? this.childBoxes[0].top
: this.childBoxes[this.childBoxes.length - 1].bottom) + "px";
} else {
top = dojo.style.getAbsoluteY(this.domNode) + "px";
}
} else {
var child = this.childBoxes[boxIndex];
top = (before ? child.top : child.bottom) + "px";
}
}
},
onDragOut: function(e) {
dojo.dom.removeNode(this.dropIndicator);
delete this.dropIndicator;
},
/**
* Inserts the DragObject as a child of this node relative to the
* position of the mouse.
*
* @return true if the DragObject was inserted, false otherwise
*/
onDrop: function(e){
this.onDragOut(e);
var i = this._getNodeUnderMouse(e);
if (i < 0) {
if (this.childBoxes.length) {
if (dojo.html.gravity(this.childBoxes[0].node, e) & dojo.html.gravity.NORTH) {
return this.insert(e, this.childBoxes[0].node, "before");
} else {
return this.insert(e, this.childBoxes[this.childBoxes.length-1].node, "after");
}
}
return this.insert(e, this.domNode, "append");
}
var child = this.childBoxes[i];
if (dojo.html.gravity(child.node, e) & dojo.html.gravity.NORTH) {
return this.insert(e, child.node, "before");
} else {
return this.insert(e, child.node, "after");
}
},
insert: function(e, refNode, position) {
var node = e.dragObject.domNode;
if(position == "before") {
return dojo.html.insertBefore(node, refNode);
} else if(position == "after") {
return dojo.html.insertAfter(node, refNode);
} else if(position == "append") {
refNode.appendChild(node);
return true;
}
return false;
}
});