diff --git a/src/css/app.css b/src/css/app.css index fbab23d..3a71ee6 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -63,7 +63,8 @@ #workarea.nw-resize * {cursor: nw-resize !important;} #workarea.sw-resize * {cursor: sw-resize !important;} -#workspace.dragging * { +#workspace.dragging *, +body.dragging * { cursor: url(../images/dragging.png), move; cursor: -webkit-grabbing; cursor: -moz-grabbing; diff --git a/src/css/draginput.css b/src/css/draginput.css index 6f28c43..29fe61b 100644 --- a/src/css/draginput.css +++ b/src/css/draginput.css @@ -223,7 +223,12 @@ margin: 0; z-index: 1; top: 0; - left: 0; + left: 0; + cursor: pointer; +} + +.draginput:hover .draginput_cursor { + border-color: var(--d6); } .draginput_cursor{ @@ -231,7 +236,6 @@ top: 50%; width: 100%; border-top: solid var(--d4) 1px; - margin-top: -2px; z-index: 0; } diff --git a/src/css/loading.css b/src/css/loading.css index 01de044..33227b6 100644 --- a/src/css/loading.css +++ b/src/css/loading.css @@ -39,6 +39,7 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); + opacity: 0.7; } diff --git a/src/css/text.css b/src/css/text.css index db6bd4d..8812164 100644 --- a/src/css/text.css +++ b/src/css/text.css @@ -57,6 +57,11 @@ right: var(--x2); } +.draginput:hover #preview_font:after { + border-right-color: var(--z3); + background: linear-gradient(to right, rgba(0,0,0,0), var(--z3)); +} + #preview_font:after { content: ''; position: absolute; diff --git a/src/index.html b/src/index.html index 0079175..b9e4143 100644 --- a/src/index.html +++ b/src/index.html @@ -372,7 +372,7 @@ Offset -
Remove path
+
Remove path
@@ -584,6 +584,7 @@
+
Place text on path
diff --git a/src/js/Canvas.js b/src/js/Canvas.js index 7001a66..4bf0474 100644 --- a/src/js/Canvas.js +++ b/src/js/Canvas.js @@ -68,13 +68,13 @@ MD.Canvas = function(){ if (w === 'fit' || h === 'fit') state.set("canvasSize", res); $("#canvas_width").val(w); $("#canvas_height").val(h); - state.set("canvasContent", svgCanvas.getSvgString()); } - function changeSize(){ + function changeSize(attr, val, completed){ const w = $("#canvas_width").val(); const h = $("#canvas_height").val(); state.set("canvasSize", [w,h]); + if (completed) editor.saveCanvas(); } function update(center, new_ctr) { diff --git a/src/js/Import.js b/src/js/Import.js index f5b8cb8..f1a0d62 100644 --- a/src/js/Import.js +++ b/src/js/Import.js @@ -79,6 +79,7 @@ MD.Import = function(){ function loadSvgString(str, callback) { var success = svgCanvas.setSvgString(str) !== false; + callback = callback || $.noop; if(success) { callback(true); @@ -96,6 +97,7 @@ MD.Import = function(){ var reader = new FileReader(); reader.onloadend = function(e) { loadSvgString(e.target.result); + editor.saveCanvas(); editor.canvas.update(true); }; reader.readAsText(f.files[0]); diff --git a/src/js/Keyboard.js b/src/js/Keyboard.js index 9f8c1f4..3437b33 100644 --- a/src/js/Keyboard.js +++ b/src/js/Keyboard.js @@ -1,61 +1,61 @@ MD.Keyboard = function(){ const keys = { - "v": { name: "Select tool", cb: ()=> state.set("canvasMode", "select") }, - "q": { name: "Freehand tool", cb: ()=> state.set("canvasMode", "fhpath") }, - "l": { name: "Line tool", cb: ()=> state.set("canvasMode", "fhplineath")}, - "r": { name: "Rectangle tool", cb: ()=> state.set("canvasMode", "rect")}, - "o": { name: "Ellipse tool", cb: ()=> state.set("canvasMode", "ellipse")}, - "s": { name: "Shape tool", cb: ()=> state.set("canvasMode", "shapelib")}, - "p": { name: "Path tool", cb: ()=> state.set("canvasMode", "path")}, - "t": { name: "Text tool", cb: ()=> state.set("canvasMode", "text")}, - "z": { name: "Zoom tool", cb: ()=> state.set("canvasMode", "zoom")}, - "e": { name: "Eyedropper tool", cb: ()=> state.set("canvasMode", "eyedropper")}, - "x": { name: "Focus fill/stroke", cb: ()=> editor.focusPaint()}, - "shift_x": { name: "Switch fill/stroke", cb: ()=> editor.switchPaint()}, + "v": { name: "Select tool", cb: ()=> state.set("canvasMode", "select") }, + "q": { name: "Freehand tool", cb: ()=> state.set("canvasMode", "fhpath") }, + "l": { name: "Line tool", cb: ()=> state.set("canvasMode", "fhplineath")}, + "r": { name: "Rectangle tool", cb: ()=> state.set("canvasMode", "rect")}, + "o": { name: "Ellipse tool", cb: ()=> state.set("canvasMode", "ellipse")}, + "s": { name: "Shape tool", cb: ()=> state.set("canvasMode", "shapelib")}, + "p": { name: "Path tool", cb: ()=> state.set("canvasMode", "path")}, + "t": { name: "Text tool", cb: ()=> state.set("canvasMode", "text")}, + "z": { name: "Zoom tool", cb: ()=> state.set("canvasMode", "zoom")}, + "e": { name: "Eyedropper tool", cb: ()=> state.set("canvasMode", "eyedropper")}, + "x": { name: "Focus fill/stroke", cb: ()=> editor.focusPaint()}, +"shift_x": { name: "Switch fill/stroke", cb: ()=> editor.switchPaint()}, "alt": { name: false, cb: ()=> $("#workarea").toggleClass("out", state.get("canvasMode") === "zoom" )}, "cmd_s": { name: "Save SVG File", cb: ()=> editor.save()}, "cmd_z": { name: "Undo", cb: ()=> editor.undo()}, "cmd_y": { name: "Redo", cb: ()=> editor.redo()}, - "cmd_shift_z": { name: "Redo", cb: ()=> editor.redo()}, - "cmd_c": { name: "Copy", cb: ()=> editor.copySelected()}, - "cmd_x": { name: "Cut", cb: ()=> editor.cutSelected()}, - "cmd_v": { name: "Paste", cb: ()=> editor.pasteSelected()}, - "cmd_d": { name: "Duplicate", cb: ()=> editor.duplicateSelected()}, - "cmd_u": { name: "View source", cb: ()=> editor.source()}, - "cmd_a": { name: "Select All", cb: ()=> svgCanvas.selectAllInCurrentLayer()}, - "cmd_b": { name: "Set bold text", cb: ()=> editor.text.setBold()}, - "cmd_i": { name: "Set italic text", cb: ()=> editor.text.setItalic()}, - "cmd_g": { name: "Group selected", cb: ()=> editor.groupSelected()}, +"cmd_shift_z": { name: "Redo", cb: ()=> editor.redo()}, + "cmd_c": { name: "Copy", cb: ()=> editor.copySelected()}, + "cmd_x": { name: "Cut", cb: ()=> editor.cutSelected()}, + "cmd_v": { name: "Paste", cb: ()=> editor.pasteSelected()}, + "cmd_d": { name: "Duplicate", cb: ()=> editor.duplicateSelected()}, + "cmd_u": { name: "View source", cb: ()=> editor.source()}, + "cmd_a": { name: "Select All", cb: ()=> svgCanvas.selectAllInCurrentLayer()}, + "cmd_b": { name: "Set bold text", cb: ()=> editor.text.setBold()}, + "cmd_i": { name: "Set italic text", cb: ()=> editor.text.setItalic()}, + "cmd_g": { name: "Group selected", cb: ()=> editor.groupSelected()}, "cmd_shift_g": { name: "Ungroup", cb: ()=> editor.ungroupSelected()}, - "cmd_o": { name: "Open SVG File", cb: ()=> editor.import.open()}, - "cmd_k": { name: "Place image", cb: ()=> editor.import.place()}, + "cmd_o": { name: "Open SVG File", cb: ()=> editor.import.open()}, + "cmd_k": { name: "Place image", cb: ()=> editor.import.place()}, "backspace": { name: "Delete", cb: ()=> editor.deleteSelected()}, "ctrl_arrowleft": { name: "Rotate -1deg", cb: ()=> editor.rotateSelected(0,1)}, "ctrl_arrowright": { name: "Rotate +1deg", cb: ()=> editor.rotateSelected(1,1)}, "ctrl_shift_arrowleft": { name: "Rotate -5deg", cb: ()=> editor.rotateSelected(0,5)}, "ctrl_shift_arrowright": { name: "Rotate +5deg ", cb: ()=> editor.rotateSelected(1,5)}, - "shift_o": { name: "Next item", cb: ()=> svgCanvas.cycleElement(0)}, - "shift_p": { name: "Prev item", cb: ()=> svgCanvas.cycleElement(1)}, - "shift_r": { name: "Show/hide rulers", cb: ()=> editor.rulers.toggleRulers()}, - "cmd_+": { name: "Zoom in", cb: ()=> editor.zoom.multiply(1.5)}, - "cmd_-": { name: "Zoom out", cb: ()=> editor.zoom.multiply(0.75)}, - "cmd_=": { name: "Actual size", cb: ()=> editor.zoom.reset()}, - "arrowleft": { name: "Nudge left", cb: ()=> editor.moveSelected(-1,0)}, - "arrowright": { name: "Nudge right", cb: ()=> editor.moveSelected(1,0)}, - "arrowup": { name: "Nudge up", cb: ()=> editor.moveSelected(0,-1)}, - "arrowdown": { name: "Nudge down", cb: ()=> editor.moveSelected(0,1)}, - "shift_arrowleft": {name: "Jump left", cb: () => editor.moveSelected(state.get("canvasSnapStep") * -1, 0)}, + "shift_o": { name: "Next item", cb: ()=> svgCanvas.cycleElement(0)}, + "shift_p": { name: "Prev item", cb: ()=> svgCanvas.cycleElement(1)}, + "shift_r": { name: "Show/hide rulers", cb: ()=> editor.rulers.toggleRulers()}, + "cmd_+": { name: "Zoom in", cb: ()=> editor.zoom.multiply(1.5)}, + "cmd_-": { name: "Zoom out", cb: ()=> editor.zoom.multiply(0.75)}, + "cmd_=": { name: "Actual size", cb: ()=> editor.zoom.reset()}, + "arrowleft": { name: "Nudge left", cb: ()=> editor.moveSelected(-1,0)}, + "arrowright": { name: "Nudge right", cb: ()=> editor.moveSelected(1,0)}, + "arrowup": { name: "Nudge up", cb: ()=> editor.moveSelected(0,-1)}, + "arrowdown": { name: "Nudge down", cb: ()=> editor.moveSelected(0,1)}, + "shift_arrowleft": {name: "Jump left", cb: () => editor.moveSelected(state.get("canvasSnapStep") * -1, 0)}, "shift_arrowright": {name: "Jump right", cb: () => editor.moveSelected(state.get("canvasSnapStep") * 1, 0)}, - "shift_arrowup": {name: "Jump up", cb: () => editor.moveSelected(0, state.get("canvasSnapStep") * -1)}, - "shift_arrowdown": {name: "Jump down", cb: () => editor.moveSelected(0, state.get("canvasSnapStep") * 1)}, - "cmd_arrowup":{ name: "Bring forward", cb: () => editor.moveUpSelected()}, + "shift_arrowup": {name: "Jump up", cb: () => editor.moveSelected(0, state.get("canvasSnapStep") * -1)}, + "shift_arrowdown": {name: "Jump down", cb: () => editor.moveSelected(0, state.get("canvasSnapStep") * 1)}, + "cmd_arrowup":{ name: "Bring forward", cb: () => editor.moveUpSelected()}, "cmd_arrowdown":{ name: "Send backward", cb: () => editor.moveDownSelected()}, "cmd_shift_arrowup":{ name: "Bring to front", cb: () => editor.moveToTopSelected()}, "cmd_shift_arrowdown":{ name: "Send to back", cb: () => editor.moveToBottomSelected()}, "escape": { name: false, cb: ()=> editor.escapeMode()}, - "enter": { name: false, cb: ()=> editor.escapeMode()}, - " ": { name: "Pan canvas", cb: (e)=> editor.pan.startPan(e)}, + "enter": { name: false, cb: ()=> editor.escapeMode()}, + " ": { name: "Pan canvas", cb: (e)=> editor.pan.startPan(e)}, }; document.addEventListener("keydown", function(e){ diff --git a/src/js/Panel.js b/src/js/Panel.js index 9ffbde6..d9db50e 100644 --- a/src/js/Panel.js +++ b/src/js/Panel.js @@ -82,6 +82,8 @@ MD.Panel = function(){ svgCanvas.pathActions.opencloseSubPath(); }); + // Text Path + function show(elem) { $('.context_panel').hide(); if (elem === "canvas") return $('#canvas_panel').show(); diff --git a/src/js/editor.js b/src/js/editor.js index 9ce3d10..f2fa8dd 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -146,7 +146,7 @@ MD.Editor = function(){ svgCanvas.leaveContext() } else - state.set("canvasContent", svgCanvas.getSvgString()) + saveCanvas() } // called when we've selected a different element @@ -198,7 +198,7 @@ MD.Editor = function(){ if (!svgCanvas.getContext()) { - state.set("canvasContent", svgCanvas.getSvgString()) + saveCanvas(); } } @@ -206,7 +206,7 @@ MD.Editor = function(){ if (attr === "opacity") value *= 0.01; if (completed) { svgCanvas.changeSelectedAttribute(attr, value); - state.set("canvasContent", svgCanvas.getSvgString()); + saveCanvas(); } else svgCanvas.changeSelectedAttributeNoUndo(attr, value); } @@ -300,6 +300,11 @@ MD.Editor = function(){ }}); }; + function saveCanvas(){ + console.log("saved") + state.set("canvasContent", svgCanvas.getSvgString()); + } + function toggleWireframe() { editor.menu.flash($('#view_menu')); $('#tool_wireframe').toggleClass('push_button_pressed'); @@ -381,6 +386,7 @@ MD.Editor = function(){ this.shortcuts = shortcuts; this.donate = donate; this.source = source; + this.saveCanvas = saveCanvas; this.export = function(){ if(window.canvg) { diff --git a/src/js/eyedropper.js b/src/js/eyedropper.js index e2696eb..919c739 100644 --- a/src/js/eyedropper.js +++ b/src/js/eyedropper.js @@ -108,7 +108,7 @@ MD.Eyedropper = function() { changes[attrname] = elem.getAttribute(attrname); elem.setAttribute(attrname, newvalue); }; - var batchCmd = new S.BatchCommand(); + var batchCmd = new svgedit.history.BatchCommand(); opts.selectedElements.forEach(function(element){ if (currentStyle.fillPaint) change(element, "fill", currentStyle.fillPaint); if (currentStyle.fillOpacity) change(element, "fill-opacity", currentStyle.fillOpacity); diff --git a/src/js/sanitize.js b/src/js/sanitize.js index 77e231a..ce6a159 100644 --- a/src/js/sanitize.js +++ b/src/js/sanitize.js @@ -128,6 +128,9 @@ $.each(svgWhiteList_, function(elt,atts){ svgWhiteListNS_[elt] = attNS; }); + +svgedit.sanitize.svgWhiteList = function() { return svgWhiteList_; } + // temporarily expose these svgedit.sanitize.getNSMap = function() { return nsMap_; } diff --git a/src/js/svgcanvas.js b/src/js/svgcanvas.js index 2d57342..f7a2a26 100644 --- a/src/js/svgcanvas.js +++ b/src/js/svgcanvas.js @@ -5830,12 +5830,25 @@ this.setSvgString = function(xmlString) { if (!href) return; const path = svgcontent.querySelector(href); const offset = el.getAttribute("startOffset"); + el.setAttribute("xml:space", "default"); // convert percentage based to absolute if (offset.includes("%") && path) { const totalLength = path.getTotalLength(); const pct = parseFloat(offset) * .01; el.setAttribute("startOffset", (pct * totalLength).toFixed(0)) } + const tspan = el.querySelector("tspan"); + const text = el.closest("text"); + if (tspan && text) { + // grab the first tspan and apply it to the text element + svgedit.sanitize.svgWhiteList()["text"].forEach(attr => { + const value = tspan.getAttribute(attr); + if (value) { + tspan.removeAttribute(attr); + text.setAttribute(attr, value); + } + }); + } }) // Set ref element for elements @@ -6043,14 +6056,7 @@ this.importSvgString = function(xmlString) { var symbol = svgdoc.createElementNS(svgns, "symbol"); var defs = findDefs(); - - if(svgedit.browser.isGecko()) { - // Move all gradients into root for Firefox, workaround for this bug: - // https://bugzilla.mozilla.org/show_bug.cgi?id=353575 - // TODO: Make this properly undo-able. - $(svg).find('linearGradient, radialGradient, pattern').appendTo(defs); - } - + while (svg.firstChild) { var first = svg.firstChild; symbol.appendChild(first); @@ -7511,10 +7517,7 @@ this.setTextContent = function(val) { this.textPath = function(){ const text = selectedElements.find(element => element.tagName === "text"); - var path = selectedElements.find(element => element.tagName === "path" || element.tagName === "ellipse"); - if (path.tagName === "ellipse") { - console.log("ellipse") - } + var path = selectedElements.find(element => element.tagName === "path"); if (!text || !path) return false; const textPath = svgdoc.createElementNS(svgns, "textPath"); textPath.textContent = text.textContent; @@ -7522,6 +7525,7 @@ this.textPath = function(){ text.setAttribute("text-anchor", "middle"); text.setAttribute("x", 0); text.setAttribute("y", 0); + textPath.setAttribute("xml:space", "default"); textPath.setAttribute("xlink:href", "#" + path.id); textPath.setAttribute("href", "#" + path.id); const offset = (path.getTotalLength()/2).toFixed(0) diff --git a/src/js/utils.js b/src/js/utils.js index 1cb0778..c3bd7a8 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -10,3 +10,16 @@ utils.findGetParameter = function(parameterName) { } return result; } + +utils.throttle = function (callback, limit) { + var waiting = false; + return function () { + if (!waiting) { + callback.apply(this, arguments); + waiting = true; + setTimeout(function () { + waiting = false; + }, limit); + } + } + } \ No newline at end of file