diff --git a/editor/svg-editor.html b/editor/svg-editor.html
index 7f854d8a..e3ba4899 100644
--- a/editor/svg-editor.html
+++ b/editor/svg-editor.html
@@ -45,7 +45,9 @@
diff --git a/editor/svg-editor.js b/editor/svg-editor.js
index 0d68825a..67dd7b4c 100644
--- a/editor/svg-editor.js
+++ b/editor/svg-editor.js
@@ -742,6 +742,7 @@ function svg_edit_setup() {
}
svgCanvas.clearSelection();
hideSourceEditor();
+ populateLayers();
};
var saveDocProperties = function(){
@@ -1068,6 +1069,22 @@ function svg_edit_setup() {
$(this).removeClass('tool_flyout_button_current');
});
+ var populateLayers = function(){
+ var layerlen = svgCanvas.getNumLayers();
+ $('#layerlist').empty();
+ for (var layer = 0; layer < layerlen; ++layer) {
+ var name = svgCanvas.getLayer(layer);
+ $('#layerlist').append("");
+ }
+ $('#layerlist option').mouseup(function(evt){
+ $('#layerlist option').removeAttr("selected");
+ var option = $(this);
+ option.attr("selected", "selected");
+ svgCanvas.setCurrentLayer(option.attr("value"));
+ });
+ };
+ populateLayers();
+
function changeResolution(x,y) {
var new_res = x+'x'+y;
var found = false;
@@ -1134,7 +1151,7 @@ function svg_edit_setup() {
svgCanvas.bind("saved", opts.save);
}
}
-
+
return svgCanvas;
};
diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js
index 2124299d..afb78586 100644
--- a/editor/svgcanvas.js
+++ b/editor/svgcanvas.js
@@ -1,17 +1,14 @@
/*
Issue 73 (Layers) TODO:
-- when a layer option is selected, deselect all other layers
-- when a layer option is double-clicked, pop up a 'rename' dialog? or provide a button?
-- upon turning SVG text into a DOM, analyze all top-level elements that have a
- and create layer options
-- create a global variable for the currently selected layer, hold a reference to the
+- create API for SvgCanvas that allows the client to:
+ - change layer order
+ - create a layer
+ - delete a layer
- when New/Delete are clicked, fire off a function
- when creating a new layer, create a with a , set the current layer to the new one
- when deleting a layer, delete all children of the and then delete the
- ensure New/Delete are undo-able
-- upon changing current layers, set pointer-events='none' on all elements in the old current layer
- then pointer-events='all' on all elements in the newly selected layer
- create a mouseover region on the sidepanels that is resizable and affects all children within
- default the side panel to closed
- add a button that opens the side panel?
@@ -594,8 +591,8 @@ function BatchCommand(text) {
svgroot.appendChild(svgzoom);
var comment = svgdoc.createComment(" created with SVG-edit - http://svg-edit.googlecode.com/ ");
svgzoom.appendChild(comment);
- // associative array of layer names to elements
- var all_layers = {};
+ // z-ordered array of tuples containing layer names and elements
+ var all_layers = [];
// pointer to the current layer
var current_layer = null;
@@ -1466,7 +1463,8 @@ function BatchCommand(text) {
"stroke-opacity": cur_shape.stroke_opacity,
"stroke-linecap": "round",
"stroke-linejoin": "round",
- "opacity": cur_shape.opacity / 2
+ "opacity": cur_shape.opacity / 2,
+ "style": "pointer-events:all"
}
});
freehand_min_x = x;
@@ -1486,7 +1484,8 @@ function BatchCommand(text) {
"width": 0,
"height": 0,
"id": getNextId(),
- "opacity": cur_shape.opacity / 2
+ "opacity": cur_shape.opacity / 2,
+ "style": "pointer-events:all"
}
});
newImage.setAttributeNS(xlinkns, "href", "images/logo.png");
@@ -1512,7 +1511,8 @@ function BatchCommand(text) {
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
- "opacity": cur_shape.opacity / 2
+ "opacity": cur_shape.opacity / 2,
+ "style": "pointer-events:all"
}
});
break;
@@ -1532,7 +1532,8 @@ function BatchCommand(text) {
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"fill": "none",
- "opacity": cur_shape.opacity / 2
+ "opacity": cur_shape.opacity / 2,
+ "style": "pointer-events:all"
}
});
break;
@@ -1551,7 +1552,8 @@ function BatchCommand(text) {
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
- "opacity": cur_shape.opacity / 2
+ "opacity": cur_shape.opacity / 2,
+ "style": "pointer-events:all"
}
});
break;
@@ -1571,7 +1573,8 @@ function BatchCommand(text) {
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
- "opacity": cur_shape.opacity / 2
+ "opacity": cur_shape.opacity / 2,
+ "style": "pointer-events:all"
}
});
break;
@@ -1593,7 +1596,8 @@ function BatchCommand(text) {
"opacity": cur_shape.opacity,
"font-size": cur_text.font_size,
"font-family": cur_text.font_family,
- "text-anchor": "middle"
+ "text-anchor": "middle",
+ "style": "pointer-events:all"
}
});
newText.textContent = "text";
@@ -2034,7 +2038,7 @@ function BatchCommand(text) {
'stroke': "#00F",
'stroke-width': 2,
'cursor': 'move',
- "pointer-events": "all"
+ 'style': 'pointer-events:all'
});
pointGrip = pointGripContainer.appendChild(pointGrip);
@@ -2214,7 +2218,8 @@ function BatchCommand(text) {
"stroke-dasharray": cur_shape.stroke_style,
"opacity": cur_shape.opacity,
"stroke-opacity": cur_shape.stroke_opacity,
- "fill-opacity": cur_shape.fill_opacity
+ "fill-opacity": cur_shape.fill_opacity,
+ "style": "pointer-events:all"
}
});
call("changed",[element]);
@@ -2238,7 +2243,8 @@ function BatchCommand(text) {
"stroke-dasharray": cur_shape.stroke_style,
"opacity": cur_shape.opacity,
"stroke-opacity": cur_shape.stroke_opacity,
- "fill-opacity": cur_shape.fill_opacity
+ "fill-opacity": cur_shape.fill_opacity,
+ "style": "pointer-events:all"
}
});
call("changed",[element]);
@@ -2282,7 +2288,8 @@ function BatchCommand(text) {
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
- "opacity": cur_shape.opacity / 2
+ "opacity": cur_shape.opacity / 2,
+ "style": "pointer-events:all"
}
});
// set stretchy line to first point
@@ -2515,6 +2522,16 @@ function BatchCommand(text) {
call("saved", str);
};
+ var walkTree = function(elem, cbFn){
+ if (elem && elem.nodeType == 1) {
+ cbFn(elem);
+ var i = elem.childNodes.length;
+ while (i--) {
+ walkTree(elem.childNodes.item(i), cbFn);
+ }
+ }
+ };
+
this.getSvgString = function() {
return svgCanvasToString();
};
@@ -2568,7 +2585,25 @@ function BatchCommand(text) {
current_zoom = 1;
// identify layers
-
+ all_layers = [];
+ var numchildren = svgzoom.childNodes.length;
+ // loop through all children of svgzoom
+ for (var i = 0; i < numchildren; ++i) {
+ var child = svgzoom.childNodes.item(i);
+ // for each g, find its layer name
+ if (child && child.tagName == "g") {
+ var name = getLayerName(child);
+ // store layer and name in global variable
+ if (name) {
+ all_layers.push( [name,child] );
+ current_layer = child;
+ walkTree(child, function(e){e.setAttribute("style", "pointer-events:none");});
+ }
+ }
+ }
+ console.dir(all_layers);
+ walkTree(current_layer, function(e){e.setAttribute("style","pointer-events:all");});
+
selectorManager.update();
addCommandToHistory(batchCmd);
@@ -2581,6 +2616,92 @@ function BatchCommand(text) {
return true;
};
+ // Layer API Functions
+ // TODO: change layer order
+ // TODO: create a layer
+ // TODO: delete a layer
+ this.getNumLayers = function() {
+ return all_layers.length;
+ };
+ this.getLayer = function(i) {
+ if (i >= 0 && i < canvas.getNumLayers()) {
+ return all_layers[i][0];
+ }
+ return "";
+ };
+ this.getCurrentLayer = function() {
+ for (var i = 0; i < all_layers.length; ++i) {
+ if (all_layers[i][1] == current_layer) {
+ return all_layers[i][0];
+ }
+ }
+ return "";
+ };
+ this.setCurrentLayer = function(name) {
+ for (var i = 0; i < all_layers.length; ++i) {
+ if (name == all_layers[i][0]) {
+ if (current_layer != all_layers[i][1]) {
+ walkTree(current_layer,function(e){e.setAttribute("style","pointer-events:none");});
+ current_layer = all_layers[i][1];
+ walkTree(current_layer,function(e){e.setAttribute("style","pointer-events:all");});
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+ this.renameLayer = function(oldname, newname) {
+ var rememberCurrentLayer = current_layer;
+ if (canvas.setCurrentLayer(oldname)) {
+ var oldLayer = current_layer;
+ // setCurrentLayer will return false if the name doesn't already exists
+ if (!canvas.setCurrentLayer(newname)) {
+ // find the index of the layer
+ for (var i = 0; i < all_layers.length; ++i) {
+ if (all_layers[i][1] == oldLayer) break;
+ }
+ all_layers[i][0] = newname;
+
+ // now change the underlying title element contents
+ var len = oldLayer.childNodes.length;
+ for (var i = 0; i < len; ++i) {
+ var child = oldLayer.childNodes.item(i);
+ // found the element, now append all the
+ if (child && child.tagName == "title") {
+ // wipe out old name
+ // TODO: make this undo-able
+ while (child.firstChild) { child.removeChild(child.firstChild); }
+ child.appendChild( svgdoc.createTextNode(newname) );
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ };
+ // used internally
+ var getLayerName = function(g) {
+ var name = "";
+ if (g && g.tagName == "g") {
+ var len = g.childNodes.length;
+ for (var i = 0; i < len; ++i) {
+ var child = g.childNodes.item(i);
+ // found the element, now append all the
+ if (child && child.tagName == "title") {
+ var tlen = child.childNodes.length;
+ for (var j = 0; j < tlen; ++j) {
+ var textNode = child.childNodes.item(j);
+ if (textNode.nodeType == 3) {
+ name += textNode.nodeValue;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return name;
+ };
+
this.clear = function() {
current_poly_pts = [];
@@ -2602,8 +2723,8 @@ function BatchCommand(text) {
layer_title.appendChild(svgdoc.createTextNode("Layer 1"));
current_layer.appendChild(layer_title);
current_layer = svgzoom.appendChild(current_layer);
- all_layers = {};
- all_layers["Layer 1"] = current_layer;
+ all_layers = [];
+ all_layers[0] = ["Layer 1",current_layer];
// clear the undo stack
resetUndoStack();