From f2893ff847329c09212dfbb9c5ba3bf0231accca Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Mon, 2 Nov 2009 20:09:02 +0000 Subject: [PATCH 02/35] Base check-in of transformlist refactoring git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@898 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 508 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 456 insertions(+), 52 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 3710db6b..77064c1d 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1,3 +1,13 @@ +/* + TODOs for TransformList: + + * Fix moving/resizing/rotating of groups (pummel the transforms down to the children?) + * Fix rotation transforms after scaling + * Fix proper selector box sizing + * Ensure resizing in negative direction works + * Ensure ungrouping works + * Ensure undo still works properly +*/ /* TODOs for Localizing: @@ -319,7 +329,7 @@ function BatchCommand(text) { this.rotateGripConnector = this.selectorGroup.appendChild( addSvgElementFromJson({ "element": "line", "attr": { - "id": ("selectorGrip_rotate_connector_" + this.id), + "id": ("selectorGrip_rotateconnector_" + this.id), "stroke": "blue", "stroke-width": "1" } @@ -342,7 +352,7 @@ function BatchCommand(text) { addSvgElementFromJson({ "element": "rect", "attr": { - "id": ("selectorGrip_" + dir + "_" + this.id), + "id": ("selectorGrip_resize_" + dir + "_" + this.id), "fill": "blue", "width": 6, "height": 6, @@ -356,13 +366,6 @@ function BatchCommand(text) { "display":"none" } }) ); - $('#'+this.selectorGrips[dir].id).mousedown( function() { - current_mode = "resize"; - current_resize_mode = this.id.substr(13,this.id.indexOf("_",13)-13); - }); - $('#selectorGrip_rotate_'+id).mousedown( function() { - current_mode = "rotate"; - }); } this.showGrips = function(show) { @@ -463,8 +466,8 @@ function BatchCommand(text) { if (angle) { var cx = round(oldbox.x + oldbox.width/2) * current_zoom cy = round(oldbox.y + oldbox.height/2) * current_zoom; - this.selectorGroup.setAttribute("transform", "rotate("+angle+" " + cx + "," + cy + ")"); } + this.selectorGroup.setAttribute("transform", transform); svgroot.unsuspendRedraw(sr_handle); }; @@ -622,7 +625,6 @@ function BatchCommand(text) { var K = 1 - m.a; var ty = ( K * m.f + m.b*m.e ) / ( K*K + m.b*m.b ); var tx = ( m.e - m.b * ty ) / K; - console.log("wooo! tx=" + tx + ", ty=" + ty); tstr += "rotate(" + xform.angle + " " + [tx,ty].join(",") + ") "; break; } @@ -1175,7 +1177,8 @@ function BatchCommand(text) { transforms around - non-uniform scales (sx!=sy) preserve the shape and orientation ONLY if the shape has not been rotated, thus we will reduce non-uniform scales when we haven't rotated the shape - but otherwise we are forced to keep them around + but otherwise we are forced to keep them around (a consequence of this is that scale + transforms will only be present in the final list with a rotate) - uniform scales (sx==sy) preserve the shape and orientation, so we can ALWAYS remove uniform scales, even when the shape has been rotated - does the removal of translate or scale transforms affect the rotational center of @@ -1214,10 +1217,355 @@ function BatchCommand(text) { var pathMap = [ 0, 'z', 'M', 'M', 'L', 'L', 'C', 'C', 'Q', 'Q', 'A', 'A', 'L', 'L', 'L', 'L', // TODO: be less lazy below and map them to h and v 'S', 'S', 'T', 'T' ]; + var truePathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', + 'H', 'h', 'V', 'v', 'S', 's', 'T', 't']; // this function returns the command which resulted from the selected change + // TODO: use suspendRedraw() and unsuspendRedraw() around this function + // TODO: get rid of selectedBBox var recalculateDimensions = function(selected,selectedBBox) { if (selected == null || selectedBBox == null) return null; + + // if this element had no transforms, we are done + var tlist = canvas.getTransformList(selected); + if (tlist.numberOfItems == 0) return null; + + // we know we have some transforms, so set up return variable + var batchCmd = new BatchCommand("Transform"); + + // store initial values that will be affected by reducing the transform list + var changes = {}, initial = null; + switch (selected.tagName) + { + case "line": + changes["x1"] = selected.getAttribute("x1"); + changes["y1"] = selected.getAttribute("y1"); + changes["x2"] = selected.getAttribute("x2"); + changes["y2"] = selected.getAttribute("y2"); + break; + case "circle": + changes["cx"] = selected.getAttribute("cx"); + changes["cy"] = selected.getAttribute("cy"); + changes["r"] = selected.getAttribute("r"); + break; + case "ellipse": + changes["cx"] = selected.getAttribute("cx"); + changes["cy"] = selected.getAttribute("cy"); + changes["rx"] = selected.getAttribute("rx"); + changes["ry"] = selected.getAttribute("ry"); + break; + case "rect": + case "image": + changes["x"] = selected.getAttribute("x"); + changes["y"] = selected.getAttribute("y"); + changes["width"] = selected.getAttribute("width"); + changes["height"] = selected.getAttribute("height"); + break; + case "text": + changes["x"] = selected.getAttribute("x"); + changes["y"] = selected.getAttribute("y"); + break; + case "polygon": + case "polyline": + initial = {}; + initial["points"] = selected.getAttribute("points"); + var list = selected.points; + var len = list.numberOfItems; + changes["points"] = new Array(len); + for (var i = 0; i < len; ++i) { + var pt = list.getItem(i); + changes["points"][i] = {x:pt.x,y:pt.y}; + } + break; + case "path": + initial = {}; + initial["d"] = selected.getAttribute("d"); + var segList = selected.pathSegList; + var len = segList.numberOfItems; + changes["d"] = new Array(len); + for (var i = 0; i < len; ++i) { + var seg = segList.getItem(i); + changes["d"][i] = { + type: seg.pathSegType, + x: seg.x, + y: seg.y, + x1: seg.x1, + y1: seg.y1, + x2: seg.x2, + y2: seg.y2, + r1: seg.r1, + r2: seg.r2, + angle: seg.angle, + largeArcFlag: seg.largeArcFlag, + sweepFlag: seg.sweepFlag + }; + } + break; + } // switch on element type to get initial values + + // if we haven't created an initial array in polygon/polyline/path, then + // make a copy of initial values and include the transform + if (initial == null) { + initial = jQuery.extend(true, {}, changes); + } + // save the transform value too + initial["transform"] = selected.getAttribute("transform"); + + // reduce the transform list here... + var box = canvas.getBBox(selected); + var newcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; + var currentMatrix = {a:1, b:0, c:0, d:1, e:0, f:0}; + var n = tlist.numberOfItems; + var tx = 0, ty = 0, sx = 1, sy = 1, r = 0.0; + while (n--) { + var xform = tlist.getItem(n); + var m = xform.matrix; + // if translate... + var remap = null, scalew = null, scaleh = null; + switch (xform.type) { + case 2: // TRANSLATE + remap = function(x,y) { return transformPoint(x,y,m); }; + scalew = function(w) { return w; } + scaleh = function(h) { return h; } + break; + case 3: // SCALE + remap = function(x,y) { return transformPoint(x,y,m); }; + scalew = function(w) { return m.a * w; } + scaleh = function(h) { return m.d * h; } + break; + case 4: // ROTATE + // TODO: re-center the rotation and then continue (we cannot reduce a rotate) + var newrot = svgroot.createSVGTransform(); + newrot.setRotate(xform.angle, newcenter.x, newcenter.y); +// tlist.replaceItem(newrot, n); + // fall through to the default: continue below + default: + continue; + } + newcenter = remap(box.x+box.width/2, box.y+box.height/2); + var bpt = remap(box.x,box.y); + box.x = bpt.x; + box.y = bpt.y; + box.width = scalew(box.width); + box.height = scaleh(box.height); + + switch (selected.tagName) + { + case "g": + var children = selected.childNodes; + var c = children.length; + while (c--) { + var child = children.item(c); + if (child.nodeType == 1) { + try { + // TODO: how to transfer the transform list of the group to each child + var childBox = child.getBBox(); + var pt = remap(childBox.x,childBox.y), + w = scalew(childBox.width), + h = scaleh(childBox.height); + console.log([pt.x,pt.y,w,h]); + childBox.x = pt.x; childBox.y = pt.y; + childBox.width = w; childBox.height = h; + batchCmd.addSubCommand(recalculateDimensions(child, childBox)); + } catch(e) {} + } + } + break; + case "line": + var pt1 = remap(changes["x1"],changes["y1"]), + pt2 = remap(changes["x2"],changes["y2"]); + changes["x1"] = pt1.x; + changes["y1"] = pt1.y; + changes["x2"] = pt2.x; + changes["y2"] = pt2.y; + break; + case "circle": + var c = remap(changes["cx"],changes["cy"]); + changes["cx"] = c.x; + changes["cy"] = c.y; + // take the minimum of the new selected box's dimensions for the new circle radius + changes["r"] = round(Math.min(box.width/2,box.height/2)); + break; + case "ellipse": + var c = remap(changes["cx"],changes["cy"]); + changes["cx"] = c.x; + changes["cy"] = c.y; + changes["rx"] = scalew(changes["rx"]); + changes["ry"] = scaleh(changes["ry"]); + console.log(changes); + break; + case "rect": + case "image": + var pt1 = remap(changes["x"],changes["y"]); + changes["x"] = pt1.x; + changes["y"] = pt1.y; + changes["width"] = scalew(changes["width"]); + changes["height"] = scaleh(changes["height"]); + break; + case "text": + var pt1 = remap(changes["x"],changes["y"]); + changes["x"] = pt1.x; + changes["y"] = pt1.y; + break; + case "polygon": + case "polyline": + var len = changes["points"].length; + for (var i = 0; i < len; ++i) { + var pt = changes["points"][i]; + pt = remap(pt.x,pt.y); + changes["points"][i].x = pt.x; + changes["points"][i].y = pt.y; + } + break; + case "path": + var len = changes["d"].length; + var firstseg = changes["d"][0]; + var firstpt = remap(firstseg.x,firstseg.y); + changes["d"][0].x = firstpt.x; + changes["d"][0].y = firstpt.y; + for (var i = 1; i < len; ++i) { + var seg = changes["d"][i]; + var type = seg.type; + // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2 + // if relative, we want to scalew, scaleh + if (type % 2 == 0) { // absolute + var pt = remap(seg.x,seg.y), + pt1 = remap(seg.x1,seg.y1), + pt2 = remap(seg.x2,seg.y2); + seg.x = pt.x; + seg.y = pt.y; + seg.x1 = pt1.x; + seg.y1 = pt1.y; + seg.x2 = pt2.x; + seg.y2 = pt2.y; + seg.r1 = scalew(seg.r1), + seg.r2 = scaleh(seg.r2); + } + else { // relative + seg.x = scalew(seg.x); + seg.y = scaleh(seg.y); + seg.x1 = scalew(seg.x1); + seg.y1 = scaleh(seg.y1); + seg.x2 = scalew(seg.x2); + seg.y2 = scaleh(seg.y2); + seg.r1 = scalew(seg.r1), + seg.r2 = scaleh(seg.r2); + } + } // for each segment + break; + } // switch on element type to get initial values + + // we have eliminated the transform, so remove it from the list + tlist.removeItem(n); + + // now loop through the other transforms and adjust accordingly + for ( var j = n; j < tlist.numberOfItems; ++j) { + var changed_xform = tlist.getItem(j); + switch (changed_xform.type) { + // TODO: TRANSLATE, SCALE? + case 4: // rotate + var newrot = svgroot.createSVGTransform(); + newrot.setRotate(changed_xform.angle, newcenter.x, newcenter.y); + tlist.replaceItem(newrot, j); + break; + } + } + } // looping for each transform + + // now we have a set of changes and an applied reduced transform list + // we apply the changes directly to the DOM + switch (selected.tagName) + { + case "line": + case "circle": + case "rect": + case "ellipse": + case "image": + case "text": + assignAttributes(selected, changes, 1000); + break; + case "polyline": + case "polygon": + var len = changes["points"].length; + var pstr = ""; + for (var i = 0; i < len; ++i) { + var pt = changes["points"][i]; + pstr += pt.x + "," + pt.y + " "; + } + selected.setAttribute("points", pstr); + break; + case "path": + var dstr = ""; + var len = changes["d"].length; + for (var i = 0; i < len; ++i) { + var seg = changes["d"][i]; + var type = seg.type; + dstr += truePathMap[type]; + switch(type) { + case 13: // relative horizontal line (h) + case 12: // absolute horizontal line (H) + dstr += seg.x + " "; + break; + case 15: // relative vertical line (v) + case 14: // absolute vertical line (V) + dstr += seg.y + " "; + break; + case 3: // relative move (m) + case 5: // relative line (l) + case 19: // relative smooth quad (t) + case 2: // absolute move (M) + case 4: // absolute line (L) + case 18: // absolute smooth quad (T) + dstr += seg.x + "," + seg.y + " "; + break; + case 7: // relative cubic (c) + case 6: // absolute cubic (C) + dstr += seg.x1 + "," + seg.y1 + " " + seg.x2 + "," + seg.y2 + " " + + seg.x + "," + seg.y + " "; + break; + case 9: // relative quad (q) + case 8: // absolute quad (Q) + dstr += seg.x + "," + seg.y + " " + seg.x1 + "," + seg.y1 + " "; + break; + case 11: // relative elliptical arc (a) + case 10: // absolute elliptical arc (A) + dstr += seg.r1 + "," + seg.r2 + " " + seg.angle + " " + seg.largeArcFlag + + " " + seg.sweepFlag + " " + seg.x + "," + seg.y + " "; + break; + case 17: // relative smooth cubic (s) + case 16: // absolute smooth cubic (S) + dstr += seg.x + "," + seg.y + " " + seg.x2 + "," + seg.y2 + " "; + break; + } + } + selected.setAttribute("d", dstr); + break; + } + + // remove any stray identity transforms + if (tlist && tlist.numberOfItems > 0) { + var removeItems = []; + var k = tlist.numberOfItems; + while (k--) { + var xform = tlist.getItem(k); + if (xform.type == 0 || xform.type == 1) { + tlist.removeItem(k); + } + } + } + + // if the transform list has been emptied, remove it + if (tlist.numberOfItems == 0) { + selected.removeAttribute("transform"); + } + + batchCmd.addSubCommand(new ChangeElementCommand(selected, initial)); + + return batchCmd; + // ----- + // TODO: once all functionality has been restored to the above function code then + // remove the below (old) function code + var box = canvas.getBBox(selected); // if we have not moved/resized, then immediately leave @@ -1736,10 +2084,24 @@ function BatchCommand(text) { mouse_target = svgroot; } // if it is a selector grip, then it must be a single element selected, - // set the mouse_target to that + // set the mouse_target to that and update the mode to rotate/resize if (mouse_target.parentNode == selectorManager.selectorParentGroup && selectedElements[0] != null) { + var gripid = evt.target.id; + var griptype = gripid.substr(0,20); + // rotating + if (griptype == "selectorGrip_rotate_") { + current_mode = "rotate"; + } + // resizing + else if(griptype == "selectorGrip_resize_") { + current_mode = "resize"; + current_resize_mode = gripid.substr(20,gripid.indexOf("_",20)-20); + console.log(current_resize_mode); + } mouse_target = selectedElements[0]; } + + var tlist = canvas.getTransformList(mouse_target); switch (current_mode) { case "select": @@ -1758,6 +2120,10 @@ function BatchCommand(text) { current_path = null; } // else if it's a path, go into pathedit mode in mouseup + + // insert a dummy transform so if the element is moved it will have + // a transform to use for its translate + tlist.insertItemBefore(svgroot.createSVGTransform(), 0); } else { canvas.clearSelection(); @@ -1795,6 +2161,11 @@ function BatchCommand(text) { started = true; start_x = x; start_y = y; + // append three dummy transforms to the tlist so that + // we can translate,scale,translate in mousemove + tlist.appendItem(svgroot.createSVGTransform()); + tlist.appendItem(svgroot.createSVGTransform()); + tlist.appendItem(svgroot.createSVGTransform()); break; case "fhellipse": case "fhrect": @@ -2029,7 +2400,7 @@ function BatchCommand(text) { y = mouse_y / current_zoom; evt.preventDefault(); - + switch (current_mode) { case "select": @@ -2040,14 +2411,33 @@ function BatchCommand(text) { var dx = x - start_x; var dy = y - start_y; if (dx != 0 || dy != 0) { - var ts = ["translate(",dx,",",dy,")"].join(''); var len = selectedElements.length; for (var i = 0; i < len; ++i) { var selected = selectedElements[i]; if (selected == null) break; var box = canvas.getBBox(selected); +// box.x += dx; box.y += dy; selectedBBoxes[i].x = box.x + dx; selectedBBoxes[i].y = box.y + dy; + + // update the dummy transform in our transform list + // to be a translate + var tlist = canvas.getTransformList(selected); + var xform = svgroot.createSVGTransform(); + xform.setTranslate(dx,dy); + tlist.replaceItem(xform, 0); + + // update our internal bbox that we're tracking while dragging + selectorManager.requestSelector(selected).resize(box); + + // now transform delta mouse movement into a translation in the + // coordinate space of the mouse target +// var startpt = transformPoint(start_x, start_y, mouse_target_ctm); +// var endpt = transformPoint(x, y, mouse_target_ctm); +// dx = endpt.x - startpt.x; +// dy = endpt.y - startpt.y; + + /* var angle = canvas.getRotationAngle(selected); if (angle) { var cx = round(box.x + box.width/2), @@ -2062,8 +2452,7 @@ function BatchCommand(text) { selected.setAttribute("transform", ts); box.x += dx; box.y += dy; } - // update our internal bbox that we're tracking while dragging - selectorManager.requestSelector(selected).resize(box); + */ } } } @@ -2152,16 +2541,19 @@ function BatchCommand(text) { tx = width; } - // find the rotation transform and prepend it - var ts = [" translate(", (left+tx), ",", (top+ty), ") scale(", sx, ",", sy, - ") translate(", -(left+tx), ",", -(top+ty), ")"].join(''); - if (angle) { - var cx = round(left+width/2), - cy = round(top+height/2); - ts = ["rotate(", angle, " ", cx, ",", cy, ")", ts].join('') - } - selected.setAttribute("transform", ts); - + // update the transform list with translate,scale,translate + var tlist = canvas.getTransformList(selected); + var translateOrigin = svgroot.createSVGTransform(), + scale = svgroot.createSVGTransform(), + translateBack = svgroot.createSVGTransform(); + translateOrigin.setTranslate(-(left+tx),-(top+ty)); + scale.setScale(sx,sy); + translateBack.setTranslate(left+tx,top+ty); + var N = tlist.numberOfItems; + tlist.replaceItem(translateBack, N-3); + tlist.replaceItem(scale, N-2); + tlist.replaceItem(translateOrigin, N-1); + var selectedBBox = selectedBBoxes[0]; // reset selected bbox top-left position @@ -2341,11 +2733,12 @@ function BatchCommand(text) { cy = round(box.y + box.height/2); var angle = round(((Math.atan2(cy-y,cx-x) * (180/Math.PI))-90) % 360); canvas.setRotationAngle(angle<-180?(360+angle):angle, true); + call("changed", selectedElements); break; default: break; } - }; + }; // mouseMove() var shortFloat = function(val) { var digits = 5; @@ -2914,7 +3307,7 @@ function BatchCommand(text) { var mouse_y = pt.y; var x = mouse_x / current_zoom; var y = mouse_y / current_zoom; - + started = false; var element = svgdoc.getElementById(getId()); var keep = false; @@ -3324,6 +3717,8 @@ function BatchCommand(text) { if (!batchCmd.isEmpty()) { addCommandToHistory(batchCmd); } + // perform recalculation to weed out any stray identity transforms that might get stuck + recalculateAllSelectedDimensions(); break; default: console.log("Unknown mode in mouseup: " + current_mode); @@ -3334,6 +3729,7 @@ function BatchCommand(text) { element = null; var t = evt.target; + // if this element is in a group, go up until we reach the top-level group // just below the layer groups // TODO: once we implement links, we also would have to check for elements @@ -4400,13 +4796,17 @@ function BatchCommand(text) { // returns an object that behaves like a SVGTransformList this.getTransformList = function(elem) { if (isWebkit) { - var t = svgTransformLists[elem]; + var t = svgTransformLists[elem.id]; if (!t) { - t = svgTransformLists[elem] = new SVGEditTransformList(elem); + svgTransformLists[elem.id] = new SVGEditTransformList(elem); + t = svgTransformLists[elem.id]; } return t; } - return elem.transform.baseVal; + else if (elem.transform) { + return elem.transform.baseVal; + } + return null; }; this.getBBox = function(elem) { @@ -4428,20 +4828,13 @@ function BatchCommand(text) { this.getRotationAngle = function(elem) { var selected = elem || selectedElements[0]; - if(!elem.transform) return null; // find the rotation transform (if any) and set it - var tlist = selected.transform.baseVal; + var tlist = canvas.getTransformList(selected); var t = tlist.numberOfItems; - var foundRot = false; while (t--) { var xform = tlist.getItem(t); if (xform.type == 4) { return xform.angle; - } else if (xform.type == 1) { - // Matrix transformation. Extract the rotation. (for Webkit) - var angle = Math.round( Math.acos(xform.matrix.a) * 180.0 / Math.PI ); - if (xform.matrix.b < 0) angle = -angle; - return angle; } } return 0; @@ -4453,18 +4846,35 @@ function BatchCommand(text) { // calculated bbox's center can change depending on the angle var bbox = elem.getBBox(); var cx = round(bbox.x+bbox.width/2), cy = round(bbox.y+bbox.height/2); - var rotate = "rotate(" + val + " " + cx + "," + cy + ")"; + var tlist = canvas.getTransformList(elem); + // if we are not rotated yet, insert a dummy xform + if (tlist.numberOfItems == 0 || tlist.getItem(0).type != 4) { + tlist.insertItemBefore(svgroot.createSVGTransform(), 0); + } + + var newrot = tlist.getItem(0); + newrot.setRotate(val, cx, cy); + if (preventUndo) { - this.changeSelectedAttributeNoUndo("transform", rotate, selectedElements); + // we don't need to undo, just update the transform list + tlist.replaceItem(newrot, 0); } else { - this.changeSelectedAttribute("transform",rotate,selectedElements); + // FIXME: we need to do it, then undo it, then redo it so it can be undo-able! :) + // TODO: figure out how to make changes to transform list undo-able cross-browser + var oldTransform = elem.getAttribute("transform"); + tlist.replaceItem(newrot, 0); + var newTransform = elem.getAttribute("transform"); + elem.setAttribute("transform", oldTransform); + this.changeSelectedAttribute("transform",newTransform,selectedElements); } var pointGripContainer = document.getElementById("pathpointgrip_container"); if(elem.nodeName == "path" && pointGripContainer) { - setPointContainerTransform(rotate); + setPointContainerTransform(elem.getAttribute("transform")); } - selectorManager.requestSelector(selectedElements[0]).updateGripCursors(val); + var selector = selectorManager.requestSelector(selectedElements[0]); + selector.resize(bbox); + selector.updateGripCursors(val); }; this.each = function(cb) { @@ -4700,8 +5110,7 @@ function BatchCommand(text) { 'elements': elements}; }; - // This function makes the changes to the elements and then - // fires the 'changed' event + // This function makes the changes to the elements this.changeSelectedAttributeNoUndo = function(attr, newValue, elems) { var handle = svgroot.suspendRedraw(1000); if(current_mode == 'pathedit') { @@ -4774,11 +5183,6 @@ function BatchCommand(text) { } // if oldValue != newValue } // for each elem svgroot.unsuspendRedraw(handle); - - // Only call "changed" if really necessary, as it updates the toolbar each time - if(current_mode == 'rotate') { - call("changed", elems); - } }; // This function returns a BatchCommand object which summarizes the From f9b4b0a647a0d4e4b7c175bd207c53b5e4e6cbaf Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Tue, 3 Nov 2009 14:27:33 +0000 Subject: [PATCH 03/35] Small fix to allow moving of multiple elements git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@899 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 77064c1d..e2d69c26 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -2422,10 +2422,14 @@ function BatchCommand(text) { // update the dummy transform in our transform list // to be a translate - var tlist = canvas.getTransformList(selected); var xform = svgroot.createSVGTransform(); + var tlist = canvas.getTransformList(selected); xform.setTranslate(dx,dy); - tlist.replaceItem(xform, 0); + if(tlist.numberOfItems) { + tlist.replaceItem(xform, 0); + } else { + tlist.appendItem(xform); + } // update our internal bbox that we're tracking while dragging selectorManager.requestSelector(selected).resize(box); From 85d35b6cbd009df6c34e025e185861309298d790 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Tue, 3 Nov 2009 15:59:27 +0000 Subject: [PATCH 04/35] transformlist branch: fix scaling or rotated elements git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@900 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 51 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index e2d69c26..b1a44e03 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1,9 +1,8 @@ /* TODOs for TransformList: - * Fix moving/resizing/rotating of groups (pummel the transforms down to the children?) - * Fix rotation transforms after scaling * Fix proper selector box sizing + * Fix moving/resizing/rotating of groups (pummel the transforms down to the children?) * Ensure resizing in negative direction works * Ensure ungrouping works * Ensure undo still works properly @@ -1313,11 +1312,13 @@ function BatchCommand(text) { // reduce the transform list here... var box = canvas.getBBox(selected); - var newcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; + var origcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; + var newcenter = {x: origcenter.x, y: origcenter.y}; var currentMatrix = {a:1, b:0, c:0, d:1, e:0, f:0}; var n = tlist.numberOfItems; var tx = 0, ty = 0, sx = 1, sy = 1, r = 0.0; while (n--) { + var bRemoveTransform = true; var xform = tlist.getItem(n); var m = xform.matrix; // if translate... @@ -1334,14 +1335,43 @@ function BatchCommand(text) { scaleh = function(h) { return m.d * h; } break; case 4: // ROTATE - // TODO: re-center the rotation and then continue (we cannot reduce a rotate) - var newrot = svgroot.createSVGTransform(); - newrot.setRotate(xform.angle, newcenter.x, newcenter.y); -// tlist.replaceItem(newrot, n); + // if the new center of the shape has moved, then + // re-center the rotation, and determine the movement + // offset required to keep the shape in the same place + if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) { + var alpha = xform.angle * Math.PI / 180.0; + + // determine where the new rotated center should be + var dx = newcenter.x - origcenter.x, + dy = newcenter.y - origcenter.y, + r = Math.sqrt(dx*dx + dy*dy), + theta = Math.atan2(dy,dx) + alpha; + var cx = r * Math.cos(theta) + origcenter.x, + cy = r * Math.sin(theta) + origcenter.y; + + dx = cx - newcenter.x; + dy = cy - newcenter.y; + + // TODO: fix this so that it determines the true translation + // required so that the rotational center lines up + remap = function(x,y) { + return { x: x + dx, y: y + dy }; + }; + scalew = function(w) { return w; } + scaleh = function(h) { return h; } + + bRemoveTransform = false; + var newrot = svgroot.createSVGTransform(); + newrot.setRotate(xform.angle, cx, cy); + tlist.replaceItem(newrot, n); + } + break; // fall through to the default: continue below default: continue; } + if (!remap) continue; + newcenter = remap(box.x+box.width/2, box.y+box.height/2); var bpt = remap(box.x,box.y); box.x = bpt.x; @@ -1363,7 +1393,6 @@ function BatchCommand(text) { var pt = remap(childBox.x,childBox.y), w = scalew(childBox.width), h = scaleh(childBox.height); - console.log([pt.x,pt.y,w,h]); childBox.x = pt.x; childBox.y = pt.y; childBox.width = w; childBox.height = h; batchCmd.addSubCommand(recalculateDimensions(child, childBox)); @@ -1392,7 +1421,6 @@ function BatchCommand(text) { changes["cy"] = c.y; changes["rx"] = scalew(changes["rx"]); changes["ry"] = scaleh(changes["ry"]); - console.log(changes); break; case "rect": case "image": @@ -1456,7 +1484,9 @@ function BatchCommand(text) { } // switch on element type to get initial values // we have eliminated the transform, so remove it from the list - tlist.removeItem(n); + if (bRemoveTransform) { + tlist.removeItem(n); + } // now loop through the other transforms and adjust accordingly for ( var j = n; j < tlist.numberOfItems; ++j) { @@ -2096,7 +2126,6 @@ function BatchCommand(text) { else if(griptype == "selectorGrip_resize_") { current_mode = "resize"; current_resize_mode = gripid.substr(20,gripid.indexOf("_",20)-20); - console.log(current_resize_mode); } mouse_target = selectedElements[0]; } From e726d8e9b1363296beff00a2450618195bd40a16 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Tue, 3 Nov 2009 17:47:33 +0000 Subject: [PATCH 05/35] Prevent selection box from scaling on resize using original method git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@901 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index b1a44e03..2bd0a82f 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -465,8 +465,8 @@ function BatchCommand(text) { if (angle) { var cx = round(oldbox.x + oldbox.width/2) * current_zoom cy = round(oldbox.y + oldbox.height/2) * current_zoom; + this.selectorGroup.setAttribute("transform", "rotate("+angle+" " + cx + "," + cy + ")"); } - this.selectorGroup.setAttribute("transform", transform); svgroot.unsuspendRedraw(sr_handle); }; From ea2d803c18056046b5707ecfcbce79e053609377 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Tue, 3 Nov 2009 18:21:23 +0000 Subject: [PATCH 06/35] Fixed Issue 301: Move to Layer doesn't behave properly in Opera and Safari git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@902 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svg-editor.js | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/editor/svg-editor.js b/editor/svg-editor.js index d0f004aa..5e743664 100644 --- a/editor/svg-editor.js +++ b/editor/svg-editor.js @@ -322,20 +322,10 @@ function svg_edit_setup() { if ( (elem && !is_node) || multiselected) { // update the selected elements' layer - $('#selLayerNames')[0].removeAttribute('disabled'); - var opts = $('#selLayerNames option'); - for (var i = 0; i < opts.length; ++i) { - var opt = opts[i]; - if (currentLayer == opt.textContent) { - opt.setAttribute('selected', 'selected'); - } - else { - opt.removeAttribute('selected'); - } - } + $('#selLayerNames').removeAttr('disabled').val(currentLayer); } else { - $('#selLayerNames')[0].setAttribute('disabled', 'disabled'); + $('#selLayerNames').attr('disabled', 'disabled'); } }; @@ -1515,7 +1505,7 @@ function svg_edit_setup() { appendstr += "" + name + ""; } layerlist.append(appendstr); - selLayerNames.append(""); + selLayerNames.append(""); } // handle selection of layer $('#layerlist td.layername') From b0d565bdeb522f381064f4143726b77c1c486b4b Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Tue, 3 Nov 2009 21:16:36 +0000 Subject: [PATCH 07/35] Stopped rounding of circles, added fix for importing SVG on Opera/Win/Non-EN git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@903 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 2bd0a82f..2221cd20 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1145,6 +1145,29 @@ function BatchCommand(text) { return out.join(''); }; // end svgToString() + // importNode, like cloneNode, causes the comma-to-period + // issue in Opera/Win/non-en. Thankfully we can compare to the original XML + // and simply use the original value when necessary + var fixOperaXML = function(elem, orig_el) { + var x_attrs = elem.attributes; + $.each(x_attrs, function(i, attr) { + if(attr.nodeValue.indexOf(',') == -1) return; + // attr val has comma, so let's get the good value + var ns = attr.nodeName == 'href' ? xlinkns : + attr.prefix == "xml" ? xmlns : null; + var good_attrval = orig_el.getAttribute(attr.nodeName); + elem.setAttributeNS(ns, attr.nodeName, good_attrval); + }); + + var childs = elem.childNodes; + var o_childs = orig_el.childNodes; + $.each(childs, function(i, child) { + if(child.nodeType == 1) { + fixOperaXML(child, o_childs[i]); + } + }); + } + var recalculateAllSelectedDimensions = function() { var text = (current_resize_mode == "none" ? "position" : "size"); var batchCmd = new BatchCommand(text); @@ -1413,7 +1436,7 @@ function BatchCommand(text) { changes["cx"] = c.x; changes["cy"] = c.y; // take the minimum of the new selected box's dimensions for the new circle radius - changes["r"] = round(Math.min(box.width/2,box.height/2)); + changes["r"] = Math.min(box.width/2,box.height/2); break; case "ellipse": var c = remap(changes["cx"],changes["cy"]); @@ -1884,7 +1907,7 @@ function BatchCommand(text) { 'cy': pt.y, // take the minimum of the new selected box's dimensions for the new circle radius - 'r': round(Math.min(selectedBBox.width/2,selectedBBox.height/2)) + 'r': Math.min(selectedBBox.width/2,selectedBBox.height/2) }, 1000); break; case "ellipse": @@ -3875,6 +3898,12 @@ function BatchCommand(text) { // set new svg document svgcontent = svgroot.appendChild(svgdoc.importNode(newDoc.documentElement, true)); + + // Fix XML for Opera/Win/Non-EN + if(window.opera) { + fixOperaXML(svgcontent, newDoc.documentElement); + } + svgcontent.setAttribute('id', 'svgcontent'); // determine proper size var w, h; From 516703cac799ddb7fd903f7b9db55c63ff8ea086 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Wed, 4 Nov 2009 15:30:10 +0000 Subject: [PATCH 08/35] transformlist branch: Fix proper sizing of selector boxes git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@906 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 162 ++++++++++++++++++++++++++++++-------------- 1 file changed, 113 insertions(+), 49 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 2221cd20..6c8a5096 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1,11 +1,10 @@ /* TODOs for TransformList: - * Fix proper selector box sizing * Fix moving/resizing/rotating of groups (pummel the transforms down to the children?) - * Ensure resizing in negative direction works - * Ensure ungrouping works - * Ensure undo still works properly + * Ensure resizing in negative direction works (nope! broken!) + * Ensure ungrouping works (surely broken) + * Ensure undo still works properly (nope! broken!) */ /* TODOs for Localizing: @@ -399,6 +398,7 @@ function BatchCommand(text) { }; }; + // TODO: update this function to not use the cur_bbox anymore this.resize = function(cur_bbox) { var selectedBox = this.selectorRect; var selectedGrips = this.selectorGrips; @@ -423,11 +423,52 @@ function BatchCommand(text) { }); } var l=bbox.x-offset, t=bbox.y-offset, w=bbox.width+(offset<<1), h=bbox.height+(offset<<1); + + // loop and transform our bounding box until we reach our first rotation + var tlist = canvas.getTransformList(this.selectedElement); + var m = svgroot.createSVGMatrix(); + var bFoundRotate = false; + var topleft = {x:l*current_zoom,y:t*current_zoom}, + botright = {x:(l+w)*current_zoom,y:(t+h)*current_zoom}; + var tstr = ""; + var i = tlist.numberOfItems; + // loop backwards through the list of transforms and update the selector box coords + while (i--) { + var xform = tlist.getItem(i); + // once we hit a rotate, we stop doing this and just save up the transform + // string fragment and apply it to the selector group + if (xform.type == 4) { + bFoundRotate = true; + } + if (bFoundRotate) { + tstr = transformToString(xform) + " " + tstr; + } + else if(!bFoundRotate) { + m = matrixMultiply(xform.matrix,m); + } + } + + // apply the transforms + topleft = transformPoint( topleft.x, topleft.y, m ); + botright = transformPoint( botright.x, botright.y, m ); + + this.selectorGroup.setAttribute("transform", ""); + this.selectorGroup.removeAttribute("transform"); + if (tstr != "") { + this.selectorGroup.setAttribute("transform", tstr); + } + + l = topleft.x; + t = topleft.y; + w = botright.x - topleft.x; + h = botright.y - topleft.y; + + // TODO: handle negative? + var sr_handle = svgroot.suspendRedraw(100); - l*=current_zoom; - t*=current_zoom; - w*=current_zoom; - h*=current_zoom; + + // TODO: move to a path instead of a rect and then plot the + // grip coordinates more carefully assignAttributes(selectedBox, { 'x': l, 'y': t, @@ -454,19 +495,6 @@ function BatchCommand(text) { assignAttributes(this.rotateGripConnector, { x1: l+w/2, y1: t-20, x2: l+w/2, y2: t }); assignAttributes(this.rotateGrip, { cx: l+w/2, cy: t-20 }); - // empty out the transform attribute - this.selectorGroup.setAttribute("transform", ""); - this.selectorGroup.removeAttribute("transform"); - - // align selector group with element coordinate axes - var elem = this.selectedElement; - var transform = elem.getAttribute("transform"); - var angle = canvas.getRotationAngle(elem); - if (angle) { - var cx = round(oldbox.x + oldbox.width/2) * current_zoom - cy = round(oldbox.y + oldbox.height/2) * current_zoom; - this.selectorGroup.setAttribute("transform", "rotate("+angle+" " + cx + "," + cy + ")"); - } svgroot.unsuspendRedraw(sr_handle); }; @@ -596,8 +624,8 @@ function BatchCommand(text) { // SVGTransform replaceItem ( in SVGTransform newItem, in unsigned long index ) // SVGTransform removeItem ( in unsigned long index ) // SVGTransform appendItem ( in SVGTransform newItem ) - // SVGTransform createSVGTransformFromMatrix ( in SVGMatrix matrix ); - // SVGTransform consolidate ( ); + // NOT IMPLEMENTED: SVGTransform createSVGTransformFromMatrix ( in SVGMatrix matrix ); + // NOT IMPLEMENTED: SVGTransform consolidate ( ); // } // ************************************************************************************** var svgTransformLists = {}; @@ -610,23 +638,7 @@ function BatchCommand(text) { var concatMatrix = svgroot.createSVGMatrix(); for (var i = 0; i < this.numberOfItems; ++i) { var xform = this._list.getItem(i); - var m = xform.matrix; - switch (xform.type) { - case 2: // translate - tstr += "translate(" + m.e + "," + m.f + ") "; - break; - case 3: // scale - if (m.a == m.d) tstr += "scale(" + m.a + ") "; - else tstr += "scale(" + m.a + "," + m.d + ") "; - break; - case 4: // rotate (with a translate) - var bbox = canvas.getBBox(this._elem); - var K = 1 - m.a; - var ty = ( K * m.f + m.b*m.e ) / ( K*K + m.b*m.b ); - var tx = ( m.e - m.b * ty ) / K; - tstr += "rotate(" + xform.angle + " " + [tx,ty].join(",") + ") "; - break; - } + tstr += transformToString(xform) + " "; } this._elem.setAttribute("transform", tstr); }; @@ -1375,8 +1387,6 @@ function BatchCommand(text) { dx = cx - newcenter.x; dy = cy - newcenter.y; - // TODO: fix this so that it determines the true translation - // required so that the rotational center lines up remap = function(x,y) { return { x: x + dx, y: y + dy }; }; @@ -2102,6 +2112,54 @@ function BatchCommand(text) { var transformPoint = function(x, y, m) { return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f}; }; + + // This is provided because WebKit doesn't implement multiply() correctly + // on the SVGMatrix interface. See https://bugs.webkit.org/show_bug.cgi?id=16062 + // It returns a SVGMatrix that is the multiplication m1*m2. + var matrixMultiply = function(m1, m2) { + var a = m1.a*m2.a + m1.c*m2.b, + b = m1.b*m2.a + m1.d*m2.b, + c = m1.a*m2.c + m1.c*m2.d, + d = m1.b*m2.c + m1.d*m2.d, + e = m1.a*m2.e + m1.c*m2.f + m1.e, + f = m1.b*m2.e + m1.d*m2.f + m1.f; + var m = svgroot.createSVGMatrix(); + m = m.translate(e,f); + m = m.scaleNonUniform(a,d); + m = m.skewX( Math.atan(c) ); + m = m.skewY( Math.atan(b) ); + return m; + } + + // This returns a single matrix Transform for a given Transform List + // (this is the equivalent of SVGTransformList.consolidate() but unlike + // that method, this one does not modify the actual SVGTransformList) + var transformListToTransform = function(tlist) { + var m = svgroot.createSVGMatrix(); + for (var i = 0; i < tlist.numberOfItems; ++i) { + m = matrixMultiply(m, tlist.getItem(i).matrix); + } + return svgroot.createSVGTransformFromMatrix(m); + }; + + // converts a string equivalent of a SVGTransform + var transformToString = function(xform) { + var m = xform.matrix; + switch(xform.type) { + case 2: // TRANSFORM + return "translate(" + m.e + "," + m.f + ")"; + case 3: // SCALE + if (m.a == m.d) return "scale(" + m.a + ")"; + return "scale(" + m.a + "," + m.d + ")"; + case 4: // ROTATE + // TODO: handle divide by zero here + var K = 1 - m.a; + var cy = ( K * m.f + m.b*m.e ) / ( K*K + m.b*m.b ); + var cx = ( m.e - m.b * cy ) / K; + return "rotate(" + xform.angle + " " + cx + "," + cy + ")"; + // TODO: matrix, skewX, skewY + } + }; // - when we are in a create mode, the element is added to the canvas // but the action is not recorded until mousing up @@ -2484,7 +2542,7 @@ function BatchCommand(text) { } // update our internal bbox that we're tracking while dragging - selectorManager.requestSelector(selected).resize(box); + selectorManager.requestSelector(selected).resize();//box); // TODO: remove box arg // now transform delta mouse movement into a translation in the // coordinate space of the mouse target @@ -2650,7 +2708,7 @@ function BatchCommand(text) { } } - selectorManager.requestSelector(selected).resize(selectedBBox); + selectorManager.requestSelector(selected).resize();//selectedBBox); // TODO: remove box arg break; case "zoom": x *= current_zoom; @@ -3406,7 +3464,7 @@ function BatchCommand(text) { if (selectedElements[i] == null) break; if(selectedElements[i].tagName != 'g') { // Not needed for groups (incorrectly resizes elems), possibly not needed at all? - selectorManager.requestSelector(selectedElements[i]).resize(selectedBBoxes[i]); + selectorManager.requestSelector(selectedElements[i]).resize();//selectedBBoxes[i]); // TODO: remove box arg } } } @@ -4919,7 +4977,13 @@ function BatchCommand(text) { if (preventUndo) { // we don't need to undo, just update the transform list - tlist.replaceItem(newrot, 0); + // Opera Bug: for whatever reason, sometimes Opera doesn't let you + // replace the 0th transform (perhaps if it's an identity matrix?) + try { + tlist.replaceItem(newrot, 0); + } catch(e) { + tlist.insertItemBefore(newrot,0); + } } else { // FIXME: we need to do it, then undo it, then redo it so it can be undo-able! :) @@ -4935,7 +4999,7 @@ function BatchCommand(text) { setPointContainerTransform(elem.getAttribute("transform")); } var selector = selectorManager.requestSelector(selectedElements[0]); - selector.resize(bbox); + selector.resize();//bbox); // TODO: remove box arg selector.updateGripCursors(val); }; @@ -5229,7 +5293,7 @@ function BatchCommand(text) { } // Timeout needed for Opera & Firefox setTimeout(function() { - selectorManager.requestSelector(elem).resize(elem.getBBox()); + selectorManager.requestSelector(elem).resize();//elem.getBBox()); // TODO: remove box arg },0); // if this element was rotated, and we changed the position of this element // we need to update the rotational transform attribute @@ -5473,7 +5537,7 @@ function BatchCommand(text) { if (cmd) { batchCmd.addSubCommand(cmd); } - selectorManager.requestSelector(selected).resize(selectedBBoxes[i]); + selectorManager.requestSelector(selected).resize();//selectedBBoxes[i]); } } if (!batchCmd.isEmpty()) { From 78bd23003cdd6180b9c90fab02bbe46e3152a2b0 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Wed, 4 Nov 2009 16:48:33 +0000 Subject: [PATCH 09/35] Made SVG-icons toolbar resizing more efficient, added new size option git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@907 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svg-editor-svgicons.html | 208 ++++++++++++-------------------- 1 file changed, 77 insertions(+), 131 deletions(-) diff --git a/editor/svg-editor-svgicons.html b/editor/svg-editor-svgicons.html index 0de8419f..752768ba 100644 --- a/editor/svg-editor-svgicons.html +++ b/editor/svg-editor-svgicons.html @@ -21,7 +21,7 @@ @@ -659,6 +604,7 @@ script type="text/javascript" src="locale/locale.min.js"> + From 59d8b00827041a33655c9c254413e40748d0ecd0 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Wed, 4 Nov 2009 21:00:21 +0000 Subject: [PATCH 10/35] SVG icon page: Added more SVG icons, made flyouts work (not on Opera), set all fallback icons, improved UI on resize git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@908 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/images/svg_edit_icons.svg | 135 +++++++++++++++++++++- editor/svg-editor-svgicons.html | 186 ++++++++++++++++++++++++++++--- 2 files changed, 301 insertions(+), 20 deletions(-) diff --git a/editor/images/svg_edit_icons.svg b/editor/images/svg_edit_icons.svg index 6b2609ed..61809b37 100644 --- a/editor/images/svg_edit_icons.svg +++ b/editor/images/svg_edit_icons.svg @@ -26,6 +26,45 @@ + + + + + + + + + + + Layer 1 + + + + + + + + + + + + + + + + + + + Layer 1 + + + + + + + + + @@ -34,8 +73,44 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layer 1 + + + + @@ -395,6 +470,62 @@ + + + + + + + + + + + + + + + Layer 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layer 1 + + + + + + + + + + + + + \ No newline at end of file diff --git a/editor/svg-editor-svgicons.html b/editor/svg-editor-svgicons.html index 752768ba..b2e86d46 100644 --- a/editor/svg-editor-svgicons.html +++ b/editor/svg-editor-svgicons.html @@ -21,7 +21,10 @@ @@ -340,7 +490,7 @@ script type="text/javascript" src="locale/locale.min.js">
| - Group +
@@ -360,7 +510,7 @@ script type="text/javascript" src="locale/locale.min.js">
| - Ungroup +
From dd602d8203f5efbe69c5ae080bb0beaca131007b Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Wed, 4 Nov 2009 21:23:40 +0000 Subject: [PATCH 11/35] Adding svg_edit_icons.svgz file for testing git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@909 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/images/svg_edit_icons.svgz | Bin 0 -> 5493 bytes editor/svg-editor-svgicons.html | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 editor/images/svg_edit_icons.svgz diff --git a/editor/images/svg_edit_icons.svgz b/editor/images/svg_edit_icons.svgz new file mode 100644 index 0000000000000000000000000000000000000000..cb8c80cc59589b7ef1230e766cdb6b72681b5289 GIT binary patch literal 5493 zcmV-*6^iN~iwFqj)bUCH19Ns~Uu9%zbYE#>Z*Frgb9QF{?Oj`Q+qjZ`p7|AA=Vhjf zqS3fx%9*O&^Kf2vpSEgWQ!+(4u0va1S&1|G_1g^sBuJ4GCClE))L2d=fg~E2?#9;_ zz&Fod@3Lf_aORafN>ul{t?*7Mu9`u_1>W3_>E?%H3ynn5!AdSAW#$L8C|E0(d0OZ+85 z+0DN#7TG_x)pA#@vX}ea=j^}#=bwveb-&9NS*Q=6_a(*MW^;#4+^nji+&o-oi-oV^ zj{eVXcK5rw`u#tbe^lEHZmyfJ{>sx5>wVsBH~*?WUVT{mpQ~(rU)T7R`Jbz11is#V z0&$h$#|Kjg3)<$qklIOGvw|7Lcn+puf-75#LNI1UzTk!C8ca>^O6y$VyHQZff-_*M zyJ68h!t;eJEaM>Y7G*TZe8CEVkKio;g*3*Rd;z@Rf=T)P>e_enn)-P|qw)LCH`o37 zo0`%8{j%IvIMDAkp|79M>}FN1pYc7r!KPHp?LW86)qUF`oFIa1c!U?dsgaVAj<{&ZYfH0L% z02#d_lA`aNSAf+V!w)63Qvw1kK zp$Zms6nzMiY&}~xpH1^E&V_u1mMTt>=Ji_RPa@6n=^66e40z<3lms!!faZykCi}vT zP35TeO010=eE;U(&s@H$y1svU{+mS9h_rtMRYLN-kR*0Oc~w|B;g$m$jJ5=Hy{TxaZ!F3eSQxM z{poNe1v?{9=qO|x5`PV1>EWLNG&;2*7e*{YP;?y1CYb9?h4fTCmiKj3M(GOE*G$sB ziDv3l)1`IOP?n3+)YDL$_Z~l}p~h9zb*qLW2mMzgM_DRJS(KzK5vA;6vNX9;?kT0N zkx|~V83LDGR^rZR?m#NDQ^hiSy zGQ)9XAZU(h*hJFemh~PtkJaTS@np@_#xLH%QF)p8MWX?Jn$~vEI-I0NF>3Xl(!BNK zI6>&8Q+UER?uLhM>_0q&FCaCW@PGd_rtJ>(uvO?_g>tcn6gA3&VgV(aX%nC*Nw0PRk)jj=s?Wq>qb4z( z7sKl$07l>hNUFKvB<|W8!D|#`WGV%yITY7efOiO_m@jl8xH5@S(yh{4GJ1`M7y9Io z3ZNFsX<^Vx3$IHp1BAxYq|wU;=afvPz4|Yo{b8?fA^XIdN}Wp*w1QGe;ia~{H`TD zIEiFvi3*6i^E!PJN5@y4+^~fE$65n?*dAYU@n$66g6o5VI1WTmee|4wCwTU-KN+-W z*>@M@H?&?0t+@OiRJ;)E_~UXfLBPM4f1Wwky>hT0?*zX+qXECdN- zwC{M5CtwmWXd%!C&ghj$QHRv~3-tBu2yc7_3O-VAq${|SX_HjaRN3}`OcLyvDn!#U za(T8{2`uh>()LZ25~;gqa~pNs-hblqggcAg}o*9-tRY>6t+!a)PV7LEv&yr2fj zoE1z9tFwkKc{n(l6xy30Xq1@Pu(EAB@{uth$)-T4Z*zkF0xQkp6R~q?56pQzI782e zpx=nQ1C;;wqdGi9P>8Hkk6l=X%`*E}f?j8u{V1IHdMmTP<5Y&iBvD)t6rgn}@MBHP z=!>L4K~`Lp3hmB@JCUm`8oZJU0CM7UuxQFTin<^Q1FeZAWdXWgI5b-orIrGUuOw%( z%W}5|T|_T0tgosQA6}APw%zQICw^H-m1h8&{zLo|Ml9%@GFWY-ROlVY zTBVC#>qGodGI||tf;vt>CQeG;$>wrdmfd1ANnpeupbBj@les3(i2^8vMV083q|{(- zq~w`wOK|y+){4xk0tbau2n$a^fp-X0nB+#lqKqa=pfU$?QWFJ}EhOqL$8he+!H3t% zh4uU?jE{Ck7-|oI$9w$8OWux-7Uekyj{*8gmXL+2%tAFpSHACy-Q-b%2dz~ zgRJiZIYtPzYnCfkQ|yAPdeOXpKVtVycXzdUnQ%j+T9$92l|yg$#nG`m<0Om(PMUR^ zVn*ZrJ?1(&!w!C|UOx5Aksl;|cxHw+K?&n{$kNuDK1LvBlSJ)A$*I;A@u6DMD;a0a z=+(z*d+w_C?&yW>kQ}7hJ6d`DCsHJ*3oeaz4U$8B1KNT(7k)f1zg9oqZjnjw(O`=a zOEYmV4q^_6Z7aj+HL%O(KD^TO8W?7C!zzun+2*q_0=iw?h%rF{g~oNmBzK81B=rd$ zv~YX79vYr=9~MSew`N`kPeE6AU0-DEOMfYdi(HgKMz3Uj46Yg|6ap%jb6iZxprPQ4 z;0SLPJ_{TGmFPmFO`#1MH?+scIv)wQ%ve`bh@Sg}Io`gAtFohwK^*FEA=ea8knp`7 zzJAhjwWim;cL@@cQ1H|wGzyqaELog?CK^MX5(g|NAQaRWHov8NaHCX&WbN>VEM+wO z6k{p}93RewPx@iT_z;X0hyrW037JG3MC)w0hPI%P(o#IADWGtTv5--iT1tP1mDtW~ zffyOb)5HRVKnR4L!EwzfMr9abAH-syl)sx>gvVr=k#YCE<*K_eRKP9H$9~d;)XC6N zu6(m03?#Q2XU|ASPCUv45VYOPI7Hg`T`_QIHLAi|%REmgh8Uh0hTa7+41qO=K+b<8 z#GZwBdQLe@@C~O(3j=LQ>0|ejsiw=^NTBeTFjm;^ZjIBfjiv~h<=R`XN=nq7WX*b0 z72&3~8Kqz_P*mVQpnZ(}P+c>#(8e5BG|61|J&>%!eR(LSj$(_jP2s{TcC4*7<ZB+rMMnu}N}yDNu+r$% zXr4(#LoJ06Hs6MKg@_=wszLvZosD7>bb>F6* zv`wSvv}=0=B!K8$aF^S{2qz^jCtd>^StB(eog)Qo@TXa5oKoDDNFOo2jF(WEbB#(p zC;x-9^c}UogFN0(tAMr&Nc#%`xWM3@3@r=FZ*WcUq+6uP84sJsjeo3{#?d7N%n&yO zZQ#;)f3vd>ntgrT7PMT9={C_ixW)Povz{xhb+OIaBxcr5j|U@<#v)gu z6mY_>x6^W+MnDsV41jse=iw`&LLGt!8nG-`OX<9ZE6r<-SEci*t{{Z9Sb&BBK4=hh zIp7rZHwaaM;HLm58qCVMavo?(t_&4OBu1doXvB!QDy%>ZO3oZhu8CY1jDt|+0wJ7L z#`{4iU5aahhDJ%sp)!a{1>=6t2_Vgqb^#fRuoW6_$q;~IlgNuG2hBEok1E>4t*l3IOh8PTPD0ps9y~CK*~n8f{di zHOZM2K1tu997-=|HpG`13QuHD=p+rTQSYU8()%2ZLX)j=F)ASGF<6uLIT~Y`@v?^Y ziwv^H4BC)_GTvPyYX&W66mtMH3B9OMOq{kOp_vI@*tiH8Gr$~4n`X?La!N2Iyu7g{ zkslaSngom~MFNN|lshJF22yd8lJLv9;#StQy1u>Lg$6g+ z|1Wt61>eIloKGH7*5bo#oUI zPS~|_Y(F@m)01LoN0i@nOz9;C*E-s@!!%Gn5D0&coGMU^K-EAABjAstV;t|PaTNzZ zdCplKicrERvf@zIB&q=?u^tcwDhe%PKJtvHxS^yt<3fI;V2r{|t%roWey~bNmINTh z{vH|8TQ%dTY@6KEZ32ddNPA)cMH<}^O_ZEd)}#wt+)?vbzsst@-T>qzPAn(lE7VHT z3fnqFnNYx6uPmx^+4@n#rIup4#WnI-f6rE$Z$t^Ak{enIl(NdzI5U#c8<`I%GAqb; z#|#}wNP!q34EJ+PC8>TCSCjc2=|Clc1B7E2%T-)eb+!A^lG5j7fe-_Vh8@%0Ft+dB z82TIN6ou{>Ka>fT*l65uQnot8LJThGG8{66GvDqFK16=K#-&qDlGh1&NPC!^NB-Q>CIXagf%)aS$WPE%^}4Ky77FHlpX>prxhbe*W%~51}+y4pDBA zB0JKGeah2>A*Fl<+uvS1c7HS&aD=5k%(a&cxBiQ-Fd9bY<_ zIZPh3V_^Z4ZtxM|+*ky zO<`BmmUOq%#(iJKD}wCElp#g&{m|?oEINasPT1iy%1^Ihr;BYVJHSb5oRbKz0Wkw3 zUyBn6IRX!$J;nuX)9uyvrl5dB9?SFdq(s%oV~~FVe_^6bi3K@{1${a zU(`j`?1-AfN0T;(Fu!fM0c(1gl)3^pcon-9uEBE zZ5j$IMxzgAFZytdCehI?9Y~Kg&i5@U1MlAU7uwB}15YHaf2af+d6m%P~iiMcc$4T!fRnzG>USR+o*ih3d zw(uv#P&R;ypBMg1SmOMhuhT0iOyjOiuLj=#C_JmL4(~@g8Ub+YRkg?dyw9totCo>z%0wv>f8e`jdkm4HD9w$cZ0vS8-^{RI`d#sd8==i zQ$ui(KLjDdUxHxiUxG-`!Su;YN;Qt4R2}$!G(AS%ngJ0Ni0DZo?W5hMcq5!!=?PlnV?WI^El=2^V|8|p z$lpryZ>9MrO7kxvt4B4z8~YPL7gGG8YI}LrJo<-=UZZ+K)h*{X=dnPXdv$IX#_E>+ rx`AJ^^2T77Z&J7J)%~h5IrcX)eoi(-RGT$D6K?-M?fw?*gKhu-3(JTQ literal 0 HcmV?d00001 diff --git a/editor/svg-editor-svgicons.html b/editor/svg-editor-svgicons.html index b2e86d46..fa2450cb 100644 --- a/editor/svg-editor-svgicons.html +++ b/editor/svg-editor-svgicons.html @@ -101,7 +101,7 @@ TODO for SVG icons: */ $(function() { - $.svgIcons('images/svg_edit_icons.svg', { + $.svgIcons('images/svg_edit_icons.svgz', { w:24, h:24, id_match: false, fallback_path:'images/', From 535a9f240e816d4230d3f708b0943ee959070a32 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Wed, 4 Nov 2009 21:27:44 +0000 Subject: [PATCH 12/35] Changing icon file ref back to .svg due to errors :( git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@910 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svg-editor-svgicons.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/svg-editor-svgicons.html b/editor/svg-editor-svgicons.html index fa2450cb..b2e86d46 100644 --- a/editor/svg-editor-svgicons.html +++ b/editor/svg-editor-svgicons.html @@ -101,7 +101,7 @@ TODO for SVG icons: */ $(function() { - $.svgIcons('images/svg_edit_icons.svgz', { + $.svgIcons('images/svg_edit_icons.svg', { w:24, h:24, id_match: false, fallback_path:'images/', From ea383530dda15d45bb20165b171476b24d7365b9 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 5 Nov 2009 15:44:03 +0000 Subject: [PATCH 13/35] transformlist branch: change transformToString to transformToObj and refactor some more code git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@911 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 123 ++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 51 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 6c8a5096..9a52ada7 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1,6 +1,8 @@ /* TODOs for TransformList: + * Fix rotation + * Fix bounding box of groups with rotated children * Fix moving/resizing/rotating of groups (pummel the transforms down to the children?) * Ensure resizing in negative direction works (nope! broken!) * Ensure ungrouping works (surely broken) @@ -372,7 +374,7 @@ function BatchCommand(text) { this.rotateGrip.setAttribute("display", bShow); this.rotateGripConnector.setAttribute("display", bShow); var elem = this.selectedElement; - if(elem && (elem.tagName == "text" || elem.tagName == "g")) bShow = "none"; + if(elem && (elem.tagName == "text")) bShow = "none";// || elem.tagName == "g")) bShow = "none"; for (dir in this.selectorGrips) { this.selectorGrips[dir].setAttribute("display", bShow); } @@ -441,7 +443,7 @@ function BatchCommand(text) { bFoundRotate = true; } if (bFoundRotate) { - tstr = transformToString(xform) + " " + tstr; + tstr = transformToObj(xform).text + " " + tstr; } else if(!bFoundRotate) { m = matrixMultiply(xform.matrix,m); @@ -638,7 +640,7 @@ function BatchCommand(text) { var concatMatrix = svgroot.createSVGMatrix(); for (var i = 0; i < this.numberOfItems; ++i) { var xform = this._list.getItem(i); - tstr += transformToString(xform) + " "; + tstr += transformToObj(xform).text + " "; } this._elem.setAttribute("transform", tstr); }; @@ -1186,7 +1188,7 @@ function BatchCommand(text) { var i = selectedElements.length; while(i--) { - var cmd = recalculateDimensions(selectedElements[i],selectedBBoxes[i]); + var cmd = recalculateDimensions(selectedElements[i]);//,selectedBBoxes[i]); if (cmd) { batchCmd.addSubCommand(cmd); } @@ -1256,10 +1258,9 @@ function BatchCommand(text) { // this function returns the command which resulted from the selected change // TODO: use suspendRedraw() and unsuspendRedraw() around this function - // TODO: get rid of selectedBBox - var recalculateDimensions = function(selected,selectedBBox) { - if (selected == null || selectedBBox == null) return null; - + var recalculateDimensions = function(selected) { + if (selected == null) return null; + if (selected.tagName == "g") return null; // if this element had no transforms, we are done var tlist = canvas.getTransformList(selected); if (tlist.numberOfItems == 0) return null; @@ -1350,10 +1351,38 @@ function BatchCommand(text) { var origcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; var newcenter = {x: origcenter.x, y: origcenter.y}; var currentMatrix = {a:1, b:0, c:0, d:1, e:0, f:0}; - var n = tlist.numberOfItems; var tx = 0, ty = 0, sx = 1, sy = 1, r = 0.0; + + var n = tlist.numberOfItems; + + // TODO: have passes where we ONLY eliminate transforms (not + // provide remap/scaling functions for the element) + // This would reduce the transforms to the bare minimum. + // For now, these passes will just collapse adjacent transform types. + // This processing does not change the geometry of the element itself, + // it will only reduce the transform list. + + // TODO: first loop and find all adjacent transform sets of the form: + // translate(tx,ty) scale(sx,sy) translate(-tx,-ty) and reduce them + // to one set (multiply sx and sy) + var tx = 0, ty = 0, sx = 0, sy = 0; while (n--) { - var bRemoveTransform = true; + } + + // TODO: then loop and find all adjacent translates of the form: + // translate(tx1,ty1) translate(tx2,ty2) => translate(tx1+tx2,ty1+ty2) + n = tlist.numberOfItems; + while (n--) { + + } + + // this loop then computes the remapping required of the element + // (this prevents abnormal scaling of strokes) + var bRemoveTransform = true; + n = tlist.numberOfItems; + while (n--) { + // once we reach an unmoveable transform, we can stop + if (!bRemoveTransform) break; var xform = tlist.getItem(n); var m = xform.matrix; // if translate... @@ -1392,7 +1421,7 @@ function BatchCommand(text) { }; scalew = function(w) { return w; } scaleh = function(h) { return h; } - + // this latches to false once we hit our first rotate transform bRemoveTransform = false; var newrot = svgroot.createSVGTransform(); newrot.setRotate(xform.angle, cx, cy); @@ -1414,6 +1443,7 @@ function BatchCommand(text) { switch (selected.tagName) { + /* case "g": var children = selected.childNodes; var c = children.length; @@ -1428,11 +1458,12 @@ function BatchCommand(text) { h = scaleh(childBox.height); childBox.x = pt.x; childBox.y = pt.y; childBox.width = w; childBox.height = h; - batchCmd.addSubCommand(recalculateDimensions(child, childBox)); + batchCmd.addSubCommand(recalculateDimensions(child));//, childBox)); } catch(e) {} } } break; + */ case "line": var pt1 = remap(changes["x1"],changes["y1"]), pt2 = remap(changes["x2"],changes["y2"]); @@ -1773,7 +1804,7 @@ function BatchCommand(text) { h = scaleh(childBox.height); childBox.x = pt.x; childBox.y = pt.y; childBox.width = w; childBox.height = h; - batchCmd.addSubCommand(recalculateDimensions(child, childBox)); + batchCmd.addSubCommand(recalculateDimensions(child));//, childBox)); } catch(e) {} } } @@ -2142,23 +2173,37 @@ function BatchCommand(text) { return svgroot.createSVGTransformFromMatrix(m); }; - // converts a string equivalent of a SVGTransform - var transformToString = function(xform) { + // converts a tiny object equivalent of a SVGTransform + // has the following properties: + // - tx, ty, sx, sy, angle, cx, cy, string + var transformToObj = function(xform) { var m = xform.matrix; + var tobj = {tx:0,ty:0,sx:1,sy:1,angle:0,cx:0,cy:0,text:""}; switch(xform.type) { case 2: // TRANSFORM - return "translate(" + m.e + "," + m.f + ")"; + tobj.tx = m.e; + tobj.ty = m.f; + tobj.text = "translate(" + m.e + "," + m.f + ")"; + break; case 3: // SCALE - if (m.a == m.d) return "scale(" + m.a + ")"; - return "scale(" + m.a + "," + m.d + ")"; + tobj.sx = m.a; + tobj.sy = m.d; + if (m.a == m.d) tobj.text = "scale(" + m.a + ")"; + else tobj.text = "scale(" + m.a + "," + m.d + ")"; + break; case 4: // ROTATE - // TODO: handle divide by zero here - var K = 1 - m.a; - var cy = ( K * m.f + m.b*m.e ) / ( K*K + m.b*m.b ); - var cx = ( m.e - m.b * cy ) / K; - return "rotate(" + xform.angle + " " + cx + "," + cy + ")"; + tobj.angle = xform.angle; + // this prevents divide by zero + if (xform.angle != 0) { + var K = 1 - m.a; + tobj.cy = ( K * m.f + m.b*m.e ) / ( K*K + m.b*m.b ); + tobj.cx = ( m.e - m.b * tobj.cy ) / K; + } + tobj.text = "rotate(" + xform.angle + " " + tobj.cx + "," + tobj.cy + ")"; + break; // TODO: matrix, skewX, skewY } + return tobj; }; // - when we are in a create mode, the element is added to the canvas @@ -2526,7 +2571,6 @@ function BatchCommand(text) { var selected = selectedElements[i]; if (selected == null) break; var box = canvas.getBBox(selected); -// box.x += dx; box.y += dy; selectedBBoxes[i].x = box.x + dx; selectedBBoxes[i].y = box.y + dy; @@ -2543,30 +2587,6 @@ function BatchCommand(text) { // update our internal bbox that we're tracking while dragging selectorManager.requestSelector(selected).resize();//box); // TODO: remove box arg - - // now transform delta mouse movement into a translation in the - // coordinate space of the mouse target -// var startpt = transformPoint(start_x, start_y, mouse_target_ctm); -// var endpt = transformPoint(x, y, mouse_target_ctm); -// dx = endpt.x - startpt.x; -// dy = endpt.y - startpt.y; - - /* - var angle = canvas.getRotationAngle(selected); - if (angle) { - var cx = round(box.x + box.width/2), - cy = round(box.y + box.height/2); - var xform = ts + [" rotate(", angle, " ", cx, ",", cy, ")"].join(''); - var r = Math.sqrt( dx*dx + dy*dy ); - var theta = Math.atan2(dy,dx) - angle * Math.PI / 180.0; - selected.setAttribute("transform", xform); - box.x += r * Math.cos(theta); box.y += r * Math.sin(theta); - } - else { - selected.setAttribute("transform", ts); - box.x += dx; box.y += dy; - } - */ } } } @@ -4589,6 +4609,7 @@ function BatchCommand(text) { var visEls = canvas.getVisibleElements(); $.each(visEls, function(i, item) { var sel_bb = item.getBBox(); + // TODO: we are not using the second argument here anymore, what to do? var cmd = recalculateDimensions(item, { x: sel_bb.x - bbox.x, y: sel_bb.y - bbox.y, @@ -5189,7 +5210,7 @@ function BatchCommand(text) { replacePathSeg(new_type, next_index, points); addAllPointGripsToPath(); - recalculateDimensions(current_path, current_path.getBBox()); + recalculateDimensions(current_path);//, current_path.getBBox()); updateSegLine(true); batchCmd.addSubCommand(new ChangeElementCommand(current_path, {d: old_d})); @@ -5451,7 +5472,7 @@ function BatchCommand(text) { elem.setAttribute("transform", ""); } batchCmd.addSubCommand(new ChangeElementCommand(elem, changes)); - batchCmd.addSubCommand(recalculateDimensions(elem, childBox)); + batchCmd.addSubCommand(recalculateDimensions(elem));//, childBox)); } } @@ -5533,7 +5554,7 @@ function BatchCommand(text) { } else { selectedBBoxes[i].y += dy; } - var cmd = recalculateDimensions(selected,selectedBBoxes[i]); + var cmd = recalculateDimensions(selected);//,selectedBBoxes[i]); if (cmd) { batchCmd.addSubCommand(cmd); } From 3301054326c85e9d8e7fb747caa105bcf883af3c Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Thu, 5 Nov 2009 21:25:33 +0000 Subject: [PATCH 14/35] Made several fixes and improments to SVG icons page git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@912 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/images/spinbtn_updn_big.png | Bin 0 -> 2049 bytes editor/images/svg_edit_icons.svg | 251 ++++++++++++++++++++++------- editor/spinbtn/JQuerySpinBtn.js | 4 +- editor/svg-editor-svgicons.html | 127 ++++++++++----- 4 files changed, 282 insertions(+), 100 deletions(-) create mode 100644 editor/images/spinbtn_updn_big.png diff --git a/editor/images/spinbtn_updn_big.png b/editor/images/spinbtn_updn_big.png new file mode 100644 index 0000000000000000000000000000000000000000..3873736f8ef6e152754ed038ea28b396403f886c GIT binary patch literal 2049 zcmV+c2>$npP)~`Xx0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU(qe(L6(nIO4`!$Oq-B}ylpL_cxb zjcj8ei6#*jgkXgvj;IjV-Nf8L5E+PaH=)mp`$@zVasQ+kHzJ}}-PBYtsBfK@KEH^b zB(4iVl|Yogkw9C3?e6E${o_#P`N-^M^>WqQBf; zw0Q_3BT*{GE9U?FU?Rgq9+HT8!lHg9wi31>h#bTYB8+%c5dBPa{UdbndyynbiRh6c zo+%F6BWCJLQ&t{Cn1~L_c6lQT=+5Boj1P0r$zrdv6IaFRd}&I=7$;=>E}r+gA7zW^ ze34k{BgxamS>boPi4b3`PKj-G;@v1QE_Mi#P~xZ<-$5k%<3}%w*d`0p_0~4K`O&)J zM221BVa2>(@jx=qZ9?A&i}RaEd9k%Z$T?!lpnePSqv*d(%p6JdP9u7Surd(^?zIoZ zeV4n+MzZm3cY6n`+$LZZ_x1;Bhm8^2gfifgcrVAr^)+HbAkPfX3DcE@6W?3miqSX) z6%0BBm3lkvj)W+fsB*Ky$)kBvO(ojISz=U=#71EZex2*bY@5&$v0=9dci8>=$B1aw ztT7_$>(h*3l!zqhQ3MB1_u;@YBBBtw`$5##e@C(k{+>2EM0))eT)#dU@4OR?gtLDSUw&yub+tvkbM|Z;ubm`+ z4?5lBBPUWKaA@e8ICpL)4jdTR{H9H7QTxivEvTxBgvs>L$cap*C1`Jd6D1{fw6w%v z(xeuuuLnz(+)(qOojdiob7wJE_QvpuBq@>tw&BPTD^(V+#0});+K`-VLuRG{QBkdE zYKp{gYJM)p1)r!>A+pyb%s9H0w zUVWbawZ?ZMS$>?e%f-HZCe+nMBP65~IXS?@iQh8lel5mS!Rg%3`$kwSHe9|OhP`{c zIEgK^!X#gabhNTus$vEzD$MHj^5wmlKfeW5>rSrJ@y|%7vk-}i4am=LSH~SZ7=sfh z!b!ZH<6^uMnM|p4$dxE9?ZMr<5fpSAZ@(>ZB_Fc4anR^q-rtM`3v^h#_?nt17ZgZn zXh@?YulG(QNeLY2;tSVf_UsPi<;i^LF#7wydZ?ORt~1oY9%N@ffs~Y6h>g{wrsg8L zyB+@$`@nIlre+lfEv4W-u1-;-g(_0Yn$2FET5;MU8buhjV51QQBRcwbIVGjYGf{ng zIC^@Fe4ztT!D8vI_<)L8~)pbp|c9 z)QB`9jY$1#Eki_d|KgGMJp;7w8MN;ipncEqKl`3x&mJAq|4^p!PNw`$rvG*y(|<7i zH}W|n(|;4w|4w=Rde=||4GB7(vSx`w9DWouLZH|8i-LyHdqKlydjWlY_e(S`m+vJS zgW+E#8uk=j7PoG>Fn8`#2njKI zC$ihC5g&gLSy>i@hg;FmFbF|U*<%sIJwV< z+RFPm!(uF0Z~++^Hud_@p|GGi?L>n9kXY*qq{o{B_+Tgf2`sPUKL}~NVJ;w9TaTFlqsE9w=R^b+(AP+ z=lkJAmJh*fK7_4X^#~5`!TIyCIB-BomUAdT=Ac8jVC~unj30lPj_K21 z(GN!N+FEbpm48>hM^jTgl9L - + + + + @@ -8,9 +111,9 @@ - + - + @@ -24,9 +127,9 @@ - + - + @@ -40,9 +143,9 @@ - + - + @@ -62,10 +165,10 @@ - + - + @@ -77,9 +180,9 @@ - + - + @@ -91,9 +194,9 @@ - + - + @@ -113,9 +216,9 @@ - + - + @@ -128,9 +231,9 @@ - + - + @@ -151,9 +254,9 @@ - + - + @@ -164,10 +267,10 @@ - + - + @@ -181,9 +284,9 @@ - + - + @@ -207,9 +310,9 @@ - + - + @@ -226,22 +329,22 @@ - + - + - + - + - + - + @@ -250,9 +353,9 @@ - + - + @@ -275,9 +378,9 @@ - + - + @@ -301,9 +404,9 @@ - + - + @@ -317,9 +420,9 @@ - + - + @@ -328,9 +431,9 @@ - + - + @@ -344,9 +447,9 @@ - + - + @@ -360,9 +463,9 @@ - + - + @@ -384,9 +487,9 @@ - + - + @@ -397,9 +500,9 @@ - + - + @@ -413,9 +516,9 @@ - + - + @@ -428,9 +531,9 @@ - + - + @@ -448,9 +551,9 @@ - + - + @@ -468,9 +571,9 @@ - + - + @@ -494,9 +597,9 @@ - + - + @@ -522,9 +625,37 @@ - + + + + + + + + + + + + Layer 1 + + + + + + + + + + + + Layer 1 + + + + + diff --git a/editor/spinbtn/JQuerySpinBtn.js b/editor/spinbtn/JQuerySpinBtn.js index 241e594a..735be005 100644 --- a/editor/spinbtn/JQuerySpinBtn.js +++ b/editor/spinbtn/JQuerySpinBtn.js @@ -81,7 +81,6 @@ $.fn.SpinButton = function(cfg){ delay: cfg && cfg.delay ? Number(cfg.delay) : 500, interval: cfg && cfg.interval ? Number(cfg.interval) : 100, _btn_width: 20, - _btn_height: 12, _direction: null, _delay: null, _repeat: null, @@ -112,9 +111,10 @@ $.fn.SpinButton = function(cfg){ var x = e.pageX || e.x; var y = e.pageY || e.y; var el = e.target || e.srcElement; + var height = $(el).outerHeight()/2; var direction = (x > coord(el,'offsetLeft') + el.offsetWidth - this.spinCfg._btn_width) - ? ((y < coord(el,'offsetTop') + this.spinCfg._btn_height) ? 1 : -1) : 0; + ? ((y < coord(el,'offsetTop') + height) ? 1 : -1) : 0; if (direction !== this.spinCfg._direction) { // Style up/down buttons: diff --git a/editor/svg-editor-svgicons.html b/editor/svg-editor-svgicons.html index b2e86d46..1ac6d8a4 100644 --- a/editor/svg-editor-svgicons.html +++ b/editor/svg-editor-svgicons.html @@ -17,7 +17,7 @@ - + @@ -492,7 +561,7 @@ script type="text/javascript" src="locale/locale.min.js">
- +
@@ -704,13 +773,13 @@ script type="text/javascript" src="locale/locale.min.js">
- + - + - + - + From 700604749b8c54824012b551eb953f4ad7c5aa38 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Mon, 9 Nov 2009 15:17:59 +0000 Subject: [PATCH 16/35] transformlist branch: more work on transforms for groups (can now move groups around but not rotate/scale them, ungrouping still broken) git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@914 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 206 ++++++++++++++++++++++++++------------------ 1 file changed, 123 insertions(+), 83 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 9a52ada7..9c82da68 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1,9 +1,11 @@ /* TODOs for TransformList: - * Fix rotation + * Fix rotation of groups (rotate being prepended) + * See if we can completely eliminate translates on groups (currently flattening to one) + * Flattening adjacent scale transform sets on groups * Fix bounding box of groups with rotated children - * Fix moving/resizing/rotating of groups (pummel the transforms down to the children?) + * Fix resizing/rotating of groups (pummel the transforms down to the children?) * Ensure resizing in negative direction works (nope! broken!) * Ensure ungrouping works (surely broken) * Ensure undo still works properly (nope! broken!) @@ -1199,54 +1201,78 @@ function BatchCommand(text) { call("changed", selectedElements); } }; - + /* - Changes need to handle resizing of groups and any number of transformations: + + The user changes shape geometry in one of several ways: + - drag/moving it + - rotating it + - FUTURE: skewing it + - resizing it + (we ignore path node editing here) + + From a transformation point of view: + - translations are always in the editor's frame of reference (NOT the element's) + - rotations rotate the element's frame of reference + - FUTURE: skewing skews the element's frame of reference + - resizing modifies the dimensions of the element in its rotated+skewed + frame of reference. + + Thus, from a coding point of view, what we do when the user is changing geometry is: + + - when the user drags an element, we PREPEND the tlist with a: + translate(tx,ty) + - when the user rotates an element, we INSERT into the tlist a: + rotate(angle,cx,cy) after any translates + - FUTURE: when the user skews an element, we INSERT into the tlist a: + skewX(angle) or skewY(angle) after the rotate + - when the user is resizing an element, we APPEND the tlist with a: + translate(tx,ty) scale(sx,sy) translate(-tx,-ty) + + Thus, a simple element's transform list looks like the following: + [ Translate ] [ Rotate ] [ SkewX/Y] [ Scale ] + + When the user is done changing the shape's geometry (i.e. upon lifting the mouse button) + we then attempt to reduce the transform list of the element by the following: + + - a translate can be removed by actually moving the element (modifying its x,y values) + - a rotate cannot be removed + - FUTURE: a skewX/skewY cannot be removed + - a scale can be removed by resizing the element (modifying its width/height values) - - while being manipulated with a mouse, shapes can have any number of transforms applied - - when moused up, we will traverse the transform list of the element and see if we can - reduce the transform list to as small as possible (preferably a zero-length transform list) - - since translations completely preserve all shapes and orientations, we can ALWAYS remove - translates (note that this doesn't apply to translates that are part of a scale operation) - - since rotations do not preserve any orientations, it is not possible to preserve rotations - unless the rotation degrees is a multiple of 90, thus we will always keep rotational - transforms around - - non-uniform scales (sx!=sy) preserve the shape and orientation ONLY if the shape has - not been rotated, thus we will reduce non-uniform scales when we haven't rotated the shape - but otherwise we are forced to keep them around (a consequence of this is that scale - transforms will only be present in the final list with a rotate) - - uniform scales (sx==sy) preserve the shape and orientation, so we can ALWAYS remove - uniform scales, even when the shape has been rotated - - does the removal of translate or scale transforms affect the rotational center of - previously visited transforms? - - ungrouping has the effect of transferring the transform list of the group down to its - individual children, thus we will then attempt to reduce the transform list further on - each child upon ungrouping - - - we traverse the transform list backwards, starting at the nth transform and modifying - frame of reference (the ctm) as we move to the next transform - - each time we reduce the transform list, we remap the coordinates of the shape - - at the end, we have a new transform list and a new set of coordinates for the shape, - we can then update the DOM with both of these elements - - - The Simplest Case: - Dragging an axis-aligned rectangle 150 pixels east. - - * Transform list has just one transform: translate(150,0) - * we translate the top-left coordinate of the rect (by adding 150 to the 'x' value) - and reduce the transform list to empty + Thus, a simple element's transform list can always be reduced to: + [ Rotate ] [ SkewX/Y ] - A Simple Case: - Dragging an ellipse which has been rotated 30 degrees by 100 pixels south - - * Transform list has two transforms: translate(0,100) rotate(30,cx,cy) - * we start at the rotation, since we cannot reduce rotations, we simply update the - current transform matrix so that it includes the translate(cx,cy) rotate(30) translate(-cx,-cy) - * next, we get to translate(0,100), standalone translates can always be reduced so first - we rotate by 30 degrees and it becomes translate(50,86.6), then we move the ellipse's - center point by 50,86.6 and then adjust the rotate so that the transform list results - as rotate(30,cx',cy') + However, a group is an element that contains one or more other elements, let's call + this a Complex Element. + + From the user point of view, a complex element is handled no differently + than a simple element. Thus its transform list looks like the following: + [ Translate] [ Rotate ] [ SkewX/SkewY ] [ Scale ] + + - all translates can be removed by moving the element's children + - all rotations can be collapsed down to one rotation + - all scales can be collapsed down to one scale (we cannot simply resize the children + because the child of a may be another group - and that may be rotated!) + + This means a complex element has a reduced transform list as: + [ Rotate ] [ SkewX/SkewY ] [ Scale ] + + Next, we have to consider the case when a group is dissolved (ungrouped). When + the group is dissolving, the transform list must make its way down to the children. + + Thus, every child of the now-dissolved group inherits the transformlist. Child N's + transform list looks like: + [ Parent Rotate ] [ Parent SkewX/SkewY ] [ Parent Scale ] [ Rotate ] [ SkewX/SkewY ] [ Scale ] + + THINGS TO FIGURE OUT: + + 1) It's not clear to me yet what happens when you want to rotate an element with the + above type of transform list. + + 2) It's also not clear to me if we need to calculate the rotation angle of the element + differently (nor what we should display as the element's rotation angle). + */ // this is how we map paths to our preferred relative segment types @@ -1260,7 +1286,6 @@ function BatchCommand(text) { // TODO: use suspendRedraw() and unsuspendRedraw() around this function var recalculateDimensions = function(selected) { if (selected == null) return null; - if (selected.tagName == "g") return null; // if this element had no transforms, we are done var tlist = canvas.getTransformList(selected); if (tlist.numberOfItems == 0) return null; @@ -1352,53 +1377,75 @@ function BatchCommand(text) { var newcenter = {x: origcenter.x, y: origcenter.y}; var currentMatrix = {a:1, b:0, c:0, d:1, e:0, f:0}; var tx = 0, ty = 0, sx = 1, sy = 1, r = 0.0; - - var n = tlist.numberOfItems; - // TODO: have passes where we ONLY eliminate transforms (not - // provide remap/scaling functions for the element) - // This would reduce the transforms to the bare minimum. - // For now, these passes will just collapse adjacent transform types. - // This processing does not change the geometry of the element itself, - // it will only reduce the transform list. - - // TODO: first loop and find all adjacent transform sets of the form: - // translate(tx,ty) scale(sx,sy) translate(-tx,-ty) and reduce them - // to one set (multiply sx and sy) - var tx = 0, ty = 0, sx = 0, sy = 0; - while (n--) { - } + var N = tlist.numberOfItems; + var n; - // TODO: then loop and find all adjacent translates of the form: - // translate(tx1,ty1) translate(tx2,ty2) => translate(tx1+tx2,ty1+ty2) - n = tlist.numberOfItems; - while (n--) { + // if it's a group, we have special reduction loops + if (selected.tagName == "g") { + // always remove translates by transferring them down to the children + // otherwise just reduce adjacent scales + + // The first pass is to remove all translates unless it is immediately + // after or before a scale + n = N; + while (n--) { + var xform = tlist.getItem(n); + if (xform.type != 2 || (n < (tlist.numberOfItems-1) && tlist.getItem(n+1).type == 3) || + (n > 0 && tlist.getItem(n-1).type == 3)) + { + continue; + } + + var tobj = transformToObj(xform); + tx += tobj.tx; + ty += tobj.ty; + // remove the translate from the group + tlist.removeItem(n); + } + // now restore just the one translate + if (tx != 0 || ty != 0) { + var newxlate = svgroot.createSVGTransform(0); + newxlate.setTranslate(tx,ty); + tlist.insertItemBefore(newxlate, 0); + } + + // TODO: The second pass is to find all adjacent transform sets of the form: + // translate(tx,ty) scale(sx,sy) translate(-tx,-ty) and reduce them + // to one set (multiply sx and sy) + n = N; + while (n--) { + } - } + return batchCmd; + } + + // else, it's a non-group - // this loop then computes the remapping required of the element - // (this prevents abnormal scaling of strokes) + // This pass loop in reverse order and removes any translates or scales. + // Once we hit our first rotate(), we will only remove translates. var bRemoveTransform = true; - n = tlist.numberOfItems; + n = N; while (n--) { // once we reach an unmoveable transform, we can stop - if (!bRemoveTransform) break; var xform = tlist.getItem(n); var m = xform.matrix; // if translate... var remap = null, scalew = null, scaleh = null; switch (xform.type) { - case 2: // TRANSLATE + case 2: // TRANSLATE - always remove remap = function(x,y) { return transformPoint(x,y,m); }; scalew = function(w) { return w; } scaleh = function(h) { return h; } break; - case 3: // SCALE + case 3: // SCALE - only remove if we haven't hit a rotate + if (!bRemoveTransform) continue; remap = function(x,y) { return transformPoint(x,y,m); }; scalew = function(w) { return m.a * w; } scaleh = function(h) { return m.d * h; } break; - case 4: // ROTATE + case 4: // ROTATE - only re-center if we haven't previously hit a rotate + if (!bRemoveTransform) continue; // if the new center of the shape has moved, then // re-center the rotation, and determine the movement // offset required to keep the shape in the same place @@ -2601,16 +2648,9 @@ function BatchCommand(text) { 'height': Math.abs(y-start_y) },100); - // this code will probably be faster than using getIntersectionList(), but - // not as accurate (only grabs an element if the mouse happens to pass over - // its bbox and elements would never be released from selection) -// var nodeName = evt.target.nodeName.toLowerCase(); -// if (nodeName != "div" && nodeName != "svg") { -// canvas.addToSelection([evt.target]); -// } - // clear out selection and set it to the new list canvas.clearSelection(); + // TODO: fix this, need to suppliy rect to getIntersectionList() canvas.addToSelection(getIntersectionList()); /* From c3d652092817d198e0bae8d1a4c5c967aa22357b Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Mon, 9 Nov 2009 16:39:29 +0000 Subject: [PATCH 17/35] transformlist branch: Fix a bug, N is not a constant. Also remove some dead code and add some more notes. git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@915 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 9c82da68..2d2f8261 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1214,7 +1214,7 @@ function BatchCommand(text) { From a transformation point of view: - translations are always in the editor's frame of reference (NOT the element's) - rotations rotate the element's frame of reference - - FUTURE: skewing skews the element's frame of reference + - FUTURE: skewing skews the element's rotated frame of reference - resizing modifies the dimensions of the element in its rotated+skewed frame of reference. @@ -1378,9 +1378,6 @@ function BatchCommand(text) { var currentMatrix = {a:1, b:0, c:0, d:1, e:0, f:0}; var tx = 0, ty = 0, sx = 1, sy = 1, r = 0.0; - var N = tlist.numberOfItems; - var n; - // if it's a group, we have special reduction loops if (selected.tagName == "g") { // always remove translates by transferring them down to the children @@ -1388,7 +1385,7 @@ function BatchCommand(text) { // The first pass is to remove all translates unless it is immediately // after or before a scale - n = N; + var n = tlist.numberOfItems; while (n--) { var xform = tlist.getItem(n); if (xform.type != 2 || (n < (tlist.numberOfItems-1) && tlist.getItem(n+1).type == 3) || @@ -1413,7 +1410,7 @@ function BatchCommand(text) { // TODO: The second pass is to find all adjacent transform sets of the form: // translate(tx,ty) scale(sx,sy) translate(-tx,-ty) and reduce them // to one set (multiply sx and sy) - n = N; + n = tlist.numberOfItems; while (n--) { } @@ -1425,7 +1422,7 @@ function BatchCommand(text) { // This pass loop in reverse order and removes any translates or scales. // Once we hit our first rotate(), we will only remove translates. var bRemoveTransform = true; - n = N; + n = tlist.numberOfItems; while (n--) { // once we reach an unmoveable transform, we can stop var xform = tlist.getItem(n); @@ -1490,27 +1487,6 @@ function BatchCommand(text) { switch (selected.tagName) { - /* - case "g": - var children = selected.childNodes; - var c = children.length; - while (c--) { - var child = children.item(c); - if (child.nodeType == 1) { - try { - // TODO: how to transfer the transform list of the group to each child - var childBox = child.getBBox(); - var pt = remap(childBox.x,childBox.y), - w = scalew(childBox.width), - h = scaleh(childBox.height); - childBox.x = pt.x; childBox.y = pt.y; - childBox.width = w; childBox.height = h; - batchCmd.addSubCommand(recalculateDimensions(child));//, childBox)); - } catch(e) {} - } - } - break; - */ case "line": var pt1 = remap(changes["x1"],changes["y1"]), pt2 = remap(changes["x2"],changes["y2"]); @@ -5021,6 +4997,12 @@ function BatchCommand(text) { return 0; }; + // TODO: if transforms are going to stay on then we need to properly find the right + // place to insert a rotate. This would be searching from the end of the tlist and + // going back until we either: + // - find an existing rotate OR + // - find a translate that is not part of a scale (in the reduced case, this will mean + // two translates next to each other) this.setRotationAngle = function(val,preventUndo) { var elem = selectedElements[0]; // we use the actual element's bbox (not the calculated one) since the From 2a46a5eca0d3b2d6d1174bbd579eea434ca9fd37 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Mon, 9 Nov 2009 18:59:53 +0000 Subject: [PATCH 18/35] Adding SVG icons to svg-editor.html and related scripts, keeping old with -classic suffix git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@916 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svg-editor-classic.css | 679 +++++++++++ editor/svg-editor-classic.html | 438 ++++++++ editor/svg-editor-classic.js | 1689 ++++++++++++++++++++++++++++ editor/svg-editor-svgicons.html | 35 +- editor/svg-editor.css | 111 +- editor/svg-editor.html | 128 ++- editor/svg-editor.js | 358 +++++- editor/svgicons/jquery.svgicons.js | 421 +++++++ 8 files changed, 3715 insertions(+), 144 deletions(-) create mode 100644 editor/svg-editor-classic.css create mode 100644 editor/svg-editor-classic.html create mode 100644 editor/svg-editor-classic.js create mode 100644 editor/svgicons/jquery.svgicons.js diff --git a/editor/svg-editor-classic.css b/editor/svg-editor-classic.css new file mode 100644 index 00000000..99925f5b --- /dev/null +++ b/editor/svg-editor-classic.css @@ -0,0 +1,679 @@ +body { + background: #E8E8E8; +} + +#svg_editor { + font-size: 8pt; + font-family: Verdana, Helvetica, Arial; + color: #000000; +} + +#svg_editor a { + color: #0000FF; +} + +#svg_editor hr { + border: none; + border-bottom: 1px solid #808080; +} + +#svg_editor select { + margin-top: 4px; +} + +#svg_editor #svgroot { + -moz-user-select: none; + position: absolute; + top: 0; + left: 0; +} + +#svg_editor #svgcanvas { + background-color: #FFFFFF; + text-align: center; + vertical-align: middle; + width: 640px; + height: 480px; + -apple-dashboard-region:dashboard-region(control rectangle 0px 0px 0px 0px); /* for widget regions that shouldn't react to dragging */ + position: relative; +} + +#svg_editor div#palette_holder { + overflow-x: scroll; + overflow-y: hidden; + height: 31px; + border: 1px solid #808080; + border-top: none; + margin-top: 2px; +} + +#svg_editor #fill_color, #svg_editor #stroke_color { + height: 16px; + width: 16px; + border: 1px solid #808080; + cursor: pointer; +} + +#svg_editor div#palette { + float: left; + width: 6848px; + height: 16px; +} + +#svg_editor div#workarea { + display: inline-block; + position:absolute; + top: 75px; + left: 40px; + bottom: 60px; + right: 14px; + background-color: #A0A0A0; + border: 1px solid #808080; + overflow: auto; +} + +#svg_editor #sidepanels { + display: inline-block; + position:absolute; + top: 75px; + bottom: 60px; + right: 0px; + width: 2px; + padding: 10px; + border-color: #808080; + border-style: solid; + border-width: 1px; + border-left: none; +} + +#svg_editor #layerpanel { + display: inline-block; + background-color: #E8E8E8; + position:absolute; + top: 1px; + bottom: 0px; + right: 0px; + width: 0px; + overflow: hidden; + margin: 0px; +} + +/* + border-style: solid; + border-color: #666; + border-width: 0px 0px 0px 1px; +*/ +#svg_editor #sidepanel_handle { + display: inline-block; + position: absolute; + background-color: #E8E8E8; + left: 0px; + top: 40%; + width: 1em; + padding: 5px 1px 5px 5px; + margin-left: 3px; + cursor: pointer; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -moz-user-select: none; +} + +#svg_editor #sidepanel_handle:hover { + font-weight: bold; +} + +#svg_editor #sidepanel_handle * { + cursor: pointer; + -moz-user-select: none; +} +#svg_editor #layerbuttons { + margin: 0px; + padding: 0px; + padding-left: 2px; + padding-right: 2px; + width: 106px; + height: 20px; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + border-left: 1px solid #808080; + border-top: 1px solid #808080; +} + +#svg_editor .layer_button { + width: 14px; + height: 14px; + padding: 1px; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; + cursor: pointer; +} + +#svg_editor .layer_buttonpressed { + width: 14px; + height: 14px; + padding: 1px; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + border-left: 1px solid #808080; + border-top: 1px solid #808080; + cursor: pointer; +} + +#svg_editor #layerlist { + margin: 1px; + padding: 0px; + width: 110px; + border-collapse: collapse; + border: 1px solid #808080; + background-color: #FFFFFF; +} + +#svg_editor #layerlist tr.layer { + background-color: #FFFFFF; + margin: 0px; + padding: 0px; +} +#svg_editor #layerlist tr.layersel { + border: 1px solid #808080; + background-color: #CCCCCC; +} + +#svg_editor #layerlist td.layervis { + background-image: url('images/eye.png'); + background-repeat: no-repeat; + background-position: 3px center; + width: 22px; + cursor:pointer; +} +#svg_editor #layerlist td.layerinvis { + background-image: none; + cursor:pointer; +} + +#svg_editor #layerlist td.layername { + cursor: pointer; +} + +#svg_editor #layerlist tr.layersel td.layername { + font-weight: bold; +} + +#svg_editor #selLayerLabel { + white-space: nowrap; +} + +#svg_editor #selLayerNames { + display: block; +} + +#svg_editor div.palette_item { + height: 16px; + width: 16px; + float: left; +} + +#svg_editor #logo { + position: absolute; + top: 4px; + left: 4px; + padding: 0px; +} + +#svg_editor #logo a img { + border: 0; + width: 32px; + height: 32px; +} + +#svg_editor #tools_top { + position: absolute; + left: 38px; + right: 2px; + top: 2px; + height: 75px; + border-bottom: none; +} + +#svg_editor #tools_top > div { + float: left; +} + +#svg_editor #tools_left { + position: absolute; + border-right: none; + width: 36px; + top: 75px; + left: 2px; +} + +#workarea.wireframe #svgcontent * { + fill: none; + stroke: #000; + stroke-width: 1px; + stroke-opacity: 1.0; + stroke-dasharray: none; + opacity: 1; + pointer-events: stroke; +} + +#workarea.wireframe #svgcontent text { + fill: #000; + stroke: none; +} + + +#svg_editor #selected_panel, +#svg_editor #multiselected_panel, +#svg_editor #g_panel, +#svg_editor #rect_panel, +#svg_editor #circle_panel, +#svg_editor #ellipse_panel, +#svg_editor #line_panel, +#svg_editor #image_panel, +#svg_editor #text_panel, +#svg_editor #path_node_panel { + display: none; +} + +#svg_editor #selected_panel .selected_tool { + vertical-align: 12px; +} + +#svg_editor #multiselected_panel .selected_tool { + vertical-align: 12px; +} + +#svg_editor #tools_top > div, #tools_top { + float: left; + line-height: 26px; +} + +#tools_top > div > * { + float: left; + margin-right: 2px; +} + +#tools_top label { + margin-top: 3px; + margin-left: 5px; +} + +#tools_top input { + margin-top: 5px; + height: 15px; +} + +#svg_editor .flyout_arrow_horiz { + float: right; + position: relative; + top: -13px; + left: -5px; + margin-bottom: -13px; +} + +.magic_field > * { + float: left; +} + +span.zoom_tool { + line-height: 26px; + padding: 3px; +} + +.magic_field input { + margin-top: 5px; +} + +.dropdown { + position: relative; +} + +.dropdown button { + background: transparent 3px 8px url('images/dropdown.gif') no-repeat; + width: 15px; + height: 21px; + margin: 6px 0 0 1px; + padding: 0; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; +} + +.dropdown button.down { + border-left: 1px solid #808080; + border-top: 1px solid #808080; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + background-color: #B0B0B0; +} + +.dropdown ul { + list-style: none; + position: absolute; + margin: 0; + padding: 0; + left: -93px; + top: 26px; + display: none; +} + +.dropup ul { + top: auto; + bottom: 26px; +} + +.dropdown li { + display: block; + width: 120px; + padding: 4px; + background: #E8E8E8; + border: 1px solid #B0B0B0; + margin: 0 0 -1px 0; + line-height: 16px; +} + +.dropdown li:hover { + background-color: #B0B0B0; +} + +.dropdown li.special { + padding: 10px 4px; +} + +.dropdown li.special:hover { + background: #E8E8E8; +} + +#opacity_dropdown li { + width: 140px; +} + +#svg_editor .tool_button, +#svg_editor .push_button, +#svg_editor .tool_button_current, +#svg_editor .tool_button_disabled { + height: 24px; + width: 24px; + margin: 2px; + padding: 2px; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; + cursor: pointer; +} + +#svg_editor .tool_button_current, +#svg_editor .push_button_pressed { + border-left: 1px solid #808080; + border-top: 1px solid #808080; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + background-color: #B0B0B0; +} + +#svg_editor .tool_button_disabled { + opacity: 0.5; + cursor: default; +} + +#svg_editor .tool_sep { + width: 2px; + height: 24px; + margin: 2px; + margin-right: 0; + padding: 2px; +} + +#svg_editor #color_picker { + position: absolute; + display: none; + background: #E8E8E8; + height: 350px; +} + +#svg_editor .tools_flyout { + position: absolute; + display: none; + cursor: pointer; +} + +#svg_editor .tools_flyout_v { + position: absolute; + display: none; + cursor: pointer; + width: 30px; +} + +#svg_editor #tool_square { background: 2px 2px url('images/square.png') no-repeat; } +#svg_editor #tool_rect { background: 2px 2px url('images/rect.png') no-repeat; } +#svg_editor #tool_fhrect { background: 2px 2px url('images/freehand-square.png') no-repeat; } +#svg_editor #tool_circle { background: 2px 2px url('images/circle.png') no-repeat; } +#svg_editor #tool_ellipse { background: 2px 2px url('images/ellipse.png') no-repeat; } +#svg_editor #tool_fhellipse { background: 2px 2px url('images/freehand-circle.png') no-repeat; } +#svg_editor #tool_stacktop { background: 2px 2px url('images/move_top.png') no-repeat; } +#svg_editor #tool_stackbottom { background: 2px 2px url('images/move_bottom.png') no-repeat; } +#svg_editor #tool_aligntop { background: 2px 2px url('images/align-top.png') no-repeat; } +#svg_editor #tool_alignmiddle { background: 2px 2px url('images/align-middle.png') no-repeat; } +#svg_editor #tool_alignbottom { background: 2px 2px url('images/align-bottom.png') no-repeat; } +#svg_editor #tool_alignleft { background: 2px 2px url('images/align-left.png') no-repeat; } +#svg_editor #tool_aligncenter { background: 2px 2px url('images/align-center.png') no-repeat; } +#svg_editor #tool_alignright { background: 2px 2px url('images/align-right.png') no-repeat; } +#svg_editor .tool_sep { background: 2px 2px url('images/sep.png') no-repeat; } + +/* TODO: figure out what more-specific selector causes me to write this atrocity and not + simply .tool_flyout_button */ +#svg_editor #tools_rect .tool_flyout_button, #svg_editor #tools_ellipse .tool_flyout_button { + float: left; + background-color: #E8E8E8; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; + height: 28px; + width: 28px; +} + +#svg_editor .tool_button:hover, +#svg_editor .push_button:hover { + background-color: #FFF; +} + +#svg_editor #tools_rect .tool_flyout_button_current, #svg_editor #tools_ellipse .tool_flyout_button_current { + border-left: 1px solid #808080; + border-top: 1px solid #808080; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + background-color: #B0B0B0; +} + +#svg_editor #tools_bottom { + position: absolute; + left: 40px; + right: 2px; + bottom: 2px; + height: 60px; +} + +#svg_editor #tools_bottom_1 { + width: 115px; + float: left; +} + +#svg_editor #tools_bottom_2 { + width: 250px; + float: left; +} + +#svg_editor #tools_bottom_3 { +} + +#svg_editor #copyright { + text-align: right; +} + +#svg_source_editor { + display: none; +} + +#svg_source_editor #svg_source_overlay { + position: absolute; + top: 0px; + right: 0px; + left: 0px; + bottom: 0px; + background-color: black; + opacity: 0.6; +} + +#svg_source_editor #svg_source_container { + position: absolute; + top: 30px; + left: 30px; + right: 30px; + bottom: 30px; + background-color: #B0B0B0; + opacity: 1.0; + text-align: center; +} + +/* + top: 100px; + left: 80px; + right: 80px; + bottom: 100px; +*/ + +#svg_docprops #svg_docprops_container { + position: absolute; + top: 50px; + padding: 10px; + background-color: #B0B0B0; + opacity: 1.0; +} + +#svg_docprops_container fieldset { + padding: 5px; + margin: 5px; +} + +#svg_docprops_container label { + display: block; + margin-bottom: .2em; +} + +#canvas_title { + display: block; +} + +#svg_source_editor #svg_source_textarea { + position: relative; + width: 95%; + top: 5px; + height: 250px; + padding: 5px; + font-size: 12px; +} + +#svg_source_editor #tool_source_back { + text-align: left; + padding-left: 20px; +} + +#svg_docprops_container div.color_block { + float: left; + margin: 2px; + padding: 20px; +} + +#change_background div.cur_background { + border: 2px solid blue; + padding: 18px; +} + +#change_background input { + color: #888; +} + +#change_background input.cur_background { + border: 2px solid blue; + color: #000; +} + +#background_img { + position: absolute; + top: 0; + left: 0; + text-align: left; +} + +#svg_source_editor button, #svg_docprops button { + padding: 5px 5px 7px 28px; + margin: 5px 20px 0 0; +} + +#svg_docprops button { + margin-top: 0; + margin-bottom: 5px; +} + +#svg_docprops { + display: none; +} + +#svg_docprops #svg_docprops_overlay { + position: absolute; + top: 0px; + right: 0px; + left: 0px; + bottom: 0px; + background-color: black; + opacity: 0.6; +} + +#tool_source_save, #tool_docprops_save { + background: #E8E8E8 url(images/save.png) no-repeat 2px 0; +} + +#tool_source_cancel, #tool_docprops_cancel { + background: #E8E8E8 url(images/cancel.png) no-repeat 2px 0; +} + +#tool_source_save, #tool_docprops_save { + background: #E8E8E8 url(images/save.png) no-repeat 2px 0; +} + +#tool_source_cancel, #tool_docprops_cancel { + background: #E8E8E8 url(images/cancel.png) no-repeat 2px 0; +} + +/* Slider +----------------------------------*/ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; } + +.ui-slider { + border: 1px solid #B0B0B0; +} + +.ui-slider-handle { + background: #B0B0B0; + border: 1px solid #000; +} \ No newline at end of file diff --git a/editor/svg-editor-classic.html b/editor/svg-editor-classic.html new file mode 100644 index 00000000..0cc36a9d --- /dev/null +++ b/editor/svg-editor-classic.html @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +SVG-edit demo (Raster icons) + + + +
+ +
+ +
+ +
+ +
+
+

Layers

+
+ New Layer + Delete Layer + Rename Layer + Move Layer up + + Move Layer Down +
+ +
fill:fill:
100%
100%
stroke:stroke:
100 %
100 %
+ + + + +
Layer 1
+ + Move elements to: + +
+
L a y e r s
+
+ +
+ +
+ + +
+ Clear + + + Save + + Source + Wireframe +
+ + +
+
+ + + +
+ + +
+ | + Copy + Delete + | + + Top + Bottom + | + + + + + + + +
+ + + +
+ | + Clone + Delete + | + Group +
+
+
+ +
+
+
+ relative to: + + | + +
+ +
+ | + + Ungroup +
+ +
+ | + + + + + + + + + + + + +
+ +
+ | + + + + + + + + + + + +
+ +
+ | + + + + + + + +
+ +
+ | + + + + + + + + + +
+ +
+ + | + + + + + + + + + +
+ +
+ | + + + + + + Bold + Italic + + + + + +
+ +
+ | + + + + + + Clone + Delete +
+ +
+ +
+ Select
+ Pencil
+ Line
+ Square + + Circle
+ + Path + + Text + Image + Zoom +
+ +
+ + +
+ zoom: + + + + | +
+ +
+ + + + + + + + + + + + + + + +
fill:
100%
stroke:
100 %
+ + + +
+
+ +
+
+
+ +
+ + +
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+
+ + + +
+
+ +
+
+
+ +
+ +
+
+
+ + + + + + +
+ Editor Background +
+ +
+ +
+ + Canvas Dimensions + + + + + + + +
+
+
+
+ + + diff --git a/editor/svg-editor-classic.js b/editor/svg-editor-classic.js new file mode 100644 index 00000000..9b9e5723 --- /dev/null +++ b/editor/svg-editor-classic.js @@ -0,0 +1,1689 @@ +/* +if(!window.console) { + window.console = new function() { + this.log = function(str) {}; + this.dir = function(str) {}; + }; +} +*/ + +function svg_edit_setup() { + var palette = ["#000000","#202020","#404040","#606060","#808080","#a0a0a0","#c0c0c0","#e0e0e0","#ffffff","#800000","#ff0000","#808000","#ffff00","#008000","#00ff00","#008080","#00ffff","#000080","#0000ff","#800080","#ff00ff","#2b0000","#550000","#800000","#aa0000","#d40000","#ff0000","#ff2a2a","#ff5555","#ff8080","#ffaaaa","#ffd5d5","#280b0b","#501616","#782121","#a02c2c","#c83737","#d35f5f","#de8787","#e9afaf","#f4d7d7","#241c1c","#483737","#6c5353","#916f6f","#ac9393","#c8b7b7","#e3dbdb","#2b1100","#552200","#803300","#aa4400","#d45500","#ff6600","#ff7f2a","#ff9955","#ffb380","#ffccaa","#ffe6d5","#28170b","#502d16","#784421","#a05a2c","#c87137","#d38d5f","#deaa87","#e9c6af","#f4e3d7","#241f1c","#483e37","#6c5d53","#917c6f","#ac9d93","#c8beb7","#e3dedb","#2b2200","#554400","#806600","#aa8800","#d4aa00","#ffcc00","#ffd42a","#ffdd55","#ffe680","#ffeeaa","#fff6d5","#28220b","#504416","#786721","#a0892c","#c8ab37","#d3bc5f","#decd87","#e9ddaf","#f4eed7","#24221c","#484537","#6c6753","#918a6f","#aca793","#c8c4b7","#e3e2db","#222b00","#445500","#668000","#88aa00","#aad400","#ccff00","#d4ff2a","#ddff55","#e5ff80","#eeffaa","#f6ffd5","#22280b","#445016","#677821","#89a02c","#abc837","#bcd35f","#cdde87","#dde9af","#eef4d7","#22241c","#454837","#676c53","#8a916f","#a7ac93","#c4c8b7","#e2e3db","#112b00","#225500","#338000","#44aa00","#55d400","#66ff00","#7fff2a","#99ff55","#b3ff80","#ccffaa","#e5ffd5","#17280b","#2d5016","#447821","#5aa02c","#71c837","#8dd35f","#aade87","#c6e9af","#e3f4d7","#1f241c","#3e4837","#5d6c53","#7c916f","#9dac93","#bec8b7","#dee3db","#002b00","#005500","#008000","#00aa00","#00d400","#00ff00","#2aff2a","#55ff55","#80ff80","#aaffaa","#d5ffd5","#0b280b","#165016","#217821","#2ca02c","#37c837","#5fd35f","#87de87","#afe9af","#d7f4d7","#1c241c","#374837","#536c53","#6f916f","#93ac93","#b7c8b7","#dbe3db","#002b11","#005522","#008033","#00aa44","#00d455","#00ff66","#2aff80","#55ff99","#80ffb3","#aaffcc","#d5ffe6","#0b2817","#16502d","#217844","#2ca05a","#37c871","#5fd38d","#87deaa","#afe9c6","#d7f4e3","#1c241f","#37483e","#536c5d","#6f917c","#93ac9d","#b7c8be","#dbe3de","#002b22","#005544","#008066","#00aa88","#00d4aa","#00ffcc","#2affd5","#55ffdd","#80ffe6","#aaffee","#d5fff6","#0b2822","#165044","#217867","#2ca089","#37c8ab","#5fd3bc","#87decd","#afe9dd","#d7f4ee","#1c2422","#374845","#536c67","#6f918a","#93aca7","#b7c8c4","#dbe3e2","#00222b","#004455","#006680","#0088aa","#00aad4","#00ccff","#2ad4ff","#55ddff","#80e5ff","#aaeeff","#d5f6ff","#0b2228","#164450","#216778","#2c89a0","#37abc8","#5fbcd3","#87cdde","#afdde9","#d7eef4","#1c2224","#374548","#53676c","#6f8a91","#93a7ac","#b7c4c8","#dbe2e3","#00112b","#002255","#003380","#0044aa","#0055d4","#0066ff","#2a7fff","#5599ff","#80b3ff","#aaccff","#d5e5ff","#0b1728","#162d50","#214478","#2c5aa0","#3771c8","#5f8dd3","#87aade","#afc6e9","#d7e3f4","#1c1f24","#373e48","#535d6c","#6f7c91","#939dac","#b7bec8","#dbdee3","#00002b","#000055","#000080","#0000aa","#0000d4","#0000ff","#2a2aff","#5555ff","#8080ff","#aaaaff","#d5d5ff","#0b0b28","#161650","#212178","#2c2ca0","#3737c8","#5f5fd3","#8787de","#afafe9","#d7d7f4","#1c1c24","#373748","#53536c","#6f6f91","#9393ac","#b7b7c8","#dbdbe3","#11002b","#220055","#330080","#4400aa","#5500d4","#6600ff","#7f2aff","#9955ff","#b380ff","#ccaaff","#e5d5ff","#170b28","#2d1650","#442178","#5a2ca0","#7137c8","#8d5fd3","#aa87de","#c6afe9","#e3d7f4","#1f1c24","#3e3748","#5d536c","#7c6f91","#9d93ac","#beb7c8","#dedbe3","#22002b","#440055","#660080","#8800aa","#aa00d4","#cc00ff","#d42aff","#dd55ff","#e580ff","#eeaaff","#f6d5ff","#220b28","#441650","#672178","#892ca0","#ab37c8","#bc5fd3","#cd87de","#ddafe9","#eed7f4","#221c24","#453748","#67536c","#8a6f91","#a793ac","#c4b7c8","#e2dbe3","#2b0022","#550044","#800066","#aa0088","#d400aa","#ff00cc","#ff2ad4","#ff55dd","#ff80e5","#ffaaee","#ffd5f6","#280b22","#501644","#782167","#a02c89","#c837ab","#d35fbc","#de87cd","#e9afdd","#f4d7ee","#241c22","#483745","#6c5367","#916f8a","#ac93a7","#c8b7c4","#e3dbe2","#2b0011","#550022","#800033","#aa0044","#d40055","#ff0066","#ff2a7f","#ff5599","#ff80b2","#ffaacc","#ffd5e5","#280b17","#50162d","#782144","#a02c5a","#c83771","#d35f8d","#de87aa","#e9afc6","#f4d7e3","#241c1f","#48373e","#6c535d","#916f7c","#ac939d","#c8b7be","#e3dbde"] + + var isMac = false; //(navigator.platform.indexOf("Mac") != -1); + var modKey = ""; //(isMac ? "meta+" : "ctrl+"); + var svgCanvas = new SvgCanvas(document.getElementById("svgcanvas")); + + var setSelectMode = function() { + $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); + $('#tool_select').addClass('tool_button_current'); + $('#styleoverrides').text('#svgcanvas svg *{cursor:move;pointer-events:all} #svgcanvas svg{cursor:default}'); + svgCanvas.setMode('select'); + }; + + // used to make the flyouts stay on the screen longer the very first time + var flyoutspeed = 1250; + var textBeingEntered = false; + var selectedElement = null; + var multiselected = false; + var editingsource = false; + var docprops = false; + var length_attrs = ['x','y','x1','x2','y1','y2','cx','cy','width','height','r','rx','ry','width','height','radius']; + var length_types = ['em','ex','px','cm','mm','in','pt','pc','%']; + + var fillPaint = new $.jGraduate.Paint({solidColor: "FF0000"}); // solid red + var strokePaint = new $.jGraduate.Paint({solidColor: "000000"}); // solid black + + // TODO: Unfortunately Mozilla does not handle internal references to gradients + // inside a data: URL document. This means that any elements filled/stroked + // with a gradient will appear black in Firefox, etc. See bug 308590 + // https://bugzilla.mozilla.org/show_bug.cgi?id=308590 + var saveHandler = function(window,svg) { + window.open("data:image/svg+xml;base64," + Utils.encode64(svg)); + }; + + // called when we've selected a different element + var selectedChanged = function(window,elems) { + // if elems[1] is present, then we have more than one element + selectedElement = (elems.length == 1 || elems[1] == null ? elems[0] : null); + multiselected = (elems.length >= 2 && elems[1] != null); + var is_node = false; + if (selectedElement != null) { + // unless we're already in always set the mode of the editor to select because + // upon creation of a text element the editor is switched into + // select mode and this event fires - we need our UI to be in sync + + is_node = !!(selectedElement.id && selectedElement.id.indexOf('pathpointgrip') == 0); + + if (svgCanvas.getMode() != "multiselect" && !is_node) { + setSelectMode(); + updateToolbar(); + } + + } // if (elem != null) + + + // Deal with pathedit mode + $('#path_node_panel').toggle(is_node); + $('#tools_bottom_2,#tools_bottom_3').toggle(!is_node); + if(is_node) { + // Change select icon + $('.tool_button').removeClass('tool_button_current'); + $('#tool_select').attr('src','images/select_node.png').addClass('tool_button_current'); + } else { + $('#tool_select').attr('src','images/select.png'); + + } + + updateContextPanel(); + }; + + // called when any element has changed + var elementChanged = function(window,elems) { + for (var i = 0; i < elems.length; ++i) { + var elem = elems[i]; + // if the element changed was the svg, then it could be a resolution change + if (elem && elem.tagName == "svg" && elem.getAttribute("viewBox")) { + var vb = elem.getAttribute("viewBox").split(' '); + changeResolution(parseInt(vb[2]), + parseInt(vb[3])); + } + } + + // we update the contextual panel with potentially new + // positional/sizing information (we DON'T want to update the + // toolbar here as that creates an infinite loop) + // also this updates the history buttons + + // we tell it to skip focusing the text control if the + // text element was previously in focus + updateContextPanel(); + }; + + var updateBgImage = function() { + var bg_img = $('#background_img'); + if(!bg_img.length) return; + var img = bg_img.find('img'); + var zoomlevel = svgCanvas.getZoom(); + img.width(zoomlevel*100 + '%'); + } + + var zoomChanged = function(window, bbox) { + var scrbar = 15; + var res = svgCanvas.getResolution(); + var w_area = $('#workarea'); + var canvas_pos = $('#svgcanvas').position(); + w_area.css('cursor','auto'); + var z_info = svgCanvas.setBBoxZoom(bbox, w_area.width()-scrbar, w_area.height()-scrbar); + if(!z_info) return; + var zoomlevel = z_info.zoom; + var bb = z_info.bbox; + $('#zoom').val(Math.round(zoomlevel*100)); + setResolution(res.w * zoomlevel, res.h * zoomlevel); + var scrLeft = bb.x * zoomlevel; + var scrOffX = w_area.width()/2 - (bb.width * zoomlevel)/2; + w_area[0].scrollLeft = Math.max(0,scrLeft - scrOffX) + Math.max(0,canvas_pos.left); + var scrTop = bb.y * zoomlevel; + var scrOffY = w_area.height()/2 - (bb.height * zoomlevel)/2; + w_area[0].scrollTop = Math.max(0,scrTop - scrOffY) + Math.max(0,canvas_pos.top); + if(svgCanvas.getMode() == 'zoom' && bb.width) { + // Go to select if a zoom box was drawn + setSelectMode(); + } + } + + // updates the toolbar (colors, opacity, etc) based on the selected element + var updateToolbar = function() { + if (selectedElement != null && + selectedElement.tagName != "image" && + selectedElement.tagName != "g") + { + // get opacity values + var fillOpacity = parseFloat(selectedElement.getAttribute("fill-opacity")); + if (isNaN(fillOpacity)) { + fillOpacity = 1.0; + } + + var strokeOpacity = parseFloat(selectedElement.getAttribute("stroke-opacity")); + if (isNaN(strokeOpacity)) { + strokeOpacity = 1.0; + } + + // update fill color and opacity + var fillColor = selectedElement.getAttribute("fill")||"none"; + // prevent undo on these canvas changes + svgCanvas.setFillColor(fillColor, true); + svgCanvas.setFillOpacity(fillOpacity, true); + + // update stroke color and opacity + var strokeColor = selectedElement.getAttribute("stroke")||"none"; + // prevent undo on these canvas changes + svgCanvas.setStrokeColor(strokeColor, true); + svgCanvas.setStrokeOpacity(strokeOpacity, true); + + fillOpacity *= 100; + strokeOpacity *= 100; + + var getPaint = function(color, opac) { + // update the editor's fill paint + var opts = null; + + if (color.substr(0,5) == "url(#") { + opts = { + alpha: opac, + linearGradient: document.getElementById(color.substr(5,color.length-6)) + }; + } + else if (color.substr(0,1) == "#") { + opts = { + alpha: opac, + solidColor: color.substr(1) + }; + } + return new $.jGraduate.Paint(opts); + } + + fillPaint = getPaint(fillColor, fillOpacity); + strokePaint = getPaint(strokeColor, strokeOpacity); + + fillOpacity = fillOpacity + " %"; + strokeOpacity = strokeOpacity + " %"; + + // update fill color + if (fillColor == "none") { + fillOpacity = "N/A"; + } + document.getElementById("gradbox_fill").parentNode.firstChild.setAttribute("fill", fillColor); + + if (strokeColor == null || strokeColor == "" || strokeColor == "none") { + strokeColor = "none"; + strokeOpacity = "N/A"; + } + + // update the rect inside #fill_color + document.getElementById("gradbox_stroke").parentNode.firstChild.setAttribute("fill", strokeColor); + + $('#fill_opacity').html(fillOpacity); + $('#stroke_opacity').html(strokeOpacity); + var opac_perc = ((selectedElement.getAttribute("opacity")||1.0)*100); + $('#group_opacity').val(opac_perc); + $('#opac_slider').slider('option', 'value', opac_perc); + $('#stroke_width').val(selectedElement.getAttribute("stroke-width")||1); + $('#stroke_style').val(selectedElement.getAttribute("stroke-dasharray")||"none"); + } + + updateToolButtonState(); + }; + + // updates the context panel tools based on the selected element + var updateContextPanel = function() { + var elem = selectedElement; + var currentLayer = svgCanvas.getCurrentLayer(); + + // No need to update anything else in rotate mode + if (svgCanvas.getMode() == 'rotate' && elem != null) { + $('#angle').val(svgCanvas.getRotationAngle(elem)); + return; + } + + var is_node = 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) { + $('#angle').val(svgCanvas.getRotationAngle(elem)); + + if(!is_node) { + $('#selected_panel').show(); + } else { + var point = svgCanvas.getNodePoint(); + if(point) { + var seg_type = $('#seg_type'); + $('#path_node_x').val(point.x); + $('#path_node_y').val(point.y); + if(point.type) { + seg_type.val(point.type).removeAttr('disabled'); + } else { + seg_type.val(4).attr('disabled','disabled'); + } + } + return; + } + + // update contextual tools here + var panels = { + g: [], + rect: ['rx','x','y','width','height'], + image: ['x','y','width','height'], + circle: ['cx','cy','r'], + ellipse: ['cx','cy','rx','ry'], + line: ['x1','y1','x2','y2'], + text: ['x','y'] + }; + + var el_name = elem.tagName; + + if(panels[el_name]) { + var cur_panel = panels[el_name]; + + + $('#' + el_name + '_panel').show(); + + $.each(cur_panel, function(i, item) { + $('#' + el_name + '_' + item).val(elem.getAttribute(item) || 0); + }); + + if(el_name == 'text') { + $('#text_panel').css("display", "inline"); + if (svgCanvas.getItalic()) { + $('#tool_italic').addClass('tool_button_current'); + } + else { + $('#tool_italic').removeClass('tool_button_current'); + } + if (svgCanvas.getBold()) { + $('#tool_bold').addClass('tool_button_current'); + } + else { + $('#tool_bold').removeClass('tool_button_current'); + } + $('#font_family').val(elem.getAttribute("font-family")); + $('#font_size').val(elem.getAttribute("font-size")); + $('#text').val(elem.textContent); + if (svgCanvas.addedNew) { + $('#text').focus().select(); + } + } // text + else if(el_name == 'image') { + var xlinkNS="http://www.w3.org/1999/xlink"; + $('#image_url').val(elem.getAttributeNS(xlinkNS, "href")); + } // image + } + } // if (elem != null) + else if (multiselected) { + $('#multiselected_panel').show(); + } + + // update history buttons + if (svgCanvas.getUndoStackSize() > 0) { + $('#tool_undo').removeClass( 'tool_button_disabled'); + } + else { + $('#tool_undo').addClass( 'tool_button_disabled'); + } + if (svgCanvas.getRedoStackSize() > 0) { + $('#tool_redo').removeClass( 'tool_button_disabled'); + } + else { + $('#tool_redo').addClass( 'tool_button_disabled'); + } + + svgCanvas.addedNew = false; + + if ( (elem && !is_node) || multiselected) { + // update the selected elements' layer + $('#selLayerNames').removeAttr('disabled').val(currentLayer); + } + else { + $('#selLayerNames').attr('disabled', 'disabled'); + } + + }; + + $('#text').focus( function(){ textBeingEntered = true; } ); + $('#text').blur( function(){ textBeingEntered = false; } ); + + // bind the selected event to our function that handles updates to the UI + svgCanvas.bind("selected", selectedChanged); + svgCanvas.bind("changed", elementChanged); + svgCanvas.bind("saved", saveHandler); + svgCanvas.bind("zoomed", zoomChanged); + + var str = '
' + $.each(palette, function(i,item){ + str += '
'; + }); + $('#palette').append(str); + + // Set up editor background functionality + var color_blocks = ['#FFF','#888','#000','url(data:image/gif;base64,R0lGODlhEAAQAIAAAP%2F%2F%2F9bW1iH5BAAAAAAALAAAAAAQABAAAAIfjG%2Bgq4jM3IFLJgpswNly%2FXkcBpIiVaInlLJr9FZWAQA7)']; + var str = ''; + $.each(color_blocks, function() { + str += '
'; + }); + $('#bg_blocks').append(str); + var blocks = $('#bg_blocks div'); + var cur_bg = 'cur_background'; + blocks.each(function() { + var blk = $(this); + blk.click(function() { + blocks.removeClass(cur_bg); + $(this).addClass(cur_bg); + $('#canvas_bg_url').removeClass(cur_bg); + }); + }); + $('#canvas_bg_url').focus(function() { + blocks.removeClass(cur_bg); + $(this).addClass(cur_bg); + }); + + var pos = $('#tools_rect_show').position(); + $('#tools_rect').css({'left': pos.left+4, 'top': pos.top+77}); + pos = $('#tools_ellipse_show').position(); + $('#tools_ellipse').css({'left': pos.left+4, 'top': pos.top+77}); + + var changeRectRadius = function(ctl) { + svgCanvas.setRectRadius(ctl.value); + } + + var changeFontSize = function(ctl) { + svgCanvas.setFontSize(ctl.value); + } + + var changeStrokeWidth = function(ctl) { + var val = ctl.value; + if(val == 0 && selectedElement && $.inArray(selectedElement.nodeName, ['line', 'polyline']) != -1) { + val = ctl.value = 1; + } + svgCanvas.setStrokeWidth(val); + } + + var changeRotationAngle = function(ctl) { + svgCanvas.setRotationAngle(ctl.value); + } + var changeZoom = function(ctl) { + var zoomlevel = ctl.value / 100; + var zoom = svgCanvas.getZoom(); + var w_area = $('#workarea'); + + zoomChanged(window, { + width: 0, + height: 0, + x: (w_area[0].scrollLeft + w_area.width()/2)/zoom, + y: (w_area[0].scrollTop + w_area.height()/2)/zoom, + zoom: zoomlevel + }); + } + + var changeOpacity = function(ctl, val) { + if(val == null) val = ctl.value; + $('#group_opacity').val(val); + if(!ctl || !ctl.handle) { + $('#opac_slider').slider('option', 'value', val); + } + svgCanvas.setOpacity(val/100); + } + + $('#stroke_style').change(function(){ + svgCanvas.setStrokeStyle(this.options[this.selectedIndex].value); + }); + + // Lose focus for select elements when changed (Allows keyboard shortcuts to work better) + $('select').change(function(){$(this).blur();}); + + // fired when user wants to move elements to another layer + var promptMoveLayerOnce = false; + $('#selLayerNames').change(function(){ + var destLayer = this.options[this.selectedIndex].value; + // TODO: localize this prompt + if (destLayer && (promptMoveLayerOnce || confirm('Move selected elements to layer \'' + destLayer + '\'?'))) { + promptMoveLayerOnce = true; + svgCanvas.moveSelectedToLayer(destLayer); + svgCanvas.clearSelection(); + populateLayers(); + } + }); + + $('#font_family').change(function(){ + svgCanvas.setFontFamily(this.options[this.selectedIndex].value); + }); + + $('#seg_type').change(function() { + svgCanvas.setSegType($(this).val()); + }); + + $('#text').keyup(function(){ + svgCanvas.setTextContent(this.value); + }); + + // TODO: consider only setting the URL once Enter has been pressed? + $('#image_url').keyup(function(){ + svgCanvas.setImageURL(this.value); + }); + + $('.attr_changer').change(function() { + var attr = this.getAttribute("data-attr"); + var val = this.value; + var valid = false; + if($.inArray(attr, length_attrs) != -1) { + if(!isNaN(val)) { + valid = true; + } else { + //TODO: Allow the values in length_types, then uncomment this: +// val = val.toLowerCase(); +// $.each(length_types, function(i, unit) { +// if(valid) return; +// var re = new RegExp('^-?[\\d\\.]+' + unit + '$'); +// if(re.test(val)) valid = true; +// }); + } + } else valid = true; + + if(!valid) { + // TODO: localize this + alert('Invalid value given for' + $(this).attr('title').replace('Change','') + + '.'); + this.value = selectedElement.getAttribute(attr); + return false; + } + svgCanvas.changeSelectedAttribute(attr, val); + }); + + // Prevent selection of elements when shift-clicking + $('#palette').mouseover(function() { + var inp = $(''); + $(this).append(inp); + inp.focus().remove(); + }); + + $('.palette_item').click(function(evt){ + var picker = (evt.shiftKey ? "stroke" : "fill"); + var id = (evt.shiftKey ? '#stroke_' : '#fill_'); + var color = $(this).attr('data-rgb'); + var rectbox = document.getElementById("gradbox_"+picker).parentNode.firstChild; + var paint = null; + + // Webkit-based browsers returned 'initial' here for no stroke + if (color == 'transparent' || color == 'initial') { + color = 'none'; + $(id + "opacity").html("N/A"); + paint = new $.jGraduate.Paint(); + } + else { + paint = new $.jGraduate.Paint({alpha: 100, solidColor: color.substr(1)}); + } + rectbox.setAttribute("fill", color); + + if (evt.shiftKey) { + strokePaint = paint; + if (svgCanvas.getStrokeColor() != color) { + svgCanvas.setStrokeColor(color); + } + if (color != 'none' && svgCanvas.getStrokeOpacity() != 1) { + svgCanvas.setStrokeOpacity(1.0); + $("#stroke_opacity").html("100 %"); + } + } else { + fillPaint = paint; + if (svgCanvas.getFillColor() != color) { + svgCanvas.setFillColor(color); + } + if (color != 'none' && svgCanvas.getFillOpacity() != 1) { + svgCanvas.setFillOpacity(1.0); + $("#fill_opacity").html("100 %"); + } + } + updateToolButtonState(); + }); + + // This is a common function used when a tool has been clicked (chosen) + // It does several common things: + // - removes the tool_button_current class from whatever tool currently has it + // - hides any flyouts + // - adds the tool_button_current class to the button passed in + var toolButtonClick = function(button, fadeFlyouts) { + if ($(button).hasClass('tool_button_disabled')) return false; + var fadeFlyouts = fadeFlyouts || 'normal'; + $('.tools_flyout').fadeOut(fadeFlyouts); + $('#styleoverrides').text(''); + $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); + $(button).addClass('tool_button_current'); + // when a tool is selected, we should deselect any currently selected elements + svgCanvas.clearSelection(); + return true; + }; + + var addDropDown = function(elem, callback, dropUp) { + var button = $(elem).find('button'); + var list = $(elem).find('ul'); + var on_button = false; + if(dropUp) { + $(elem).addClass('dropup'); + } + + $(elem).find('li').bind('mouseup', callback); + + $().mouseup(function(evt) { + if(!on_button) { + button.removeClass('down'); + list.hide(); + } + on_button = false; + }); + + button.bind('mousedown',function() { + if (!button.hasClass('down')) { + button.addClass('down'); + list.show(); + on_button = true; + } else { + button.removeClass('down'); + list.hide(); + } + }).hover(function() { + on_button = true; + }).mouseout(function() { + on_button = false; + }); + } + + addDropDown('#opacity_dropdown', function() { + if($(this).find('div').length) return; + var perc = parseInt($(this).text().split('%')[0]); + changeOpacity(false, perc); + }); + + // For slider usage, see: http://jqueryui.com/demos/slider/ + $("#opac_slider").slider({ + start: function() { + $('#opacity_dropdown li:not(.special)').hide(); + }, + stop: function() { + $('#opacity_dropdown li').show(); + }, + slide: function(evt, ui){ + changeOpacity(ui); + } + }); + + addDropDown('#zoom_dropdown', function() { + var item = $(this); + var val = item.attr('data-val'); + if(val) { + zoomChanged(window, val); + } else { + changeZoom({value:parseInt(item.text())}); + } + }, true); + + var clickSelect = function() { + if (toolButtonClick('#tool_select')) { + svgCanvas.setMode('select'); + $('#styleoverrides').text('#svgcanvas svg *{cursor:move;pointer-events:all}, #svgcanvas svg{cursor:default}'); + } + }; + + var clickFHPath = function() { + if (toolButtonClick('#tool_fhpath')) { + svgCanvas.setMode('fhpath'); + } + }; + + var clickLine = function() { + if (toolButtonClick('#tool_line')) { + svgCanvas.setMode('line'); + } + }; + + var clickSquare = function(){ + if (toolButtonClick('#tools_rect_show', flyoutspeed)) { + flyoutspeed = 'normal'; + svgCanvas.setMode('square'); + } + $('#tools_rect_show').attr('src', 'images/square.png'); + }; + + var clickRect = function(){ + if (toolButtonClick('#tools_rect_show')) { + svgCanvas.setMode('rect'); + } + $('#tools_rect_show').attr('src', 'images/rect.png'); + }; + + var clickImage = function(){ + if (toolButtonClick('#tool_image')) { + svgCanvas.setMode('image'); + } + }; + + var clickZoom = function(){ + if (toolButtonClick('#tool_zoom')) { + $('#workarea').css('cursor','crosshair'); + svgCanvas.setMode('zoom'); + } + }; + + var dblclickZoom = function(){ + if (toolButtonClick('#tool_zoom')) { + var res = svgCanvas.getResolution(); + setResolution(res.w, res.h); + $('#zoom').val(100); + svgCanvas.setZoom(1); + setSelectMode(); + } + }; + + var clickFHRect = function(){ + if (toolButtonClick('#tools_rect_show')) { + svgCanvas.setMode('fhrect'); + } + $('#tools_rect_show').attr('src', 'images/freehand-square.png'); + }; + + var clickCircle = function(){ + if (toolButtonClick('#tools_ellipse_show', flyoutspeed)) { + flyoutspeed = 'normal'; + svgCanvas.setMode('circle'); + } + $('#tools_ellipse_show').attr('src', 'images/circle.png'); + }; + + var clickEllipse = function(){ + if (toolButtonClick('#tools_ellipse_show')) { + svgCanvas.setMode('ellipse'); + } + $('#tools_ellipse_show').attr('src', 'images/ellipse.png'); + }; + + var clickFHEllipse = function(){ + if (toolButtonClick('#tools_ellipse_show')) { + svgCanvas.setMode('fhellipse'); + } + $('#tools_ellipse_show').attr('src', 'images/freehand-circle.png'); + }; + + var clickText = function(){ + toolButtonClick('#tool_text'); + svgCanvas.setMode('text'); + }; + + var clickPath = function(){ + toolButtonClick('#tool_path'); + svgCanvas.setMode('path'); + }; + + // Delete is a contextual tool that only appears in the ribbon if + // an element has been selected + var deleteSelected = function() { + if (selectedElement != null || multiselected) { + svgCanvas.deleteSelectedElements(); + } + }; + + var moveToTopSelected = function() { + if (selectedElement != null) { + svgCanvas.moveToTopSelectedElement(); + } + }; + + var moveToBottomSelected = function() { + if (selectedElement != null) { + svgCanvas.moveToBottomSelectedElement(); + } + }; + + var moveSelected = function(dx,dy) { + if (selectedElement != null || multiselected) { + svgCanvas.moveSelectedElements(dx,dy); + } + }; + + var clonePathNode = function() { + if (svgCanvas.getNodePoint()) { + svgCanvas.clonePathNode(); + } + }; + + var deletePathNode = function() { + if (svgCanvas.getNodePoint()) { + svgCanvas.deletePathNode(); + } + }; + + var selectNext = function() { + svgCanvas.cycleElement(1); + } + + var selectPrev = function() { + svgCanvas.cycleElement(0); + } + + var rotateSelected = function(cw) { + if (selectedElement == null || multiselected) return; + var step = 5; + if(!cw) step *= -1; + var new_angle = $('#angle').val()*1 + step; + svgCanvas.setRotationAngle(new_angle); + } + + var clickClear = function(){ + // TODO: localize this prompt + if( confirm('Do you want to clear the drawing?\nThis will also erase your undo history!') ) { + svgCanvas.clear(); + updateContextPanel(); + } + }; + + var clickBold = function(){ + svgCanvas.setBold( !svgCanvas.getBold() ); + updateContextPanel(); + }; + + var clickItalic = function(){ + svgCanvas.setItalic( !svgCanvas.getItalic() ); + updateContextPanel(); + }; + + var clickSave = function(){ + svgCanvas.save(); + }; + + var clickOpen = function(){ + svgCanvas.open(); + }; + + var clickUndo = function(){ + if (svgCanvas.getUndoStackSize() > 0) { + svgCanvas.undo(); + populateLayers(); + } + }; + + var clickRedo = function(){ + if (svgCanvas.getRedoStackSize() > 0) { + svgCanvas.redo(); + populateLayers(); + } + }; + + var clickGroup = function(){ + // group + if (multiselected) { + svgCanvas.groupSelectedElements(); + } + // ungroup + else { + svgCanvas.ungroupSelectedElement(); + } + }; + + var clickClone = function(){ + svgCanvas.cloneSelectedElements(); + }; + + var clickAlignLeft = function(){ + svgCanvas.alignSelectedElements('l', $('#align_relative_to option:selected').val() ); + }; + var clickAlignCenter = function(){ + svgCanvas.alignSelectedElements('c', $('#align_relative_to option:selected').val() ); + }; + var clickAlignRight = function(){ + svgCanvas.alignSelectedElements('r', $('#align_relative_to option:selected').val() ); + }; + var clickAlignTop = function(){ + svgCanvas.alignSelectedElements('t', $('#align_relative_to option:selected').val() ); + }; + var clickAlignMiddle = function(){ + svgCanvas.alignSelectedElements('m', $('#align_relative_to option:selected').val() ); + }; + var clickAlignBottom = function(){ + svgCanvas.alignSelectedElements('b', $('#align_relative_to option:selected').val() ); + }; + + var zoomImage = function(zoomIn) { + var res = svgCanvas.getResolution(); + var multiplier = zoomIn? res.zoom * 2 : res.zoom * 0.5; + setResolution(res.w * multiplier, res.h * multiplier, true); + $('#zoom').val(multiplier * 100); + svgCanvas.setZoom(multiplier); + }; + + var clickWireframe = function() { + $('#tool_wireframe').toggleClass('push_button_pressed'); + $('#workarea').toggleClass('wireframe'); + } + + var showSourceEditor = function(){ + if (editingsource) return; + editingsource = true; + var str = svgCanvas.getSvgString(); + $('#svg_source_textarea').val(str); + $('#svg_source_editor').fadeIn(); + properlySourceSizeTextArea(); + $('#svg_source_textarea').focus(); + }; + + var showDocProperties = function(){ + if (docprops) return; + docprops = true; + + // update resolution option with actual resolution + // TODO: what if SVG source is changed? + var res = svgCanvas.getResolution(); + $('#canvas_width').val(res.w); + $('#canvas_height').val(res.h); + $('#canvas_title').val(svgCanvas.getImageTitle()); + + // Update background color with current one + var blocks = $('#bg_blocks div'); + var cur_bg = 'cur_background'; + var canvas_bg = $('#svgcanvas').css('background'); + var url = canvas_bg.match(/url\("?(.*?)"?\)/); + if(url) url = url[1]; + blocks.each(function() { + var blk = $(this); + var is_bg = blk.css('background') == canvas_bg; + blk.toggleClass(cur_bg, is_bg); + if(is_bg) $('#canvas_bg_url').removeClass(cur_bg); + }); + if(!canvas_bg) blocks.eq(0).addClass(cur_bg); + if(!$('#bg_blocks .' + cur_bg).length && url) { + $('#canvas_bg_url').val(url); + } + + $('#svg_docprops').fadeIn(); + }; + + var properlySourceSizeTextArea = function(){ + // TODO: remove magic numbers here and get values from CSS + var height = $('#svg_source_container').height() - 80; + $('#svg_source_textarea').css('height', height); + }; + + var saveSourceEditor = function(){ + if (!editingsource) return; + + if (!svgCanvas.setSvgString($('#svg_source_textarea').val())) { + // TODO: localize this prompt + if( !confirm('There were parsing errors in your SVG source.\nRevert back to original SVG source?') ) { + return false; + } + } + svgCanvas.clearSelection(); + hideSourceEditor(); + populateLayers(); + }; + + var saveDocProperties = function(){ + // set title + svgCanvas.setImageTitle($('#canvas_title').val()); + + // update resolution + var x = parseInt($('#canvas_width').val()); + var y = parseInt($('#canvas_height').val()); + if(isNaN(x) || isNaN(y)) { + x ='fit'; + } + if(!svgCanvas.setResolution(x,y)) { + alert('No content to fit to'); + return false; + } + + // set background + var new_bg, bg_url = $('#canvas_bg_url').val(); + var bg_blk = $('#bg_blocks div.cur_background'); + if(bg_blk.length) { + new_bg = bg_blk.css('background'); + $('#svgcanvas').css('background',new_bg); + $('#background_img').remove(); + } else if(bg_url) { + if(!$('#background_img').length) { + $('
') + .prependTo('#svgcanvas'); + } else { + $('#background_img img').attr('src',bg_url); + } + } else { + new_bg = '#FFF'; + $('#svgcanvas').css('background',new_bg); + $('#background_img').remove(); + } + + hideDocProperties(); + }; + + var cancelOverlays = function() { + if (!editingsource && !docprops) return; + + if (editingsource) { + var oldString = svgCanvas.getSvgString(); + if (oldString != $('#svg_source_textarea').val()) { + // TODO: localize this prompt + if( !confirm('Ignore changes made to SVG source?') ) { + return false; + } + } + hideSourceEditor(); + } + else if (docprops) { + hideDocProperties(); + } + }; + + var hideSourceEditor = function(){ + $('#svg_source_editor').hide(); + editingsource = false; + $('#svg_source_textarea').blur(); + }; + + var hideDocProperties = function(){ + $('#svg_docprops').hide(); + $('#canvas_width,#canvas_height').removeAttr('disabled'); + $('#resolution')[0].selectedIndex = 0; + docprops = false; + }; + + // TODO: add canvas-centering code in here + $(window).resize(function(evt) { + if (!editingsource) return; + properlySourceSizeTextArea(); + }); + + $('#tool_select').click(clickSelect); + $('#tool_fhpath').click(clickFHPath); + $('#tool_line').click(clickLine); + $('#tool_square').mouseup(clickSquare); + $('#tool_rect').mouseup(clickRect); + $('#tool_fhrect').mouseup(clickFHRect); + $('#tool_circle').mouseup(clickCircle); + $('#tool_ellipse').mouseup(clickEllipse); + $('#tool_fhellipse').mouseup(clickFHEllipse); + $('#tool_image').mouseup(clickImage); + $('#tool_zoom').mouseup(clickZoom); + $('#tool_zoom').dblclick(dblclickZoom); + $('#tool_text').click(clickText); + $('#tool_path').click(clickPath); + $('#tool_clear').click(clickClear); + $('#tool_save').click(clickSave); + $('#tool_open').click(clickOpen); + $('#tool_source').click(showSourceEditor); + $('#tool_wireframe').click(clickWireframe); + $('#tool_source_cancel,#svg_source_overlay,#tool_docprops_cancel').click(cancelOverlays); + $('#tool_source_save').click(saveSourceEditor); + $('#tool_docprops_save').click(saveDocProperties); + $('#tool_docprops').click(showDocProperties); + $('#tool_delete').click(deleteSelected); + $('#tool_delete_multi').click(deleteSelected); + $('#tool_node_clone').click(clonePathNode); + $('#tool_node_delete').click(deletePathNode); + $('#tool_move_top').click(moveToTopSelected); + $('#tool_move_bottom').click(moveToBottomSelected); + $('#tool_undo').click(clickUndo); + $('#tool_redo').click(clickRedo); + $('#tool_clone').click(clickClone); + $('#tool_clone_multi').click(clickClone); + $('#tool_group').click(clickGroup); + $('#tool_ungroup').click(clickGroup); + $('#tool_alignleft').click(clickAlignLeft); + $('#tool_aligncenter').click(clickAlignCenter); + $('#tool_alignright').click(clickAlignRight); + $('#tool_aligntop').click(clickAlignTop); + $('#tool_alignmiddle').click(clickAlignMiddle); + $('#tool_alignbottom').click(clickAlignBottom); + // these two lines are required to make Opera work properly with the flyout mechanism + $('#tools_rect_show').click(clickSquare); + $('#tools_ellipse_show').click(clickCircle); + $('#tool_bold').mousedown(clickBold); + $('#tool_italic').mousedown(clickItalic); + + // added these event handlers for all the push buttons so they + // behave more like buttons being pressed-in and not images + function setPushButtons() { + var toolnames = ['clear','open','save','source','delete','delete_multi','paste','clone','clone_multi','move_top','move_bottom']; + var all_tools = ''; + var cur_class = 'tool_button_current'; + + $.each(toolnames, function(i,item) { + all_tools += '#tool_' + item + (i==toolnames.length-1?',':''); + }); + + $(all_tools).mousedown(function() { + $(this).addClass(cur_class); + }).bind('mousedown mouseout', function() { + $(this).removeClass(cur_class); + }); + + $('#tool_undo, #tool_redo').mousedown(function(){ + if (!$(this).hasClass('tool_button_disabled')) $(this).addClass(cur_class); + }).bind('mousedown mouseout',function(){ + $(this).removeClass(cur_class);} + ); + } + + setPushButtons(); + + $('#workarea').bind("mousewheel DOMMouseScroll", function(e){ + if(!e.shiftKey) return; + e.preventDefault(); + var off = $('#svgcanvas').offset(); + var zoom = svgCanvas.getZoom(); + var bbox = { + 'x': (e.pageX - off.left)/zoom, + 'y': (e.pageY - off.top)/zoom, + 'width': 0, + 'height': 0 + }; + + // Respond to mouse wheel in IE/Webkit/Opera. + // (It returns up/dn motion in multiples of 120) + if(e.wheelDelta) { + if (e.wheelDelta >= 120) { + bbox.factor = 2; + } else if (e.wheelDelta <= -120) { + bbox.factor = .5; + } + } else if(e.detail) { + if (e.detail > 0) { + bbox.factor = .5; + } else if (e.detail < 0) { + bbox.factor = 2; + } + } + + if(!bbox.factor) return; + zoomChanged(window, bbox); + }); + + // switch modifier key in tooltips if mac + // NOTE: This code is not used yet until I can figure out how to successfully bind ctrl/meta + // in Opera and Chrome + if (isMac) { + var shortcutButtons = ["tool_clear", "tool_save", "tool_source", "tool_undo", "tool_redo", "tool_clone"]; + var i = shortcutButtons.length; + while (i--) { + var button = document.getElementById(shortcutButtons[i]); + var title = button.title; + var index = title.indexOf("Ctrl+"); + button.title = [title.substr(0,index), "Cmd+", title.substr(index+5)].join(''); + } + } + + // do keybindings using jquery-hotkeys plugin + function setKeyBindings() { + var keys = [ + ['1', clickSelect], + ['2', clickFHPath], + ['3', clickLine], + ['Shift+4', clickSquare], + ['4', clickRect], + ['Shift+5', clickCircle], + ['5', clickEllipse], + ['6', clickPath], + ['7', clickText], + ['8', clickImage], + [modKey+'N', function(evt){clickClear();evt.preventDefault();}], + [modKey+'S', function(evt){editingsource?saveSourceEditor():clickSave();evt.preventDefault();}], + [modKey+'O', function(evt){clickOpen();evt.preventDefault();}], + ['del', function(evt){deleteSelected();evt.preventDefault();}], + ['backspace', function(evt){deleteSelected();evt.preventDefault();}], + ['shift+up', moveToTopSelected], + ['shift+down', moveToBottomSelected], + ['shift+left', function(){rotateSelected(0)}], + ['shift+right', function(){rotateSelected(1)}], + ['shift+O', selectPrev], + ['shift+P', selectNext], + ['ctrl+up', function(evt){zoomImage(true);evt.preventDefault();}], + ['ctrl+down', function(evt){zoomImage();evt.preventDefault();}], + ['up', function(evt){moveSelected(0,-1);evt.preventDefault();}], + ['down', function(evt){moveSelected(0,1);evt.preventDefault();}], + ['left', function(evt){moveSelected(-1,0);evt.preventDefault();}], + ['right', function(evt){moveSelected(1,0);evt.preventDefault();}], + [modKey+'z', function(evt){clickUndo();evt.preventDefault();}], + [modKey+'y', function(evt){clickRedo();evt.preventDefault();}], + [modKey+'u', function(evt){showSourceEditor();evt.preventDefault();}], + [modKey+'i', function(evt){showDocProperties();evt.preventDefault();}], + [modKey+'c', function(evt){clickClone();evt.preventDefault();}], + [modKey+'g', function(evt){clickGroup();evt.preventDefault();}], + [modKey+'f', function(evt){clickWireframe();evt.preventDefault();}], + [modKey+'x', function(evt){toggleSidePanel();}], + ['esc', cancelOverlays, false], + ]; + + $.each(keys,function(i,item) { + var disable = !(item.length > 2 && !item[2]); + $(document).bind('keydown', {combi:item[0], disableInInput: disable}, item[1]); + }); + + $('.attr_changer').bind('keydown', {combi:'return', disableInInput: false}, + function(evt) {$(this).change();evt.preventDefault();} + ); + } + + setKeyBindings(); + + // TODO: go back to the color boxes having white background-color and then setting + // background-image to none.png (otherwise partially transparent gradients look weird) + var colorPicker = function(elem) { + var picker = elem.attr('id') == 'stroke_color' ? 'stroke' : 'fill'; + var opacity = (picker == 'stroke' ? $('#stroke_opacity') : $('#fill_opacity')); + var paint = (picker == 'stroke' ? strokePaint : fillPaint); + var title = (picker == 'stroke' ? 'Pick a Stroke Paint and Opacity' : 'Pick a Fill Paint and Opacity'); + var was_none = false; + if (paint.type == "none") { + // if it was none, then set to solid white + paint = new $.jGraduate.Paint({solidColor: 'ffffff'}); + was_none = true; + } + var pos = elem.position(); + $("#color_picker") + .draggable({cancel:'.jPicker_table,.jGraduate_lgPick'}) + .css({'left': pos.left, 'bottom': 50 - pos.top}) + .jGraduate( + { + paint: paint, + window: { pickerTitle: title }, + images: { clientPath: "jgraduate/images/" }, + }, + function(p) { + paint = new $.jGraduate.Paint(p); + + var oldgrad = document.getElementById("gradbox_"+picker); + var svgbox = oldgrad.parentNode; + var rectbox = svgbox.firstChild; + + if (paint.type == "linearGradient") { + svgbox.removeChild(oldgrad); + var newgrad = svgbox.appendChild(document.importNode(paint.linearGradient, true)); + newgrad.id = "gradbox_"+picker; + rectbox.setAttribute("fill", "url(#gradbox_" + picker + ")"); + } + else { + rectbox.setAttribute("fill", "#" + paint.solidColor); + } + opacity.html(paint.alpha + " %"); + + if (picker == 'stroke') { + svgCanvas.setStrokePaint(paint, true); + } + else { + svgCanvas.setFillPaint(paint, true); + } + updateToolbar(); + $('#color_picker').hide(); + }, + function(p) { + $('#color_picker').hide(); + }); + }; + + var updateToolButtonState = function() { + var bNoFill = (svgCanvas.getFillColor() == 'none'); + var bNoStroke = (svgCanvas.getStrokeColor() == 'none'); + var buttonsNeedingStroke = [ '#tool_path', '#tool_line' ]; + var buttonsNeedingFillAndStroke = [ '#tools_rect_show', '#tools_ellipse_show', '#tool_text' ]; + if (bNoStroke) { + for (index in buttonsNeedingStroke) { + var button = buttonsNeedingStroke[index]; + if ($(button).hasClass('tool_button_current')) { + clickSelect(); + } + $(button).removeClass('tool_button').addClass('tool_button_disabled'); + } + } + else { + for (index in buttonsNeedingStroke) { + var button = buttonsNeedingStroke[index]; + $(button).removeClass('tool_button_disabled').addClass('tool_button'); + } + } + + if (bNoStroke && bNoFill) { + for (index in buttonsNeedingFillAndStroke) { + var button = buttonsNeedingFillAndStroke[index]; + if ($(button).hasClass('tool_button_current')) { + clickSelect(); + } + $(button).removeClass('tool_button').addClass('tool_button_disabled'); + } + } + else { + for (index in buttonsNeedingFillAndStroke) { + var button = buttonsNeedingFillAndStroke[index]; + $(button).removeClass('tool_button_disabled').addClass('tool_button'); + } + } + }; + + // set up gradients to be used for the buttons + var svgdocbox = new DOMParser().parseFromString( + '\ + \ + \ + \ + ', 'text/xml'); + + var boxgrad = svgdocbox.getElementById('gradbox_'); + boxgrad.id = 'gradbox_fill'; + $('#fill_color').append( document.importNode(svgdocbox.documentElement,true) ); + + boxgrad.id = 'gradbox_stroke'; + $(svgdocbox.documentElement.firstChild).attr('fill', '#000000'); + $('#stroke_color').append( document.importNode(svgdocbox.documentElement,true) ); + + $('#fill_color').click(function(){ + colorPicker($(this)); + updateToolButtonState(); + }); + + $('#stroke_color').click(function(){ + colorPicker($(this)); + updateToolButtonState(); + }); + + $('#tools_rect_show').mousedown(function(evt){ + $('#tools_rect').show(); + // this prevents the 'image drag' behavior in Firefox + evt.preventDefault(); + }); + $('#tools_rect').mouseleave(function(){$('#tools_rect').fadeOut();}); + + $('#tool_move_top').mousedown(function(evt){ + $('#tools_stacking').show(); + evt.preventDefault(); + }); + + $('#tools_ellipse_show').mousedown(function(evt){ + $('#tools_ellipse').show(); + // this prevents the 'image drag' behavior in Firefox + evt.preventDefault(); + }); + $('#tools_ellipse').mouseleave(function() {$('#tools_ellipse').fadeOut();}); + + $('.tool_flyout_button').mouseover(function() { + $(this).addClass('tool_flyout_button_current'); + }).mouseout(function() { + $(this).removeClass('tool_flyout_button_current'); + }); + + $('.layer_button').mousedown(function() { + $(this).addClass('layer_buttonpressed'); + }).mouseout(function() { + $(this).removeClass('layer_buttonpressed'); + }).mouseup(function() { + $(this).removeClass('layer_buttonpressed'); + }); + + $('.push_button').mousedown(function() { + if (!$(this).hasClass('tool_button_disabled')) { + $(this).addClass('push_button_pressed'); + } + }).mouseout(function() { + $(this).removeClass('push_button_pressed'); + }).mouseup(function() { + $(this).removeClass('push_button_pressed'); + }); + + $('#layer_new').click(function() { + var curNames = new Array(svgCanvas.getNumLayers()); + for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); } + + var j = (curNames.length+1); + var uniqName = "Layer " + j; + while (jQuery.inArray(uniqName, curNames) != -1) { + j++; + uniqName = "Layer " + j; + } + // TODO: localize this + var newName = prompt("Please enter a unique layer name",uniqName); + if (!newName) return; + if (jQuery.inArray(newName, curNames) != -1) { + alert("There is already a layer named that!"); + return; + } + svgCanvas.createLayer(newName); + updateContextPanel(); + populateLayers(); + $('#layerlist tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:first').addClass("layersel"); + }); + + $('#layer_delete').click(function() { + if (svgCanvas.deleteCurrentLayer()) { + updateContextPanel(); + populateLayers(); + // This matches what SvgCanvas does + // TODO: make this behavior less brittle (svg-editor should get which + // layer is selected from the canvas and then select that one in the UI) + $('#layerlist tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:first').addClass("layersel"); + } + }); + + $('#layer_up').click(function() { + // find index position of selected option + var curIndex = $('#layerlist tr.layersel').prevAll().length; + if (curIndex > 0) { + var total = $('#layerlist tr.layer').length; + curIndex--; + svgCanvas.setCurrentLayerPosition(total-curIndex-1); + populateLayers(); + $('#layerlist tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:eq('+curIndex+')').addClass("layersel"); + } + }); + + $('#layer_down').click(function() { + // find index position of selected option + var curIndex = $('#layerlist tr.layersel').prevAll().length; + var total = $('#layerlist tr.layer').length; + if (curIndex < total-1) { + curIndex++; + svgCanvas.setCurrentLayerPosition(total-curIndex-1); + populateLayers(); + $('#layerlist tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:eq('+curIndex+')').addClass("layersel"); + } + }); + + $('#layer_rename').click(function() { + var curIndex = $('#layerlist tr.layersel').prevAll().length; + var oldName = $('#layerlist tr.layersel td.layername').text(); + // TODO: localize this + var newName = prompt("Please enter the new layer name",""); + if (!newName) return; + if (oldName == newName) { + alert("Layer already has that name"); + return; + } + + var curNames = new Array(svgCanvas.getNumLayers()); + for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); } + if (jQuery.inArray(newName, curNames) != -1) { + alert("There is already a layer named that!"); + return; + } + + svgCanvas.renameCurrentLayer(newName); + populateLayers(); + $('#layerlist tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:eq('+curIndex+')').addClass("layersel"); + }); + + var SIDEPANEL_MAXWIDTH = 300; + var SIDEPANEL_OPENWIDTH = 150; + var sidedrag = -1, sidedragging = false; + $('#sidepanel_handle') + .mousedown(function(evt) {sidedrag = evt.pageX;}) + .mouseup(function(evt) { + if (!sidedragging) toggleSidePanel(); + sidedrag = -1; + sidedragging = false; + }); + $('#svg_editor') + .mouseup(function(){sidedrag=-1;}) + .mouseout(function(evt){ + if (sidedrag == -1) return; + // if we've moused out of the browser window, then we can stop dragging + // and close the drawer + if (evt.pageX > this.clientWidth) { + sidedrag = -1; + toggleSidePanel(true); + } + }) + .mousemove(function(evt) { + if (sidedrag == -1) return; + sidedragging = true; + var deltax = sidedrag - evt.pageX; + + var sidepanels = $('#sidepanels'); + var sidewidth = parseInt(sidepanels.css('width')); + if (sidewidth+deltax > SIDEPANEL_MAXWIDTH) { + deltax = SIDEPANEL_MAXWIDTH - sidewidth; + sidewidth = SIDEPANEL_MAXWIDTH; + } + else if (sidewidth+deltax < 2) { + deltax = 2 - sidewidth; + sidewidth = 2; + } + + if (deltax == 0) return; + sidedrag -= deltax; + + var workarea = $('#workarea'); + var layerpanel = $('#layerpanel'); + workarea.css('right', parseInt(workarea.css('right'))+deltax); + sidepanels.css('width', parseInt(sidepanels.css('width'))+deltax); + layerpanel.css('width', parseInt(layerpanel.css('width'))+deltax); + centerCanvasIfNeeded(); + }); + + // if width is non-zero, then fully close it, otherwise fully open it + // the optional close argument forces the side panel closed + var toggleSidePanel = function(close){ + var w = parseInt($('#sidepanels').css('width')); + var deltax = (w > 2 || close ? 2 : SIDEPANEL_OPENWIDTH) - w; + var workarea = $('#workarea'); + var sidepanels = $('#sidepanels'); + var layerpanel = $('#layerpanel'); + workarea.css('right', parseInt(workarea.css('right'))+deltax); + sidepanels.css('width', parseInt(sidepanels.css('width'))+deltax); + layerpanel.css('width', parseInt(layerpanel.css('width'))+deltax); + centerCanvasIfNeeded(); + }; + + // this function highlights the layer passed in (by fading out the other layers) + // if no layer is passed in, this function restores the other layers + var toggleHighlightLayer = function(layerNameToHighlight) { + var curNames = new Array(svgCanvas.getNumLayers()); + for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); } + + if (layerNameToHighlight) { + for (var i = 0; i < curNames.length; ++i) { + if (curNames[i] != layerNameToHighlight) { + svgCanvas.setLayerOpacity(curNames[i], 0.5); + } + } + } + else { + for (var i = 0; i < curNames.length; ++i) { + svgCanvas.setLayerOpacity(curNames[i], 1.0); + } + } + }; + + var populateLayers = function(){ + var layerlist = $('#layerlist tbody'); + var selLayerNames = $('#selLayerNames'); + layerlist.empty(); + selLayerNames.empty(); + var currentlayer = svgCanvas.getCurrentLayer(); + var layer = svgCanvas.getNumLayers(); + // we get the layers in the reverse z-order (the layer rendered on top is listed first) + while (layer--) { + var name = svgCanvas.getLayer(layer); + // contenteditable=\"true\" + var appendstr = ""; + + if (svgCanvas.getLayerVisibility(name)) { + appendstr += "" + name + ""; + } + else { + appendstr += "" + name + ""; + } + layerlist.append(appendstr); + selLayerNames.append(""); + } + // handle selection of layer + $('#layerlist td.layername') + .click(function(evt){ + $('#layerlist tr.layer').removeClass("layersel"); + var row = $(this.parentNode); + row.addClass("layersel"); + svgCanvas.setCurrentLayer(this.textContent); + evt.preventDefault(); + }) + .mouseover(function(evt){ + $(this).css({"font-style": "italic", "color":"blue"}); + toggleHighlightLayer(this.textContent); + }) + .mouseout(function(evt){ + $(this).css({"font-style": "normal", "color":"black"}); + toggleHighlightLayer(); + }); + $('#layerlist td.layervis').click(function(evt){ + var row = $(this.parentNode).prevAll().length; + var name = $('#layerlist tr.layer:eq(' + row + ') td.layername').text(); + var vis = $(this).hasClass('layerinvis'); + svgCanvas.setLayerVisibility(name, vis); + if (vis) { + $(this).removeClass('layerinvis'); + } + else { + $(this).addClass('layerinvis'); + } + }); + + // if there were too few rows, let's add a few to make it not so lonely + var num = 5 - $('#layerlist tr.layer').size(); + while (num-- > 0) { + // FIXME: there must a better way to do this + layerlist.append("_"); + } + }; + populateLayers(); + + function changeResolution(x,y) { + var zoom = svgCanvas.getResolution().zoom; + setResolution(x * zoom, y * zoom); + } + + var centerCanvasIfNeeded = function() { + // this centers the canvas in the workarea if it's small enough + var wa = {w: parseInt($('#workarea').css('width')), + h: parseInt($('#workarea').css('height'))}; + var ca = {w: parseInt($('#svgcanvas').css('width')), + h: parseInt($('#svgcanvas').css('height'))}; + if (wa.w > ca.w) { + $('#svgcanvas').css({'left': (wa.w-ca.w)/2}); + } + if (wa.h > ca.h) { + $('#svgcanvas').css({'top': (wa.h-ca.h)/2}); + } + }; + + $(window).resize( centerCanvasIfNeeded ); + + function stepFontSize(elem, step) { + var orig_val = elem.value-0; + var sug_val = orig_val + step; + var increasing = sug_val >= orig_val; + if(step === 0) return orig_val; + + if(orig_val >= 24) { + if(increasing) { + return Math.round(orig_val * 1.1); + } else { + return Math.round(orig_val / 1.1); + } + } else if(orig_val <= 1) { + if(increasing) { + return orig_val * 2; + } else { + return orig_val / 2; + } + } else { + return sug_val; + } + } + + function stepZoom(elem, step) { + var orig_val = elem.value-0; + var sug_val = orig_val + step; + if(step === 0) return orig_val; + + if(orig_val >= 100) { + return sug_val; + } else { + if(sug_val >= orig_val) { + return orig_val * 2; + } else { + return orig_val / 2; + } + } + } + + function setResolution(w, h, center) { + w-=0; h-=0; + $('#svgcanvas').css( { 'width': w, 'height': h } ); + $('#canvas_width').val(w); + $('#canvas_height').val(h); + + centerCanvasIfNeeded(); + setTimeout(updateBgImage,10); + + if(center) { + var w_area = $('#workarea'); + var scroll_y = h/2 - w_area.height()/2; + var scroll_x = w/2 - w_area.width()/2; + w_area[0].scrollTop = scroll_y; + w_area[0].scrollLeft = scroll_x; + } + } + + + $('#resolution').change(function(){ + var wh = $('#canvas_width,#canvas_height'); + if(!this.selectedIndex) { + if($('#canvas_width').val() == 'fit') { + wh.removeAttr("disabled").val(100); + } + } else if(this.value == 'content') { + wh.val('fit').attr("disabled","disabled"); + } else { + var dims = this.value.split('x'); + $('#canvas_width').val(dims[0]); + $('#canvas_height').val(dims[1]); + wh.removeAttr("disabled"); + } + }); + + //Prevent browser from erroneously repopulating fields + $('input,select').attr("autocomplete","off"); + + $('#rect_rx').SpinButton({ min: 0, max: 1000, step: 1, callback: changeRectRadius }); + $('#stroke_width').SpinButton({ min: 0, max: 99, step: 1, callback: changeStrokeWidth }); + $('#angle').SpinButton({ min: -180, max: 180, step: 5, callback: changeRotationAngle }); + $('#font_size').SpinButton({ step: 1, min: 0.001, stepfunc: stepFontSize, callback: changeFontSize }); + $('#group_opacity').SpinButton({ step: 5, min: 0, max: 100, callback: changeOpacity }); + $('#zoom').SpinButton({ min: 0.001, max: 10000, step: 50, stepfunc: stepZoom, callback: changeZoom }); + + svgCanvas.setCustomHandlers = function(opts) { + if(opts.open) { + $('#tool_open').show(); + svgCanvas.bind("opened", opts.open); + } + if(opts.save) { + svgCanvas.bind("saved", opts.save); + } + } + + // set starting resolution (centers canvas) + setResolution(640,480); + +// var revnums = "svg-editor.js ($Rev: 902 $) "; +// revnums += svgCanvas.getVersion(); +// $('#copyright')[0].setAttribute("title", revnums); + + return svgCanvas; +}; + +// This happens when the page is loaded +$(function() { + put_locale(); + svgCanvas = svg_edit_setup(); + + try{ + window.addEventListener("message", function(e){ + try{ + e.source.postMessage(eval(e.data), e.origin); + }catch(err){ + e.source.postMessage("error:"+err.message, e.origin); + } + }, false) + }catch(err){} + +}); diff --git a/editor/svg-editor-svgicons.html b/editor/svg-editor-svgicons.html index c455393d..6cff2c67 100644 --- a/editor/svg-editor-svgicons.html +++ b/editor/svg-editor-svgicons.html @@ -11,16 +11,20 @@ + + + - -