123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- /* Drag and drop tools by Tim Taylor. Code is under public license.
- http://tool-man.org/examples/sorting.html */
- var Coordinates = {
- ORIGIN : new Coordinate(0, 0),
- northwestPosition : function(element) {
- var x = parseInt(element.style.left);
- var y = parseInt(element.style.top);
- return new Coordinate(isNaN(x) ? 0 : x, isNaN(y) ? 0 : y);
- },
- southeastPosition : function(element) {
- return Coordinates.northwestPosition(element).plus(
- new Coordinate(element.offsetWidth, element.offsetHeight));
- },
- northwestOffset : function(element, isRecursive) {
- var offset = new Coordinate(element.offsetLeft, element.offsetTop);
- if (!isRecursive) return offset;
- var parent = element.offsetParent;
- while (parent) {
- offset = offset.plus(
- new Coordinate(parent.offsetLeft, parent.offsetTop));
- parent = parent.offsetParent;
- }
- return offset;
- },
- southeastOffset : function(element, isRecursive) {
- return Coordinates.northwestOffset(element, isRecursive).plus(
- new Coordinate(element.offsetWidth, element.offsetHeight));
- },
- fixEvent : function(event) {
- event.windowCoordinate = new Coordinate(event.clientX, event.clientY);
- }
- };
- function Coordinate(x, y) {
- this.x = x;
- this.y = y;
- }
- Coordinate.prototype.toString = function() {
- return "(" + this.x + "," + this.y + ")";
- }
- Coordinate.prototype.plus = function(that) {
- return new Coordinate(this.x + that.x, this.y + that.y);
- }
- Coordinate.prototype.minus = function(that) {
- return new Coordinate(this.x - that.x, this.y - that.y);
- }
- Coordinate.prototype.distance = function(that) {
- var deltaX = this.x - that.x;
- var deltaY = this.y - that.y;
- return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
- }
- Coordinate.prototype.max = function(that) {
- var x = Math.max(this.x, that.x);
- var y = Math.max(this.y, that.y);
- return new Coordinate(x, y);
- }
- Coordinate.prototype.constrain = function(min, max) {
- if (min.x > max.x || min.y > max.y) return this;
- var x = this.x;
- var y = this.y;
- if (min.x != null) x = Math.max(x, min.x);
- if (max.x != null) x = Math.min(x, max.x);
- if (min.y != null) y = Math.max(y, min.y);
- if (max.y != null) y = Math.min(y, max.y);
- return new Coordinate(x, y);
- }
- Coordinate.prototype.reposition = function(element) {
- element.style["top"] = this.y + "px";
- element.style["left"] = this.x + "px";
- }
- Coordinate.prototype.equals = function(that) {
- if (this == that) return true;
- if (!that || that == null) return false;
- return this.x == that.x && this.y == that.y;
- }
- /*
- * drag.js - click & drag DOM elements
- *
- * originally based on Youngpup's dom-drag.js, www.youngpup.net
- */
- var Drag = {
- BIG_Z_INDEX : 10000,
- group : null,
- isDragging : false,
- makeDraggable : function(group) {
- group.handle = group;
- group.handle.group = group;
- group.minX = null;
- group.minY = null;
- group.maxX = null;
- group.maxY = null;
- group.threshold = 0;
- group.thresholdY = 0;
- group.thresholdX = 0;
- group.onDragStart = new Function();
- group.onDragEnd = new Function();
- group.onDrag = new Function();
-
- // TODO: use element.prototype.myFunc
- group.setDragHandle = Drag.setDragHandle;
- group.setDragThreshold = Drag.setDragThreshold;
- group.setDragThresholdX = Drag.setDragThresholdX;
- group.setDragThresholdY = Drag.setDragThresholdY;
- group.constrain = Drag.constrain;
- group.constrainVertical = Drag.constrainVertical;
- group.constrainHorizontal = Drag.constrainHorizontal;
- group.onmousedown = Drag.onMouseDown;
- },
- constrainVertical : function() {
- var nwOffset = Coordinates.northwestOffset(this, true);
- this.minX = nwOffset.x;
- this.maxX = nwOffset.x;
- },
- constrainHorizontal : function() {
- var nwOffset = Coordinates.northwestOffset(this, true);
- this.minY = nwOffset.y;
- this.maxY = nwOffset.y;
- },
- constrain : function(nwPosition, sePosition) {
- this.minX = nwPosition.x;
- this.minY = nwPosition.y;
- this.maxX = sePosition.x;
- this.maxY = sePosition.y;
- },
- setDragHandle : function(handle) {
- if (handle && handle != null)
- this.handle = handle;
- else
- this.handle = this;
- this.handle.group = this;
- this.onmousedown = null;
- this.handle.onmousedown = Drag.onMouseDown;
- },
- setDragThreshold : function(threshold) {
- if (isNaN(parseInt(threshold))) return;
- this.threshold = threshold;
- },
- setDragThresholdX : function(threshold) {
- if (isNaN(parseInt(threshold))) return;
- this.thresholdX = threshold;
- },
- setDragThresholdY : function(threshold) {
- if (isNaN(parseInt(threshold))) return;
- this.thresholdY = threshold;
- },
- onMouseDown : function(event) {
- event = Drag.fixEvent(event);
- Drag.group = this.group;
- var group = this.group;
- var mouse = event.windowCoordinate;
- var nwOffset = Coordinates.northwestOffset(group, true);
- var nwPosition = Coordinates.northwestPosition(group);
- var sePosition = Coordinates.southeastPosition(group);
- var seOffset = Coordinates.southeastOffset(group, true);
- group.originalOpacity = group.style.opacity;
- group.originalZIndex = group.style.zIndex;
- group.initialWindowCoordinate = mouse;
- // TODO: need a better name, but don't yet understand how it
- // participates in the magic while dragging
- group.dragCoordinate = mouse;
- Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset);
- group.onDragStart(nwPosition, sePosition, nwOffset, seOffset);
- // TODO: need better constraint API
- if (group.minX != null)
- group.minMouseX = mouse.x - nwPosition.x +
- group.minX - nwOffset.x;
- if (group.maxX != null)
- group.maxMouseX = group.minMouseX + group.maxX - group.minX;
- if (group.minY != null)
- group.minMouseY = mouse.y - nwPosition.y +
- group.minY - nwOffset.y;
- if (group.maxY != null)
- group.maxMouseY = group.minMouseY + group.maxY - group.minY;
- group.mouseMin = new Coordinate(group.minMouseX, group.minMouseY);
- group.mouseMax = new Coordinate(group.maxMouseX, group.maxMouseY);
- document.onmousemove = Drag.onMouseMove;
- document.onmouseup = Drag.onMouseUp;
- return false;
- },
- showStatus : function(mouse, nwPosition, sePosition, nwOffset, seOffset) {
- window.status =
- "mouse: " + mouse.toString() + " " +
- "NW pos: " + nwPosition.toString() + " " +
- "SE pos: " + sePosition.toString() + " " +
- "NW offset: " + nwOffset.toString() + " " +
- "SE offset: " + seOffset.toString();
- },
- onMouseMove : function(event) {
- event = Drag.fixEvent(event);
- var group = Drag.group;
- var mouse = event.windowCoordinate;
- var nwOffset = Coordinates.northwestOffset(group, true);
- var nwPosition = Coordinates.northwestPosition(group);
- var sePosition = Coordinates.southeastPosition(group);
- var seOffset = Coordinates.southeastOffset(group, true);
- Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset);
- if (!Drag.isDragging) {
- if (group.threshold > 0) {
- var distance = group.initialWindowCoordinate.distance(
- mouse);
- if (distance < group.threshold) return true;
- } else if (group.thresholdY > 0) {
- var deltaY = Math.abs(group.initialWindowCoordinate.y - mouse.y);
- if (deltaY < group.thresholdY) return true;
- } else if (group.thresholdX > 0) {
- var deltaX = Math.abs(group.initialWindowCoordinate.x - mouse.x);
- if (deltaX < group.thresholdX) return true;
- }
- Drag.isDragging = true;
- group.style["zIndex"] = Drag.BIG_Z_INDEX;
- group.style["opacity"] = 0.75;
- }
- // TODO: need better constraint API
- var adjusted = mouse.constrain(group.mouseMin, group.mouseMax);
- nwPosition = nwPosition.plus(adjusted.minus(group.dragCoordinate));
- nwPosition.reposition(group);
- group.dragCoordinate = adjusted;
- // once dragging has started, the position of the group
- // relative to the mouse should stay fixed. They can get out
- // of sync if the DOM is manipulated while dragging, so we
- // correct the error here
- //
- // TODO: what we really want to do is find the offset from
- // our corner to the mouse coordinate and adjust to keep it
- // the same
- var offsetBefore = Coordinates.northwestOffset(group);
- group.onDrag(nwPosition, sePosition, nwOffset, seOffset);
- var offsetAfter = Coordinates.northwestOffset(group);
- if (!offsetBefore.equals(offsetAfter)) {
- var errorDelta = offsetBefore.minus(offsetAfter);
- nwPosition = Coordinates.northwestPosition(group).plus(errorDelta);
- nwPosition.reposition(group);
- }
- return false;
- },
- onMouseUp : function(event) {
- event = Drag.fixEvent(event);
- var group = Drag.group;
- var mouse = event.windowCoordinate;
- var nwOffset = Coordinates.northwestOffset(group, true);
- var nwPosition = Coordinates.northwestPosition(group);
- var sePosition = Coordinates.southeastPosition(group);
- var seOffset = Coordinates.southeastOffset(group, true);
- document.onmousemove = null;
- document.onmouseup = null;
- group.onDragEnd(nwPosition, sePosition, nwOffset, seOffset);
- if (Drag.isDragging) {
- // restoring zIndex before opacity avoids visual flicker in Firefox
- group.style["zIndex"] = group.originalZIndex;
- group.style["opacity"] = group.originalOpacity;
- }
- Drag.group = null;
- Drag.isDragging = false;
- return false;
- },
- fixEvent : function(event) {
- if (typeof event == 'undefined') event = window.event;
- Coordinates.fixEvent(event);
- return event;
- }
- };
|