");
table.append(tr1).append(tr2); // removed as on FF there are rounging issues //.css({width:computedTableWidth});
var head = table.clone().addClass("ganttFixHead");
table.append(trBody).addClass("ganttTable");
var height = self.master.editor.element.height();
table.height(height);
var box = $("");
box.addClass("gantt unselectable").attr("unselectable", "true").css({position:"relative", width:computedTableWidth});
box.append(table);
box.append(head);
//create the svg
box.svg({settings:{class:"ganttSVGBox"},
onLoad: function (svg) {
//console.debug("svg loaded", svg);
//creates gradient and definitions
var defs = svg.defs('myDefs');
//create backgound_self.master.resourceUrl +"hasExternalDeps.png",
var extDep = svg.pattern(defs, "extDep", 0, 0, 10, 10, 0, 0, 10, 10, {patternUnits:'userSpaceOnUse'});
var img = svg.image(extDep, 0, 0, 10, 10, "/Content/jquerygantt/res/hasExternalDeps.png", { opacity: .3 });
self.svg = svg;
$(svg).addClass("ganttSVGBox");
//creates grid group
var gridGroup = svg.group("gridGroup");
//creates rows grid
for (var i = 40; i <= height; i += self.rowHeight)
//svg.line(gridGroup, 0, i, "100%", i, {class:"ganttLinesSVG"});
svg.rect(gridGroup, 0, i, "100%",self.rowHeight, {class:"ganttLinesSVG"});
//creates links group
self.linksGroup = svg.group("linksGroup");
//creates tasks group
self.tasksGroup = svg.group("tasksGroup");
//compute scalefactor fx
//self.fx = computedTableWidth / (endPeriod - startPeriod);
self.fx = computedScaleX;
// drawTodayLine
if (new Date().getTime() > self.startMillis && new Date().getTime() < self.endMillis) {
var x = Math.round(((new Date().getTime()) - self.startMillis) * self.fx);
svg.line(gridGroup, x, 0, x, "100%", {class:"ganttTodaySVG"});
}
}
});
return box;
}
//if include today synch extremes
if (this.includeToday) {
var today = new Date().getTime();
originalStartmillis = originalStartmillis > today ? today : originalStartmillis;
originalEndMillis = originalEndMillis < today ? today : originalEndMillis;
}
//get best dimension fo gantt
var period = getPeriod(zoom, originalStartmillis, originalEndMillis); //this is enlarged to match complete periods basing on zoom level
//console.debug(new Date(period.start) + " " + new Date(period.end));
self.startMillis = period.start; //real dimension of gantt
self.endMillis = period.end;
self.originalStartMillis = originalStartmillis; //minimal dimension required by user or by task duration
self.originalEndMillis = originalEndMillis;
var table = createGantt(zoom, period.start, period.end);
return table;
};
//<%-------------------------------------- GANT TASK GRAPHIC ELEMENT --------------------------------------%>
Ganttalendar.prototype.drawTask = function (task) {
//console.debug("drawTask", task.name,new Date(task.start));
var self = this;
//var prof = new Profiler("ganttDrawTask");
editorRow = task.rowElement;
var top = editorRow.position().top + editorRow.offsetParent().scrollTop();
//var normStart=Math.round(task.start/(3600000*24))*(3600000*24)
//var normX = Math.round((normStart - self.startMillis) * self.fx);
var x = Math.round((task.start - self.startMillis) * self.fx);
//console.debug(x,normX)
task.hasChild = task.isParent();
var taskBox = $(_createTaskSVG(task, {x:x, y:top+self.taskVertOffset, width:Math.round((task.end - task.start) * self.fx),height:self.taskHeight}));
task.ganttElement = taskBox;
if (self.showCriticalPath && task.isCritical)
taskBox.addClass("critical");
if (this.master.permissions.canWrite && task.canWrite) {
//bind all events on taskBox
taskBox
.click(function (e) { // manages selection
e.stopPropagation();// to avoid body remove focused
self.element.find("[class*=focused]").removeClass("focused");
$(".ganttSVGBox .focused").removeClass("focused");
var el = $(this);
if (!self.resDrop)
el.addClass("focused");
self.resDrop = false; //hack to avoid select
$("body").off("click.focused").one("click.focused", function () {
$(".ganttSVGBox .focused").removeClass("focused");
})
}).dblclick(function () {
self.master.showTaskEditor($(this).attr("taskid"));
}).mouseenter(function () {
//bring to top
var el = $(this);
if (!self.linkOnProgress) {
el.find("[class*=linkHandleSVG]").show();
} else {
el.addClass("linkOver");
}
}).mouseleave(function () {
var el = $(this);
el.removeClass("linkOver").find("[class*=linkHandleSVG]").hide();
}).mouseup(function (e) {
$(":focus").blur(); // in order to save grid field when moving task
}).mousedown(function () {
var task = self.master.getTask($(this).attr("taskid"));
task.rowElement.click();
}).dragExtedSVG($(self.svg.root()), {
canResize: this.master.permissions.canWrite && task.canWrite,
canDrag: !task.depends && this.master.permissions.canWrite && task.canWrite,
startDrag: function (e) {
$(".ganttSVGBox .focused").removeClass("focused");
},
drag: function (e) {
$("[from=" + task.id + "],[to=" + task.id + "]").trigger("update");
},
drop: function (e) {
self.resDrop = true; //hack to avoid select
var taskbox = $(this);
var task = self.master.getTask(taskbox.attr("taskid"));
var s = Math.round((parseFloat(taskbox.attr("x")) / self.fx) + self.startMillis);
self.master.beginTransaction();
self.master.moveTask(task, new Date(s));
self.master.endTransaction();
},
startResize:function (e) {
//console.debug("startResize");
$(".ganttSVGBox .focused").removeClass("focused");
var taskbox = $(this);
var text = $(self.svg.text(parseInt(taskbox.attr("x")) + parseInt(taskbox.attr("width") + 8), parseInt(taskbox.attr("y")), "", {"font-size":"10px", "fill":"red"}));
taskBox.data("textDur", text);
},
resize: function (e) {
//find and update links from, to
var taskbox = $(this);
var st = Math.round((parseFloat(taskbox.attr("x")) / self.fx) + self.startMillis);
var en = Math.round(((parseFloat(taskbox.attr("x")) + parseFloat(taskbox.attr("width"))) / self.fx) + self.startMillis);
var d = computeStartDate(st).distanceInWorkingDays(computeEndDate(en))+1;
var text = taskBox.data("textDur");
text.attr("x", parseInt(taskbox.attr("x")) + parseInt(taskbox.attr("width")) + 8).html(d);
$("[from=" + task.id + "],[to=" + task.id + "]").trigger("update");
},
stopResize: function (e) {
self.resDrop = true; //hack to avoid select
//console.debug(ui)
var textBox = taskBox.data("textDur");
if (textBox)
textBox.remove();
var taskbox = $(this);
var task = self.master.getTask(taskbox.attr("taskid"));
var st = Math.round((parseFloat(taskbox.attr("x")) / self.fx) + self.startMillis);
var en = Math.round(((parseFloat(taskbox.attr("x")) + parseFloat(taskbox.attr("width"))) / self.fx) + self.startMillis);
self.master.beginTransaction();
self.master.changeTaskDates(task, new Date(st), new Date(en));
self.master.endTransaction();
}
});
//binding for creating link
taskBox.find("[class*=linkHandleSVG]").mousedown(function (e) {
e.preventDefault();
e.stopPropagation();
var taskBox = $(this).closest(".taskBoxSVG");
var svg = $(self.svg.root());
var offs = svg.offset();
self.linkOnProgress = true;
self.linkFromEnd = $(this).is(".taskLinkEndSVG");
svg.addClass("linkOnProgress");
// create the line
var startX = parseFloat(taskBox.attr("x")) + (self.linkFromEnd ? parseFloat(taskBox.attr("width")) : 0);
var startY = parseFloat(taskBox.attr("y")) + parseFloat(taskBox.attr("height")) / 2;
var line = self.svg.line(startX, startY, e.pageX - offs.left - 5, e.pageY - offs.top - 5, {class:"linkLineSVG"});
var circle = self.svg.circle(startX, startY, 5, {class:"linkLineSVG"});
//bind mousemove to draw a line
svg.bind("mousemove.linkSVG", function (e) {
var offs = svg.offset();
var nx = e.pageX - offs.left;
var ny = e.pageY - offs.top;
var c = Math.sqrt(Math.pow(nx - startX, 2) + Math.pow(ny - startY, 2));
nx = nx - (nx - startX) * 10 / c;
ny = ny - (ny - startY) * 10 / c;
self.svg.change(line, { x2:nx, y2:ny});
self.svg.change(circle, { cx:nx, cy:ny});
});
//bind mouseup un body to stop
$("body").one("mouseup.linkSVG", function (e) {
$(line).remove();
$(circle).remove();
self.linkOnProgress = false;
svg.removeClass("linkOnProgress");
$(self.svg.root()).unbind("mousemove.linkSVG");
var targetBox = $(e.target).closest(".taskBoxSVG");
//console.debug("create link from " + taskBox.attr("taskid") + " to " + targetBox.attr("taskid"));
if (targetBox && targetBox.attr("taskid") != taskBox.attr("taskid")) {
var taskTo;
var taskFrom;
if (self.linkFromEnd) {
taskTo = self.master.getTask(targetBox.attr("taskid"));
taskFrom = self.master.getTask(taskBox.attr("taskid"));
} else {
taskFrom = self.master.getTask(targetBox.attr("taskid"));
taskTo = self.master.getTask(taskBox.attr("taskid"));
}
if (taskTo && taskFrom) {
var gap = 0;
var depInp = taskTo.rowElement.find("[name=depends]");
depInp.val(depInp.val() + ((depInp.val() + "").length > 0 ? "," : "") + (taskFrom.getRow() + 1) + (gap != 0 ? ":" + gap : ""));
depInp.blur();
}
}
})
});
}
//ask for redraw link
self.redrawLinks();
//prof.stop();
function _createTaskSVG(task, dimensions) {
var svg = self.svg;
var taskSvg = svg.svg(self.tasksGroup, dimensions.x, dimensions.y, dimensions.width, dimensions.height, {class:"taskBox taskBoxSVG taskStatusSVG", status:task.status, taskid:task.id });
//svg.title(taskSvg, task.name);
//external box
var layout = svg.rect(taskSvg, 0, 0, "100%", "100%", {class:"taskLayout", rx:"2", ry:"2"});
//svg.rect(taskSvg, 0, 0, "100%", "100%", {fill:"rgba(255,255,255,.3)"});
//external dep
if (task.hasExternalDep)
svg.rect(taskSvg, 0, 0, "100%", "100%", {fill:"url(#extDep)"});
//progress
if (task.progress > 0) {
var progress = svg.rect(taskSvg, 0, "20%", (task.progress > 100 ? 100 : task.progress) + "%", "60%", {rx:"2", ry:"2",fill:"rgba(0,0,0,.4)"});
if (dimensions.width > 50) {
var textStyle = {fill:"#888", "font-size":"10px",class:"textPerc teamworkIcons",transform:"translate(5)"};
if (task.progress > 100)
textStyle["font-weight"]="bold";
if (task.progress > 90)
textStyle.transform = "translate(-40)";
svg.text(taskSvg, (task.progress > 90 ? 100 : task.progress) + "%", (self.rowHeight-5)/2, (task.progress>100?"!!! ":"")+ task.progress + "%", textStyle);
}
}
if (task.hasChild)
svg.rect(taskSvg, 0, 0, "100%", 3, {fill:"#000"});
if (task.startIsMilestone) {
svg.image(taskSvg, -9, dimensions.height / 2 - 9, 18, 18, "/Content/jquerygantt/res/milestone.png")
}
if (task.endIsMilestone) {
svg.image(taskSvg, "100%", dimensions.height / 2 - 9, 18, 18, "/Content/jquerygantt/res/milestone.png", { transform: "translate(-9)" })
}
//task label
svg.text(taskSvg, "100%", 18, task.name, {class:"taskLabelSVG", transform:"translate(20,-5)"});
//link tool
if (task.level>0){
svg.circle(taskSvg, "0", dimensions.height/2,dimensions.height/3, {class:"taskLinkStartSVG linkHandleSVG", transform:"translate("+(-dimensions.height/3+1)+")"});
svg.circle(taskSvg, "100%",dimensions.height/2,dimensions.height/3, {class:"taskLinkEndSVG linkHandleSVG", transform:"translate("+(dimensions.height/3-1)+")"});
}
return taskSvg
}
};
Ganttalendar.prototype.addTask = function (task) {
//set new boundaries for gantt
this.originalEndMillis = this.originalEndMillis > task.end ? this.originalEndMillis : task.end;
this.originalStartMillis = this.originalStartMillis < task.start ? this.originalStartMillis : task.start;
};
//<%-------------------------------------- GANT DRAW LINK SVG ELEMENT --------------------------------------%>
//'from' and 'to' are tasks already drawn
Ganttalendar.prototype.drawLink = function (from, to, type) {
var self = this;
//console.debug("drawLink")
var peduncolusSize = 10;
/**
* Given an item, extract its rendered position
* width and height into a structure.
*/
function buildRect(item) {
var p = item.ganttElement.position();
var rect = {
left: parseFloat(item.ganttElement.attr("x")),
top: parseFloat(item.ganttElement.attr("y")),
width: parseFloat(item.ganttElement.attr("width")),
height:parseFloat(item.ganttElement.attr("height"))
};
return rect;
}
/**
* The default rendering method, which paints a start to end dependency.
*/
function drawStartToEnd(from, to, ps) {
var svg = self.svg;
//this function update an existing link
function update() {
var group = $(this);
var from = group.data("from");
var to = group.data("to");
var rectFrom = buildRect(from);
var rectTo = buildRect(to);
var fx1 = rectFrom.left;
var fx2 = rectFrom.left + rectFrom.width;
var fy = rectFrom.height / 2 + rectFrom.top;
var tx1 = rectTo.left;
var tx2 = rectTo.left + rectTo.width;
var ty = rectTo.height / 2 + rectTo.top;
var tooClose = tx1 < fx2 + 2 * ps;
var r = 5; //radius
var arrowOffset = 5;
var up = fy > ty;
var fup = up ? -1 : 1;
var prev = fx2 + 2 * ps > tx1;
var fprev = prev ? -1 : 1;
var image = group.find("image");
var p = svg.createPath();
if (tooClose) {
var firstLine = fup * (rectFrom.height / 2 - 2 * r + 2);
p.move(fx2, fy)
.line(ps, 0, true)
.arc(r, r, 90, false, !up, r, fup * r, true)
.line(0, firstLine, true)
.arc(r, r, 90, false, !up, -r, fup * r, true)
.line(fprev * 2 * ps + (tx1 - fx2), 0, true)
.arc(r, r, 90, false, up, -r, fup * r, true)
.line(0, (Math.abs(ty - fy) - 4 * r - Math.abs(firstLine)) * fup - arrowOffset, true)
.arc(r, r, 90, false, up, r, fup * r, true)
.line(ps, 0, true);
image.attr({x:tx1 - 5, y:ty - 5 - arrowOffset});
} else {
p.move(fx2, fy)
.line((tx1 - fx2) / 2 - r, 0, true)
.arc(r, r, 90, false, !up, r, fup * r, true)
.line(0, ty - fy - fup * 2 * r + arrowOffset, true)
.arc(r, r, 90, false, up, r, fup * r, true)
.line((tx1 - fx2) / 2 - r, 0, true);
image.attr({x:tx1 - 5, y:ty - 5 + arrowOffset});
}
group.find("path").attr({d:p.path()});
}
// create the group
var group = svg.group(self.linksGroup, "" + from.id + "-" + to.id);
svg.title(group, from.name + " -> " + to.name);
var p = svg.createPath();
//add the arrow
svg.image(group, 0, 0, 5, 10, "/Content/jquerygantt/res/linkArrow.png");
//create empty path
svg.path(group, p, {class:"taskLinkPathSVG"});
//set "from" and "to" to the group, bind "update" and trigger it
var jqGroup = $(group).data({from:from, to:to }).attr({from:from.id, to:to.id}).on("update", update).trigger("update");
if (self.showCriticalPath && from.isCritical && to.isCritical)
jqGroup.addClass("critical");
jqGroup.addClass("linkGroup");
return jqGroup;
}
/**
* A rendering method which paints a start to start dependency.
*/
function drawStartToStart(from, to) {
console.error("StartToStart not supported on SVG");
var rectFrom = buildRect(from);
var rectTo = buildRect(to);
}
var link;
// Dispatch to the correct renderer
if (type == 'start-to-start') {
link = drawStartToStart(from, to, peduncolusSize);
} else {
link = drawStartToEnd(from, to, peduncolusSize);
}
if (this.master.permissions.canWrite && (from.canWrite || to.canWrite)) {
link.click(function (e) {
var el = $(this);
e.stopPropagation();// to avoid body remove focused
self.element.find("[class*=focused]").removeClass("focused");
$(".ganttSVGBox .focused").removeClass("focused");
var el = $(this);
if (!self.resDrop)
el.addClass("focused");
self.resDrop = false; //hack to avoid select
$("body").off("click.focused").one("click.focused", function () {
$(".ganttSVGBox .focused").removeClass("focused");
})
});
}
};
Ganttalendar.prototype.redrawLinks = function () {
//console.debug("redrawLinks ");
var self = this;
this.element.stopTime("ganttlnksredr");
this.element.oneTime(60, "ganttlnksredr", function () {
//var prof=new Profiler("gd_drawLink_real");
//remove all links
$("#linksSVG").empty();
var collapsedDescendant = [];
//[expand]
var collapsedDescendant = self.master.getCollapsedDescendant();
for (var i = 0; i < self.master.links.length; i++) {
var link = self.master.links[i];
if (collapsedDescendant.indexOf(link.from) >= 0 || collapsedDescendant.indexOf(link.to) >= 0) continue;
self.drawLink(link.from, link.to);
}
//prof.stop();
});
};
Ganttalendar.prototype.reset = function () {
this.element.find("[class*=linkGroup]").remove();
this.element.find("[taskid]").remove();
};
Ganttalendar.prototype.redrawTasks = function () {
//[expand]
var collapsedDescendant = this.master.getCollapsedDescendant();
for (var i = 0; i < this.master.tasks.length; i++) {
var task = this.master.tasks[i];
if (collapsedDescendant.indexOf(task) >= 0) continue;
this.drawTask(task);
}
};
Ganttalendar.prototype.refreshGantt = function () {
//console.debug("refreshGantt")
if (this.showCriticalPath) {
this.master.computeCriticalPath();
}
var par = this.element.parent();
//try to maintain last scroll
var scrollY = par.scrollTop();
var scrollX = par.scrollLeft();
this.element.remove();
//guess the zoom level in base of period
if (!this.zoom) {
var days = Math.round((this.originalEndMillis - this.originalStartMillis) / (3600000 * 24));
//"d", "w","w2","w3", "m","m2", "q", "s", "y"
this.zoom = this.zoomLevels[days < 2 ? 0 : (days < 15 ? 1 : (days < 30 ? 2 : (days < 45 ? 3 : (days < 60 ? 4 : (days < 90 ? 5 : (days < 180 ? 6 : (days < 600 ? 7 : 8 ) ) ) ) ) ) )];
}
var domEl = this.create(this.zoom, this.originalStartMillis, this.originalEndMillis);
this.element = domEl;
par.append(domEl);
this.redrawTasks();
//set old scroll
//console.debug("old scroll:",scrollX,scrollY)
par.scrollTop(scrollY);
par.scrollLeft(scrollX);
//set current task
this.synchHighlight();
};
Ganttalendar.prototype.fitGantt = function () {
delete this.zoom;
this.refreshGantt();
};
Ganttalendar.prototype.synchHighlight = function () {
//console.debug("synchHighlight",this.master.currentTask);
if (this.master.currentTask ){
// take care of collapsed rows
var ganttHighLighterPosition=this.master.editor.element.find(".taskEditRow:visible").index(this.master.currentTask.rowElement);
this.master.gantt.element.find(".ganttLinesSVG").removeClass("rowSelected").eq(ganttHighLighterPosition).addClass("rowSelected");
} else {
$(".rowSelected").removeClass("rowSelected"); // todo non c'era
}
};
Ganttalendar.prototype.getCenterMillis= function () {
return parseInt((this.element.parent().scrollLeft()+this.element.parent().width()/2)/this.fx+this.startMillis);
};
Ganttalendar.prototype.goToMillis= function (millis) {
var x = Math.round(((millis) - this.startMillis) * this.fx) -this.element.parent().width()/2;
this.element.parent().scrollLeft(x);
};
Ganttalendar.prototype.centerOnToday = function () {
this.goToMillis(new Date().getTime());
};
/**
* Allows drag and drop and extesion of task boxes. Only works on x axis
* @param opt
* @return {*}
*/
$.fn.dragExtedSVG = function (svg, opt) {
//doing this can work with one svg at once only
var target;
var svgX;
var offsetMouseRect;
var options = {
canDrag: true,
canResize: true,
resizeZoneWidth:10,
minSize: 10,
startDrag: function (e) {},
drag: function (e) {},
drop: function (e) {},
startResize: function (e) {},
resize: function (e) {},
stopResize: function (e) {}
};
$.extend(options, opt);
this.each(function () {
var el = $(this);
svgX = svg.parent().offset().left; //parent is used instead of svg for a Firefox oddity
if (options.canDrag)
el.addClass("deSVGdrag");
if (options.canResize || options.canDrag) {
el.bind("mousedown.deSVG",function (e) {
//console.debug("mousedown.deSVG");
if ($(e.target).is("image")) {
e.preventDefault();
}
target = $(this);
var x1 = parseFloat(el.find("[class*=taskLayout]").offset().left);
var x2 = x1 + parseFloat(el.attr("width"));
var posx = e.pageX;
$("body").unselectable();
//start resize end
if (options.canResize && (x2-x1)>3*options.resizeZoneWidth && (posx<=x2 && posx >= x2- options.resizeZoneWidth)) {
//store offset mouse x2
offsetMouseRect = x2 - e.pageX;
target.attr("oldw", target.attr("width"));
var one = true;
//bind event for start resizing
$(svg).bind("mousemove.deSVG", function (e) {
if (one) {
//trigger startResize
options.startResize.call(target.get(0), e);
one = false;
}
//manage resizing
var nW = e.pageX - x1 + offsetMouseRect;
target.attr("width", nW < options.minSize ? options.minSize : nW);
//callback
options.resize.call(target.get(0), e);
});
//bind mouse up on body to stop resizing
$("body").one("mouseup.deSVG", stopResize);
//start resize start
} else if (options.canResize && (x2-x1)>3*options.resizeZoneWidth && (posx>=x1 && posx<=x1+options.resizeZoneWidth)) {
//store offset mouse x1
offsetMouseRect = parseFloat(target.attr("x"));
target.attr("oldw", target.attr("width")); //todo controllare se รจ ancora usato oldw
var one = true;
//bind event for start resizing
$(svg).bind("mousemove.deSVG", function (e) {
if (one) {
//trigger startResize
options.startResize.call(target.get(0), e);
one = false;
}
//manage resizing
var nx1= offsetMouseRect-(posx-e.pageX);
var nW = (x2-x1) + (posx-e.pageX);
nW=nW < options.minSize ? options.minSize : nW;
target.attr("x",nx1);
target.attr("width", nW);
//callback
options.resize.call(target.get(0), e);
});
//bind mouse up on body to stop resizing
$("body").one("mouseup.deSVG", stopResize);
// start drag
} else if (options.canDrag) {
//store offset mouse x1
offsetMouseRect = parseFloat(target.attr("x")) - e.pageX;
target.attr("oldx", target.attr("x"));
var one = true;
//bind event for start dragging
$(svg).bind("mousemove.deSVG",function (e) {
if (one) {
//trigger startDrag
options.startDrag.call(target.get(0), e);
one = false;
}
//manage resizing
target.attr("x", offsetMouseRect + e.pageX);
//callback
options.drag.call(target.get(0), e);
}).bind("mouseleave.deSVG", drop);
//bind mouse up on body to stop resizing
$("body").one("mouseup.deSVG", drop);
}
}
).bind("mousemove.deSVG",
function (e) {
var el = $(this);
var x1 = el.find("[class*=taskLayout]").offset().left;
var x2 = x1 + parseFloat(el.attr("width"));
var posx = e.pageX;
//set cursor handle
if (options.canResize && (x2-x1)>3*options.resizeZoneWidth &&((posx<=x2 && posx >= x2- options.resizeZoneWidth) || (posx>=x1 && posx<=x1+options.resizeZoneWidth))) {
el.addClass("deSVGhand");
} else {
el.removeClass("deSVGhand");
}
}
).addClass("deSVG");
}
});
return this;
function stopResize(e) {
$(svg).unbind("mousemove.deSVG").unbind("mouseup.deSVG").unbind("mouseleave.deSVG");
if (target && target.attr("oldw")!=target.attr("width"))
options.stopResize.call(target.get(0), e); //callback
target = undefined;
$("body").clearunselectable();
}
function drop(e) {
$(svg).unbind("mousemove.deSVG").unbind("mouseup.deSVG").unbind("mouseleave.deSVG");
if (target && target.attr("oldx") != target.attr("x"))
options.drop.call(target.get(0), e); //callback
target = undefined;
$("body").clearunselectable();
}
};