<html>
<head>
<title>Simulated Drag And Drop Example</title>
<script type="text/javascript">
/*------------------------------------------------------------------------------
* JavaScript zEvents Library
* Version 1.1
* by Nicholas C. Zakas, http://www.nczonline.net/
* Copyright (c) 2004-2005 Nicholas C. Zakas. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*------------------------------------------------------------------------------
*/
/**
* Encapsulates information about an event.
* @scope public
* @class
*/
function zEvent() {
/**
* The type of event.
* @scope public
*/
this.type /*: String */ = null;
/**
* The object that caused the event.
* @scope public
*/
this.target /*: zEventTarget */ = null;
/**
* A secondary object related to the event.
* @scope public
*/
this.relatedTarget /*: zEventTarget */ = null;
/**
* Indicates whether or not the event can be canceled.
* @scope public
*/
this.cancelable /*: boolean */ = false;
/**
* The time that the event occurred.
* @scope public
*/
this.timeStamp /*: long */ = null;
/*
* Set to false to cancel event.
* @scope public
*/
this.returnValue /*: boolean */ = true;
}
/**
* Initializes the event object with information for the event.
* @scope public
* @param sType The type of event encapsulated by the object.
* @param bCancelable True if the event can be cancelled.
*/
zEvent.prototype.initEvent = function (sType /*: String */,
bCancelable /*: boolean */) {
this.type = sType;
this.cancelable = bCancelable;
this.timeStamp = (new Date()).getTime();
};
/**
* Prevents the default behavior for an event.
* @scope public
*/
zEvent.prototype.preventDefault = function () {
if (this.cancelable) {
this.returnValue = false;
}
};
/**
* Any class that wants to support events should inherit from this.
* @class
* @scope public
*/
function zEventTarget() {
/**
* Array of event handlers.
* @scope private
*/
this.eventhandlers /*: Object */ = new Object();
}
/**
* Adds an event listener function to handle the type of event.
* @scope public
* @param sType The type of event to handle (i.e., "mousemove", not "onmousemove").
* @param fnListener The listener function for the event.
*/
zEventTarget.prototype.addEventListener = function (sType /*: String */,
fnListener /*: Function */) {
if (typeof this.eventhandlers[sType] == "undefined") {
this.eventhandlers[sType] = new Array;
}
this.eventhandlers[sType][this.eventhandlers[sType].length] = fnListener;
};
/**
* Causes an event to fire.
* @scope public
* @param oEvent The event object containing information about the event to fire.
* @return True if the event should continue, false if not.
*/
zEventTarget.prototype.dispatchEvent = function (oEvent /*: zEvent */) /*: boolean */ {
/*
* Set the target of the event.
*/
oEvent.target = this;
/*
* Call each event handler and pass in the event object.
*/
if (typeof this.eventhandlers[oEvent.type] != "undefined") {
for (var i=0; i < this.eventhandlers[oEvent.type].length; i++) {
this.eventhandlers[oEvent.type][i](oEvent);
}
}
/*
* Return the value of returnValue, which is changed to false
* when preventDefault() is called.
*/
return oEvent.returnValue;
};
/**
* Removes an event listener function from handling the type of event.
* @scope public
* @param sType The type of event to remove from (i.e., "mousemove", not "onmousemove").
* @param fnListener The listener function to remove.
*/
zEventTarget.prototype.removeEventListener = function (sType /*: String */,
fnListener /*: Function */) {
if (typeof this.eventhandlers[sType] != "undefined") {
var arrTemp = new Array;
for (var i=0; i < this.eventhandlers[sType].length; i++) {
if (this.eventhandlers[sType][i] != fnListener) {
arrTemp[arrTemp.length] = this.eventhandlers[sType][i];
}
}
this.eventhandlers[sType] = arrTemp;
}
};
</script>
<script type="text/javascript">
/*------------------------------------------------------------------------------
* JavaScript zDragDrop Library
* Version 1.1
* by Nicholas C. Zakas, http://www.nczonline.net/
* Copyright (c) 2004-2005 Nicholas C. Zakas. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*------------------------------------------------------------------------------
*/
/*
* This library requires the free zEvents library available from
* http://www.nczonline.net/.
*/
/**
* Contains global settings and methods for drag and drop functionality.
* @scope public
*/
function zDrag() {}
/**
* The item currently being dragged.
* @scope private
*/
zDrag.current /*: zDraggable */ = null;
/**
* Indicates whether or not an item is being dragged.
* @scope private
*/
zDrag.dragging /*: boolean */ = false;
/**
* Returns true if an item is being dragged.
* @scope public
* @return True if an item is being dragged, false if not.
* @type boolean
*/
zDrag.isDragging = function () /*: boolean */ {
return this.dragging;
};
/**
* Sets the item being dragged.
* @scope protected
* @param {zDraggable} oDraggable The draggable item.
* @type void
*/
zDrag.setCurrent = function (oDraggable /*: zDraggable */) {
this.current = oDraggable;
this.dragging = true;
};
/**
* Returns the currently dragged item.
* @scope public
* @return The currently dragged item.
* @type zDraggable
*/
zDrag.getCurrent = function () /*: zDraggable */ {
return this.current;
};
/**
* Clears the currently dragged item from memory and sets the dragging
* flag to false.
* @scope protected
* @type void
*/
zDrag.clearCurrent = function () {
this.current = null;
this.dragging = false;
};
/**
* Encapsulates the functionality for a draggable element.
* @scope public
* @extends zEventTarget
* @class
*/
function zDraggable(oElement, iConstraints) {
/*
* Inherit properties from zEventTarget.
*/
zEventTarget.call(this);
/*
* Call constructor.
*/
this.construct(oElement, iConstraints);
/**
* The difference between the x cursor position and left edge of the element.
* @scope private
* @type int
*/
this.diffX /*: int */ = 0;
/**
* The difference between the y cursor position and top edge of the element.
* @scope private
* @type int
*/
this.diffY /*: int */ = 0;
/**
* Collection of drop targets for this item.
* @scope private
* @type Array
*/
this.targets /*: Array */ = [];
}
/*
* Inherit methods from zEventTarget.
*/
zDraggable.prototype = new zEventTarget;
/**
* Indicates the dragged item can be dragged along the X axis.
* @scope public
* @type int
* @final
*/
zDraggable.DRAG_X /*: int */ = 1;
/**
* Indicates the dragged item can be dragged along the Y axis.
* @scope public
* @type int
* @final
*/
zDraggable.DRAG_Y /*: int */ = 2;
/**
* Adds a new drop target to the draggable item.
* @scope public
* @param {zDropTarget} oDropTarget The drop target to register for this item.
* @type void
*/
zDraggable.prototype.addDropTarget = function (oDropTarget /*: zDropTarget */) {
this.targets.push(oDropTarget);
};
/**
* Creates a new instance based on the given element and the constraints.
* @scope private
* @constructor
* @param {HTMLElement} oElement The DOM element to make draggable.
* @param {int} iConstraints The rules for dragging.
*/
zDraggable.prototype.construct = function (oElement /*: HTMLElement */,
iConstraints /*: int */) {
/**
* The element to make draggable.
* @scope private
* @type HTMLElement
*/
this.element /*: HTMLElement */ = oElement;
/**
* The constraints indicating the rules for dragging.
* @scope private
* @type int
*/
this.constraints /*: int */ = iConstraints;
/*
* Create a pointer to this object.
*/
var oThis = this;
/*
* Create a temporary function named fnTemp.
*/
var fnTemp = function () {
/*
* Create a dragstart event and fire it.
*/
var oDragStartEvent = new zDragDropEvent();
oDragStartEvent.initDragDropEvent("dragstart", true);
/*
* If the event isn't cancelled, proceed.
*/
if (oThis.dispatchEvent(oDragStartEvent)) {
/*
* Get the event objects, which is either the first
* argument (for DOM-compliant browsers and Netscape 4.x)
* or window.event (for IE).
*/
var oEvent = arguments[0] || window.event;
/*
* Get the difference between the clientX and clientY
* and the position of the element.
*/
oThis.diffX = oEvent.clientX - oThis.element.offsetLeft;
oThis.diffY = oEvent.clientY - oThis.element.offsetTop;
/*
* Add all DOM event handlers
*/
oThis.attachEventHandlers();
/*
* Set the currently dragged item.
*/
zDrag.setCurrent(oThis);
}
};
/*
* Determine which method to use to add the event handler.
*/
if (this.element.addEventListener) {
this.element.addEventListener("mousedown", fnTemp, false);
} else if (this.element.attachEvent) {
this.element.attachEvent("onmousedown", fnTemp);
} else {
throw new Error("zDrag not supported in this browser.");
}
};
/**
* Attaches event handlers for the mousemove and mouseup events.
* @scope private
* @private
* @type void
*/
zDraggable.prototype.attachEventHandlers = function () {
/*
* Create a pointer to this object.
*/
var oThis = this;
/*
* Create a temporary function named tempMouseMove.
*/
this.tempMouseMove = function () {
/*
* Get the event objects, which is either the first
* argument (for DOM-compliant browsers and Netscape 4.x)
* or window.event (for IE).
*/
var oEvent = arguments[0] || window.event;
/*
* Get the new x and y coordinates for the dragged element by
* subtracting the difference in the x and y direction from
* the mouse position on the screen (clientX and clientY).
*/
var iNewX = oEvent.clientX - oThis.diffX;
var iNewY = oEvent.clientY - oThis.diffY;
/*
* Move the x coordinate if zDraggable.DRAG_X is an option.
*/
if (oThis.constraints & zDraggable.DRAG_X) {
oThis.element.style.left = iNewX + "px";
}
/*
* Move the y coordinate if zDraggable.DRAG_Y is an option.
*/
if (oThis.constraints & zDraggable.DRAG_Y) {
oThis.element.style.top = iNewY + "px";
}
/*
* Create and fire a drag event.
*/
var oDragEvent = new zDragDropEvent();
oDragEvent.initDragDropEvent("drag", false);
oThis.dispatchEvent(oDragEvent);
};
/*
* Create a temporary function for the mouseup event.
*/
oThis.tempMouseUp = function () {
/*
* Get the event object.
*/
var oEvent = arguments[0] || window.event;
/*
* Determine if the mouse is over a drop target.
*/
var oDropTarget = oThis.getDropTarget(oEvent.clientX, oEvent.clientY);
if (oDropTarget != null) {
/*
* Fire the drop event.
*/
var oDropEvent = new zDragDropEvent();
oDropEvent.initDragDropEvent("drop", false, oThis);
oDropTarget.dispatchEvent(oDropEvent);
}
/*
* Create and fire a dragend event.
*/
var oDragEndEvent = new zDragDropEvent();
oDragEndEvent.initDragDropEvent("dragend", false);
oThis.dispatchEvent(oDragEndEvent);
/*
* Clear the currently dragged item.
*/
zDrag.clearCurrent();
/*
* Detach all of the event handlers.
*/
oThis.detachEventHandlers();
};
/*
* Determine which method to use to add the event handlers for
* the mousemove and mouseup events.
*/
if (document.addEventListener) {
document.addEventListener("mousemove", this.tempMouseMove, false);
document.addEventListener("mouseup", this.tempMouseUp, false);
} else if (document.body.attachEvent) {
document.body.attachEvent("onmousemove", this.tempMouseMove);
document.body.attachEvent("onmouseup", this.tempMouseUp);
} else {
throw new Error("zDrag doesn't support this browser.");
}
};
/**
* Detaches event handlers for the mousemove and mouseup events.
* @scope private
*/
zDraggable.prototype.detachEventHandlers = function () {
/*
* Determine the method for removing the event handlers for the
* mousemove and mouseup events.
*/
if (document.removeEventListener) {
document.removeEventListener("mousemove", this.tempMouseMove, false);
document.removeEventListener("mouseup", this.tempMouseUp, false);
} else if (document.body.detachEvent) {
document.body.detachEvent("onmousemove", this.tempMouseMove);
document.body.detachEvent("onmouseup", this.tempMouseUp);
} else {
throw new Error("zDrag doesn't support this browser.");
}
};
/**
* Determines the drop target that the mouse is over.
* @scope private
* @param iX The x-coordinate of the mouse.
* @param iY The y-coordinate of the mouse.
* @return The drop target if the mouse is over one, null otherwise.
*/
zDraggable.prototype.getDropTarget = function (iX /*: int */,
iY /*: int */) /*: zDropTarget */ {
for (var i=0; i < this.targets.length; i++) {
if (this.targets[i].isOver(iX, iY)) {
return this.targets[i];
}
}
return null;
};
/**
* Moves the draggable element to a given position.
* @scope public
* @param iX The x-coordinate to move to.
* @param iY The y-coordinate to move to.
*/
zDraggable.prototype.moveTo = function (iX /*: int */, iY /*: int */) {
this.element.style.left = iX + "px";
this.element.style.top = iY + "px";
};
/**
* Returns the left coordinate of the element.
* @scope public
* @return The left coordinate of the element.
*/
zDraggable.prototype.getLeft = function () /*: int */ {
return this.element.offsetLeft;
};
/**
* Returns the top coordinate of the element.
* @scope public
* @return The top coordinate of the element.
*/
zDraggable.prototype.getTop = function () /*: int */ {
return this.element.offsetTop;
};
/**
* Encapsulates information about a drag drop event.
* @class
* @scope public
* @extends zEvent
*/
function zDragDropEvent() {
/*
* Inherit properties from zEvent.
*/
zEvent.call(this);
}
/*
* Inherit methods from zEvent.
*/
zDragDropEvent.prototype = new zEvent();
/**
* Initializes the event object with information for the event.
* @scope public
* @param sType The type of event encapsulated by the object.
* @param bCancelable True if the event can be cancelled.
* @param oRelatedTarget The alternate target related to the event.
*/
zDragDropEvent.prototype.initDragDropEvent = function(sType /*: String */,
bCancelable /*: boolean */,
oRelatedTarget /*: zEventTarget */) {
/*
* Call inherited method initEvent().
*/
this.initEvent(sType, bCancelable);
/*
* Assign related target (may be null).
*/
this.relatedTarget = oRelatedTarget;
}
/**
* A target for a zDraggable to be dropped.
* @scope public
* @class
* @extends zEventTarget
*/
function zDropTarget(oElement) {
/*
* Inherit properties from zEventTarget.
*/
zEventTarget.call(this);
/*
* Call constructor.
*/
this.construct(oElement);
}
/*
* Inherit methods from zEventTarget.
*/
zDropTarget.prototype = new zEventTarget;
/**
* Creates a new instance based on the given DOM element.
* @constructor
* @scope public
* @param oElement The DOM element to make into a drop target.
*/
zDropTarget.prototype.construct = function (oElement /*: HTMLElement */) {
/**
* The DOM element to use as a drop target.
* @scope private
*/
this.element = oElement;
};
/**
* Determines if a given set of coordinates is over the element.
* @scope protected
* @param iX The x-coordinate to check.
* @param iY The y-coordinate to check.
* @return True if the coordinates are over the element, false if not.
*/
zDropTarget.prototype.isOver = function (iX /*: int */, iY /*: int */) /*: boolean */ {
var iX1 = this.element.offsetLeft;
var iX2 = iX1 + this.element.offsetWidth;
var iY1 = this.element.offsetTop;
var iY2 = iY1 + this.element.offsetHeight;
return (iX >= iX1 && iX <= iX2 && iY >= iY1 && iY <= iY2);
};
/**
* Returns the left coordinate of the drop target.
* @scope public
* @return The left coordinate of the drop target.
*/
zDropTarget.prototype.getLeft = function () /*: int */ {
return this.element.offsetLeft;
};
/**
* Returns the top coordinate of the drop target.
* @scope public
* @return The top coordinate of the drop target.
*/
zDropTarget.prototype.getTop = function () /*: int */{
return this.element.offsetTop;
};
/**
* Returns the height of the drop target.
* @scope public
* @return The height of the drop target.
*/
zDropTarget.prototype.getHeight = function () /*: int */{
return this.element.offsetHeight;
};
/**
* Returns the width of the drop target.
* @scope public
* @return The width of the drop target.
*/
zDropTarget.prototype.getWidth = function () /*: int */{
return this.element.offsetWidth;
};
</script>
<script type="text/javascript">
function doLoad() {
var oDraggable = new zDraggable(document.getElementById("div1"), zDraggable.DRAG_X | zDraggable.DRAG_Y);
var oDropTarget = new zDropTarget(document.getElementById("divDropTarget"));
oDraggable.addDropTarget(oDropTarget);
oDropTarget.addEventListener("drop", function (oEvent) {
oEvent.relatedTarget.moveTo(oDropTarget.getLeft(), oDropTarget.getTop());
});
}
</script>
<style type="text/css">
#div1 {
background-color: red;
height: 100px;
width: 100px;
position: absolute;
z-index: 10;
}
#divDropTarget {
background-color: blue;
height: 200px;
width: 200px;
position: absolute;
left: 300px;
}
</style>
</head>
<body onload="doLoad()">
<P>Try dragging the red square onto the blue square.</p>
<div id="div1"></div>
<div id="divDropTarget"></div>
</body>
</html>