Sync with latest SVG-Edit

Fixes Issue 502 (Connector
lines are not re-editable).
This commit is contained in:
Jacques Distler 2010-02-26 14:51:27 -06:00
parent f7f1668e31
commit 8a851eeab2
5 changed files with 927 additions and 1827 deletions

View file

@ -228,8 +228,12 @@ $(function() {
if(conn) {
this.setAttribute('class', conn_sel.substr(1));
var conn_data = conn.split(' ');
var sbb = svgCanvas.getStrokedBBox([getElem(conn_data[0])]);
var ebb = svgCanvas.getStrokedBBox([getElem(conn_data[1])]);
$(this).data('c_start',conn_data[0])
.data('c_end',conn_data[1]);
.data('c_end',conn_data[1])
.data('start_bb', sbb)
.data('end_bb', ebb);
svgCanvas.getEditorNS(true);
}
});
@ -311,7 +315,6 @@ $(function() {
"style": "pointer-events:none"
}
});
$(cur_line).data('start_bb', bb);
}
return {

View file

@ -246,7 +246,8 @@ function svg_edit_setup() {
}
// Update selectedElement if element is no longer part of the image.
// This occurs for the text elements in Firefox
else if(elem && selectedElement && selectedElement.parentNode == null) {
else if(elem && selectedElement && selectedElement.parentNode == null
|| elem && elem.tagName == "path") {
selectedElement = elem;
}
}
@ -738,7 +739,6 @@ function svg_edit_setup() {
if(elem != null && !elem.parentNode) elem = null;
var currentLayer = svgCanvas.getCurrentLayer();
var currentMode = svgCanvas.getMode();
// No need to update anything else in rotate mode
if (currentMode == 'rotate' && elem != null) {
var ang = svgCanvas.getRotationAngle(elem);
@ -747,7 +747,6 @@ function svg_edit_setup() {
return;
}
var is_node = currentMode == 'pathedit'; //elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false;
$('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,\
#ellipse_panel, #line_panel, #text_panel, #image_panel').hide();
if (elem != null) {
@ -2779,7 +2778,7 @@ function svg_edit_setup() {
updateCanvas(true);
});
// var revnums = "svg-editor.js ($Rev: 1423 $) ";
// var revnums = "svg-editor.js ($Rev: 1430 $) ";
// revnums += svgCanvas.getVersion();
// $('#copyright')[0].setAttribute("title", revnums);
return svgCanvas;

File diff suppressed because it is too large Load diff

View file

@ -1,271 +0,0 @@
/*
* ext-arrows.js
*
* Licensed under the Apache License, Version 2
*
* Copyright(c) 2010 Alexis Deveria
*
*/
$(function() {
svgCanvas.addExtension("Arrows", function(S) {
var svgcontent = S.svgcontent,
addElem = S.addSvgElementFromJson,
selElems;
var lang_list = {
"en":[
{"id": "arrow_none", "textContent": "No arrow" }
],
"fr":[
{"id": "arrow_none", "textContent": "Sans flèche" }
]
};
var pathdata = {
fw: {d:"m0,0l10,5l-10,5l5,-5l-5,-5z", refx:8, id:"se_arrow_fw"},
bk: {d:"m10,0l-10,5l10,5l-5,-5l5,-5z", refx:2, id:"se_arrow_bk"}
}
function getLinked(elem, attr) {
var str = elem.getAttribute(attr);
if(!str) return null;
var m = str.match(/\(\#(.*)\)/);
if(!m || m.length !== 2) {
return null;
}
return S.getElem(m[1]);
}
function showPanel(on) {
$('#arrow_panel').toggle(on);
if(on) {
var el = selElems[0];
var end = el.getAttribute("marker-end");
var start = el.getAttribute("marker-start");
var mid = el.getAttribute("marker-mid");
var val;
if(end && start) {
val = "both";
} else if(end) {
val = "end";
} else if(start) {
val = "start";
} else if(mid) {
val = "mid";
if(mid.indexOf("bk") != -1) {
val = "mid_bk";
}
}
if(!start && !mid && !end) {
val = "none";
}
$("#arrow_list").val(val);
}
}
function resetMarker() {
var el = selElems[0];
el.removeAttribute("marker-start");
el.removeAttribute("marker-mid");
el.removeAttribute("marker-end");
}
function addMarker(dir, type, id) {
// TODO: Make marker (or use?) per arrow type, since refX can be different
id = id || 'se_arrow_' + dir;
var marker = S.getElem(id);
var data = pathdata[dir];
if(type == "mid") {
data.refx = 5;
}
if(!marker) {
marker = addElem({
"element": "marker",
"attr": {
"viewBox": "0 0 10 10",
"id": id,
"refY": 5,
"markerUnits": "strokeWidth",
"markerWidth": 5,
"markerHeight": 5,
"orient": "auto",
"style": "pointer-events:none" // Currently needed for Opera
}
});
var arrow = addElem({
"element": "path",
"attr": {
"d": data.d,
"fill": "#000000"
}
});
marker.appendChild(arrow);
S.findDefs().appendChild(marker);
}
marker.setAttribute('refX', data.refx);
return marker;
}
function setArrow() {
var type = this.value;
resetMarker();
if(type == "none") {
return;
}
// Set marker on element
var dir = "fw";
if(type == "mid_bk") {
type = "mid";
dir = "bk";
} else if(type == "both") {
addMarker("bk", type);
svgCanvas.changeSelectedAttribute("marker-start", "url(#" + pathdata.bk.id + ")");
type = "end";
dir = "fw";
} else if (type == "start") {
dir = "bk";
}
addMarker(dir, type);
svgCanvas.changeSelectedAttribute("marker-"+type, "url(#" + pathdata[dir].id + ")");
S.call("changed", selElems);
}
function colorChanged(elem) {
var color = elem.getAttribute('stroke');
var mtypes = ['start','mid','end'];
var defs = S.findDefs();
$.each(mtypes, function(i, type) {
var marker = getLinked(elem, 'marker-'+type);
if(!marker) return;
var cur_color = $(marker).children().attr('fill');
var cur_d = $(marker).children().attr('d');
var new_marker = null;
if(cur_color === color) return;
var all_markers = $(defs).find('marker');
// Different color, check if already made
all_markers.each(function() {
var attrs = $(this).children().attr(['fill', 'd']);
if(attrs.fill === color && attrs.d === cur_d) {
// Found another marker with this color and this path
new_marker = this;
}
});
if(!new_marker) {
// Create a new marker with this color
var last_id = marker.id;
var dir = last_id.indexOf('_fw') !== -1?'fw':'bk';
new_marker = addMarker(dir, type, 'se_arrow_' + dir + all_markers.length);
$(new_marker).children().attr('fill', color);
}
$(elem).attr('marker-'+type, "url(#" + new_marker.id + ")");
// Check if last marker can be removed
var remove = true;
$(S.svgcontent).find('line, polyline, path, polygon').each(function() {
var elem = this;
$.each(mtypes, function(j, mtype) {
if($(elem).attr('marker-' + mtype) === "url(#" + marker.id + ")") {
return remove = false;
}
});
if(!remove) return false;
});
// Not found, so can safely remove
if(remove) {
$(marker).remove();
}
});
}
return {
name: "Arrows",
context_tools: [{
type: "select",
panel: "arrow_panel",
title: "Select arrow type",
id: "arrow_list",
options: {
none: "No arrow",
end: "---->",
start: "<----",
both: "<--->",
mid: "-->--",
mid_bk: "--<--"
},
defval: "none",
events: {
change: setArrow
}
}],
callback: function() {
$('#arrow_panel').hide();
},
addLangData: function(lang) {
return {
data: lang_list[lang]
};
},
selectedChanged: function(opts) {
// Use this to update the current selected elements
selElems = opts.elems;
var i = selElems.length;
var marker_elems = ['line','path','polyline','polygon'];
while(i--) {
var elem = selElems[i];
if(elem && $.inArray(elem.tagName, marker_elems) != -1) {
if(opts.selectedElement && !opts.multiselected) {
showPanel(true);
} else {
showPanel(false);
}
} else {
showPanel(false);
}
}
},
elementChanged: function(opts) {
var elem = opts.elems[0];
if(elem && (
elem.getAttribute("marker-start") ||
elem.getAttribute("marker-mid") ||
elem.getAttribute("marker-end")
)) {
// var start = elem.getAttribute("marker-start");
// var mid = elem.getAttribute("marker-mid");
// var end = elem.getAttribute("marker-end");
// Has marker, so see if it should match color
colorChanged(elem);
}
}
};
});
});

View file

@ -1,574 +0,0 @@
/*
* ext-connector.js
*
* Licensed under the Apache License, Version 2
*
* Copyright(c) 2010 Alexis Deveria
*
*/
$(function() {
svgCanvas.addExtension("Connector", function(S) {
var svgcontent = S.svgcontent,
svgroot = S.svgroot,
getNextId = S.getNextId,
getElem = S.getElem,
addElem = S.addSvgElementFromJson,
selManager = S.selectorManager,
started = false,
start_x,
start_y,
cur_line,
start_elem,
end_elem,
connections = [],
conn_sel = ".se_connector",
se_ns,
// connect_str = "-SE_CONNECT-",
selElems = [];
var lang_list = {
"en":[
{"id": "mode_connect", "title": "Connect two objects" }
],
"fr":[
{"id": "mode_connect", "title": "Connecter deux objets"}
]
};
function showPanel(on) {
var conn_rules = $('#connector_rules');
if(!conn_rules.length) {
conn_rules = $('<style id="connector_rules"><\/style>').appendTo('head');
}
conn_rules.text(!on?"":"#tool_clone, #tool_topath, #tool_angle, #xy_panel { display: none !important; }");
$('#connector_panel').toggle(on);
}
function setPoint(elem, pos, x, y, setMid) {
var pts = elem.points;
var pt = svgroot.createSVGPoint();
pt.x = x;
pt.y = y;
if(pos === 'end') pos = pts.numberOfItems-1;
// TODO: Test for this on init, then use alt only if needed
try {
pts.replaceItem(pt, pos);
} catch(err) {
// Should only occur in FF which formats points attr as "n,n n,n", so just split
var pt_arr = elem.getAttribute("points").split(" ");
for(var i=0; i< pt_arr.length; i++) {
if(i == pos) {
pt_arr[i] = x + ',' + y;
}
}
elem.setAttribute("points",pt_arr.join(" "));
}
if(setMid) {
// Add center point
var pt_start = pts.getItem(0);
var pt_end = pts.getItem(pts.numberOfItems-1);
setPoint(elem, 1, (pt_end.x + pt_start.x)/2, (pt_end.y + pt_start.y)/2);
}
}
function findConnectors() {
// Check if selected elements have connections
var elems = selElems;
var i = elems.length;
var connectors = $(svgcontent).find(conn_sel);
if(!connectors.length) return;
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
connectors.each(function() {
var start = $(this).data("c_start");
var end = $(this).data("c_end");
// Connector found for this element
if(start == elem.id || end == elem.id) {
if(was_deleted) {
$(this).remove();
return;
}
var bb = svgCanvas.getStrokedBBox([elem]);
connections.push({
elem: elem,
connector: this,
is_start: start == elem.id,
start_x: bb.x,
start_y: bb.y
});
}
});
}
}
function updateConnectors() {
// Updates connector lines based on selected elements
// Is not used on mousemove, as it runs getStrokedBBox every time,
// which isn't necessary there.
findConnectors();
if(connections.length) {
// Update line with element
var i = connections.length;
while(i--) {
var conn = connections[i];
var line = conn.connector;
var elem = conn.elem;
var sw = line.getAttribute('stroke-width');
var pre = conn.is_start?'start':'end';
// Update bbox for this element
var bb = svgCanvas.getStrokedBBox([elem]);
bb.x = conn.start_x;
bb.y = conn.start_y;
$(line).data(pre+'_bb', bb);
var add_offset = $(line).data(pre+'_off');
var alt_pre = conn.is_start?'end':'start';
// Get center pt of connected element
var bb2 = $(line).data(alt_pre+'_bb');
var src_x = bb2.x + bb2.width/2;
var src_y = bb2.y + bb2.height/2;
// Set point of element being moved
var pt = getBBintersect(src_x, src_y, bb, add_offset?sw:0);
setPoint(line, conn.is_start?0:'end', pt.x, pt.y, true);
// Set point of connected element
var pt2 = getBBintersect(pt.x, pt.y, $(line).data(alt_pre + '_bb'), $(line).data(alt_pre + '_off')?sw:0);
setPoint(line, conn.is_start?'end':0, pt2.x, pt2.y, true);
// Update points attribute manually for webkit
if(navigator.userAgent.indexOf('AppleWebKit') != -1) {
var pts = line.points;
var len = pts.numberOfItems;
var pt_arr = Array(len);
for(var j=0; j< len; j++) {
var pt = pts.getItem(j);
pt_arr[j] = pt.x + ',' + pt.y;
}
line.setAttribute("points",pt_arr.join(" "));
}
}
}
}
function getBBintersect(x, y, bb, offset) {
if(offset) {
offset -= 0;
bb = $.extend({}, bb);
bb.width += offset;
bb.height += offset;
bb.x -= offset/2;
bb.y -= offset/2;
}
var mid_x = bb.x + bb.width/2;
var mid_y = bb.y + bb.height/2;
var len_x = x - mid_x;
var len_y = y - mid_y;
var slope = Math.abs(len_y/len_x);
var ratio;
if(slope < bb.height/bb.width) {
ratio = (bb.width/2) / Math.abs(len_x);
} else {
ratio = (bb.height/2) / Math.abs(len_y);
}
return {
x: mid_x + len_x * ratio,
y: mid_y + len_y * ratio
}
}
// Do once
(function() {
var gse = svgCanvas.groupSelectedElements;
svgCanvas.groupSelectedElements = function() {
svgCanvas.removeFromSelection($(conn_sel).toArray());
gse();
}
var mse = svgCanvas.moveSelectedElements;
svgCanvas.moveSelectedElements = function() {
svgCanvas.removeFromSelection($(conn_sel).toArray());
mse.apply(this, arguments);
updateConnectors();
}
se_ns = svgCanvas.getEditorNS();
}());
// Do on reset
function init() {
// Make sure all connectors have data set
$(svgcontent).find('*').each(function() {
var conn = this.getAttributeNS(se_ns, "connector");
if(conn) {
this.setAttribute('class', conn_sel.substr(1));
var conn_data = conn.split(' ');
$(this).data('c_start',conn_data[0])
.data('c_end',conn_data[1]);
svgCanvas.getEditorNS(true);
}
});
// updateConnectors();
}
// $(svgroot).parent().mousemove(function(e) {
// // if(started
// // || svgCanvas.getMode() != "connector"
// // || e.target.parentNode.parentNode != svgcontent) return;
//
// console.log('y')
// // if(e.target.parentNode.parentNode === svgcontent) {
// //
// // }
// });
return {
name: "Connector",
svgicons: "images/conn.svg",
buttons: [{
id: "mode_connect",
type: "mode",
icon: "images/cut.png",
title: "Connect two objects",
key: "Shift+3",
includeWith: {
button: '#tool_line',
isDefault: false,
position: 1
},
events: {
'click': function() {
svgCanvas.setMode("connector");
}
}
}],
addLangData: function(lang) {
return {
data: lang_list[lang]
};
},
mouseDown: function(opts) {
var e = opts.event;
start_x = opts.start_x,
start_y = opts.start_y;
var mode = svgCanvas.getMode();
if(mode == "connector") {
if(started) return;
var mouse_target = e.target;
var parents = $(mouse_target).parents();
if($.inArray(svgcontent, parents) != -1) {
// Connectable element
start_elem = mouse_target;
// Get center of source element
var bb = svgCanvas.getStrokedBBox([start_elem]);
var x = bb.x + bb.width/2;
var y = bb.y + bb.height/2;
started = true;
cur_line = addElem({
"element": "polyline",
"attr": {
"id": getNextId(),
"points": (x+','+y+' '+x+','+y+' '+start_x+','+start_y),
"stroke": '#000',
"stroke-width": 3,
"fill": "none",
"opacity": .5,
"style": "pointer-events:none"
}
});
$(cur_line).data('start_bb', bb);
}
return {
started: true
};
} else if(mode == "select") {
findConnectors();
}
},
mouseMove: function(opts) {
var zoom = svgCanvas.getZoom();
var e = opts.event;
var x = opts.mouse_x/zoom;
var y = opts.mouse_y/zoom;
var diff_x = x - start_x,
diff_y = y - start_y;
var mode = svgCanvas.getMode();
if(mode == "connector" && started) {
// Set start point (adjusts based on bb)
var pt = getBBintersect(x, y, $(cur_line).data('start_bb'));
start_x = pt.x;
start_y = pt.y;
setPoint(cur_line, 0, pt.x, pt.y, true);
// Set end point
setPoint(cur_line, 'end', x, y, true);
} else if(mode == "select") {
var slen = selElems.length;
while(slen--) {
var elem = selElems[slen];
// Look for selected connector elements
if(elem && $(elem).data('c_start')) {
// Remove the "translate" transform given to move
svgCanvas.removeFromSelection([elem]);
svgCanvas.getTransformList(elem).clear();
}
}
if(connections.length) {
// Update line with element
var i = connections.length;
while(i--) {
var conn = connections[i];
var line = conn.connector;
var elem = conn.elem;
var pre = conn.is_start?'start':'end';
var sw = line.getAttribute('stroke-width');
// Update bbox for this element
var bb = $(line).data(pre+'_bb');
bb.x = conn.start_x + diff_x;
bb.y = conn.start_y + diff_y;
$(line).data(pre+'_bb', bb);
var alt_pre = conn.is_start?'end':'start';
// Get center pt of connected element
var bb2 = $(line).data(alt_pre+'_bb');
var src_x = bb2.x + bb2.width/2;
var src_y = bb2.y + bb2.height/2;
// Set point of element being moved
var pt = getBBintersect(src_x, src_y, bb, $(line).data(pre+'_off')?sw:0);
setPoint(line, conn.is_start?0:'end', pt.x, pt.y, true);
// Set point of connected element
var pt2 = getBBintersect(pt.x, pt.y, $(line).data(alt_pre + '_bb'), $(line).data(alt_pre+'_off')?sw:0);
setPoint(line, conn.is_start?'end':0, pt2.x, pt2.y, true);
}
}
}
},
mouseUp: function(opts) {
var zoom = svgCanvas.getZoom();
var e = opts.event,
x = opts.mouse_x/zoom,
y = opts.mouse_y/zoom;
if(svgCanvas.getMode() == "connector") {
if(e.target.parentNode.parentNode != svgcontent) {
// Not a valid target element, so remove line
$(cur_line).remove();
started = false;
return {
keep: false,
element: null,
started: started
}
} else if(e.target == start_elem) {
// Start line through click
started = true;
return {
keep: true,
element: null,
started: started
}
} else {
// Valid end element
end_elem = e.target;
var start_id = start_elem.id, end_id = end_elem.id;
var conn_str = start_id + " " + end_id;
var alt_str = end_id + " " + start_id;
// Don't create connector if one already exists
var dupe = $(svgcontent).find(conn_sel).filter(function() {
var conn = this.getAttributeNS(se_ns, "connector");
if(conn == conn_str || conn == alt_str) return true;
});
if(dupe.length) {
$(cur_line).remove();
return {
keep: false,
element: null,
started: false
}
}
var bb = svgCanvas.getStrokedBBox([end_elem]);
var pt = getBBintersect(start_x, start_y, bb);
setPoint(cur_line, 'end', pt.x, pt.y, true);
$(cur_line)
.data("c_start", start_id)
.data("c_end", end_id)
.data("end_bb", bb);
se_ns = svgCanvas.getEditorNS(true);
cur_line.setAttributeNS(se_ns, "se:connector", conn_str);
cur_line.setAttribute('class', conn_sel.substr(1));
cur_line.setAttribute('opacity', 1);
svgCanvas.addToSelection([cur_line]);
svgCanvas.moveToBottomSelectedElement();
selManager.requestSelector(cur_line).showGrips(false);
started = false;
return {
keep: true,
element: cur_line,
started: started
}
}
}
},
selectedChanged: function(opts) {
// Use this to update the current selected elements
selElems = opts.elems;
var i = selElems.length;
while(i--) {
var elem = selElems[i];
if(elem && $(elem).data('c_start')) {
selManager.requestSelector(elem).showGrips(false);
if(opts.selectedElement && !opts.multiselected) {
// TODO: Set up context tools and hide most regular line tools
showPanel(true);
} else {
showPanel(false);
}
} else {
showPanel(false);
}
}
},
elementChanged: function(opts) {
var elem = opts.elems[0];
if (elem && elem.tagName == 'svg' && elem.id == "svgcontent") {
// Update svgcontent (can change on import)
svgcontent = elem;
init();
}
// Has marker, so change offset
if(elem && (
elem.getAttribute("marker-start") ||
elem.getAttribute("marker-mid") ||
elem.getAttribute("marker-end")
)) {
var start = elem.getAttribute("marker-start");
var mid = elem.getAttribute("marker-mid");
var end = elem.getAttribute("marker-end");
cur_line = elem;
$(elem)
.data("start_off", !!start)
.data("end_off", !!end);
if(elem.tagName == "line" && mid) {
// Convert to polyline to accept mid-arrow
var x1 = elem.getAttribute('x1')-0;
var x2 = elem.getAttribute('x2')-0;
var y1 = elem.getAttribute('y1')-0;
var y2 = elem.getAttribute('y2')-0;
var id = elem.id;
var mid_pt = (' '+((x1+x2)/2)+','+((y1+y2)/2) + ' ');
var pline = addElem({
"element": "polyline",
"attr": {
"points": (x1+','+y1+ mid_pt +x2+','+y2),
"stroke": elem.getAttribute('stroke'),
"stroke-width": elem.getAttribute('stroke-width'),
"marker-mid": mid,
"fill": "none",
"opacity": elem.getAttribute('opacity') || 1
}
});
$(elem).after(pline).remove();
svgCanvas.clearSelection();
pline.id = id;
svgCanvas.addToSelection([pline]);
}
}
updateConnectors();
},
toolButtonStateUpdate: function(opts) {
if(opts.nostroke) {
if ($('#mode_connect').hasClass('tool_button_current')) {
clickSelect();
}
}
$('#mode_connect')
.toggleClass('disabled',opts.nostroke);
}
};
});
});