Fix connectors to work with foreignObject

Alexis got this working on SVG-Edit trunk.
This commit is contained in:
Jacques Distler 2010-02-18 10:12:44 -06:00
parent 08cd194330
commit 2dfb852727
5 changed files with 95 additions and 84 deletions

View file

@ -74,70 +74,44 @@ $(function() {
} }
function findConnectors() { function findConnectors() {
// Check if selected elements have connections
var elems = selElems; var elems = selElems;
var i = elems.length;
var connectors = $(svgcontent).find(conn_sel); var connectors = $(svgcontent).find(conn_sel);
if(!connectors.length) return;
connections = []; connections = [];
var sel = ':not(a,g,svg,'+conn_sel+')';
var all_els = [];
// Get children from groups
while(i--) {
var elem = elems[i];
if(!elem) continue;
// Get all children that cannot contain children
var solid_elems = $(elem).find(sel);
// Include self if okay
if($(elem).filter(sel).length) {
solid_elems.push(elem);
}
$.merge(all_els, solid_elems);
}
i = all_els.length;
if(i > 1) {
// Multiselected, so unselect connector
svgCanvas.removeFromSelection($(conn_sel).toArray());
}
// Loop through selected elements
while(i--) {
var elem = all_els[i];
if(!elem) continue;
// Element was deleted, so delete connector
var was_deleted = !elem.parentNode;
// Skip connector
if($(elem).data('c_start')) continue;
// Loop through connectors to see if one is connected to the element // Loop through connectors to see if one is connected to the element
connectors.each(function() { connectors.each(function() {
var start = $(this).data("c_start"); var start = $(this).data("c_start");
var end = $(this).data("c_end"); var end = $(this).data("c_end");
// Connector found for this element var parts = [getElem(start), getElem(end)];
if(start == elem.id || end == elem.id) { for(var i=0; i<2; i++) {
var c_elem = parts[i];
if(was_deleted) { var add_this = false;
$(this).remove(); // The connected element might be part of a selected group
return; $(c_elem).parents().each(function() {
if($.inArray(this, elems) !== -1) {
// Pretend this element is selected
add_this = true;
} }
var bb = svgCanvas.getStrokedBBox([elem]); });
if(!c_elem || !c_elem.parentNode) {
$(this).remove();
continue;
}
if($.inArray(c_elem, elems) !== -1 || add_this) {
var bb = svgCanvas.getStrokedBBox([c_elem]);
connections.push({ connections.push({
elem: elem, elem: c_elem,
connector: this, connector: this,
is_start: start == elem.id, is_start: (i === 0),
start_x: bb.x, start_x: bb.x,
start_y: bb.y start_y: bb.y
}); });
} }
});
} }
});
} }
function updateConnectors() { function updateConnectors() {
@ -300,7 +274,6 @@ $(function() {
}, },
mouseDown: function(opts) { mouseDown: function(opts) {
var e = opts.event; var e = opts.event;
start_x = opts.start_x, start_x = opts.start_x,
start_y = opts.start_y; start_y = opts.start_y;
var mode = svgCanvas.getMode(); var mode = svgCanvas.getMode();
@ -316,7 +289,9 @@ $(function() {
if($.inArray(svgcontent, parents) != -1) { if($.inArray(svgcontent, parents) != -1) {
// Connectable element // Connectable element
start_elem = mouse_target; // If child of foreignObject, use parent
var fo = $(mouse_target).closest("foreignObject");
start_elem = fo.length ? fo[0] : mouse_target;
// Get center of source element // Get center of source element
var bb = svgCanvas.getStrokedBBox([start_elem]); var bb = svgCanvas.getStrokedBBox([start_elem]);
@ -422,10 +397,13 @@ $(function() {
var zoom = svgCanvas.getZoom(); var zoom = svgCanvas.getZoom();
var e = opts.event, var e = opts.event,
x = opts.mouse_x/zoom, x = opts.mouse_x/zoom,
y = opts.mouse_y/zoom; y = opts.mouse_y/zoom,
mouse_target = e.target;
if(svgCanvas.getMode() == "connector") { if(svgCanvas.getMode() == "connector") {
if(e.target.parentNode.parentNode != svgcontent) { var fo = $(mouse_target).closest("foreignObject");
if(fo.length) mouse_target = fo[0];
if(mouse_target.parentNode.parentNode != svgcontent) {
// Not a valid target element, so remove line // Not a valid target element, so remove line
$(cur_line).remove(); $(cur_line).remove();
started = false; started = false;
@ -434,7 +412,7 @@ $(function() {
element: null, element: null,
started: started started: started
} }
} else if(e.target == start_elem) { } else if(mouse_target == start_elem) {
// Start line through click // Start line through click
started = true; started = true;
return { return {
@ -444,7 +422,8 @@ $(function() {
} }
} else { } else {
// Valid end element // Valid end element
end_elem = e.target; end_elem = mouse_target;
var start_id = start_elem.id, end_id = end_elem.id; var start_id = start_elem.id, end_id = end_elem.id;
var conn_str = start_id + " " + end_id; var conn_str = start_id + " " + end_id;
var alt_str = end_id + " " + start_id; var alt_str = end_id + " " + start_id;

View file

@ -63,7 +63,6 @@ $(function() {
var newDoc = Utils.text2xml('<svg xmlns="'+svgns+'" xmlns:xlink="'+xlinkns+'">'+xmlString+'</svg>'); var newDoc = Utils.text2xml('<svg xmlns="'+svgns+'" xmlns:xlink="'+xlinkns+'">'+xmlString+'</svg>');
// run it through our sanitizer to remove anything we do not support // run it through our sanitizer to remove anything we do not support
S.sanitizeSvg(newDoc.documentElement); S.sanitizeSvg(newDoc.documentElement);
elt.parentNode.replaceChild(svgdoc.importNode(newDoc.documentElement.firstChild, true), elt); elt.parentNode.replaceChild(svgdoc.importNode(newDoc.documentElement.firstChild, true), elt);
S.call("changed", [elt]); S.call("changed", [elt]);
svgCanvas.clearSelection(); svgCanvas.clearSelection();

View file

@ -45,7 +45,6 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
<title>SVG-edit</title> <title>SVG-edit</title>
</head> </head>
<body> <body>
<div id="svg_editor"> <div id="svg_editor">
<div id="workarea"> <div id="workarea">

View file

@ -1385,6 +1385,11 @@ function BatchCommand(text) {
} }
} }
// Safari crashes on a <use> without a xlink:href, so we just remove the node here
if (node.nodeName == "use" && !node.getAttributeNS(xlinkns,"href")) {
parent.removeChild(node);
return;
}
// if the element has attributes pointing to a non-local reference, // if the element has attributes pointing to a non-local reference,
// need to remove the attribute // need to remove the attribute
$.each(["clip-path", "fill", "marker-end", "marker-mid", "marker-start", "mask", "stroke"],function(i,attr) { $.each(["clip-path", "fill", "marker-end", "marker-mid", "marker-start", "mask", "stroke"],function(i,attr) {
@ -2647,6 +2652,7 @@ function BatchCommand(text) {
}; };
var hasMatrixTransform = function(tlist) { var hasMatrixTransform = function(tlist) {
if(!tlist) return false;
var num = tlist.numberOfItems; var num = tlist.numberOfItems;
while (num--) { while (num--) {
var xform = tlist.getItem(num); var xform = tlist.getItem(num);
@ -3083,11 +3089,13 @@ function BatchCommand(text) {
start_x: start_x, start_x: start_x,
start_y: start_y, start_y: start_y,
selectedElements: selectedElements selectedElements: selectedElements
}); }, true);
if(ext_result) { $.each(ext_result, function(i, r) {
started = ext_result.started; if(r && r.started) {
started = true;
} }
});
}; };
// in this function we do not record any state changes yet (but we do update // in this function we do not record any state changes yet (but we do update
@ -3604,13 +3612,15 @@ function BatchCommand(text) {
event: evt, event: evt,
mouse_x: mouse_x, mouse_x: mouse_x,
mouse_y: mouse_y mouse_y: mouse_y
}); }, true);
if(ext_result) { $.each(ext_result, function(i, r) {
keep = ext_result.keep; if(r) {
element = ext_result.element; keep = r.keep || keep;
started = ext_result.started; element = r.element;
started = r.started || started;
} }
});
if (!keep && element != null) { if (!keep && element != null) {
element.parentNode.removeChild(element); element.parentNode.removeChild(element);
@ -5618,28 +5628,33 @@ function BatchCommand(text) {
var content = $(svgcontent); var content = $(svgcontent);
var attrs = {
id: 'svgcontent',
overflow: 'visible'
};
// determine proper size // determine proper size
var w, h;
if (content.attr("viewBox")) { if (content.attr("viewBox")) {
var vb = content.attr("viewBox").split(' '); var vb = content.attr("viewBox").split(' ');
w = vb[2]; attrs.width = vb[2];
h = vb[3]; attrs.height = vb[3];
} }
// handle content that doesn't have a viewBox // handle content that doesn't have a viewBox
else { else {
var dims = content.attr(["width", "height"]); $.each(['width', 'height'], function(i, dim) {
w = convertToNum('width', dims.width); // Set to 100 if not given
h = convertToNum('height', dims.height); var val = content.attr(dim) || 100;
// svgcontent.setAttribute("viewBox", ["0", "0", w, h].join(" "));
if((val+'').substr(-1) === "%") {
// Use user units if percentage given
attrs[dim] = parseInt(val);
} else {
attrs[dim] = convertToNum(dim, val);
}
});
} }
content.attr({ content.attr(attrs);
id: 'svgcontent',
width: w,
height: h,
overflow: 'visible'
});
batchCmd.addSubCommand(new InsertElementCommand(svgcontent)); batchCmd.addSubCommand(new InsertElementCommand(svgcontent));
// update root to the correct size // update root to the correct size
var changes = content.attr(["width", "height"]); var changes = content.attr(["width", "height"]);
@ -6602,7 +6617,19 @@ function BatchCommand(text) {
ret.y += parseFloat(selected.getAttribute('y')); ret.y += parseFloat(selected.getAttribute('y'));
} else { } else {
try { ret = selected.getBBox(); } try { ret = selected.getBBox(); }
catch(e) { ret = null; } catch(e) {
// Check if element is child of a foreignObject
var fo = $(selected).closest("foreignObject");
if(fo.length) {
try {
ret = fo[0].getBBox();
} catch(e) {
ret = null;
}
} else {
ret = null;
}
}
} }
// get the bounding box from the DOM (which is in that element's coordinate system) // get the bounding box from the DOM (which is in that element's coordinate system)
@ -7326,6 +7353,7 @@ function BatchCommand(text) {
// re-creating the getCheckedBBox() function? shouldn't we make this a function // re-creating the getCheckedBBox() function? shouldn't we make this a function
// at the 'canvas' level // at the 'canvas' level
var getCheckedBBox = function(elem) { var getCheckedBBox = function(elem) {
try { try {
// TODO: Fix issue with rotated groups. Currently they work // TODO: Fix issue with rotated groups. Currently they work
// fine in FF, but not in other browsers (same problem mentioned // fine in FF, but not in other browsers (same problem mentioned
@ -7399,7 +7427,10 @@ function BatchCommand(text) {
} }
return bb; return bb;
} catch(e) { return null; } } catch(e) {
console.log(elem, e);
return null;
}
} }
var full_bb; var full_bb;
@ -7565,7 +7596,7 @@ function BatchCommand(text) {
new_el.appendChild(copyElem(child)); new_el.appendChild(copyElem(child));
break; break;
case 3: // text node case 3: // text node
new_el.appendChild(child.cloneNode(false)); new_el.textContent = child.nodeValue;
break; break;
default: default:
break; break;
@ -7723,7 +7754,7 @@ function BatchCommand(text) {
// Function: getVersion // Function: getVersion
// Returns a string which describes the revision number of SvgCanvas. // Returns a string which describes the revision number of SvgCanvas.
this.getVersion = function() { this.getVersion = function() {
return "svgcanvas.js ($Rev: 1404 $)"; return "svgcanvas.js ($Rev: 1409 $)";
}; };
this.setUiStrings = function(strs) { this.setUiStrings = function(strs) {

View file

@ -124,19 +124,22 @@
module("Import Module"); module("Import Module");
test("Test import use", function() { test("Test import use", function() {
expect(2); expect(3);
svgCanvas.setSvgString("<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='400' x='300'>" + svgCanvas.setSvgString("<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='400' x='300'>" +
"<rect id='the-rect' width='200' height='200'/>" + "<rect id='the-rect' width='200' height='200'/>" +
"<use id='the-use' xlink:href='#the-rect'/>" + "<use id='the-use' xlink:href='#the-rect'/>" +
"<use id='foreign-use' xlink:href='somefile.svg#the-rect'/>" + "<use id='foreign-use' xlink:href='somefile.svg#the-rect'/>" +
"<use id='no-use'/>" +
"</svg>"); "</svg>");
var u = document.getElementById("the-use"), var u = document.getElementById("the-use"),
fu = document.getElementById("foreign-use"); fu = document.getElementById("foreign-use"),
nfu = document.getElementById("no-use");
equals((u && u.nodeName == "use"), true, "Did not import <use> element"); equals((u && u.nodeName == "use"), true, "Did not import <use> element");
equals((fu && !fu.getAttributeNS(xlinkns,"href")), true, "Did not remove reference to foreign element in <use>"); equals(fu, null, "Removed <use> element that had a foreign href");
equals(nfu, null, "Removed <use> element that had no href");
}); });
test("Test getUrlFromAttr", function() { test("Test getUrlFromAttr", function() {