From 8aa3a9e9298aaadedb7f864e38f0982914f7af46 Mon Sep 17 00:00:00 2001 From: Jacques Distler Date: Tue, 5 Oct 2010 10:08:48 -0500 Subject: [PATCH] SVG-Edit fixes Some bugfixes from the latest SVG-Edit --- .../editor/extensions/ext-imagelib.js | 12 +- public/svg-edit/editor/svg-editor.css | 54 +++++ public/svg-edit/editor/svg-editor.html | 21 +- public/svg-edit/editor/svg-editor.js | 162 ++++++++++++-- public/svg-edit/editor/svgcanvas.js | 206 +++++++++++------- .../editor/svgicons/jquery.svgicons.js | 3 +- test/functional/wiki_controller_test.rb | 1 + 7 files changed, 360 insertions(+), 99 deletions(-) diff --git a/public/svg-edit/editor/extensions/ext-imagelib.js b/public/svg-edit/editor/extensions/ext-imagelib.js index 642f0071..cb874694 100644 --- a/public/svg-edit/editor/extensions/ext-imagelib.js +++ b/public/svg-edit/editor/extensions/ext-imagelib.js @@ -173,10 +173,12 @@ svgEditor.addExtension("imagelib", function() { } else { $(this).text(title); } + submit.removeAttr('disabled'); } }); } else { preview.append('
'+title+'
'); + submit.removeAttr('disabled'); } } else { if(cur_meta && cur_meta.preview_url) { @@ -192,10 +194,12 @@ svgEditor.addExtension("imagelib", function() { preview.children().each(function() { if($(this).data('id') == id) { $(this).html(entry); + submit.removeAttr('disabled'); } }); } else { preview.append($('
').append(entry)); + submit.removeAttr('disabled'); } } @@ -214,11 +218,10 @@ svgEditor.addExtension("imagelib", function() { } }, true); - var preview; + var preview, submit; function toggleMulti(show) { - var submit; - + $('#lib_framewrap, #imglib_opts').css({right: (show ? 200 : 10)}); if(!preview) { preview = $('
').css({ @@ -231,7 +234,7 @@ svgEditor.addExtension("imagelib", function() { overflow: 'auto' }).insertAfter('#lib_framewrap'); - submit = $('').appendTo('#imgbrowse').click(function() { + submit = $('').appendTo('#imgbrowse').click(function() { $.each(multi_arr, function(i) { var type = this[0]; var data = this[1]; @@ -254,6 +257,7 @@ svgEditor.addExtension("imagelib", function() { } preview.toggle(show); + submit.toggle(show); } function showBrowser() { diff --git a/public/svg-edit/editor/svg-editor.css b/public/svg-edit/editor/svg-editor.css index 709383a1..a792239c 100644 --- a/public/svg-edit/editor/svg-editor.css +++ b/public/svg-edit/editor/svg-editor.css @@ -54,6 +54,59 @@ background: -webkit-gradient(radial, center center, 3, center center, 1000, from(#bbb), to(#222)); } +#rulers > div { + position: absolute; + background: #DDD; + z-index: 1; + overflow: hidden; +} + +#ruler_corner { + top: 76px; + left: 41px; + width: 15px; + height: 15px; +} + +#ruler_x { + height: 15px; + top: 76px; + left: 56px; + right: 30px; + border-bottom: 1px solid; + border-left: 1px solid #777; +} + +#rulers.moved #ruler_corner, +#rulers.moved #ruler_x { + top: 101px; +} + +#ruler_y { + width: 15px; + top: 91px; + left: 41px; + bottom: 78px; + border-right: 1px solid; + border-top: 1px solid #777; +} + +#rulers.moved #ruler_y { + top: 116px; +} + + +#ruler_x canvas { + margin-left: -16px; +} + +#ruler_y canvas { + margin-top: -16px; +} + + + + #svg_editor div#palette_holder { overflow-x: scroll; overflow-y: hidden; @@ -721,6 +774,7 @@ span.zoom_tool { display: none; cursor: pointer; width: 400px; + z-index: 1; } #svg_editor .tools_flyout_v { diff --git a/public/svg-edit/editor/svg-editor.html b/public/svg-edit/editor/svg-editor.html index e2599618..6b51c267 100644 --- a/public/svg-edit/editor/svg-editor.html +++ b/public/svg-edit/editor/svg-editor.html @@ -49,9 +49,21 @@ script type="text/javascript" src="locale/locale.min.js">
+
+
+
+ +
+
+ +
+
+
-
+
+ +
@@ -528,7 +540,7 @@ script type="text/javascript" src="locale/locale.min.js">
- +
@@ -669,6 +681,11 @@ script type="text/javascript" src="locale/locale.min.js"> +
+ Units & Rulers + +
+
diff --git a/public/svg-edit/editor/svg-editor.js b/public/svg-edit/editor/svg-editor.js index 41fbc369..894fd89f 100644 --- a/public/svg-edit/editor/svg-editor.js +++ b/public/svg-edit/editor/svg-editor.js @@ -51,7 +51,8 @@ wireframe: false, colorPickerCSS: null, gridSnapping: false, - snappingStep: 10 + snappingStep: 10, + showRulers: true }, uiStrings = Editor.uiStrings = { "invalidAttrValGiven":"Invalid value given", @@ -93,7 +94,7 @@ if(val) curPrefs[key] = val; key = 'svg-edit-'+key; var host = location.hostname, - onweb = host && host.indexOf('.') != -1, + onweb = host && host.indexOf('.') >= 0, store = (val != undefined), storage = false; // Some FF versions throw security errors here @@ -251,7 +252,7 @@ $.svgIcons(curConfig.imgPath + 'svg_edit_icons.svg', { w:24, h:24, id_match: false, - no_img: true, + no_img: (!!window.opera), // Opera gives odd behavior w/images fallback_path: curConfig.imgPath, fallback:{ 'new_image':'clear.png', @@ -435,6 +436,12 @@ }); svgEditor.runCallbacks(); + + setTimeout(function() { + $('.flyout_arrow_horiz:empty').each(function() { + $(this).append($.getSvgIcon('arrow_right').width(5).height(5)); + }); + }, 1); } }); @@ -452,7 +459,7 @@ "#aaaaff", "#d4aaff", "#ffaaff", "#ffaad4", ]; - isMac = (navigator.platform.indexOf("Mac") != -1); + isMac = (navigator.platform.indexOf("Mac") >= 0); modKey = (isMac ? "meta+" : "ctrl+"); // ⌘ path = svgCanvas.pathActions, undoMgr = svgCanvas.undoMgr, @@ -646,8 +653,9 @@ // 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 - - if (mode != "multiselect" && !is_node) { + + if (mode !== "multiselect" && !is_node) { + // FIXME: This also needs to fire if only one element is selected via multiselect updateToolbar(); } @@ -738,6 +746,7 @@ var contextChanged = function(win, context) { $('#workarea,#sidepanels').css('top', context?100:75); + $('#rulers').toggleClass('moved', context); if(cur_context && !context) { // Back to normal workarea[0].scrollTop -= 25; @@ -1237,7 +1246,7 @@ $.svgIcons(svgicons, { w:24, h:24, id_match: false, - no_img: true, + no_img: (!!window.opera), fallback: fallback_obj, placement: placement_obj, callback: function(icons) { @@ -1281,7 +1290,7 @@ // updates the toolbar (colors, opacity, etc) based on the selected element // This function also updates the opacity and id elements that are in the context panel var updateToolbar = function() { - if (selectedElement != null && $.inArray(selectedElement.tagName, ['use', 'image', 'foreignObject', 'g', 'a']) === -1) { + if (selectedElement != null && ['use', 'image', 'foreignObject', 'g', 'a'].indexOf(selectedElement.tagName) === -1) { // get opacity values var fillOpacity = parseFloat(selectedElement.getAttribute("fill-opacity")); @@ -1444,12 +1453,12 @@ if(!is_node && currentMode != 'pathedit') { $('#selected_panel').show(); // Elements in this array already have coord fields - if($.inArray(elname, ['line', 'circle', 'ellipse']) != -1) { + if(['line', 'circle', 'ellipse'].indexOf(elname) >= 0) { $('#xy_panel').hide(); } else { var x,y; // Get BBox vals for g, polyline and path - if($.inArray(elname, ['g', 'polyline', 'path']) != -1) { + if(['g', 'polyline', 'path'].indexOf(elname) >= 0) { var bb = svgCanvas.getStrokedBBox([elem]); if(bb) { x = bb.x; @@ -1465,7 +1474,7 @@ } // Elements in this array cannot be converted to a path - var no_path = $.inArray(elname, ['image', 'text', 'path', 'g', 'use']) == -1; + var no_path = ['image', 'text', 'path', 'g', 'use'].indexOf(elname) == -1; $('#tool_topath').toggle(no_path); $('#tool_reorient').toggle(elname == 'path'); $('#tool_reorient').toggleClass('disabled', angle == 0); @@ -1658,7 +1667,7 @@ var changeStrokeWidth = function(ctl) { var val = ctl.value; - if(val == 0 && selectedElement && $.inArray(selectedElement.nodeName, ['line', 'polyline']) != -1) { + if(val == 0 && selectedElement && ['line', 'polyline'].indexOf(selectedElement.nodeName) >= 0) { val = ctl.value = 1; } svgCanvas.setStrokeWidth(val); @@ -2698,6 +2707,11 @@ // set grid setting curConfig.gridSnapping = $('#grid_snapping_on')[0].checked; curConfig.snappingStep = $('#grid_snapping_step').val(); + curConfig.showRulers = $('#show_rulers')[0].checked; + + $('#rulers').toggle(curConfig.showRulers); + if(curConfig.showRulers) updateRulers(); + svgCanvas.setConfig(curConfig); updateCanvas(); @@ -2714,7 +2728,7 @@ } var setIcon = Editor.setIcon = function(elem, icon_id, forcedSize) { - var icon = (typeof icon_id == 'string') ? $.getSvgIcon(icon_id, true) : icon_id; + var icon = (typeof icon_id === 'string') ? $.getSvgIcon(icon_id, true) : icon_id.clone(); if(!icon) { console.log('NOTE: Icon image missing: ' + icon_id); return; @@ -3045,6 +3059,14 @@ }); }); + (function() { + workarea.scroll(function() { + $('#ruler_x')[0].scrollLeft = workarea[0].scrollLeft; + $('#ruler_y')[0].scrollTop = workarea[0].scrollTop; + }); + + }()); + $('#url_notice').click(function() { $.alert(this.title); }); @@ -3433,6 +3455,8 @@ workarea.css('right', parseInt(workarea.css('right'))+deltax); sidepanels.css('width', parseInt(sidepanels.css('width'))+deltax); layerpanel.css('width', parseInt(layerpanel.css('width'))+deltax); + var ruler_x = $('#ruler_x'); + ruler_x.css('right', parseInt(ruler_x.css('right')) + deltax); } $('#sidepanel_handle') @@ -3464,9 +3488,11 @@ var deltax = (w > 2 || close ? 2 : SIDEPANEL_OPENWIDTH) - w; 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); + var ruler_x = $('#ruler_x'); + workarea.css('right', parseInt(workarea.css('right')) + deltax); + sidepanels.css('width', parseInt(sidepanels.css('width')) + deltax); + layerpanel.css('width', parseInt(layerpanel.css('width')) + deltax); + ruler_x.css('right', parseInt(ruler_x.css('right')) + deltax); }; // this function highlights the layer passed in (by fading out the other layers) @@ -4111,13 +4137,115 @@ w_area[0].scrollLeft = new_ctr.x - w_orig/2; w_area[0].scrollTop = new_ctr.y - h_orig/2; } + + if(curConfig.showRulers) { + updateRulers(cnvs, zoom); + workarea.scroll(); + } + } + + // Make [1,2,5] array + var r_intervals = []; + for(var i = .1; i < 1E5; i *= 10) { + r_intervals.push(1 * i); + r_intervals.push(2 * i); + r_intervals.push(5 * i); + } + + function updateRulers(scanvas, zoom) { + if(!zoom) zoom = svgCanvas.getZoom(); + if(!scanvas) scanvas = $("#svgcanvas"); + + var c_elem = svgCanvas.getContentElem(); + + for(var d = 0; d < 2; d++) { + var is_x = (d === 0); + var dim = is_x ? 'x' : 'y'; + var lentype = is_x?'width':'height'; + + var content_d = c_elem.getAttribute(dim)-0; + + var hcanv = $('#ruler_' + dim + ' canvas')[0]; + // Set the canvas size to the width of the container + var len = hcanv[lentype] = scanvas[lentype](); + var ctx = hcanv.getContext("2d"); + + var unit = 1; // 1 = 1px + + // Calculate the main number interval + var raw_m = 50 / zoom; + + var multi = 1; + for(var i = 0; i < r_intervals.length; i++) { + var num = r_intervals[i]; + multi = num; + if(raw_m <= num) { + break; + } + } + + var big_int = unit * multi * zoom; + + ctx.font = "9px sans-serif"; + + var ruler_d = ((content_d / zoom) % multi) * zoom; + + for (; ruler_d < len; ruler_d += big_int) { + var real_d = Math.round((ruler_d) - content_d ); + + var cur_d = Math.round(ruler_d) + .5; + if(is_x) { + ctx.moveTo(cur_d, 15); + ctx.lineTo(cur_d, 0); + } else { + ctx.moveTo(15, cur_d); + ctx.lineTo(0, cur_d); + } + + var label = Math.round(real_d / zoom); + + // Do anything special for negative numbers? +// var is_neg = label < 0; +// real_d2 = Math.abs(real_d2); + + // Change 1000s to Ks + if(label !== 0 && label !== 1000 && label % 1000 === 0) { + label = (label / 1000) + 'K'; + } + + if(is_x) { + ctx.fillText(label, ruler_d+2, 8); + } else { + var str = (label+'').split(''); + for(var i = 0; i < str.length; i++) { + ctx.fillText(str[i], 1, (ruler_d+9) + i*9); + } + } + + var part = big_int / 10; + for(var i = 1; i < 10; i++) { + var sub_d = Math.round(ruler_d + part * i) + .5; + var line_num = (i % 2)?12:10; + if(is_x) { + ctx.moveTo(sub_d, 15); + ctx.lineTo(sub_d, line_num); + } else { + ctx.moveTo(15, sub_d); + ctx.lineTo(line_num ,sub_d); + } + } + } + + ctx.strokeStyle = "#000"; + ctx.stroke(); + } } // $(function() { updateCanvas(true); // }); - // var revnums = "svg-editor.js ($Rev: 1762 $) "; + // var revnums = "svg-editor.js ($Rev: 1774 $) "; // revnums += svgCanvas.getVersion(); // $('#copyright')[0].setAttribute("title", revnums); diff --git a/public/svg-edit/editor/svgcanvas.js b/public/svg-edit/editor/svgcanvas.js index 45c8984e..423d7bf1 100644 --- a/public/svg-edit/editor/svgcanvas.js +++ b/public/svg-edit/editor/svgcanvas.js @@ -89,8 +89,11 @@ if(window.opera) { // config - An object that contains configuration data $.SvgCanvas = function(container, config) { -var isOpera = !!window.opera, - isWebkit = navigator.userAgent.indexOf("AppleWebKit") != -1, +var userAgent = navigator.userAgent, + // Note: Browser sniffing should only be used if no other detection method is possible + isOpera = !!window.opera, + isWebkit = userAgent.indexOf("AppleWebKit") >= 0, + isGecko = userAgent.indexOf('Gecko/') >= 0, // Object populated later with booleans indicating support for features support = {}, @@ -406,7 +409,7 @@ var Utils = this.Utils = function() { // Cross-browser compatible method of converting a string to an XML tree // found this function here: http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f "text2xml": function(sXML) { - if(sXML.indexOf('= 0) { sXML = sXML.replace(/<(\/?)svg:/g, '<$1').replace('xmlns:svg', 'xmlns'); } @@ -513,7 +516,7 @@ var svgWhiteListNS = {}; $.each(svgWhiteList, function(elt,atts){ var attNS = {}; $.each(atts, function(i, att){ - if (att.indexOf(':') != -1) { + if (att.indexOf(':') >= 0) { var v = att.split(':'); attNS[v[1]] = nsRevMap[v[0]]; } else { @@ -559,9 +562,9 @@ var convertToNum, convertToUnit, setUnitAttr; var num = val.substr(0, val.length-1)/100; var res = getResolution(); - if($.inArray(attr, w_attrs) !== -1) { + if(w_attrs.indexOf(attr) >= 0) { return num * res.w; - } else if($.inArray(attr, h_attrs) !== -1) { + } else if(h_attrs.indexOf(attr) >= 0) { return num * res.h; } else { return num * Math.sqrt((res.w*res.w) + (res.h*res.h))/Math.sqrt(2); @@ -594,9 +597,9 @@ var convertToNum, convertToUnit, setUnitAttr; var res = getResolution(); unit = '%'; val *= 100; - if($.inArray(attr, w_attrs) !== -1) { + if(w_attrs.indexOf(attr) >= 0) { val = val / res.w; - } else if($.inArray(attr, h_attrs) !== -1) { + } else if(h_attrs.indexOf(attr) >= 0) { val = val / res.h; } else { return val / Math.sqrt((res.w*res.w) + (res.h*res.h))/Math.sqrt(2); @@ -621,7 +624,7 @@ var convertToNum, convertToUnit, setUnitAttr; // val - String with the attribute value to check canvas.isValidUnit = function(attr, val) { var valid = false; - if($.inArray(attr, unit_attrs) != -1) { + if(unit_attrs.indexOf(attr) >= 0) { // True if it's just a number if(!isNaN(val)) { valid = true; @@ -2134,7 +2137,7 @@ var getStrokedBBox = this.getStrokedBBox = function(elems) { // Get the BBox from the raw path for these elements var elemNames = ['ellipse','path','line','polyline','polygon']; - if($.inArray(elem.tagName, elemNames) != -1) { + if(elemNames.indexOf(elem.tagName) >= 0) { bb = good_bb = canvas.convertToPath(elem, true); } else if(elem.tagName == 'rect') { // Look for radius @@ -2515,7 +2518,7 @@ var sanitizeSvg = this.sanitizeSvg = function(node) { while(p--) { var nv = props[p].split(":"); // now check that this attribute is supported - if (allowedAttrs.indexOf(nv[0]) != -1) { + if (allowedAttrs.indexOf(nv[0]) >= 0) { node.setAttribute(nv[0],nv[1]); } } @@ -2531,8 +2534,8 @@ var sanitizeSvg = this.sanitizeSvg = function(node) { // (but not for links) var href = getHref(node); if(href && - $.inArray(node.nodeName, ["filter", "linearGradient", "pattern", - "radialGradient", "textPath", "use"]) != -1) + ["filter", "linearGradient", "pattern", + "radialGradient", "textPath", "use"].indexOf(node.nodeName) >= 0) { // TODO: we simply check if the first character is a #, is this bullet-proof? if (href[0] != "#") { @@ -2639,7 +2642,7 @@ var getBBox = this.getBBox = function(elem) { bb.x = ret.x + parseFloat(selected.getAttribute('x')||0); bb.y = ret.y + parseFloat(selected.getAttribute('y')||0); ret = bb; - } else if(~$.inArray(elname, visElems_arr)) { + } else if(~visElems_arr.indexOf(elname)) { try { ret = selected.getBBox();} catch(e) { // Check if element is child of a foreignObject @@ -2670,7 +2673,7 @@ var getBBox = this.getBBox = function(elem) { // Parameters: // elem - The (text) DOM element to clone var ffClone = function(elem) { - if(navigator.userAgent.indexOf('Gecko/') == -1) return elem; + if(isGecko) return elem; var clone = elem.cloneNode(true) elem.parentNode.insertBefore(clone, elem); elem.parentNode.removeChild(elem); @@ -3216,24 +3219,24 @@ var recalculateDimensions = this.recalculateDimensions = function(selected) { var k = tlist.numberOfItems; while (k--) { var xform = tlist.getItem(k); - if (xform.type == 0) { + if (xform.type === 0) { tlist.removeItem(k); } // remove identity matrices - else if (xform.type == 1) { + else if (xform.type === 1) { if (isIdentity(xform.matrix)) { tlist.removeItem(k); } } // remove zero-degree rotations - else if (xform.type == 4) { - if (xform.angle == 0) { + else if (xform.type === 4) { + if (xform.angle === 0) { tlist.removeItem(k); } } } // End here if all it has is a rotation - if(tlist.numberOfItems == 1 && getRotationAngle(selected)) return null; + if(tlist.numberOfItems === 1 && getRotationAngle(selected)) return null; } // if this element had no transforms, we are done @@ -3241,6 +3244,43 @@ var recalculateDimensions = this.recalculateDimensions = function(selected) { selected.removeAttribute("transform"); return null; } + + // TODO: Make this work for more than 2 + if (tlist) { + var k = tlist.numberOfItems; + var mxs = []; + while (k--) { + var xform = tlist.getItem(k); + if (xform.type === 1) { + mxs.push([xform.matrix, k]); + } else if(mxs.length) { + mxs = []; + } + } + if(mxs.length === 2) { + var m_new = svgroot.createSVGTransformFromMatrix(matrixMultiply(mxs[1][0], mxs[0][0])); + tlist.removeItem(mxs[0][1]); + tlist.removeItem(mxs[1][1]); + tlist.insertItemBefore(m_new, mxs[1][1]); + } + + // combine matrix + translate + k = tlist.numberOfItems; + + if(k === 2 && tlist.getItem(0).type === 1 && tlist.getItem(1).type === 2) { + var mt = svgroot.createSVGTransform(); + logMatrix(tlist.getItem(0).matrix); + logMatrix(transformListToTransform(tlist).matrix); + + mt.setMatrix(transformListToTransform(tlist).matrix); + tlist.clear(); + tlist.appendItem(mt); + } + } + + + + // Grouped SVG element var gsvg = $(selected).data('gsvg'); @@ -3503,7 +3543,7 @@ var recalculateDimensions = this.recalculateDimensions = function(selected) { if(child.getAttribute('clip-path')) { // tx, ty var attr = child.getAttribute('clip-path'); - if($.inArray(attr, clipPaths_done) === -1) { + if(clipPaths_done.indexOf(attr) === -1) { updateClipPath(attr, tx, ty); clipPaths_done.push(attr); } @@ -3902,9 +3942,6 @@ var isIdentity = function(m) { return (m.a == 1 && m.b == 0 && m.c == 0 && m.d == 1 && m.e == 0 && m.f == 0); } -// matrixMultiply() is provided because WebKit didn't implement multiply() correctly -// on the SVGMatrix interface. See https://bugs.webkit.org/show_bug.cgi?id=16062 - // Function: matrixMultiply // This function tries to return a SVGMatrix that is the multiplication m1*m2. // We also round to zero when it's near zero @@ -3916,21 +3953,11 @@ var isIdentity = function(m) { // The matrix object resulting from the calculation var matrixMultiply = this.matrixMultiply = function() { var NEAR_ZERO = 1e-14, - multi2 = function(m1, m2) { - var m = svgroot.createSVGMatrix(); - m.a = m1.a*m2.a + m1.c*m2.b; - m.b = m1.b*m2.a + m1.d*m2.b, - m.c = m1.a*m2.c + m1.c*m2.d, - m.d = m1.b*m2.c + m1.d*m2.d, - m.e = m1.a*m2.e + m1.c*m2.f + m1.e, - m.f = m1.b*m2.e + m1.d*m2.f + m1.f; - return m; - }, args = arguments, i = args.length, m = args[i-1]; while(i-- > 1) { var m1 = args[i-1]; - m = multi2(m1, m); + m = m1.multiply(m); } if (Math.abs(m.a) < NEAR_ZERO) m.a = 0; if (Math.abs(m.b) < NEAR_ZERO) m.b = 0; @@ -4170,8 +4197,7 @@ var removeFromSelection = this.removeFromSelection = function(elemsToRemove) { if (elemsToRemove.length == 0) { return; } // find every element and remove it from our array copy - var newSelectedItems = new Array(selectedElements.length), - newSelectedBBoxes = new Array(selectedBBoxes.length), + var newSelectedItems = new Array(selectedElements.length); j = 0, len = selectedElements.length; for (var i = 0; i < len; ++i) { @@ -4180,7 +4206,6 @@ var removeFromSelection = this.removeFromSelection = function(elemsToRemove) { // keep the item if (elemsToRemove.indexOf(elem) == -1) { newSelectedItems[j] = elem; - if (j==0) newSelectedBBoxes[j] = selectedBBoxes[i]; j++; } else { // remove the item and its selector @@ -4190,7 +4215,6 @@ var removeFromSelection = this.removeFromSelection = function(elemsToRemove) { } // the copy becomes the master now selectedElements = newSelectedItems; - selectedBBoxes = newSelectedBBoxes; }; // Function: selectAllInCurrentLayer @@ -4273,7 +4297,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { // for foreign content, go up until we find the foreignObject // WebKit browsers set the mouse target to the svgcanvas div - if ($.inArray(mouse_target.namespaceURI, [mathns, htmlns]) != -1 && + if ([mathns, htmlns].indexOf(mouse_target.namespaceURI) >= 0 && mouse_target.id != "svgcanvas") { while (mouse_target.nodeName != "foreignObject") { @@ -4284,7 +4308,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { // Get the desired mouse_target with jQuery selector-fu // If it's root-like, select the root - if($.inArray(mouse_target, [svgroot, container, svgcontent, current_layer]) !== -1) { + if([svgroot, container, svgcontent, current_layer].indexOf(mouse_target) >= 0) { return svgroot; } @@ -4365,7 +4389,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { } // This would seem to be unnecessary... -// if($.inArray(current_mode, ['select', 'resize']) == -1) { +// if(['select', 'resize'].indexOf(current_mode) == -1) { // setGradient(); // } @@ -4830,13 +4854,13 @@ var getMouseTarget = this.getMouseTarget = function(evt) { sy = height ? (height+dy)/height : 1, sx = width ? (width+dx)/width : 1; // if we are dragging on the north side, then adjust the scale factor and ty - if(current_resize_mode.indexOf("n") != -1) { + if(current_resize_mode.indexOf("n") >= 0) { sy = height ? (height-dy)/height : 1; ty = height; } // if we dragging on the east side, then adjust the scale factor and tx - if(current_resize_mode.indexOf("w") != -1) { + if(current_resize_mode.indexOf("w") >= 0) { sx = width ? (width-dx)/width : 1; tx = width; } @@ -6442,7 +6466,7 @@ var pathActions = this.pathActions = function() { var index = indexes[i]; var seg = p.segs[index]; if(seg.ptgrip) { - if($.inArray(index, p.selected_pts) == -1 && index >= 0) { + if(p.selected_pts.indexOf(index) == -1 && index >= 0) { p.selected_pts.push(index); } } @@ -6466,7 +6490,7 @@ var pathActions = this.pathActions = function() { } this.removePtFromSelection = function(index) { - var pos = $.inArray(index, p.selected_pts); + var pos = p.selected_pts.indexOf(index); if(pos == -1) { return; } @@ -7691,7 +7715,7 @@ var removeUnusedDefElems = this.removeUnusedDefElems = function() { while (i--) { var defelem = defelems[i]; var id = defelem.id; - if($.inArray(id, defelem_uses) == -1) { + if(defelem_uses.indexOf(id) == -1) { // Not found, so remove defelem.parentNode.removeChild(defelem); numRemoved++; @@ -7723,7 +7747,7 @@ var svgCanvasToString = this.svgCanvasToString = function() { // Keep SVG-Edit comment on top $.each(svgcontent.childNodes, function(i, node) { - if(i && node.nodeType == 8 && node.data.indexOf('Created with') != -1) { + if(i && node.nodeType == 8 && node.data.indexOf('Created with') >= 0) { svgcontent.insertBefore(node, svgcontent.firstChild); } }); @@ -7815,7 +7839,7 @@ var svgToString = this.svgToString = function(elem, indent) { // only serialize attributes we don't use internally if (attrVal != "" && - $.inArray(attr.localName, ['width','height','xmlns','x','y','viewBox','id','overflow']) == -1) + ['width','height','xmlns','x','y','viewBox','id','overflow'].indexOf(attr.localName) == -1) { if(!attr.namespaceURI || nsMap[attr.namespaceURI]) { @@ -7830,7 +7854,7 @@ var svgToString = this.svgToString = function(elem, indent) { attr = attrs.item(i); var attrVal = toXml(attr.nodeValue); //remove bogus attributes added by Gecko - if ($.inArray(attr.localName, ['-moz-math-font-style', '_moz-math-font-style']) !== -1) continue; + if (['-moz-math-font-style', '_moz-math-font-style'].indexOf(attr.localName) >= 0) continue; if (attrVal != "") { if(attrVal.indexOf('pointer-events') === 0) continue; if(attr.localName === "class" && attrVal.indexOf('se_') === 0) continue; @@ -8071,8 +8095,8 @@ var uniquifyElems = this.uniquifyElems = function(g) { var href = getHref(n); // TODO: what if an or element refers to an element internally? if(href && - $.inArray(n.nodeName, ["filter", "linearGradient", "pattern", - "radialGradient", "textPath", "use"]) != -1) + ["filter", "linearGradient", "pattern", + "radialGradient", "textPath", "use"].indexOf(n.nodeName) >= 0) { var refid = href.substr(1); if (!(refid in ids)) { @@ -8120,7 +8144,15 @@ var uniquifyElems = this.uniquifyElems = function(g) { // Function convertGradients // Converts gradients from userSpaceOnUse to objectBoundingBox var convertGradients = this.convertGradients = function(elem) { - $(elem).find('linearGradient, radialGradient').each(function() { + var elems = $(elem).find('linearGradient, radialGradient'); + if(!elems.length && isWebkit) { + // Bug in webkit prevents regular *Gradient selector search + elems = $(elem).find('*').filter(function() { + return (this.tagName.indexOf('Gradient') >= 0); + }); + } + + elems.each(function() { var grad = this; if($(grad).attr('gradientUnits') === 'userSpaceOnUse') { // TODO: Support more than one element with this ref by duplicating parent grad @@ -8128,7 +8160,7 @@ var convertGradients = this.convertGradients = function(elem) { if(!elems.length) return; // get object's bounding box - var bb = elems[0].getBBox(); + var bb = getBBox(elems[0]); if(grad.tagName === 'linearGradient') { var g_coords = $(grad).attr(['x1', 'y1', 'x2', 'y2']); @@ -8199,9 +8231,14 @@ var convertToGroup = this.convertToGroup = function(elem) { ts = $elem.attr('transform'); var pos = $elem.attr(['x','y']); - + +// if(ts.length) { +// +// ts += " "; +// } + // Not ideal, but works - ts += "translate(" + pos.x + "," + pos.y + ")"; + ts += "translate(" + (pos.x || 0) + "," + (pos.x || 0) + ")"; var prev = $elem.prev(); @@ -8223,6 +8260,7 @@ var convertToGroup = this.convertToGroup = function(elem) { // g.appendChild(elem.firstChild.cloneNode(true)); if (ts) g.setAttribute("transform", ts); + console.log('t',g.getAttribute('transform')); var parent = elem.parentNode; @@ -8241,6 +8279,7 @@ var convertToGroup = this.convertToGroup = function(elem) { } batchCmd.addSubCommand(new InsertElementCommand(g)); } + convertGradients(g); // recalculate dimensions on the top-level children so that unnecessary transforms @@ -8258,7 +8297,7 @@ var convertToGroup = this.convertToGroup = function(elem) { // TODO: See what ungroupSelectedElement does to absorb matrix canvas.ungroupSelectedElement(); canvas.groupSelectedElements(); - +// addCommandToHistory(batchCmd); } else { @@ -8340,6 +8379,12 @@ this.setSvgString = function(xmlString) { } }); + // For Firefox: Put all gradients in defs + if(isGecko) { + content.find('linearGradient, radialGradient').appendTo(findDefs()); + } + + // Set ref element for elements // TODO: This should also be done if the object is re-added through "redo" @@ -8489,16 +8534,23 @@ this.importSvgString = function(xmlString) { // Uncomment this once Firefox has fixed their symbol bug: // https://bugzilla.mozilla.org/show_bug.cgi?id=353575 -// var symbol = svgdoc.createElementNS(svgns, "symbol"); -// while (svg.firstChild) { -// symbol.appendChild(svg.firstChild); -// } -// var attrs = svg.attributes; -// for(var i=0; i < attrs.length; i++) { -// var attr = attrs[i]; -// symbol.setAttribute(attr.nodeName, attr.nodeValue); -// } - var symbol = svg; + var symbol = svgdoc.createElementNS(svgns, "symbol"); + var defs = findDefs(); + + while (svg.firstChild) { + var first = svg.firstChild; + if(isGecko && first.tagName === 'defs') { + // Move all gradients into root for Firefox + $(first).find('linearGradient, radialGradient').appendTo(defs); + } + symbol.appendChild(first); + } + var attrs = svg.attributes; + for(var i=0; i < attrs.length; i++) { + var attr = attrs[i]; + symbol.setAttribute(attr.nodeName, attr.nodeValue); + } +// var symbol = svg; symbol.id = getNextId(); var use_el = svgdoc.createElementNS(svgns, "use"); @@ -8577,7 +8629,7 @@ var identifyLayers = function() { // create a new layer and add all the orphans to it if (orphans.length > 0) { var i = 1; - while ($.inArray(("Layer " + i), layernames) != -1) { i++; } + while (layernames.indexOf(("Layer " + i)) >= 0) { i++; } var newname = "Layer " + i; current_layer = svgdoc.createElementNS(svgns, "g"); var layer_title = svgdoc.createElementNS(svgns, "title"); @@ -9136,7 +9188,7 @@ this.getZoom = function(){return current_zoom;}; // Function: getVersion // Returns a string which describes the revision number of SvgCanvas. this.getVersion = function() { - return "svgcanvas.js ($Rev: 1764 $)"; + return "svgcanvas.js ($Rev: 1775 $)"; }; // Function: setUiStrings @@ -9657,7 +9709,7 @@ this.getStrokeWidth = function() { // Parameters: // val - A Float indicating the new stroke width value this.setStrokeWidth = function(val) { - if(val == 0 && $.inArray(current_mode, ['line', 'path']) != -1) { + if(val == 0 && ['line', 'path'].indexOf(current_mode) >= 0) { canvas.setStrokeWidth(1); return; } @@ -10313,7 +10365,7 @@ var changeSelectedAttributeNoUndo = function(attr, newValue, elems) { } // Set x,y vals on elements that don't have them - if((attr == 'x' || attr == 'y') && $.inArray(elem.tagName, ['g', 'polyline', 'path']) != -1) { + if((attr == 'x' || attr == 'y') && ['g', 'polyline', 'path'].indexOf(elem.tagName) >= 0) { var bbox = getStrokedBBox([elem]); var diff_x = attr == 'x' ? newValue - bbox.x : 0; var diff_y = attr == 'y' ? newValue - bbox.y : 0; @@ -10322,7 +10374,7 @@ var changeSelectedAttributeNoUndo = function(attr, newValue, elems) { } // only allow the transform/opacity attribute to change on elements, slightly hacky - if (elem.tagName == "g" && $.inArray(attr, ['transform', 'opacity', 'filter']) !== -1); + if (elem.tagName == "g" && ['transform', 'opacity', 'filter'].indexOf(attr) >= 0); var oldval = attr == "#text" ? elem.textContent : elem.getAttribute(attr); if (oldval == null) oldval = ""; if (oldval != String(newValue)) { @@ -10356,7 +10408,7 @@ var changeSelectedAttributeNoUndo = function(attr, newValue, elems) { // Use the Firefox ffClone hack for text elements with gradients or // where other text attributes are changed. if(elem.nodeName == 'text') { - if((newValue+'').indexOf('url') == 0 || $.inArray(attr, ['font-size','font-family','x','y']) != -1 && elem.textContent) { + if((newValue+'').indexOf('url') == 0 || ['font-size','font-family','x','y'].indexOf(attr) >= 0 && elem.textContent) { elem = ffClone(elem); } } @@ -10364,7 +10416,7 @@ var changeSelectedAttributeNoUndo = function(attr, newValue, elems) { // codedread: it is now possible for this function to be called with elements // that are not in the selectedElements array, we need to only request a // selector if the element is in that array - if ($.inArray(elem, selectedElements) != -1) { + if (selectedElements.indexOf(elem) >= 0) { setTimeout(function() { // Due to element replacement, this element may no longer // be part of the DOM @@ -10566,6 +10618,12 @@ this.ungroupSelectedElement = function() { if($(g).data('gsvg') || $(g).data('symbol')) { // Is svg, so actually convert to group + convertToGroup(g); + return; + } else if(g.tagName === 'use') { + // Somehow doesn't have data set, so retrieve + var symbol = getElem(getHref(g).substr(1)); + $(g).data('symbol', symbol); convertToGroup(g); return; } diff --git a/public/svg-edit/editor/svgicons/jquery.svgicons.js b/public/svg-edit/editor/svgicons/jquery.svgicons.js index aa50d7cc..b3ffb14d 100644 --- a/public/svg-edit/editor/svgicons/jquery.svgicons.js +++ b/public/svg-edit/editor/svgicons/jquery.svgicons.js @@ -212,7 +212,6 @@ $(function() { elems = $(svgdoc.firstChild).children(); //.getElementsByTagName('foreignContent'); if(!opts.no_img) { - var testSrc = data_pre + 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNzUiIGhlaWdodD0iMjc1Ij48L3N2Zz4%3D'; testImg = $(new Image()).attr({ @@ -222,7 +221,7 @@ $(function() { }).appendTo('body') .load(function () { // Safari 4 crashes, Opera and Chrome don't - makeIcons(!isSafari); + makeIcons(true); }).error(function () { makeIcons(); }); diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb index 7066cc87..0017b06f 100644 --- a/test/functional/wiki_controller_test.rb +++ b/test/functional/wiki_controller_test.rb @@ -142,6 +142,7 @@ class WikiControllerTest < ActionController::TestCase # Tempfile doesn't know how to open files with binary flag, hence the two-step process Tempfile.open('instiki_export_file') { |f| @tempfile_path = f.path } + # some wacky bug in Ruby 1.9.2p0's Tempfile is fixed by @tempfile_path.to_s begin File.open(@tempfile_path, 'wb') { |f| f.write(r.body); @exported_file = f.path }