Update SVG-Edit

This commit is contained in:
Jacques Distler 2011-02-03 11:13:18 -06:00
parent a12a778c5a
commit 1bef71bbf1
9 changed files with 277 additions and 192 deletions

View file

@ -85,6 +85,17 @@ var supportsTextCharPos_ = (function() {
return retValue; return retValue;
})(); })();
var supportsPathBBox_ = (function() {
var svgcontent = document.createElementNS(svgns, 'svg');
document.documentElement.appendChild(svgcontent);
var path = document.createElementNS(svgns, 'path');
path.setAttribute('d','M0,0 C0,0 10,10 10,0');
svgcontent.appendChild(path);
var bbox = path.getBBox();
document.documentElement.removeChild(svgcontent);
return (bbox.height > 4 && bbox.height < 5);
})();
var supportsEditableText_ = (function() { var supportsEditableText_ = (function() {
// TODO: Find better way to check support for this // TODO: Find better way to check support for this
return isOpera_; return isOpera_;
@ -129,6 +140,7 @@ svgedit.browser.supportsXpath = function() { return supportsXpath_; }
svgedit.browser.supportsPathReplaceItem = function() { return supportsPathReplaceItem_; } svgedit.browser.supportsPathReplaceItem = function() { return supportsPathReplaceItem_; }
svgedit.browser.supportsPathInsertItemBefore = function() { return supportsPathInsertItemBefore_; } svgedit.browser.supportsPathInsertItemBefore = function() { return supportsPathInsertItemBefore_; }
svgedit.browser.supportsPathBBox = function() { return supportsPathBBox_; }
svgedit.browser.supportsTextCharPos = function() { return supportsTextCharPos_; } svgedit.browser.supportsTextCharPos = function() { return supportsTextCharPos_; }
svgedit.browser.supportsEditableText = function() { return supportsEditableText_; } svgedit.browser.supportsEditableText = function() { return supportsEditableText_; }
svgedit.browser.supportsGoodDecimals = function() { return supportsGoodDecimals_; } svgedit.browser.supportsGoodDecimals = function() { return supportsGoodDecimals_; }

View file

@ -23,6 +23,13 @@ var svg_ns = "http://www.w3.org/2000/svg";
var se_ns = "http://svg-edit.googlecode.com"; var se_ns = "http://svg-edit.googlecode.com";
var xmlns_ns = "http://www.w3.org/2000/xmlns/"; var xmlns_ns = "http://www.w3.org/2000/xmlns/";
var RandomizeModes = {
LET_DOCUMENT_DECIDE: 0,
ALWAYS_RANDOMIZE: 1,
NEVER_RANDOMIZE: 2
};
var randomize_ids = RandomizeModes.LET_DOCUMENT_DECIDE;
/** /**
* This class encapsulates the concept of a layer in the drawing * This class encapsulates the concept of a layer in the drawing
* @param name {String} Layer name * @param name {String} Layer name
@ -42,6 +49,23 @@ svgedit.draw.Layer.prototype.getGroup = function() {
}; };
// Called to ensure that drawings will or will not have randomized ids.
// The current_drawing will have its nonce set if it doesn't already.
//
// Params:
// enableRandomization - flag indicating if documents should have randomized ids
svgedit.draw.randomizeIds = function(enableRandomization, current_drawing) {
randomize_ids = enableRandomization == false ?
RandomizeModes.NEVER_RANDOMIZE :
RandomizeModes.ALWAYS_RANDOMIZE;
if (randomize_ids == RandomizeModes.ALWAYS_RANDOMIZE && !current_drawing.getNonce()) {
current_drawing.setNonce(Math.floor(Math.random() * 100001));
} else if (randomize_ids == RandomizeModes.NEVER_RANDOMIZE && current_drawing.getNonce()) {
current_drawing.clearNonce();
}
};
/** /**
* This class encapsulates the concept of a SVG-edit drawing * This class encapsulates the concept of a SVG-edit drawing
* *
@ -100,7 +124,15 @@ svgedit.draw.Drawing = function(svgElem, opt_idPrefix) {
* The nonce to use to uniquely identify elements across drawings. * The nonce to use to uniquely identify elements across drawings.
* @type {!String} * @type {!String}
*/ */
this.nonce_ = this.svgElem_.getAttributeNS(se_ns, 'nonce') || ""; this.nonce_ = "";
var n = this.svgElem_.getAttributeNS(se_ns, 'nonce');
// If already set in the DOM, use the nonce throughout the document
// else, if randomizeIds(true) has been called, create and set the nonce.
if (!!n && randomize_ids != RandomizeModes.NEVER_RANDOMIZE) {
this.nonce_ = n;
} else if (randomize_ids == RandomizeModes.ALWAYS_RANDOMIZE) {
this.setNonce(Math.floor(Math.random() * 100001));
}
}; };
svgedit.draw.Drawing.prototype.getElem_ = function(id) { svgedit.draw.Drawing.prototype.getElem_ = function(id) {
@ -121,10 +153,16 @@ svgedit.draw.Drawing.prototype.getNonce = function() {
return this.nonce_; return this.nonce_;
}; };
svgedit.draw.Drawing.prototype.setNonce = function(nonce) { svgedit.draw.Drawing.prototype.setNonce = function(n) {
this.nonce_ = nonce;
this.svgElem_.setAttributeNS(xmlns_ns, 'xmlns:se', se_ns); this.svgElem_.setAttributeNS(xmlns_ns, 'xmlns:se', se_ns);
this.svgElem_.setAttributeNS(se_ns, 'se:nonce', nonce); this.svgElem_.setAttributeNS(se_ns, 'se:nonce', n);
this.nonce_ = n;
};
svgedit.draw.Drawing.prototype.clearNonce = function() {
// We deliberately leave any se:nonce attributes alone,
// we just don't use it to randomize ids.
this.nonce_ = "";
}; };
/** /**

View file

@ -15,8 +15,8 @@ svgEditor.addExtension("Arrows", function(S) {
randomize_ids = S.randomize_ids, randomize_ids = S.randomize_ids,
selElems; selElems;
svgCanvas.bind('setarrownonce', setArrowNonce); svgCanvas.bind('setnonce', setArrowNonce);
svgCanvas.bind('unsetsetarrownonce', unsetArrowNonce); svgCanvas.bind('unsetnonce', unsetArrowNonce);
var lang_list = { var lang_list = {
"en":[ "en":[

View file

@ -36,13 +36,23 @@ var removedElements = {};
* An interface that all command objects must implement. * An interface that all command objects must implement.
* *
* interface svgedit.history.HistoryCommand { * interface svgedit.history.HistoryCommand {
* void apply(); * void apply(svgedit.history.HistoryEventHandler);
* void unapply(); * void unapply(svgedit.history.HistoryEventHandler);
* Element[] elements(); * Element[] elements();
* String getText(); * String getText();
* *
* static String type(); * static String type();
* } * }
*
* Interface: svgedit.history.HistoryEventHandler
* An interface for objects that will handle history events.
*
* interface svgedit.history.HistoryEventHandler {
* void handleHistoryEvent(eventType, command);
* }
*
* eventType is a string conforming to one of the HistoryEvent types.
* command is an object fulfilling the HistoryCommand interface.
*/ */
// Class: svgedit.history.MoveElementCommand // Class: svgedit.history.MoveElementCommand
@ -213,8 +223,12 @@ svgedit.history.RemoveElementCommand.prototype.unapply = function(handler) {
} }
svgedit.transformlist.removeElementFromListMap(this.elem); svgedit.transformlist.removeElementFromListMap(this.elem);
if(this.nextSibling == null) {
console.log('Error: reference element was lost');
}
this.parent.insertBefore(this.elem, this.nextSibling); this.parent.insertBefore(this.elem, this.nextSibling);
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
} }
@ -443,18 +457,6 @@ svgedit.history.BatchCommand.prototype.isEmpty = function() {
}; };
/**
* Interface: svgedit.history.HistoryEventHandler
* An interface for objects that will handle history events.
*
* interface svgedit.history.HistoryEventHandler {
* void handleHistoryEvent(eventType, command);
* }
*
* eventType is a string conforming to one of the HistoryEvent types (see above).
* command is an object fulfilling the HistoryCommand interface (see above).
*/
// Class: svgedit.history.UndoManager // Class: svgedit.history.UndoManager
// Parameters: // Parameters:
// historyEventHandler - an object that conforms to the HistoryEventHandler interface // historyEventHandler - an object that conforms to the HistoryEventHandler interface

View file

@ -532,7 +532,7 @@
position: absolute; position: absolute;
top: 77px; top: 77px;
left: 40px; left: 40px;
right: -2px; right: 0;
line-height: 22px; line-height: 22px;
overflow: auto; overflow: auto;
border: 1px solid #777; border: 1px solid #777;

View file

@ -1535,8 +1535,8 @@
} }
if(unit) { if(unit) {
x = svgCanvas.convertUnit(x); x = svgedit.units.convertUnit(x);
y = svgCanvas.convertUnit(y); y = svgedit.units.convertUnit(y);
} }
$('#selected_x').val(x || 0); $('#selected_x').val(x || 0);
@ -1560,8 +1560,8 @@
if(point) { if(point) {
var seg_type = $('#seg_type'); var seg_type = $('#seg_type');
if(unit) { if(unit) {
point.x = svgCanvas.convertUnit(point.x); point.x = svgedit.units.convertUnit(point.x);
point.y = svgCanvas.convertUnit(point.y); point.y = svgedit.units.convertUnit(point.y);
} }
$('#path_node_x').val(point.x); $('#path_node_x').val(point.x);
$('#path_node_y').val(point.y); $('#path_node_y').val(point.y);
@ -1622,7 +1622,7 @@
var attrVal = elem.getAttribute(item); var attrVal = elem.getAttribute(item);
if(curConfig.baseUnit !== 'px' && elem[item]) { if(curConfig.baseUnit !== 'px' && elem[item]) {
var bv = elem[item].baseVal.value; var bv = elem[item].baseVal.value;
attrVal = svgCanvas.convertUnit(bv); attrVal = svgedit.units.convertUnit(bv);
} }
$('#' + el_name + '_' + item).val(attrVal || 0); $('#' + el_name + '_' + item).val(attrVal || 0);
@ -2734,8 +2734,8 @@
// update resolution option with actual resolution // update resolution option with actual resolution
var res = svgCanvas.getResolution(); var res = svgCanvas.getResolution();
if(curConfig.baseUnit !== "px") { if(curConfig.baseUnit !== "px") {
res.w = svgCanvas.convertUnit(res.w) + curConfig.baseUnit; res.w = svgedit.units.convertUnit(res.w) + curConfig.baseUnit;
res.h = svgCanvas.convertUnit(res.h) + curConfig.baseUnit; res.h = svgedit.units.convertUnit(res.h) + curConfig.baseUnit;
} }
$('#canvas_width').val(res.w); $('#canvas_width').val(res.w);
@ -4583,7 +4583,7 @@
updateCanvas(true); updateCanvas(true);
// }); // });
// var revnums = "svg-editor.js ($Rev: 1956 $) "; // var revnums = "svg-editor.js ($Rev: 1973 $) ";
// revnums += svgCanvas.getVersion(); // revnums += svgCanvas.getVersion();
// $('#copyright')[0].setAttribute("title", revnums); // $('#copyright')[0].setAttribute("title", revnums);
@ -4721,6 +4721,13 @@
}); });
}; };
Editor.disableUI = function(featList) {
// $(function() {
// $('#tool_wireframe, #tool_image, #main_button, #tool_source, #sidepanels').remove();
// $('#tools_top').css('left', 5);
// });
};
Editor.loadFromURL = function(url, opts) { Editor.loadFromURL = function(url, opts) {
if(!opts) opts = {}; if(!opts) opts = {};

View file

@ -151,17 +151,29 @@ container.appendChild(svgroot);
// The actual element that represents the final output SVG element // The actual element that represents the final output SVG element
var svgcontent = svgdoc.createElementNS(svgns, "svg"); var svgcontent = svgdoc.createElementNS(svgns, "svg");
$(svgcontent).attr({ // This function resets the svgcontent element while keeping it in the DOM.
id: 'svgcontent', var clearSvgContentElement = canvas.clearSvgContentElement = function() {
width: dimensions[0], while (svgcontent.firstChild) { svgcontent.removeChild(svgcontent.firstChild); }
height: dimensions[1],
x: dimensions[0], // TODO: Clear out all other attributes first?
y: dimensions[1], $(svgcontent).attr({
overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden', id: 'svgcontent',
xmlns: svgns, width: dimensions[0],
"xmlns:se": se_ns, height: dimensions[1],
"xmlns:xlink": xlinkns x: dimensions[0],
}).appendTo(svgroot); y: dimensions[1],
overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden',
xmlns: svgns,
"xmlns:se": se_ns,
"xmlns:xlink": xlinkns
}).appendTo(svgroot);
// TODO: make this string optional and set by the client
var comment = svgdoc.createComment(" Created with SVG-edit - http://svg-edit.googlecode.com/ ");
// Lead to invalid content with Instiki's Sanitizer
// svgcontent.appendChild(comment);
};
clearSvgContentElement();
// Prefix string for element IDs // Prefix string for element IDs
var idprefix = "svg_"; var idprefix = "svg_";
@ -177,27 +189,15 @@ canvas.setIdPrefix = function(p) {
// Current svgedit.draw.Drawing object // Current svgedit.draw.Drawing object
// @type {svgedit.draw.Drawing} // @type {svgedit.draw.Drawing}
var current_drawing = new svgedit.draw.Drawing(svgcontent, idprefix); canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent, idprefix);
// Function: getCurrentDrawing // Function: getCurrentDrawing
// Returns the current Drawing. // Returns the current Drawing.
// @return {svgedit.draw.Drawing} // @return {svgedit.draw.Drawing}
var getCurrentDrawing = canvas.getCurrentDrawing = function() { var getCurrentDrawing = canvas.getCurrentDrawing = function() {
return current_drawing; return canvas.current_drawing_;
}; };
// nonce to uniquify id's
var nonce = Math.floor(Math.random() * 100001);
// Boolean to indicate whether or not IDs given to elements should be random
var randomize_ids = false;
// Set nonce if randomize_ids = true
// TODO: Remove this? Given randomize_ids is set to false above, it seems useless.
if (randomize_ids) {
getCurrentDrawing().setNonce(nonce);
}
// Float displaying the current zoom level (1 = 100%, .5 = 50%, etc) // Float displaying the current zoom level (1 = 100%, .5 = 50%, etc)
var current_zoom = 1; var current_zoom = 1;
@ -386,7 +386,8 @@ svgedit.units.init({
getBaseUnit: function() { return curConfig.baseUnit; }, getBaseUnit: function() { return curConfig.baseUnit; },
getElement: getElem, getElement: getElem,
getHeight: function() { return svgcontent.getAttribute("height")/current_zoom; }, getHeight: function() { return svgcontent.getAttribute("height")/current_zoom; },
getWidth: function() { return svgcontent.getAttribute("width")/current_zoom; } getWidth: function() { return svgcontent.getAttribute("width")/current_zoom; },
getRoundDigits: function() { return save_options.round_digits; }
}); });
// import from units.js // import from units.js
var convertToNum = canvas.convertToNum = svgedit.units.convertToNum; var convertToNum = canvas.convertToNum = svgedit.units.convertToNum;
@ -426,7 +427,7 @@ canvas.undoMgr = new svgedit.history.UndoManager({
call("changed", elems); call("changed", elems);
var cmdType = cmd.type(); var cmdType = cmd.type();
var isApply = eventType == EventTypes.AFTER_APPLY; var isApply = (eventType == EventTypes.AFTER_APPLY);
if (cmdType == MoveElementCommand.type()) { if (cmdType == MoveElementCommand.type()) {
var parent = isApply ? cmd.newParent : cmd.oldParent; var parent = isApply ? cmd.newParent : cmd.oldParent;
if (parent == svgcontent) { if (parent == svgcontent) {
@ -442,6 +443,10 @@ canvas.undoMgr = new svgedit.history.UndoManager({
} else { } else {
if (!isApply) restoreRefElems(cmd.elem); if (!isApply) restoreRefElems(cmd.elem);
} }
if(cmd.elem.tagName === 'use') {
setUseData(cmd.elem);
}
} else if (cmdType == ChangeElementCommand.type()) { } else if (cmdType == ChangeElementCommand.type()) {
// if we are changing layer names, re-identify all layers // if we are changing layer names, re-identify all layers
if (cmd.elem.tagName == "title" && cmd.elem.parentNode.parentNode == svgcontent) { if (cmd.elem.tagName == "title" && cmd.elem.parentNode.parentNode == svgcontent) {
@ -524,14 +529,17 @@ var restoreRefElems = function(elem) {
} }
} }
} }
var childs = elem.getElementsByTagName('*');
if(childs.length) {
for(var i = 0, l = childs.length; i < l; i++) {
restoreRefElems(childs[i]);
}
}
}; };
(function() { (function() {
// TODO: make this string optional and set by the client
var comment = svgdoc.createComment(" Created with SVG-edit - http://svg-edit.googlecode.com/ ");
// Lead to invalid content with Instiki's Sanitizer
// svgcontent.appendChild(comment);
// TODO For Issue 208: this is a start on a thumbnail // TODO For Issue 208: this is a start on a thumbnail
// var svgthumb = svgdoc.createElementNS(svgns, "use"); // var svgthumb = svgdoc.createElementNS(svgns, "use");
// svgthumb.setAttribute('width', '100'); // svgthumb.setAttribute('width', '100');
@ -628,7 +636,7 @@ this.addExtension = function(name, ext_func) {
var ext = ext_func($.extend(canvas.getPrivateMethods(), { var ext = ext_func($.extend(canvas.getPrivateMethods(), {
svgroot: svgroot, svgroot: svgroot,
svgcontent: svgcontent, svgcontent: svgcontent,
nonce: nonce, nonce: getCurrentDrawing().getNonce(),
selectorManager: selectorManager selectorManager: selectorManager
})); }));
} else { } else {
@ -640,32 +648,6 @@ this.addExtension = function(name, ext_func) {
console.log('Cannot add extension "' + name + '", an extension by that name already exists"'); console.log('Cannot add extension "' + name + '", an extension by that name already exists"');
} }
}; };
// Function: shortFloat
// Rounds a given value to a float with number of digits defined in save_options
//
// Parameters:
// val - The value as a String, Number or Array of two numbers to be rounded
//
// Returns:
// If a string/number was given, returns a Float. If an array, return a string
// with comma-seperated floats
var shortFloat = function(val) {
var digits = save_options.round_digits;
if(!isNaN(val)) {
// Note that + converts to Number
return +((+val).toFixed(digits));
} else if($.isArray(val)) {
return shortFloat(val[0]) + ',' + shortFloat(val[1]);
} else {
return parseFloat(val).toFixed(digits) - 0;
}
}
this.convertUnit = function(val, unit) {
return shortFloat(svgedit.units.convertUnit(val, unit));
};
// This method rounds the incoming value to the nearest value based on the current_zoom // This method rounds the incoming value to the nearest value based on the current_zoom
var round = this.round = function(val) { var round = this.round = function(val) {
@ -1547,6 +1529,23 @@ var recalculateDimensions = this.recalculateDimensions = function(selected) {
} }
} }
// If it still has a single [M] or [R][M], return null too (prevents BatchCommand from being returned).
switch ( selected.tagName ) {
// Ignore these elements, as they can absorb the [M]
case 'line':
case 'polyline':
case 'polygon':
case 'path':
break;
default:
if(
(tlist.numberOfItems === 1 && tlist.getItem(0).type === 1)
|| (tlist.numberOfItems === 2 && tlist.getItem(0).type === 1 && tlist.getItem(0).type === 4)
) {
return null;
}
}
// Grouped SVG element // Grouped SVG element
var gsvg = $(selected).data('gsvg'); var gsvg = $(selected).data('gsvg');
@ -2184,6 +2183,7 @@ var recalculateDimensions = this.recalculateDimensions = function(selected) {
if (tlist.numberOfItems == 0) { if (tlist.numberOfItems == 0) {
selected.removeAttribute("transform"); selected.removeAttribute("transform");
} }
batchCmd.addSubCommand(new ChangeElementCommand(selected, initial)); batchCmd.addSubCommand(new ChangeElementCommand(selected, initial));
return batchCmd; return batchCmd;
@ -5832,9 +5832,9 @@ var pathActions = canvas.pathActions = function() {
var addToD = function(pnts, more, last) { var addToD = function(pnts, more, last) {
var str = ''; var str = '';
var more = more?' '+more.join(' '):''; var more = more?' '+more.join(' '):'';
var last = last?' '+shortFloat(last):''; var last = last?' '+svgedit.units.shortFloat(last):'';
$.each(pnts, function(i, pnt) { $.each(pnts, function(i, pnt) {
pnts[i] = shortFloat(pnt); pnts[i] = svgedit.units.shortFloat(pnt);
}); });
d += letter + pnts.join(' ') + more + last; d += letter + pnts.join(' ') + more + last;
} }
@ -5984,6 +5984,8 @@ var removeUnusedDefElems = this.removeUnusedDefElems = function() {
var defs = svgcontent.getElementsByTagNameNS(svgns, "defs"); var defs = svgcontent.getElementsByTagNameNS(svgns, "defs");
if(!defs || !defs.length) return 0; if(!defs || !defs.length) return 0;
// if(!defs.firstChild) return;
var defelem_uses = [], var defelem_uses = [],
numRemoved = 0; numRemoved = 0;
var attrs = ['fill', 'stroke', 'filter', 'marker-start', 'marker-mid', 'marker-end']; var attrs = ['fill', 'stroke', 'filter', 'marker-start', 'marker-mid', 'marker-end'];
@ -5996,7 +5998,9 @@ var removeUnusedDefElems = this.removeUnusedDefElems = function() {
var el = all_els[i]; var el = all_els[i];
for(var j = 0; j < alen; j++) { for(var j = 0; j < alen; j++) {
var ref = getUrlFromAttr(el.getAttribute(attrs[j])); var ref = getUrlFromAttr(el.getAttribute(attrs[j]));
if(ref) defelem_uses.push(ref.substr(1)); if(ref) {
defelem_uses.push(ref.substr(1));
}
} }
// gradients can refer to other gradients // gradients can refer to other gradients
@ -6019,16 +6023,7 @@ var removeUnusedDefElems = this.removeUnusedDefElems = function() {
numRemoved++; numRemoved++;
} }
} }
// Remove defs if empty
var i = defs.length;
while (i--) {
var def = defs[i];
if(!def.getElementsByTagNameNS(svgns,'*').length) {
def.parentNode.removeChild(def);
}
}
return numRemoved; return numRemoved;
} }
@ -6119,16 +6114,16 @@ this.svgToString = function(elem, indent) {
// if(curConfig.baseUnit !== "px") { // if(curConfig.baseUnit !== "px") {
// var unit = curConfig.baseUnit; // var unit = curConfig.baseUnit;
// var unit_m = svgedit.units.getTypeMap()[unit]; // var unit_m = svgedit.units.getTypeMap()[unit];
// res.w = shortFloat(res.w / unit_m) // res.w = svgedit.units.shortFloat(res.w / unit_m)
// res.h = shortFloat(res.h / unit_m) // res.h = svgedit.units.shortFloat(res.h / unit_m)
// vb = ' viewBox="' + [0, 0, res.w, res.h].join(' ') + '"'; // vb = ' viewBox="' + [0, 0, res.w, res.h].join(' ') + '"';
// res.w += unit; // res.w += unit;
// res.h += unit; // res.h += unit;
// } // }
if(unit !== "px") { if(unit !== "px") {
res.w = this.convertUnit(res.w, unit) + unit; res.w = svgedit.units.convertUnit(res.w, unit) + unit;
res.h = this.convertUnit(res.h, unit) + unit; res.h = svgedit.units.convertUnit(res.h, unit) + unit;
} }
out.push(' width="' + res.w + '" height="' + res.h + '"' + vb + ' xmlns="'+svgns+'"'); out.push(' width="' + res.w + '" height="' + res.h + '"' + vb + ' xmlns="'+svgns+'"');
@ -6168,6 +6163,9 @@ this.svgToString = function(elem, indent) {
} }
} }
} else { } else {
// Skip empty defs
if(elem.nodeName === 'defs' && !elem.firstChild) return;
var moz_attrs = ['-moz-math-font-style', '_moz-math-font-style']; var moz_attrs = ['-moz-math-font-style', '_moz-math-font-style'];
for (var i=attrs.length-1; i>=0; i--) { for (var i=attrs.length-1; i>=0; i--) {
attr = attrs.item(i); attr = attrs.item(i);
@ -6180,9 +6178,9 @@ this.svgToString = function(elem, indent) {
out.push(" "); out.push(" ");
if(attr.localName === 'd') attrVal = pathActions.convertPath(elem, true); if(attr.localName === 'd') attrVal = pathActions.convertPath(elem, true);
if(!isNaN(attrVal)) { if(!isNaN(attrVal)) {
attrVal = shortFloat(attrVal); attrVal = svgedit.units.shortFloat(attrVal);
} else if(unit_re.test(attrVal)) { } else if(unit_re.test(attrVal)) {
attrVal = shortFloat(attrVal) + unit; attrVal = svgedit.units.shortFloat(attrVal) + unit;
} }
// Embed images when saving // Embed images when saving
@ -6350,9 +6348,9 @@ this.getSvgString = function() {
return this.svgCanvasToString(); return this.svgCanvasToString();
}; };
//function randomizeIds // Function: randomizeIds
// This function determines whether to add a nonce to the prefix, when // This function determines whether to use a nonce in the prefix, when
// generating IDs in SVG-Edit // generating IDs for future documents in SVG-Edit.
// //
// Parameters: // Parameters:
// an opional boolean, which, if true, adds a nonce to the prefix. Thus // an opional boolean, which, if true, adds a nonce to the prefix. Thus
@ -6363,17 +6361,11 @@ this.getSvgString = function() {
// //
this.randomizeIds = function() { this.randomizeIds = function() {
if (arguments.length > 0 && arguments[0] == false) { if (arguments.length > 0 && arguments[0] == false) {
randomize_ids = false; svgedit.draw.randomizeIds(false, getCurrentDrawing());
if (extensions["Arrows"]) call("unsetarrownonce") ;
} else { } else {
randomize_ids = true; svgedit.draw.randomizeIds(true, getCurrentDrawing());
var drawing = getCurrentDrawing();
if (!drawing.getSvgElem().getAttributeNS(se_ns, 'nonce')) {
drawing.setNonce(nonce);
if (extensions["Arrows"]) call("setarrownonce", nonce);
}
} }
} };
// Function: uniquifyElems // Function: uniquifyElems
// Ensure each element has a unique ID // Ensure each element has a unique ID
@ -6472,9 +6464,16 @@ var uniquifyElems = this.uniquifyElems = function(g) {
// Function setUseData // Function setUseData
// Assigns reference data for each use element // Assigns reference data for each use element
var setUseData = this.setUseData = function(parent) { var setUseData = this.setUseData = function(parent) {
$(parent).find('use').each(function() { var elems = $(parent);
if(parent.tagName !== 'use') {
elems = elems.find('use');
}
elems.each(function() {
var id = getHref(this).substr(1); var id = getHref(this).substr(1);
var ref_elem = getElem(id); var ref_elem = getElem(id);
if(!ref_elem) return;
$(this).data('ref', ref_elem); $(this).data('ref', ref_elem);
if(ref_elem.tagName == 'symbol' || ref_elem.tagName == 'svg') { if(ref_elem.tagName == 'symbol' || ref_elem.tagName == 'svg') {
$(this).data('symbol', ref_elem); $(this).data('symbol', ref_elem);
@ -6669,8 +6668,11 @@ var convertToGroup = this.convertToGroup = function(elem) {
selectOnly([g]); selectOnly([g]);
batchCmd.addSubCommand(pushGroupProperties(g, true)); var cm = pushGroupProperties(g, true);
// if(cm) {
batchCmd.addSubCommand(cm);
}
addCommandToHistory(batchCmd); addCommandToHistory(batchCmd);
} else { } else {
@ -6706,19 +6708,16 @@ this.setSvgString = function(xmlString) {
var content = $(svgcontent); var content = $(svgcontent);
current_drawing = new svgedit.draw.Drawing(svgcontent, idprefix); canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent, idprefix);
// retrieve or set the nonce // retrieve or set the nonce
n = svgcontent.getAttributeNS(se_ns, 'nonce'); var nonce = getCurrentDrawing().getNonce();
if (n) { if (nonce) {
randomize_ids = true; call("setnonce", nonce);
nonce = n; } else {
if (extensions["Arrows"]) call("setarrownonce", n) ; call("unsetnonce");
} else if (randomize_ids) {
current_drawing.setNonce(nonce);
if (extensions["Arrows"]) call("setarrownonce", nonce) ;
} }
// change image href vals if possible // change image href vals if possible
content.find('image').each(function() { content.find('image').each(function() {
var image = this; var image = this;
@ -7042,10 +7041,10 @@ this.cloneLayer = function(name) {
// Deletes the current layer from the drawing and then clears the selection. This function // Deletes the current layer from the drawing and then clears the selection. This function
// then calls the 'changed' handler. This is an undoable action. // then calls the 'changed' handler. This is an undoable action.
this.deleteCurrentLayer = function() { this.deleteCurrentLayer = function() {
var current_layer = current_drawing.getCurrentLayer(); var current_layer = getCurrentDrawing().getCurrentLayer();
var nextSibling = current_layer.nextSibling; var nextSibling = current_layer.nextSibling;
var parent = current_layer.parentNode; var parent = current_layer.parentNode;
current_layer = current_drawing.deleteCurrentLayer(); current_layer = getCurrentDrawing().deleteCurrentLayer();
if (current_layer) { if (current_layer) {
var batchCmd = new BatchCommand("Delete Layer"); var batchCmd = new BatchCommand("Delete Layer");
// store in our Undo History // store in our Undo History
@ -7086,18 +7085,19 @@ this.setCurrentLayer = function(name) {
// Returns: // Returns:
// true if the rename succeeded, false otherwise. // true if the rename succeeded, false otherwise.
this.renameCurrentLayer = function(newname) { this.renameCurrentLayer = function(newname) {
if (current_drawing.current_layer) { var drawing = getCurrentDrawing();
var oldLayer = current_drawing.current_layer; if (drawing.current_layer) {
var oldLayer = drawing.current_layer;
// setCurrentLayer will return false if the name doesn't already exist // setCurrentLayer will return false if the name doesn't already exist
// this means we are free to rename our oldLayer // this means we are free to rename our oldLayer
if (!canvas.setCurrentLayer(newname)) { if (!canvas.setCurrentLayer(newname)) {
var batchCmd = new BatchCommand("Rename Layer"); var batchCmd = new BatchCommand("Rename Layer");
// find the index of the layer // find the index of the layer
for (var i = 0; i < current_drawing.getNumLayers(); ++i) { for (var i = 0; i < drawing.getNumLayers(); ++i) {
if (current_drawing.all_layers[i][1] == oldLayer) break; if (drawing.all_layers[i][1] == oldLayer) break;
} }
var oldname = current_drawing.getLayerName(i); var oldname = drawing.getLayerName(i);
current_drawing.all_layers[i][0] = svgedit.utilities.toXml(newname); drawing.all_layers[i][0] = svgedit.utilities.toXml(newname);
// now change the underlying title element contents // now change the underlying title element contents
var len = oldLayer.childNodes.length; var len = oldLayer.childNodes.length;
@ -7116,7 +7116,7 @@ this.renameCurrentLayer = function(newname) {
} }
} }
} }
current_drawing.current_layer = oldLayer; drawing.current_layer = oldLayer;
} }
return false; return false;
}; };
@ -7133,31 +7133,32 @@ this.renameCurrentLayer = function(newname) {
// Returns: // Returns:
// true if the current layer position was changed, false otherwise. // true if the current layer position was changed, false otherwise.
this.setCurrentLayerPosition = function(newpos) { this.setCurrentLayerPosition = function(newpos) {
if (current_drawing.current_layer && newpos >= 0 && newpos < current_drawing.getNumLayers()) { var drawing = getCurrentDrawing();
for (var oldpos = 0; oldpos < current_drawing.getNumLayers(); ++oldpos) { if (drawing.current_layer && newpos >= 0 && newpos < drawing.getNumLayers()) {
if (current_drawing.all_layers[oldpos][1] == current_drawing.current_layer) break; for (var oldpos = 0; oldpos < drawing.getNumLayers(); ++oldpos) {
if (drawing.all_layers[oldpos][1] == drawing.current_layer) break;
} }
// some unknown error condition (current_layer not in all_layers) // some unknown error condition (current_layer not in all_layers)
if (oldpos == current_drawing.getNumLayers()) { return false; } if (oldpos == drawing.getNumLayers()) { return false; }
if (oldpos != newpos) { if (oldpos != newpos) {
// if our new position is below us, we need to insert before the node after newpos // if our new position is below us, we need to insert before the node after newpos
var refLayer = null; var refLayer = null;
var oldNextSibling = current_drawing.current_layer.nextSibling; var oldNextSibling = drawing.current_layer.nextSibling;
if (newpos > oldpos ) { if (newpos > oldpos ) {
if (newpos < current_drawing.getNumLayers()-1) { if (newpos < drawing.getNumLayers()-1) {
refLayer = current_drawing.all_layers[newpos+1][1]; refLayer = drawing.all_layers[newpos+1][1];
} }
} }
// if our new position is above us, we need to insert before the node at newpos // if our new position is above us, we need to insert before the node at newpos
else { else {
refLayer = current_drawing.all_layers[newpos][1]; refLayer = drawing.all_layers[newpos][1];
} }
svgcontent.insertBefore(current_drawing.current_layer, refLayer); svgcontent.insertBefore(drawing.current_layer, refLayer);
addCommandToHistory(new MoveElementCommand(current_drawing.current_layer, oldNextSibling, svgcontent)); addCommandToHistory(new MoveElementCommand(drawing.current_layer, oldNextSibling, svgcontent));
identifyLayers(); identifyLayers();
canvas.setCurrentLayer(current_drawing.getLayerName(newpos)); canvas.setCurrentLayer(drawing.getLayerName(newpos));
return true; return true;
} }
@ -7207,9 +7208,10 @@ this.setLayerVisibility = function(layername, bVisible) {
this.moveSelectedToLayer = function(layername) { this.moveSelectedToLayer = function(layername) {
// find the layer // find the layer
var layer = null; var layer = null;
for (var i = 0; i < current_drawing.getNumLayers(); ++i) { var drawing = getCurrentDrawing();
if (current_drawing.getLayerName(i) == layername) { for (var i = 0; i < drawing.getNumLayers(); ++i) {
layer = current_drawing.all_layers[i][1]; if (drawing.getLayerName(i) == layername) {
layer = drawing.all_layers[i][1];
break; break;
} }
} }
@ -7237,28 +7239,29 @@ this.moveSelectedToLayer = function(layername) {
this.mergeLayer = function(skipHistory) { this.mergeLayer = function(skipHistory) {
var batchCmd = new BatchCommand("Merge Layer"); var batchCmd = new BatchCommand("Merge Layer");
var prev = $(current_drawing.current_layer).prev()[0]; var drawing = getCurrentDrawing();
var prev = $(drawing.current_layer).prev()[0];
if(!prev) return; if(!prev) return;
var childs = current_drawing.current_layer.childNodes; var childs = drawing.current_layer.childNodes;
var len = childs.length; var len = childs.length;
var layerNextSibling = current_drawing.current_layer.nextSibling; var layerNextSibling = drawing.current_layer.nextSibling;
batchCmd.addSubCommand(new RemoveElementCommand(current_drawing.current_layer, layerNextSibling, svgcontent)); batchCmd.addSubCommand(new RemoveElementCommand(drawing.current_layer, layerNextSibling, svgcontent));
while(current_drawing.current_layer.firstChild) { while(drawing.current_layer.firstChild) {
var ch = current_drawing.current_layer.firstChild; var ch = drawing.current_layer.firstChild;
if(ch.localName == 'title') { if(ch.localName == 'title') {
var chNextSibling = ch.nextSibling; var chNextSibling = ch.nextSibling;
batchCmd.addSubCommand(new RemoveElementCommand(ch, chNextSibling, current_drawing.current_layer)); batchCmd.addSubCommand(new RemoveElementCommand(ch, chNextSibling, drawing.current_layer));
current_drawing.current_layer.removeChild(ch); drawing.current_layer.removeChild(ch);
continue; continue;
} }
var oldNextSibling = ch.nextSibling; var oldNextSibling = ch.nextSibling;
prev.appendChild(ch); prev.appendChild(ch);
batchCmd.addSubCommand(new MoveElementCommand(ch, oldNextSibling, current_drawing.current_layer)); batchCmd.addSubCommand(new MoveElementCommand(ch, oldNextSibling, drawing.current_layer));
} }
// Remove current layer // Remove current layer
svgcontent.removeChild(current_drawing.current_layer); svgcontent.removeChild(drawing.current_layer);
if(!skipHistory) { if(!skipHistory) {
clearSelection(); clearSelection();
@ -7269,13 +7272,14 @@ this.mergeLayer = function(skipHistory) {
addCommandToHistory(batchCmd); addCommandToHistory(batchCmd);
} }
current_drawing.current_layer = prev; drawing.current_layer = prev;
return batchCmd; return batchCmd;
} }
this.mergeAllLayers = function() { this.mergeAllLayers = function() {
var batchCmd = new BatchCommand("Merge all Layers"); var batchCmd = new BatchCommand("Merge all Layers");
current_drawing.current_layer = current_drawing.all_layers[current_drawing.getNumLayers()-1][1]; var drawing = getCurrentDrawing();
drawing.current_layer = drawing.all_layers[drawing.getNumLayers()-1][1];
while($(svgcontent).children('g').length > 1) { while($(svgcontent).children('g').length > 1) {
batchCmd.addSubCommand(canvas.mergeLayer(true)); batchCmd.addSubCommand(canvas.mergeLayer(true));
} }
@ -7342,29 +7346,26 @@ var setContext = this.setContext = function(elem) {
this.clear = function() { this.clear = function() {
pathActions.clear(); pathActions.clear();
// clear the svgcontent node
var nodes = svgcontent.childNodes;
var len = svgcontent.childNodes.length;
var i = 0;
clearSelection(); clearSelection();
for(var rep = 0; rep < len; rep++){
if (nodes[i].nodeType == 1) { // element node // clear the svgcontent node
svgcontent.removeChild(nodes[i]); canvas.clearSvgContentElement();
} else {
i++; // create new document
} canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent);
}
// create empty first layer // create empty first layer
current_drawing.all_layers = [];
canvas.createLayer("Layer 1"); canvas.createLayer("Layer 1");
// clear the undo stack // clear the undo stack
canvas.undoMgr.resetUndoStack(); canvas.undoMgr.resetUndoStack();
// reset the selector manager // reset the selector manager
selectorManager.initGroup(); selectorManager.initGroup();
// reset the rubber band box // reset the rubber band box
rubberBox = selectorManager.getRubberBandBox(); rubberBox = selectorManager.getRubberBandBox();
call("cleared"); call("cleared");
}; };
@ -7407,7 +7408,7 @@ this.getZoom = function(){return current_zoom;};
// 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: 1961 $)"; return "svgcanvas.js ($Rev: 1972 $)";
}; };
// Function: setUiStrings // Function: setUiStrings
@ -8730,8 +8731,8 @@ var changeSelectedAttributeNoUndo = function(attr, newValue, elems) {
selectedBBoxes[i] = getBBox(elem); selectedBBoxes[i] = getBBox(elem);
// Use the Firefox ffClone hack for text elements with gradients or // Use the Firefox ffClone hack for text elements with gradients or
// where other text attributes are changed. // where other text attributes are changed.
if(elem.nodeName == 'text') { if(svgedit.browser.isGecko() && elem.nodeName === 'text' && /rotate/.test(elem.getAttribute('transform'))) {
if((newValue+'').indexOf('url') == 0 || ['font-size','font-family','x','y'].indexOf(attr) >= 0 && elem.textContent) { if((newValue+'').indexOf('url') === 0 || ['font-size','font-family','x','y'].indexOf(attr) >= 0 && elem.textContent) {
elem = ffClone(elem); elem = ffClone(elem);
} }
} }
@ -9015,6 +9016,8 @@ var pushGroupProperties = this.pushGroupProperties = function(g, undoable) {
for(var i = 0; i < len; i++) { for(var i = 0; i < len; i++) {
var elem = children[i]; var elem = children[i];
if(elem.nodeType !== 1) continue;
if(gattrs.opacity !== null && gattrs.opacity !== 1) { if(gattrs.opacity !== null && gattrs.opacity !== 1) {
var c_opac = elem.getAttribute('opacity') || 1; var c_opac = elem.getAttribute('opacity') || 1;
var new_opac = Math.round((elem.getAttribute('opacity') || 1) * gattrs.opacity * 100)/100; var new_opac = Math.round((elem.getAttribute('opacity') || 1) * gattrs.opacity * 100)/100;
@ -9149,7 +9152,8 @@ var pushGroupProperties = this.pushGroupProperties = function(g, undoable) {
newxform.setMatrix(gm); newxform.setMatrix(gm);
chtlist.appendItem(newxform); chtlist.appendItem(newxform);
} }
batchCmd.addSubCommand(recalculateDimensions(elem)); var cmd = recalculateDimensions(elem);
if(cmd) batchCmd.addSubCommand(cmd);
} }
} }
@ -9670,7 +9674,6 @@ this.getPrivateMethods = function() {
round: round, round: round,
runExtensions: runExtensions, runExtensions: runExtensions,
sanitizeSvg: sanitizeSvg, sanitizeSvg: sanitizeSvg,
shortFloat: shortFloat,
SVGEditTransformList: svgedit.transformlist.SVGTransformList, SVGEditTransformList: svgedit.transformlist.SVGTransformList,
toString: toString, toString: toString,
transformBox: svgedit.math.transformBox, transformBox: svgedit.math.transformBox,

View file

@ -186,7 +186,7 @@ svgedit.utilities.convertToXMLReferences = function(input) {
return output; return output;
}; };
// Function: text2xml // Function: svgedit.utilities.text2xml
// Cross-browser compatible method of converting a string to an XML tree // Cross-browser compatible method of converting a string to an XML tree
// found this function here: http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f // found this function here: http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f
svgedit.utilities.text2xml = function(sXML) { svgedit.utilities.text2xml = function(sXML) {
@ -209,7 +209,7 @@ svgedit.utilities.text2xml = function(sXML) {
return out; return out;
}; };
// Function: bboxToObj // Function: svgedit.utilities.bboxToObj
// Converts a SVGRect into an object. // Converts a SVGRect into an object.
// //
// Parameters: // Parameters:
@ -226,7 +226,7 @@ svgedit.utilities.bboxToObj = function(bbox) {
} }
}; };
// Function: walkTree // Function: svgedit.utilities.walkTree
// Walks the tree and executes the callback on each element in a top-down fashion // Walks the tree and executes the callback on each element in a top-down fashion
// //
// Parameters: // Parameters:
@ -242,7 +242,7 @@ svgedit.utilities.walkTree = function(elem, cbFn){
} }
}; };
// Function: walkTreePost // Function: svgedit.utilities.walkTreePost
// Walks the tree and executes the callback on each element in a depth-first fashion // Walks the tree and executes the callback on each element in a depth-first fashion
// TODO: FIXME: Shouldn't this be calling walkTreePost? // TODO: FIXME: Shouldn't this be calling walkTreePost?
// //
@ -418,7 +418,7 @@ svgedit.utilities.getBBox = function(elem) {
selected.textContent = 'a'; // Some character needed for the selector to use. selected.textContent = 'a'; // Some character needed for the selector to use.
ret = selected.getBBox(); ret = selected.getBBox();
selected.textContent = ''; selected.textContent = '';
} else if(elname === 'path' && svgedit.browser.isWebkit()) { } else if(elname === 'path' && !svgedit.browser.supportsPathBBox()) {
ret = svgedit.utilities.getPathBBox(selected); ret = svgedit.utilities.getPathBBox(selected);
} else if(elname === 'use' && !svgedit.browser.isWebkit() || elname === 'foreignObject') { } else if(elname === 'use' && !svgedit.browser.isWebkit() || elname === 'foreignObject') {
ret = selected.getBBox(); ret = selected.getBBox();

View file

@ -51,6 +51,7 @@ var typeMap_ = {px: 1};
* function getElement() - returns an element in the container given an id * function getElement() - returns an element in the container given an id
* function getHeight() - returns the container's height * function getHeight() - returns the container's height
* function getWidth() - returns the container's width * function getWidth() - returns the container's width
* function getRoundDigits() - returns the number of digits number should be rounded to
*/ */
/** /**
@ -95,6 +96,26 @@ svgedit.units.getTypeMap = function() {
return typeMap_; return typeMap_;
}; };
// Function: svgedit.units.shortFloat
// Rounds a given value to a float with number of digits defined in save_options
//
// Parameters:
// val - The value as a String, Number or Array of two numbers to be rounded
//
// Returns:
// If a string/number was given, returns a Float. If an array, return a string
// with comma-seperated floats
svgedit.units.shortFloat = function(val) {
var digits = elementContainer_.getRoundDigits();
if(!isNaN(val)) {
// Note that + converts to Number
return +((+val).toFixed(digits));
} else if($.isArray(val)) {
return svgedit.units.shortFloat(val[0]) + ',' + svgedit.units.shortFloat(val[1]);
}
return parseFloat(val).toFixed(digits) - 0;
};
// Function: svgedit.units.convertUnit // Function: svgedit.units.convertUnit
// Converts the number to given unit or baseUnit // Converts the number to given unit or baseUnit
svgedit.units.convertUnit = function(val, unit) { svgedit.units.convertUnit = function(val, unit) {
@ -102,7 +123,7 @@ svgedit.units.convertUnit = function(val, unit) {
// baseVal.convertToSpecifiedUnits(unitNumMap[unit]); // baseVal.convertToSpecifiedUnits(unitNumMap[unit]);
// var val = baseVal.valueInSpecifiedUnits; // var val = baseVal.valueInSpecifiedUnits;
// baseVal.convertToSpecifiedUnits(1); // baseVal.convertToSpecifiedUnits(1);
return val / typeMap_[unit]; return svgedit.unit.shortFloat(val / typeMap_[unit]);
}; };
// Function: svgedit.units.setUnitAttr // Function: svgedit.units.setUnitAttr
@ -249,7 +270,9 @@ svgedit.units.isValidUnit = function(attr, val) {
result = (elem == null); result = (elem == null);
} catch(e) {} } catch(e) {}
return result; return result;
} else valid = true; } else {
valid = true;
}
return valid; return valid;
}; };