Skip to content

Instantly share code, notes, and snippets.

@nrafter
Created February 1, 2015 06:04
Show Gist options
  • Save nrafter/c3dc961fc2b2602d004e to your computer and use it in GitHub Desktop.
Save nrafter/c3dc961fc2b2602d004e to your computer and use it in GitHub Desktop.
var mouseUp section of event loop in svgcanvas.js
var mouseUp = function(evt) {
canvas.addClones = false;
window.removeEventListener("keyup", canvas.removeClones)
selectedElements = selectedElements.filter(Boolean)
if(evt.button === 2) {
return;
}
var tempJustSelected = justSelected;
justSelected = null;
if (!started) {
return;
}
var pt = transformPoint( evt.pageX, evt.pageY, root_sctm ),
mouse_x = pt.x * current_zoom,
mouse_y = pt.y * current_zoom,
x = mouse_x / current_zoom,
y = mouse_y / current_zoom,
element = getElem(getId());
//,NSR issue 123: let's keep elements by default. also, this could break something
//keep = false;
var real_x = x;
var real_y = y;
// TODO: Make true when in multi-unit mode
var useUnit = false; // (curConfig.baseUnit !== 'px');
started = false;
switch (current_mode) {
// intentionally fall-through to select here
case "bounds":
var menu_items = $('#cmenu_canvas li');
var selected = canvas.getSelectedElems();
centerInBounds(canvas.getBoundingBoxElems(selected, rubberBox));
call("changed", selected);
if (rubberBox != null) {
rubberBox.setAttribute("display", "none");
curBBoxes = [];
}
keep = true;
menu_items.enableContextMenuItems('#create_bounds,#view_bounds,#delete_bounds');
window.prevMode = 'bounds';
canvas.setMode('select');
break;
case "resize":
if (startedElements) {
endedElements = _.map(canvas.getSelectedElems(), function(e){ return _.extend(createObjectNode(e), canvas.getBBox(e), { rotationAngle: canvas.getRotationAngle(e) }); });
logChanged('resize', startedElements, endedElements);
startedElements = null;
}
case "multiselect":
if (rubberBox != null) {
rubberBox.setAttribute("display", "none");
curBBoxes = [];
}
current_mode = "select";
case "select":
if (selectedElements[0] != null) {
// if we only have one selected element
if (selectedElements.length == 1) {
// set our current stroke/fill properties to the element's
var selected = selectedElements[0];
switch ( selected.tagName ) {
case "g":
case "use":
case "image":
case "foreignObject":
break;
default:
cur_properties.fill = selected.getAttribute("fill");
cur_properties.fill_opacity = selected.getAttribute("fill-opacity");
cur_properties.stroke = selected.getAttribute("stroke");
cur_properties.stroke_opacity = selected.getAttribute("stroke-opacity");
cur_properties.stroke_width = selected.getAttribute("stroke-width");
cur_properties.stroke_dasharray = selected.getAttribute("stroke-dasharray");
cur_properties.stroke_linejoin = selected.getAttribute("stroke-linejoin");
cur_properties.stroke_linecap = selected.getAttribute("stroke-linecap");
}
if (selected.tagName == "text") {
cur_text.font_size = selected.getAttribute("font-size");
cur_text.font_family = selected.getAttribute("font-family");
}
selectorManager.requestSelector(selected).showGrips(true);
// This shouldn't be necessary as it was done on mouseDown...
// call("selected", [selected]);
}
// always recalculate dimensions to strip off stray identity transforms
recalculateAllSelectedDimensions();
//NSR alignment: assuming testing will only happen with a single selected element for now, while building
console.log("Updating alignmenat from select...");
canvas.updateAlignment(selectedElements[0]);
// if it was being dragged/resized
var isBotchedZoom = svgedit.browser.isGecko();
r_start_x = isBotchedZoom ? r_start_x * current_zoom : r_start_x;
r_start_y = isBotchedZoom ? r_start_y * current_zoom : r_start_y;
var difference_x = Math.abs(real_x-r_start_x);
var difference_y = Math.abs(real_y-r_start_y);
if (difference_y > 1 || difference_y > 1) {
var len = selectedElements.length;
for (var i = 0; i < len; ++i) {
if (selectedElements[i] == null) break;
if(!selectedElements[i].firstChild) {
// Not needed for groups (incorrectly resizes elems), possibly not needed at all?
selectorManager.requestSelector(selectedElements[i]).resize();
}
}
if(startedElements) {
endedElements = _.map(canvas.getSelectedElems(), function(e){ return _.extend(createObjectNode(e), canvas.getBBox(e), { rotationAngle: canvas.getRotationAngle(e) }); });
logChanged('move', startedElements, endedElements);
startedElements = null;
}
}
// no change in position/size, so maybe we should move to pathedit
else {
var t = evt.target;
if (selectedElements[0].nodeName === "path" && selectedElements[1] == null) {
//NSR: don't need to drop into pathEdit, also fixes clicking too many times tries to replace elements
// pathActions.select(selectedElements[0]);
} // if it was a path
// else, if it was selected and this is a shift-click, remove it from selection
else if (evt.shiftKey) {
if(tempJustSelected != t) {
canvas.removeFromSelection([t]);
}
}
} // no change in mouse position
// Remove non-scaling stroke
if(svgedit.browser.supportsNonScalingStroke()) {
var elem = selectedElements[0];
if (elem) {
elem.removeAttribute('style');
svgedit.utilities.walkTree(elem, function(elem) {
elem.removeAttribute('style');
});
}
}
}
//return;
break;
case "zoom":
if (rubberBox != null) {
rubberBox.setAttribute("display", "none");
}
var factor = evt.altKey?.5:2;
call("zoomed", {
'x': Math.min(r_start_x, real_x),
'y': Math.min(r_start_y, real_y),
'width': Math.abs(real_x - r_start_x),
'height': Math.abs(real_y - r_start_y),
'factor': factor
});
return;
case "fhpath":
// Check that the path contains at least 2 points; a degenerate one-point path
// causes problems.
// Webkit ignores how we set the points attribute with commas and uses space
// to separate all coordinates, see https://bugs.webkit.org/show_bug.cgi?id=29870
var coords = element.getAttribute('points');
var commaIndex = coords.indexOf(',');
if (commaIndex >= 0) {
keep = coords.indexOf(',', commaIndex+1) >= 0;
} else {
keep = coords.indexOf(' ', coords.indexOf(' ')+1) >= 0;
}
if (keep) {
element = pathActions.smoothPolylineIntoPath(element);
}
logCreated(element);
break;
case "line":
var attrs = $(element).attr(["x1", "x2", "y1", "y2"]);
keep = (attrs.x1 != attrs.x2 || attrs.y1 != attrs.y2);
logCreated(element);
break;
case "foreignObject":
case "square":
case "rect":
case "image":
var attrs = $(element).attr(["width", "height"]);
// Image should be kept regardless of size (use inherit dimensions later)
keep = (attrs.width != 0 || attrs.height != 0) || current_mode === "image";
//NSR alignment
canvas.addToAlignment(element);
//console.log("Adding initial FSRI element... ", element);
logCreated(element);
break;
case "circle":
keep = (element.getAttribute('r') != 0);
logCreated(element);
//NSR alignment
canvas.addToAlignment(element);
//console.log("Adding initial circle element... ", element);
break;
case "ellipse":
var attrs = $(element).attr(["rx", "ry"]);
keep = (attrs.rx != null || attrs.ry != null);
logCreated(element);
//NSR alignment
canvas.addToAlignment(element);
//console.log("Adding initial ellipse element... ", element);
break;
case "fhellipse":
if ((freehand.maxx - freehand.minx) > 0 &&
(freehand.maxy - freehand.miny) > 0) {
element = addSvgElementFromJson({
"element": "ellipse",
"curStyles": true,
"attr": {
"cx": (freehand.minx + freehand.maxx) / 2,
"cy": (freehand.miny + freehand.maxy) / 2,
"rx": (freehand.maxx - freehand.minx) / 2,
"ry": (freehand.maxy - freehand.miny) / 2,
"id": getId()
}
});
call("changed",[element]);
keep = true;
}
logCreated(element);
break;
case "fhrect":
if ((freehand.maxx - freehand.minx) > 0 &&
(freehand.maxy - freehand.miny) > 0) {
element = addSvgElementFromJson({
"element": "rect",
"curStyles": true,
"attr": {
"x": freehand.minx,
"y": freehand.miny,
"width": (freehand.maxx - freehand.minx),
"height": (freehand.maxy - freehand.miny),
"id": getId()
}
});
call("changed",[element]);
keep = true;
}
logCreated(element);
break;
case "text":
keep = true;
selectOnly([element]);
textActions.start(element);
//NSR alignment
canvas.addToAlignment(element);
//console.log("Adding initial text element... ", element);
break;
case "path":
// set element to null here so that it is not removed nor finalized
element = null;
// continue to be set to true so that mouseMove happens
started = true;
var res = pathActions.mouseUp(evt, element, mouse_x, mouse_y);
element = res.element;
keep = res.keep;
logCreated(element);
//NSR alignment
canvas.addToAlignment(element);
//console.log("Adding initial path element... ", element);
break;
case "pathedit":
keep = true;
element = null;
pathActions.mouseUp(evt);
//NSR alignment
canvas.addToAlignment(element);
//console.log("Adding initial pathedit element... ", element);
break;
case "textedit":
keep = false;
element = null;
textActions.mouseUp(evt, mouse_x, mouse_y);
break;
case "rotate":
keep = true;
element = null;
current_mode = "select";
var batchCmd = canvas.undoMgr.finishUndoableChange();
if (!batchCmd.isEmpty()) {
addCommandToHistory(batchCmd);
}
// perform recalculation to weed out any stray identity transforms that might get stuck
recalculateAllSelectedDimensions();
call("changed", selectedElements);
// TODO: WSL Mark
if(startedElements) {
endedElements = _.map(canvas.getSelectedElems(), function(e){ return _.extend(createObjectNode(e), canvas.getBBox(e), { rotationAngle: canvas.getRotationAngle(e) }); });
logChanged('rotate', startedElements, endedElements);
startedElements = null;
}
break;
default:
// This could occur in an extension
break;
}
var ext_result = runExtensions("mouseUp", {
event: evt,
mouse_x: mouse_x,
mouse_y: mouse_y
}, true);
$.each(ext_result, function(i, r) {
if(r) {
keep = r.keep || keep;
element = r.element;
started = r.started || started;
}
});
if( !keep && element != null && element.parentNode != null ) {
// WSL Testing;
if( evt == null || evt.target == null) {
console.log("t NULL for ", element);
}
getCurrentDrawing().releaseId(getId());
element.parentNode.removeChild(element);
element = null;
var t = evt.target;
// if this element is in a group, go up until we reach the top-level group
// just below the layer groups
// TODO: once we implement links, we also would have to check for <a> elements
while( t.parentNode.parentNode.tagName == "g" ) {
t = t.parentNode;
}
// if we are not in the middle of creating a path, and we've clicked on some shape,
// then go to Select mode.
// WebKit returns <div> when the canvas is clicked, Firefox/Opera return <svg>
if ( (current_mode != "path" || !drawn_path) &&
t.parentNode.id != "selectorParentGroup" &&
t.id != "svgcanvas" && t.id != "svgroot")
{
// switch into "select" mode if we've clicked on an element
canvas.setMode("select");
selectOnly([t], true);
}
//NSR issue 123: confirmed it broke stuff, an item that was selected when the canvas is clicked is treated
//as a new element and reselected immediately right here
} else if (element != null && current_mode !== "select") {
canvas.addedNew = true;
if(useUnit) svgedit.units.convertAttrs(element);
var ani_dur = .2, c_ani;
if(opac_ani.beginElement && element.getAttribute('opacity') != cur_shape.opacity) {
c_ani = $(opac_ani).clone().attr({
to: cur_shape.opacity,
dur: ani_dur
}).appendTo(element);
try {
// Fails in FF4 on foreignObject
c_ani[0].beginElement();
} catch(e){}
} else {
ani_dur = 0;
}
// Ideally this would be done on the endEvent of the animation,
// but that doesn't seem to be supported in Webkit
setTimeout(function() {
if(c_ani) c_ani.remove();
element.setAttribute("opacity", cur_shape.opacity);
element.setAttribute("style", "pointer-events:inherit");
cleanupElement(element);
if(current_mode === "path") {
pathActions.toEditMode(element);
} else {
if(curConfig.selectNew) {
selectOnly([element], true);
}
}
// we create the insert command that is stored on the stack
// undo means to call cmd.unapply(), redo means to call cmd.apply()
addCommandToHistory(new InsertElementCommand(element));
call("changed",[element]);
}, ani_dur * 1000);
}
start_transform = null;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment