af_DragNDrop = function() { var that = this; // offset inside clicked element that.mouseOffset = {}; // all drop targets that.dropTargets = {}; // container which is actually being moved around that.dragContainer = null; // element that is currently being dragged that.currentlyDragging = null; // drag-Id that.currentId = ''; // dragging already initialized? that.draggingInitialized = false; // position, where we started dragging that.startPosition = {}; that.dragRegex = /\bdrag_(.*?)\b/; that.dropRegex = /\bdrop_(.*?)\b/; // @TODO: maybe this can be replaced by manually setting the regex.global flag? that.dropRegexG = /\bdrop_(.*?)\b/g; that.load = function() { // create drag container that.dragContainer = document.createElement('div'); that.dragContainer.className = 'dragcontainer'; // better solution for this? document.getElementsByTagName('body')[0].appendChild(that.dragContainer); // find draggable elements var elements = af_CSS.getElementsByClass(that.dragRegex); for (var i = 0; i < elements.length; ++i) { af_Events.addEventListener(elements[i], 'mousedown', that.startDrag); af_Events.addEventListener(elements[i], 'click', that.interceptClick); // af_Events.addEventListener(elements[i], 'click', function(event) {alert('Clicked!'); }); } // find drop targets var elements = af_CSS.getElementsByClass(that.dropRegex); var match; for (var i = 0; i < elements.length; ++i) { // an elment can serve as a drop target for differend IDs while (match = that.dropRegexG.exec(elements[i].className)) { var id = match[1]; that.dropTargets[id] = that.dropTargets[id] || new Array(); that.dropTargets[id].push(elements[i]); } } } that.startDrag = function(event) { event = event || window.event; var mpos = af_Events.getMousePosition(event); var epos = af_CSS.getPosition(this); // record mouse offset that.mouseOffset = { x: mpos.x - epos.x, y: mpos.y - epos.y } that.startPosition = mpos; that.currentlyDragging = this; that.draggingInitialized = false; af_Events.stopEvent(event); return false; } that.initDrag = function(event) { // find drag id var match = that.dragRegex.exec(that.currentlyDragging.className); if (!match || !match[1]) { return false; } that.currentId = match[1]; // are there any drop targets? if (!that.dropTargets[that.currentId]) { return false; } // highlight drop targets for (var i = 0; i < that.dropTargets[that.currentId].length; ++i) { af_CSS.addClass(that.dropTargets[that.currentId][i], 'active_' + that.currentId); } var mpos = af_Events.getMousePosition(event); // prepare drag container that.dragContainer.innerHTML = ''; that.dragContainer.style.top = mpos.y - that.mouseOffset.y + 'px'; that.dragContainer.style.left = mpos.x - that.mouseOffset.x + 'px'; // copy element into drag container var clone = that.currentlyDragging.cloneNode(true) var dims = af_CSS.getDimensions(that.currentlyDragging); clone.style.width = dims.width + 'px'; clone.style.height = dims.height + 'px'; that.dragContainer.appendChild(clone); that.dragContainer.style.display = 'block'; // mark element as being dragged af_CSS.addClass(that.currentlyDragging, 'dragging'); that.draggingInitialized = true; return true; } that.runDrag = function(event) { if (!that.currentlyDragging) { return; } event = event || window.event; if (!that.draggingInitialized) { // first mouse move, set up dragging if (!that.initDrag(event)) { af_Events.stopEvent(event); return false; } } var mpos = af_Events.getMousePosition(event); // move drag container that.dragContainer.style.left = (mpos.x - that.mouseOffset.x) + 'px'; that.dragContainer.style.top = (mpos.y - that.mouseOffset.y) + 'px'; var dropTarget = null; for (var i = 0; i < that.dropTargets[that.currentId].length; ++i) { if ( that.dropTargets[that.currentId][i] != that.currentlyDragging && that.testBoundingBox(that.getBoundingBox(that.dropTargets[that.currentId][i]), mpos) ) { af_CSS.addClass(that.dropTargets[that.currentId][i], 'hover_' + that.currentId); } else { af_CSS.removeClass(that.dropTargets[that.currentId][i], 'hover_' + that.currentId); } } // required for Internet Explorer: return false; } that.endDrag = function(event) { if (!that.draggingInitialized) { that.currentlyDragging = null; } if (!that.currentlyDragging) { return; } event = event || window.event; // find drop target if (!that.dropTargets[that.currentId]) { return; } var dropTarget = null; var mpos = af_Events.getMousePosition(event); for (var i = 0; i < that.dropTargets[that.currentId].length; ++i) { if (that.testBoundingBox(that.getBoundingBox(that.dropTargets[that.currentId][i]), mpos)) { dropTarget = that.dropTargets[that.currentId][i]; break; } } // reset highlighted drop targets for (var i = 0; i < that.dropTargets[that.currentId].length; ++i) { af_CSS.removeClass(that.dropTargets[that.currentId][i], 'active_' + that.currentId); af_CSS.removeClass(that.dropTargets[that.currentId][i], 'hover_' + that.currentId); } // backup element var element = that.currentlyDragging; // stop dragging af_CSS.removeClass(that.currentlyDragging, 'dragging'); that.currentlyDragging = null; that.dragContainer.style.display = 'none'; that.dragContainer.style.top = 0; that.dragContainer.style.left = 0; that.dragContainer.innerHTML = ''; af_Events.stopEvent(event); if (dropTarget && that['drop' + that.currentId]) { return that['drop' + that.currentId](element, dropTarget); } return false; } // intercepts clicks if mouse has moved that.interceptClick = function(event) { event = event || window.event; var mpos = af_Events.getMousePosition(event); if (mpos.x != that.startPosition.x || mpos.y != that.startPosition.y) { // mouse was moved => dragging took place => intercept click af_Events.stopEvent(event); return false; } return true; } that.getBoundingBox = function(element) { var coords = af_CSS.getPosition(element); var dimensions = af_CSS.getDimensions(element); return { xMin: coords.x, yMin: coords.y, xMax: coords.x + dimensions.width, yMax: coords.y + dimensions.height } } that.testBoundingBox = function(bbox, mpos) { return ( mpos.x > bbox.xMin && mpos.x < bbox.xMax && mpos.y > bbox.yMin && mpos.y < bbox.yMax ); } af_Events.addOnEvent(window, 'load', that.load); af_Events.addEventListener(document, 'mousemove', that.runDrag); af_Events.addEventListener(document, 'mouseup', that.endDrag); }