var SVGDocument = null; var SVGRoot = null; var TrueCoords = null; var LastCoords = null; var GrabPoint = null; var DragTarget = null; var DragBBox = null; var DropArray = null; var dragActive = true; var dragArea = null; var dragCheck = null; var bboxArray = new Array(); function Init(evt) { SVGDocument = evt.target.ownerDocument; SVGRoot = SVGDocument.documentElement; SVGRoot.addEventListener('mousedown', Grab, false); SVGRoot.addEventListener('mousemove', Drag, false); SVGRoot.addEventListener('mouseup', Drop, false); // these svg points hold x and y values... // very handy, but they do not display on the screen (just so you know) TrueCoords = SVGRoot.createSVGPoint(); GrabPoint = SVGRoot.createSVGPoint(); LastCoords = SVGRoot.createSVGPoint(); dragCheck = DragOptions(); RecordBBoxes(); } function Grab(evt) { // find out which element we moused down on var targetElement = evt.target; // you can only drag elements that have draggable='true' var draggable = targetElement.getAttributeNS(null, 'draggable' ); if ( draggable ) { //set the item moused down on as the element to be dragged DragTarget = targetElement; DragBBox = bboxArray[DragTarget.id]; // move this element to the "top" of the display, so it is (almost) // always over other elements (exception: in this case, elements that are // "in the folder" (children of the folder group) with only maintain // hierarchy within that group DragTarget.parentNode.appendChild( DragTarget ); // turn off all pointer events to the dragged element, this does 2 things: // 1) allows us to drag text elements without selecting the text // 2) allows us to find out where the dragged element is dropped (see Drop) DragTarget.setAttributeNS(null, 'pointer-events', 'none'); // we need to find the current position and translation of the grabbed element, // so that we only apply the differential between the current location // and the new location var transMatrix = DragTarget.getCTM(); GrabPoint.x = TrueCoords.x - Number(transMatrix.e); GrabPoint.y = TrueCoords.y - Number(transMatrix.f); var dropString = targetElement.getAttributeNS(null, 'drop' ); if ( dropString ) { DropArray = dropString.split(' '); for ( var d = 0, dLen = DropArray.length; dLen > d; d++ ) { DropArray[d] = DropArray[d].replace(/url\(#(.+)\)/, "$1"); } } var dragAreaRef = targetElement.getAttributeNS(null, 'dragArea' ); if ( dragAreaRef ) { var dragAreaId = dragAreaRef.replace(/url\(#(.+)\)/, "$1"); dragArea = SVGDocument.getElementById( dragAreaId ); dragArea.addEventListener('mouseout', StopDrag, false); dragArea.addEventListener('mouseover', StartDrag, false); } } }; function Drag(evt) { // account for zooming and panning GetTrueCoords(evt); // if we don't currently have an element in tow, don't do anything if ( dragActive && DragTarget) { // account for the offset between the element's origin and the // exact place we grabbed it... this way, the drag will look more natural var newX = TrueCoords.x - GrabPoint.x; var newY = TrueCoords.y - GrabPoint.y; var newCoords = CheckIntersection(newX, newY); // apply a new tranform translation to the dragged element, to display // it in its new location DragTarget.setAttributeNS(null, 'transform', 'translate(' + newCoords.x + ',' + newCoords.y + ')'); DragBBox.setX(newCoords.x); DragBBox.setY(newCoords.y); LastCoords.x = newCoords.x; LastCoords.y = newCoords.y; } }; function Drop(evt) { // if we aren't currently dragging an element, don't do anything if ( DragTarget ) { // since the element currently being dragged has its pointer-events turned off, // we are afforded the opportunity to find out the element it's being dropped on var targetElement = evt.target; var targetId = targetElement.id; //buh() var isValid = true; if ( DropArray ) { isValid = false; for ( var d = 0, dLen = DropArray.length; dLen > d; d++ ) { var eachDrop = DropArray[d]; if ( targetId == eachDrop ) { isValid = true; break; } } } if ( isValid ) { // turn the pointer-events back on, so we can grab this item later DragTarget.setAttributeNS(null, 'pointer-events', 'all'); DragTarget = null; DropArray = null; if ( dragArea ) { dragArea.removeEventListener('mouseout', StopDrag, false); dragArea.removeEventListener('mouseover', StartDrag, false); } } } }; function GetTrueCoords(evt) { // find the current zoom level and pan setting, and adjust the reported // mouse position accordingly var newScale = SVGRoot.currentScale; var translation = SVGRoot.currentTranslate; TrueCoords.x = (evt.clientX - translation.x)/newScale; TrueCoords.y = (evt.clientY - translation.y)/newScale; }; function StartDrag() { dragActive = true; }; function StopDrag(evt) { var targetElement = evt.relatedTarget; var targetId = targetElement.id; //buh() var isValid = true; if ( DropArray ) { for ( var d = 0, dLen = DropArray.length; dLen > d; d++ ) { var eachDrop = DropArray[d]; if ( targetId == eachDrop ) { isValid = false; break; } } } if ( isValid ) { dragActive = false; } }; function RecordBBoxes() { //return list of target node's children of this element type as objects var allElements = SVGDocument.getElementsByTagName('*'); for ( var e = 0, eLen = allElements.length; eLen > e; e++ ) { var eachElement = allElements.item(e); if ( 1 == eachElement.nodeType && eachElement.getBBox ) { //if ( eachElement.id && 'background' != eachElement.id ) if ( 'blockStop' == eachElement.getAttributeNS(null, 'behavior') ) { //return rectangle around object as SVGRect object var outline = eachElement.getBBox(); bboxArray[eachElement.id] = new BoundingBox(outline.x, outline.y, outline.width, outline.height); } } } //buh() }; function CheckIntersection(newX, newY) { var newCoords = SVGRoot.createSVGPoint(); newCoords.x = newX; newCoords.y = newY; var direction = ''; for ( var eachBox in bboxArray ) { var boxDef = bboxArray[eachBox]; var xVal = 0; var yVal = 0; if ( eachBox != DragTarget.id ) { //alert('eachBox: ' + eachBox + '\nDragTarget.id: ' + DragTarget.id ); var block = false; if ( LastCoords.x > newX ) //left { var border = boxDef.x2; if ( border <= DragBBox.x ) { if ( CheckVertical( DragBBox, boxDef) ) { xVal = border - DragBBox.baseX + 1; if ( newCoords.x < xVal ) { newCoords.x = xVal; } block = true; } } } else if ( LastCoords.x < newX ) //right { var border = boxDef.x; if ( border >= DragBBox.x2 ) { if ( CheckVertical( DragBBox, boxDef) ) { xVal = border - (DragBBox.width + DragBBox.baseX) - 1; if ( newCoords.x > xVal ) { newCoords.x = xVal; } block = true; } } } if ( LastCoords.y > newY ) //up { var border = boxDef.y2; if ( border <= DragBBox.y ) { if ( CheckHorizontal( DragBBox, boxDef) ) { yVal = border - DragBBox.baseY + 1; if ( newCoords.y < yVal ) { newCoords.y = yVal; } block = true; } } } else if ( LastCoords.y < newY ) //down { var border = boxDef.y; if ( border >= DragBBox.y2 ) { if ( CheckHorizontal( DragBBox, boxDef) ) { yVal = border - (DragBBox.height + DragBBox.baseY) - 1; if ( newCoords.y > yVal ) { newCoords.y = yVal; } block = true; } } } if ( block ) { } } } //window.status = direction + DragBBox.y2 + ' ' + newY; return newCoords; }; function CheckVertical( dragBox, blockBox ) { var block = false; if ( dragBox.y >= blockBox.y && dragBox.y <= blockBox.y2 ) { block = true; } else if ( dragBox.y2 >= blockBox.y && dragBox.y2 <= blockBox.y2 ) { block = true; } else if ( dragBox.y > blockBox.y && dragBox.y2 < blockBox.y2 ) { block = true; } else if ( dragBox.y < blockBox.y && dragBox.y2 > blockBox.y2 ) { block = true; } return block; }; function CheckHorizontal( dragBox, blockBox ) { var block = false; if ( dragBox.x >= blockBox.x && dragBox.x <= blockBox.x2 ) { block = true; } else if ( dragBox.x2 >= blockBox.x && dragBox.x2 <= blockBox.x2 ) { block = true; } else if ( dragBox.x > blockBox.x && dragBox.x2 < blockBox.x2 ) { block = true; } else if ( dragBox.x < blockBox.x && dragBox.x2 > blockBox.x2 ) { block = true; } return block; }; function BoundingBox(x, y, width, height) { this.baseX = Number(x); this.baseY = Number(y); this.width = Number(width); this.height = Number(height); this.x = Number(x); this.y = Number(y); this.x2 = this.x + this.width; this.y2 = this.y + this.height; this.setX = function(newX) { this.x = this.baseX + Number(newX); this.x2 = this.x + this.width; }; this.setY = function(newY) { this.y = this.baseY + Number(newY); this.y2 = this.y + this.height; }; }; function DragOptions() { this.left = true; this.right = true; this.up = true; this.down = true; };