Fix a bug in batch command where elements were not properly returned. Added canvas function to return resolution. Added parsing of XML string into SVG document. Added whitelist of elems/attributes for SVG-edit.
git-svn-id: http://svg-edit.googlecode.com/svn/trunk@308 eee81c28-f429-11dd-99c0-75d572ba1dddmaster
parent
f7f4a9d0ec
commit
d694197af7
|
@ -6,7 +6,8 @@
|
||||||
<!--link rel="stylesheet" href="svg-editor.min.css" type="text/css"/-->
|
<!--link rel="stylesheet" href="svg-editor.min.css" type="text/css"/-->
|
||||||
<link rel="stylesheet" href="spinbtn/JQuerySpinBtn.css" type="text/css"/>
|
<link rel="stylesheet" href="spinbtn/JQuerySpinBtn.css" type="text/css"/>
|
||||||
<!--link rel="stylesheet" href="spinbtn/JQuerySpinBtn.min.css" type="text/css"/-->
|
<!--link rel="stylesheet" href="spinbtn/JQuerySpinBtn.min.css" type="text/css"/-->
|
||||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
|
<script type="text/javascript" src="jquery.js"></script>
|
||||||
|
<!--script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script-->
|
||||||
<script type="text/javascript" src="js-hotkeys/jquery.hotkeys.min.js"></script>
|
<script type="text/javascript" src="js-hotkeys/jquery.hotkeys.min.js"></script>
|
||||||
<script type="text/javascript" src="jpicker/jpicker.js"></script>
|
<script type="text/javascript" src="jpicker/jpicker.js"></script>
|
||||||
<script type="text/javascript" src="spinbtn/JQuerySpinBtn.js"></script>
|
<script type="text/javascript" src="spinbtn/JQuerySpinBtn.js"></script>
|
||||||
|
|
|
@ -43,7 +43,7 @@ function svg_edit_setup() {
|
||||||
var elementChanged = function(window,elems) {
|
var elementChanged = function(window,elems) {
|
||||||
for (var i = 0; i < elems.length; ++i) {
|
for (var i = 0; i < elems.length; ++i) {
|
||||||
var elem = elems[i];
|
var elem = elems[i];
|
||||||
// if the element changed was the svg, then it must be a resolution change
|
// if the element changed was the svg, then it could be a resolution change
|
||||||
if (elem && elem.tagName == "svg") {
|
if (elem && elem.tagName == "svg") {
|
||||||
changeResolution(parseInt(elem.getAttribute("width")),
|
changeResolution(parseInt(elem.getAttribute("width")),
|
||||||
parseInt(elem.getAttribute("height")));
|
parseInt(elem.getAttribute("height")));
|
||||||
|
@ -479,7 +479,18 @@ function svg_edit_setup() {
|
||||||
$(document).bind('keydown', {combi:'z', disableInInput: true}, clickUndo);
|
$(document).bind('keydown', {combi:'z', disableInInput: true}, clickUndo);
|
||||||
$(document).bind('keydown', {combi:'shift+z', disableInInput: true}, clickRedo);
|
$(document).bind('keydown', {combi:'shift+z', disableInInput: true}, clickRedo);
|
||||||
$(document).bind('keydown', {combi:'y', disableInInput: true}, clickRedo);
|
$(document).bind('keydown', {combi:'y', disableInInput: true}, clickRedo);
|
||||||
|
// temporary binding to test setSvgString()
|
||||||
|
/*
|
||||||
|
$(document).bind('keydown', {combi:'t', disableInInput: true}, function() {
|
||||||
|
if (svgCanvas.setSvgString(
|
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="foo" width="300" height="200"><bar><knuckle/><head/><rect width="200" height="100" fill="red" /></bar><circle cx="100" cy="100" r="40" fill="green" strike="blue"/><foo/></svg>')) {
|
||||||
|
updateContextPanel();
|
||||||
|
var dims = svgCanvas.getResolution();
|
||||||
|
changeResolution(dims[0],dims[1]);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
var colorPicker = function(elem) {
|
var colorPicker = function(elem) {
|
||||||
var oldbg = elem.css('background');
|
var oldbg = elem.css('background');
|
||||||
var color = elem.css('background-color');
|
var color = elem.css('background-color');
|
||||||
|
@ -623,8 +634,8 @@ function svg_edit_setup() {
|
||||||
|
|
||||||
function changeResolution(x,y) {
|
function changeResolution(x,y) {
|
||||||
$('#resolution').val(x+'x'+y);
|
$('#resolution').val(x+'x'+y);
|
||||||
$('#svgroot').css( { 'width': x, 'height': y } );
|
$('#svgroot').css( { 'width': x+'px', 'height': y+'px' } );
|
||||||
$('#svgcanvas').css( { 'width': x, 'height': y } );
|
$('#svgcanvas').css( { 'width': x+'px', 'height': y+'px' } );
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#resolution').change(function(){
|
$('#resolution').change(function(){
|
||||||
|
|
|
@ -5,6 +5,18 @@ if(!window.console) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this defines which elements and attributes that we support
|
||||||
|
var svgWhiteList = {
|
||||||
|
"circle": ["cx", "cy", "fill", "fill-opacity", "id", "stroke", "r", "stroke-opacity", "stroke-width", "stroke-dasharray"],
|
||||||
|
"ellipse": ["cx", "cy", "fill", "fill-opacity", "id", "stroke", "rx", "ry", "stroke-opacity", "stroke-width", "stroke-dasharray"],
|
||||||
|
"line": ["fill", "fill-opacity", "id", "stroke", "stroke-opacity", "stroke-width", "stroke-dasharray", "x1", "x2", "y1", "y2"],
|
||||||
|
"path": ["d", "fill", "fill-opacity", "id", "stroke", "stroke-opacity", "stroke-width", "stroke-dasharray"],
|
||||||
|
"rect": ["fill", "fill-opacity", "height", "id", "rx", "ry", "stroke", "stroke-opacity", "stroke-width", "stroke-dasharray", "width"],
|
||||||
|
"svg": ["id", "height", "width", "xmlns"],
|
||||||
|
"text": ["font-family", "font-size", "font-style", "font-weight", "id", "x", "y"],
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// These command objects are used for the Undo/Redo stack
|
// These command objects are used for the Undo/Redo stack
|
||||||
// attrs contains the values that the attributes had before the change
|
// attrs contains the values that the attributes had before the change
|
||||||
function ChangeElementCommand(elem, attrs, text) {
|
function ChangeElementCommand(elem, attrs, text) {
|
||||||
|
@ -103,7 +115,6 @@ function MoveElementCommand(elem, oldNextSibling, oldParent, text) {
|
||||||
|
|
||||||
// this command object acts an arbitrary number of subcommands
|
// this command object acts an arbitrary number of subcommands
|
||||||
function BatchCommand(text) {
|
function BatchCommand(text) {
|
||||||
this.elems = [];
|
|
||||||
this.text = text || "Batch Command";
|
this.text = text || "Batch Command";
|
||||||
this.stack = [];
|
this.stack = [];
|
||||||
|
|
||||||
|
@ -120,7 +131,19 @@ function BatchCommand(text) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.elements = function() { return this.elems; };
|
this.elements = function() {
|
||||||
|
// iterate through all our subcommands and find all the elements we are changing
|
||||||
|
var elems = [];
|
||||||
|
var cmd = this.stack.length;
|
||||||
|
while (cmd--) {
|
||||||
|
var thisElems = this.stack[cmd].elements();
|
||||||
|
var elem = thisElems.length;
|
||||||
|
while (elem--) {
|
||||||
|
if (elems.indexOf(thisElems[elem]) == -1) elems.push(thisElems[elem]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return elems;
|
||||||
|
};
|
||||||
|
|
||||||
this.addSubCommand = function(cmd) { this.stack.push(cmd); };
|
this.addSubCommand = function(cmd) { this.stack.push(cmd); };
|
||||||
|
|
||||||
|
@ -507,25 +530,73 @@ function SvgCanvas(c)
|
||||||
var addSvgElementFromJson = function(data) {
|
var addSvgElementFromJson = function(data) {
|
||||||
return canvas.updateElementFromJson(data)
|
return canvas.updateElementFromJson(data)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// this function sanitizes the input node and its children
|
||||||
|
// this function only keeps what is allowed from our whitelist defined above
|
||||||
|
var sanitizeSvg = function(node) {
|
||||||
|
// we only care about element nodes
|
||||||
|
// automatically return for all text, comment, etc nodes
|
||||||
|
if (node.nodeType != 1) return;
|
||||||
|
|
||||||
|
var doc = node.ownerDocument;
|
||||||
|
var parent = node.parentNode;
|
||||||
|
// can parent ever be null here? I think the root node's parent is the document...
|
||||||
|
if (!doc || !parent) return;
|
||||||
|
|
||||||
|
var allowedAttrs = svgWhiteList[node.nodeName];
|
||||||
|
// if this element is allowed
|
||||||
|
if (allowedAttrs != undefined) {
|
||||||
|
var i = node.attributes.length;
|
||||||
|
while (i--) {
|
||||||
|
// if the attribute is not in our whitelist, then remove it
|
||||||
|
// could use jQuery's inArray(), but I don't know if that's any better
|
||||||
|
var attrName = node.attributes.item(i).nodeName;
|
||||||
|
if (allowedAttrs.indexOf(attrName) == -1) {
|
||||||
|
// TODO: do I need to call setAttribute(..., "") here for Fx2?
|
||||||
|
node.removeAttribute(attrName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse to children
|
||||||
|
i = node.childNodes.length;
|
||||||
|
while (i--) { sanitizeSvg(node.childNodes.item(i)); }
|
||||||
|
}
|
||||||
|
// else, remove this element
|
||||||
|
else {
|
||||||
|
// remove all children from this node and insert them before this node
|
||||||
|
var children = [];
|
||||||
|
while (node.hasChildNodes()) {
|
||||||
|
children.push(parent.insertBefore(node.firstChild, node));
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove this node from the document altogether
|
||||||
|
parent.removeChild(node);
|
||||||
|
|
||||||
|
// call sanitizeSvg on each of those children
|
||||||
|
var i = children.length;
|
||||||
|
while (i--) { sanitizeSvg(children[i]); }
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var svgToString = function(elem, indent) {
|
var svgToString = function(elem, indent) {
|
||||||
// TODO: could use the array.join() optimization trick here too
|
var out = new Array();
|
||||||
var out = "";
|
|
||||||
if (elem) {
|
if (elem) {
|
||||||
var attrs = elem.attributes;
|
var attrs = elem.attributes;
|
||||||
var attr;
|
var attr;
|
||||||
var i;
|
var i;
|
||||||
var childs = elem.childNodes;
|
var childs = elem.childNodes;
|
||||||
for (i=0; i<indent; i++) out += " ";
|
for (i=0; i<indent; i++) out.push(" ");
|
||||||
out += "<" + elem.nodeName;
|
out.push("<"); out.push(elem.nodeName);
|
||||||
for (i=attrs.length-1; i>=0; i--) {
|
for (i=attrs.length-1; i>=0; i--) {
|
||||||
attr = attrs.item(i);
|
attr = attrs.item(i);
|
||||||
if (attr.nodeValue != "") {
|
if (attr.nodeValue != "") {
|
||||||
out += " " + attr.nodeName + "=\"" + attr.nodeValue+ "\"";
|
out.push(" "); out.push(attr.nodeName); out.push("=\"");
|
||||||
|
out.push(attr.nodeValue); out.push("\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (elem.hasChildNodes()) {
|
if (elem.hasChildNodes()) {
|
||||||
out += ">";
|
out.push(">");
|
||||||
indent++;
|
indent++;
|
||||||
var bOneLine = false;
|
var bOneLine = false;
|
||||||
for (i=0; i<childs.length; i++)
|
for (i=0; i<childs.length; i++)
|
||||||
|
@ -533,23 +604,24 @@ function SvgCanvas(c)
|
||||||
var child = childs.item(i);
|
var child = childs.item(i);
|
||||||
if (child.id == "selectorParentGroup") continue;
|
if (child.id == "selectorParentGroup") continue;
|
||||||
if (child.nodeType == 1) { // element node
|
if (child.nodeType == 1) { // element node
|
||||||
out += "\n" + svgToString(childs.item(i), indent);
|
out.push("\n");
|
||||||
|
out.push(svgToString(childs.item(i), indent));
|
||||||
} else if (child.nodeType == 3) { // text node
|
} else if (child.nodeType == 3) { // text node
|
||||||
bOneLine = true;
|
bOneLine = true;
|
||||||
out += child.nodeValue + "";
|
out.push(child.nodeValue + "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
indent--;
|
indent--;
|
||||||
if (!bOneLine) {
|
if (!bOneLine) {
|
||||||
out += "\n";
|
out.push("\n");
|
||||||
for (i=0; i<indent; i++) out += " ";
|
for (i=0; i<indent; i++) out += " ";
|
||||||
}
|
}
|
||||||
out += "</" + elem.nodeName + ">";
|
out.push("</"); out.push(elem.nodeName); out.push(">");
|
||||||
} else {
|
} else {
|
||||||
out += "/>";
|
out.push("/>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out.join('');
|
||||||
}; // end svgToString()
|
}; // end svgToString()
|
||||||
|
|
||||||
var recalculateAllSelectedDimensions = function() {
|
var recalculateAllSelectedDimensions = function() {
|
||||||
|
@ -927,6 +999,9 @@ function SvgCanvas(c)
|
||||||
|
|
||||||
// in mouseMove we do not record any state changes yet (but we do update
|
// in mouseMove we do not record any state changes yet (but we do update
|
||||||
// any elements that are still being created, moved or resized on the canvas)
|
// any elements that are still being created, moved or resized on the canvas)
|
||||||
|
// TODO: svgcanvas should just retain a reference to the image being dragged instead
|
||||||
|
// of the getId() and getElementById() funkiness - this will help us customize the ids
|
||||||
|
// a little bit for squares and polys
|
||||||
var mouseMove = function(evt)
|
var mouseMove = function(evt)
|
||||||
{
|
{
|
||||||
if (!started) return;
|
if (!started) return;
|
||||||
|
@ -1242,6 +1317,40 @@ function SvgCanvas(c)
|
||||||
str += svgToString(svgroot, 0);
|
str += svgToString(svgroot, 0);
|
||||||
this.saveHandler(str);
|
this.saveHandler(str);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.getSvgString = function() {
|
||||||
|
return svgToString(svgroot, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// this function returns false if the set was unsuccessful, true otherwise
|
||||||
|
// TODO: should this function keep throwing the exception?
|
||||||
|
this.setSvgString = function(xmlString) {
|
||||||
|
try {
|
||||||
|
// convert string into XML document
|
||||||
|
var newDoc = Utils.text2xml(xmlString);
|
||||||
|
|
||||||
|
// run it through our sanitizer to remove anything we do not support
|
||||||
|
sanitizeSvg(newDoc.documentElement);
|
||||||
|
|
||||||
|
var batchCmd = new BatchCommand("Change Source");
|
||||||
|
|
||||||
|
// remove old root
|
||||||
|
var oldroot = container.removeChild(svgroot);
|
||||||
|
batchCmd.addSubCommand(new RemoveElementCommand(oldroot, container));
|
||||||
|
|
||||||
|
// set new root
|
||||||
|
svgroot = container.appendChild(svgdoc.importNode(newDoc.documentElement, true));
|
||||||
|
batchCmd.addSubCommand(new InsertElementCommand(svgroot));
|
||||||
|
|
||||||
|
addCommandToHistory(batchCmd);
|
||||||
|
call("changed", [svgroot]);
|
||||||
|
} catch(e) {
|
||||||
|
console.log(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
this.clear = function() {
|
this.clear = function() {
|
||||||
var nodes = svgroot.childNodes;
|
var nodes = svgroot.childNodes;
|
||||||
|
@ -1260,6 +1369,9 @@ function SvgCanvas(c)
|
||||||
call("cleared");
|
call("cleared");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.getResolution = function() {
|
||||||
|
return [svgroot.getAttribute("width"), svgroot.getAttribute("height")];
|
||||||
|
};
|
||||||
this.setResolution = function(x, y) {
|
this.setResolution = function(x, y) {
|
||||||
var w = svgroot.getAttribute("width"),
|
var w = svgroot.getAttribute("width"),
|
||||||
h = svgroot.getAttribute("height");
|
h = svgroot.getAttribute("height");
|
||||||
|
@ -1647,5 +1759,24 @@ var Utils = {
|
||||||
(r2.x+r2.width) > r1.x &&
|
(r2.x+r2.width) > r1.x &&
|
||||||
r2.y < (r1.y+r1.height) &&
|
r2.y < (r1.y+r1.height) &&
|
||||||
(r2.y+r2.height) > r1.y;
|
(r2.y+r2.height) > r1.y;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// found this function http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f
|
||||||
|
"text2xml": function(sXML) {
|
||||||
|
// NOTE: I'd like to use jQuery for this, but jQuery makes all tags uppercase
|
||||||
|
//return $(xml)[0];
|
||||||
|
var out;
|
||||||
|
try{
|
||||||
|
var dXML = ($.browser.msie)?new ActiveXObject("Microsoft.XMLDOM"):new DOMParser();
|
||||||
|
dXML.async = false;
|
||||||
|
} catch(e){
|
||||||
|
throw new Error("XML Parser could not be instantiated");
|
||||||
|
};
|
||||||
|
try{
|
||||||
|
if($.browser.msie) out = (dXML.loadXML(sXML))?dXML:false;
|
||||||
|
else out = dXML.parseFromString(sXML, "text/xml");
|
||||||
|
}
|
||||||
|
catch(e){ throw new Error("Error parsing XML string"); };
|
||||||
|
return out;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue