From 7ec9326ce00d08f9d957981f2edff6df26f24a28 Mon Sep 17 00:00:00 2001
From: jinlin <jinlin>
Date: 星期四, 26 九月 2024 13:25:02 +0800
Subject: [PATCH] 修改

---
 web/public/SVGOrigin/Method-Draw-master/method-draw/src/svgcanvas.js | 1718 +++++++++++++++++++++++++++++-----------------------------
 1 files changed, 859 insertions(+), 859 deletions(-)

diff --git a/web/public/SVGOrigin/Method-Draw-master/method-draw/src/svgcanvas.js b/web/public/SVGOrigin/Method-Draw-master/method-draw/src/svgcanvas.js
index 093f8ae..f0a4d9c 100644
--- a/web/public/SVGOrigin/Method-Draw-master/method-draw/src/svgcanvas.js
+++ b/web/public/SVGOrigin/Method-Draw-master/method-draw/src/svgcanvas.js
@@ -39,10 +39,10 @@
 
   // This fixes $(...).attr() to work as expected with SVG elements.
   // Does not currently use *AttributeNS() since we rarely need that.
-  
+
   // See http://api.jquery.com/attr/ for basic documentation of .attr()
-  
-  // Additional functionality: 
+
+  // Additional functionality:
   // - When getting attributes, a string that's a number is return as type number.
   // - If an array is supplied as first parameter, multiple values are returned
   // as an object with values for each given attributes
@@ -72,7 +72,7 @@
             obj[aname] = attr;
           }
           return obj;
-        
+
         } else if(typeof key === "object") {
           // Setting attributes form object
           for(var v in key) {
@@ -93,7 +93,7 @@
     }
     return this;
   };
-  
+
 }());
 
 // Class: SvgCanvas
@@ -117,7 +117,7 @@
 var curConfig = {
   show_outside_canvas: true,
   selectNew: true,
-  dimensions: [640, 480]
+  dimensions: [200, 200]
 };
 
 // Update config with new one if given
@@ -183,8 +183,8 @@
 // Function: setIdPrefix
 // Changes the ID prefix to the given value
 //
-// Parameters: 
-// p - String with the new prefix 
+// Parameters:
+// p - String with the new prefix
 canvas.setIdPrefix = function(p) {
   idprefix = p;
 };
@@ -238,7 +238,7 @@
 
 // Function: addSvgElementFromJson
 // Create a new SVG element based on the given object keys/values and add it to the current layer
-// The element will be ran through cleanupElement before being returned 
+// The element will be ran through cleanupElement before being returned
 //
 // Parameters:
 // data - Object with the following keys/values:
@@ -342,7 +342,7 @@
       var elems = cmd.elements();
       canvas.pathActions.clear();
       call("changed", elems);
-      
+
       var cmdType = cmd.type();
       var isApply = (eventType == EventTypes.AFTER_APPLY);
       if (cmdType == MoveElementCommand.type()) {
@@ -360,7 +360,7 @@
         } else {
           if (!isApply) restoreRefElems(cmd.elem);
         }
-        
+
         if(cmd.elem.tagName === 'use') {
           setUseData(cmd.elem);
         }
@@ -374,8 +374,8 @@
         if (values["stdDeviation"]) {
           canvas.setBlurOffsets(cmd.elem.parentNode, values["stdDeviation"]);
         }
-        
-        // Remove & Re-add hack for Webkit (issue 775) 
+
+        // Remove & Re-add hack for Webkit (issue 775)
         //if(cmd.elem.tagName === 'use' && svgedit.browser.isWebkit()) {
         //  var elem = cmd.elem;
         //  if(!elem.getAttribute('x') && !elem.getAttribute('y')) {
@@ -462,9 +462,9 @@
       }
     }
   }
-  
+
   var childs = elem.getElementsByTagName('*');
-  
+
   if(childs.length) {
     for(var i = 0, l = childs.length; i < l; i++) {
       restoreRefElems(childs[i]);
@@ -484,55 +484,55 @@
 
 // Object to contain image data for raster images that were found encodable
 var encodableImages = {},
-  
+
   // String with image URL of last loadable image
   last_good_img_url = curConfig.imgPath + 'logo.png',
-  
+
   // Array with current disabled elements (for in-group editing)
   disabled_elems = [],
-  
+
   // Object with save options
   save_options = {round_digits: 5},
-  
+
   // Boolean indicating whether or not a draw action has been started
   started = false,
-  
+
   // String with an element's initial transform attribute value
   start_transform = null,
-  
+
   // String indicating the current editor mode
   current_mode = "select",
-  
+
   // String with the current direction in which an element is being resized
   current_resize_mode = "none",
-  
+
   // Object with IDs for imported files, to see if one was already added
   import_ids = {};
 
 // Current text style properties
 var cur_text = all_properties.text,
-  
+
   // Current general properties
   cur_properties = cur_shape,
-  
+
   // Array with selected elements' Bounding box object
 //  selectedBBoxes = new Array(1),
-  
+
   // The DOM element that was just selected
   justSelected = null,
-  
+
   // DOM element for selection rectangle drawn by the user
   rubberBox = null,
-  
+
   // Array of current BBoxes (still needed?)
   curBBoxes = [],
-  
+
   // Object to contain all included extensions
   extensions = {},
-  
+
   // Canvas point for the most recent right click
   lastClickPoint = null,
-  
+
   // Map of deleted reference elements
   removedElements = {}
 
@@ -558,14 +558,14 @@
 
 // Function: addExtension
 // Add an extension to the editor
-// 
+//
 // Parameters:
 // name - String with the ID of the extension
 // ext_func - Function supplied by the extension with its data
 this.addExtension = function(name, ext_func) {
   if(!(name in extensions)) {
     // Provide private vars/funcs here. Is there a better way to do this?
-    
+
     if($.isFunction(ext_func)) {
     var ext = ext_func($.extend(canvas.getPrivateMethods(), {
       svgroot: svgroot,
@@ -582,7 +582,7 @@
     console.log('Cannot add extension "' + name + '", an extension by that name already exists"');
   }
 };
-  
+
 // This method rounds the incoming value to the nearest value based on the current_zoom
 var round = this.round = function(val) {
   return parseInt(val*current_zoom)/current_zoom;
@@ -590,10 +590,10 @@
 
 // This method sends back an array or a NodeList full of elements that
 // intersect the multi-select rubber-band-box on the current_layer only.
-// 
-// Since the only browser that supports the SVG DOM getIntersectionList is Opera, 
+//
+// Since the only browser that supports the SVG DOM getIntersectionList is Opera,
 // we need to provide an implementation here.  We brute-force it for now.
-// 
+//
 // Reference:
 // Firefox does not implement getIntersectionList(), see https://bugzilla.mozilla.org/show_bug.cgi?id=501421
 // Webkit does not implement getIntersectionList(), see https://bugs.webkit.org/show_bug.cgi?id=11274
@@ -601,12 +601,12 @@
   if (rubberBox == null) { return null; }
 
   var parent = current_group || getCurrentDrawing().getCurrentLayer();
-  
+
   if(!curBBoxes.length) {
     // Cache all bboxes
     curBBoxes = getVisibleElementsAndBBoxes(parent);
   }
-  
+
   var resultList = null;
   try {
     resultList = parent.getIntersectionList(rect, null);
@@ -614,16 +614,16 @@
 
   if (resultList == null || typeof(resultList.item) != "function") {
     resultList = [];
-    
+
     if(!rect) {
       var rubberBBox = rubberBox.getBBox();
       var bb = {};
-      
+
       for(var o in rubberBBox) {
         bb[o] = rubberBBox[o] / current_zoom;
       }
       rubberBBox = bb;
-      
+
     } else {
       var rubberBBox = rect;
     }
@@ -635,8 +635,8 @@
       }
     }
   }
-  // addToSelection expects an array, but it's ok to pass a NodeList 
-  // because using square-bracket notation is allowed: 
+  // addToSelection expects an array, but it's ok to pass a NodeList
+  // because using square-bracket notation is allowed:
   // http://www.w3.org/TR/DOM-Level-2-Core/ecma-script-binding.html
   return resultList;
 };
@@ -644,34 +644,34 @@
 // TODO(codedread): Migrate this into svgutils.js
 // Function: getStrokedBBox
 // Get the bounding box for one or more stroked and/or transformed elements
-// 
+//
 // Parameters:
 // elems - Array with DOM elements to check
-// 
+//
 // Returns:
 // A single bounding box object
 getStrokedBBox = this.getStrokedBBox = function(elems) {
   if(!elems) elems = getVisibleElements();
   if(!elems.length) return false;
-  
+
   // Make sure the expected BBox is returned if the element is a group
   var getCheckedBBox = function(elem) {
-  
+
     try {
       // TODO: Fix issue with rotated groups. Currently they work
       // fine in FF, but not in other browsers (same problem mentioned
       // in Issue 339 comment #2).
-      
+
       var bb = svgedit.utilities.getBBox(elem);
-      
+
       var angle = svgedit.utilities.getRotationAngle(elem);
       if ((angle && angle % 90) ||
           svgedit.math.hasMatrixTransform(svgedit.transformlist.getTransformList(elem))) {
         // Accurate way to get BBox of rotated element in Firefox:
         // Put element in group and get its BBox
-        
+
         var good_bb = false;
-        
+
         // Get the BBox from the raw path for these elements
         var elemNames = ['ellipse','path','line','polyline','polygon'];
         if(elemNames.indexOf(elem.tagName) >= 0) {
@@ -684,10 +684,10 @@
             bb = good_bb = canvas.convertToPath(elem, true);
           }
         }
-        
+
         if(!good_bb) {
           // Must use clone else FF freaks out
-          var clone = elem.cloneNode(true); 
+          var clone = elem.cloneNode(true);
           var g = document.createElementNS(svgns, "g");
           var parent = elem.parentNode;
           parent.appendChild(g);
@@ -695,17 +695,17 @@
           bb = svgedit.utilities.bboxToObj(g.getBBox());
           parent.removeChild(g);
         }
-        
+
 
         // Old method: Works by giving the rotated BBox,
         // this is (unfortunately) what Opera and Safari do
         // natively when getting the BBox of the parent group
 //            var angle = angle * Math.PI / 180.0;
-//            var rminx = Number.MAX_VALUE, rminy = Number.MAX_VALUE, 
+//            var rminx = Number.MAX_VALUE, rminy = Number.MAX_VALUE,
 //              rmaxx = Number.MIN_VALUE, rmaxy = Number.MIN_VALUE;
 //            var cx = round(bb.x + bb.width/2),
 //              cy = round(bb.y + bb.height/2);
-//            var pts = [ [bb.x - cx, bb.y - cy], 
+//            var pts = [ [bb.x - cx, bb.y - cy],
 //                  [bb.x + bb.width - cx, bb.y - cy],
 //                  [bb.x + bb.width - cx, bb.y + bb.height - cy],
 //                  [bb.x - cx, bb.y + bb.height - cy] ];
@@ -717,23 +717,23 @@
 //              var theta = Math.atan2(y,x) + angle;
 //              x = round(r * Math.cos(theta) + cx);
 //              y = round(r * Math.sin(theta) + cy);
-//    
+//
 //              // now set the bbox for the shape after it's been rotated
 //              if (x < rminx) rminx = x;
 //              if (y < rminy) rminy = y;
 //              if (x > rmaxx) rmaxx = x;
 //              if (y > rmaxy) rmaxy = y;
 //            }
-//            
+//
 //            bb.x = rminx;
 //            bb.y = rminy;
 //            bb.width = rmaxx - rminx;
 //            bb.height = rmaxy - rminy;
       }
       return bb;
-    } catch(e) { 
+    } catch(e) {
       console.log(elem, e);
-    } 
+    }
   };
 
   var full_bb;
@@ -742,18 +742,18 @@
     if(!this.parentNode) return;
     full_bb = getCheckedBBox(this);
   });
-  
+
   // This shouldn't ever happen...
   if(full_bb == null) return null;
-  
+
   // full_bb doesn't include the stoke, so this does no good!
 //    if(elems.length == 1) return full_bb;
-  
+
   var max_x = full_bb.x + full_bb.width;
   var max_y = full_bb.y + full_bb.height;
   var min_x = full_bb.x;
   var min_y = full_bb.y;
-  
+
   // FIXME: same re-creation problem with this function as getCheckedBBox() above
   var getOffset = function(elem) {
     var sw = elem.getAttribute("stroke-width");
@@ -773,10 +773,10 @@
       bboxes.push(cur_bb);
     }
   });
-  
+
   full_bb.x = min_x;
   full_bb.y = min_y;
-  
+
   $.each(elems, function(i, elem) {
     var cur_bb = bboxes[i];
     // ensure that elem is really an element node
@@ -786,7 +786,7 @@
       max_y = Math.max(max_y, cur_bb.y + cur_bb.height + offset);
     }
   });
-  
+
   full_bb.width = max_x - min_x;
   full_bb.height = max_y - min_y;
   return full_bb;
@@ -830,7 +830,7 @@
 // * bbox - The element's BBox as retrieved from getStrokedBBox
 var getVisibleElementsAndBBoxes = this.getVisibleElementsAndBBoxes = function(parent) {
   if(!parent) parent = $(svgcontent).children(); // Prevent layers from being included
-  
+
   var contentElems = [];
   $(parent).children().each(function(i, elem) {
     try {
@@ -870,7 +870,7 @@
       new_el.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.nodeValue);
     }
   });
-  
+
   // Opera's "d" value needs to be reset for Opera/Win/non-EN
   // Also needed for webkit (else does not keep curved segments on clone)
   if(svgedit.browser.isWebkit() && el.nodeName == 'path') {
@@ -891,7 +891,7 @@
         break;
     }
   });
-  
+
   if($(el).data('gsvg')) {
     $(new_el).data('gsvg', new_el.firstChild);
   } else if($(el).data('symbol')) {
@@ -915,7 +915,7 @@
 
   getId = c.getId = function() { return getCurrentDrawing().getId(); };
   getNextId = c.getNextId = function() { return getCurrentDrawing().getNextId(); };
-  
+
   // Function: call
   // Run the callback function associated with the given event
   //
@@ -927,14 +927,14 @@
       return events[event](this, arg);
     }
   };
-  
+
   // Function: bind
   // Attaches a callback function to an event
   //
   // Parameters:
   // event - String indicating the name of the event
   // f - The callback function to bind to the event
-  // 
+  //
   // Return:
   // The previous event
   c.bind = function(event, f) {
@@ -942,7 +942,7 @@
     events[event] = f;
     return old;
   };
-  
+
 }(canvas));
 
 // Function: canvas.prepareSvg
@@ -972,13 +972,13 @@
 }
 
 // Function: ffClone
-// Hack for Firefox bugs where text element features aren't updated or get 
+// Hack for Firefox bugs where text element features aren't updated or get
 // messed up. See issue 136 and issue 137.
-// This function clones the element and re-selects it 
-// TODO: Test for this bug on load and add it to "support" object instead of 
+// This function clones the element and re-selects it
+// TODO: Test for this bug on load and add it to "support" object instead of
 // browser sniffing
 //
-// Parameters: 
+// Parameters:
 // elem - The (text) DOM element to clone
 var ffClone = function(elem) {
   if(!svgedit.browser.isGecko()) return elem;
@@ -1016,7 +1016,7 @@
   var bbox = svgedit.utilities.getBBox(elem);
   var cx = bbox.x+bbox.width/2, cy = bbox.y+bbox.height/2;
   var tlist = getTransformList(elem);
-  
+
   // only remove the real rotational transform if present (i.e. at index=0)
   if (tlist.numberOfItems > 0) {
     var xform = tlist.getItem(0);
@@ -1038,7 +1038,7 @@
   else if (tlist.numberOfItems == 0) {
     elem.removeAttribute("transform");
   }
-  
+
   if (!preventUndo) {
     // we need to 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?
@@ -1057,7 +1057,7 @@
 };
 
 // Function: recalculateAllSelectedDimensions
-// Runs recalculateDimensions on the selected elements, 
+// Runs recalculateDimensions on the selected elements,
 // adding the changes to a single batch command
 var recalculateAllSelectedDimensions = this.recalculateAllSelectedDimensions = function() {
   var text = (current_resize_mode == "none" ? "position" : "size");
@@ -1080,9 +1080,9 @@
 };
 
 // this is how we map paths to our preferred relative segment types
-var pathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', 
+var pathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a',
           'H', 'h', 'V', 'v', 'S', 's', 'T', 't'];
-          
+
 // Debug tool to easily see the current matrix in the browser's console
 var logMatrix = function(m) {
   console.log([m.a,m.b,m.c,m.d,m.e,m.f]);
@@ -1106,7 +1106,7 @@
       assignAttributes(selected, changes, 1000, true);
     }
     box = svgedit.utilities.getBBox(selected);
-  
+
   for(var i = 0; i < 2; i++) {
     var type = i === 0 ? 'fill' : 'stroke';
     var attrVal = selected.getAttribute(type);
@@ -1114,15 +1114,15 @@
       if(m.a < 0 || m.d < 0) {
         var grad = getRefElem(attrVal);
         var newgrad = grad.cloneNode(true);
-  
+
         if(m.a < 0) {
           //flip x
           var x1 = newgrad.getAttribute('x1');
           var x2 = newgrad.getAttribute('x2');
           newgrad.setAttribute('x1', -(x1 - 1));
           newgrad.setAttribute('x2', -(x2 - 1));
-        } 
-        
+        }
+
         if(m.d < 0) {
           //flip y
           var y1 = newgrad.getAttribute('y1');
@@ -1134,7 +1134,7 @@
         findDefs().appendChild(newgrad);
         selected.setAttribute(type, 'url(#' + newgrad.id + ')');
       }
-      
+
       // Not really working :(
 //      if(selected.tagName === 'path') {
 //        reorientGrads(selected, m);
@@ -1146,8 +1146,8 @@
   var elName = selected.tagName;
   if(elName === "g" || elName === "text" || elName === "use") {
     // if it was a translate, then just update x,y
-    if (m.a == 1 && m.b == 0 && m.c == 0 && m.d == 1 && 
-      (m.e != 0 || m.f != 0) ) 
+    if (m.a == 1 && m.b == 0 && m.c == 0 && m.d == 1 &&
+      (m.e != 0 || m.f != 0) )
     {
       // [T][M] = [M][T']
       // therefore [T'] = [M_inv][T][M]
@@ -1165,7 +1165,7 @@
       chlist.appendItem(mt);
     }
   }
-  
+
   // now we have a set of changes and an applied reduced transform list
   // we apply the changes directly to the DOM
   switch (elName)
@@ -1173,7 +1173,7 @@
     case "foreignObject":
     case "rect":
     case "image":
-      
+
       // Allow images to be inverted (give them matrix when flipped)
       if(elName === 'image' && (m.a < 0 || m.d < 0)) {
         // Convert to matrix
@@ -1184,10 +1184,10 @@
         chlist.appendItem(mt);
       } else {
         var pt1 = remap(changes.x,changes.y);
-        
+
         changes.width = scalew(changes.width);
         changes.height = scaleh(changes.height);
-        
+
         changes.x = pt1.x + Math.min(0,changes.width);
         changes.y = pt1.y + Math.min(0,changes.height);
         changes.width = Math.abs(changes.width);
@@ -1201,7 +1201,7 @@
       changes.cy = c.y;
       changes.rx = scalew(changes.rx);
       changes.ry = scaleh(changes.ry);
-    
+
       changes.rx = Math.abs(changes.rx);
       changes.ry = Math.abs(changes.ry);
       finishUp();
@@ -1225,7 +1225,7 @@
       changes.y1 = pt1.y;
       changes.x2 = pt2.x;
       changes.y2 = pt2.y;
-      
+
     case "text":
       var tspan = selected.querySelectorAll('tspan');
       var i = tspan.length
@@ -1272,7 +1272,7 @@
       selected.setAttribute("points", pstr);
       break;
     case "path":
-    
+
       var segList = selected.pathSegList;
       var len = segList.numberOfItems;
       changes.d = new Array(len);
@@ -1293,7 +1293,7 @@
           sweepFlag: seg.sweepFlag
         };
       }
-      
+
       var len = changes.d.length,
         firstseg = changes.d[0],
         currentpt = remap(firstseg.x,firstseg.y);
@@ -1330,7 +1330,7 @@
           seg.r2 = scaleh(seg.r2);
         }
       } // for each segment
-    
+
       var dstr = "";
       var len = changes.d.length;
       for (var i = 0; i < len; ++i) {
@@ -1359,7 +1359,7 @@
             dstr += seg.x1 + "," + seg.y1 + " " + seg.x2 + "," + seg.y2 + " " +
                seg.x + "," + seg.y + " ";
             break;
-          case 9: // relative quad (q) 
+          case 9: // relative quad (q)
           case 8: // absolute quad (Q)
             dstr += seg.x1 + "," + seg.y1 + " " + seg.x + "," + seg.y + " ";
             break;
@@ -1389,14 +1389,14 @@
 // ty - The translation's y value
 var updateClipPath = function(attr, tx, ty) {
   var path = getRefElem(attr).firstChild;
-  
+
   var cp_xform = getTransformList(path);
-  
+
   var newxlate = svgroot.createSVGTransform();
   newxlate.setTranslate(tx, ty);
 
   cp_xform.appendItem(newxlate);
-  
+
   // Update clipPath's dimensions
   recalculateDimensions(path);
 }
@@ -1407,13 +1407,13 @@
 // Parameters:
 // selected - The DOM element to recalculate
 //
-// Returns: 
+// Returns:
 // Undo command object with the resulting change
 var recalculateDimensions = this.recalculateDimensions = function(selected) {
   if (selected == null) return null;
-  
+
   var tlist = getTransformList(selected);
-  
+
   // remove any unnecessary transforms
   if (tlist && tlist.numberOfItems > 0) {
     var k = tlist.numberOfItems;
@@ -1438,13 +1438,13 @@
     // End here if all it has is a rotation
     if(tlist.numberOfItems === 1 && getRotationAngle(selected)) return null;
   }
-  
+
   // if this element had no transforms, we are done
   if (!tlist || tlist.numberOfItems == 0) {
     selected.removeAttribute("transform");
     return null;
   }
-  
+
   // TODO: Make this work for more than 2
   if (tlist) {
     var k = tlist.numberOfItems;
@@ -1463,23 +1463,23 @@
       tlist.removeItem(mxs[1][1]);
       tlist.insertItemBefore(m_new, mxs[1][1]);
     }
-    
+
     // combine matrix + translate
     k = tlist.numberOfItems;
     if(k >= 2 && tlist.getItem(k-2).type === 1 && tlist.getItem(k-1).type === 2) {
       var mt = svgroot.createSVGTransform();
-      
+
       var m = matrixMultiply(
-        tlist.getItem(k-2).matrix, 
+        tlist.getItem(k-2).matrix,
         tlist.getItem(k-1).matrix
-      );    
+      );
       mt.setMatrix(m);
       tlist.removeItem(k-2);
       tlist.removeItem(k-2);
       tlist.appendItem(mt);
     }
   }
-  
+
   // If it still has a single [M] or [R][M], return null too (prevents BatchCommand from being returned).
   switch ( selected.tagName ) {
     // Ignore these elements, as they can absorb the [M]
@@ -1496,13 +1496,13 @@
         return null;
       }
   }
-  
-  // Grouped SVG element 
+
+  // Grouped SVG element
   var gsvg = $(selected).data('gsvg');
-  
-  // we know we have some transforms, so set up return variable   
+
+  // 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, attrs = [];
   switch (selected.tagName)
@@ -1544,7 +1544,7 @@
       changes["d"] = selected.getAttribute("d");
       break;
   } // switch on element type to get initial values
-  
+
   if(attrs.length) {
     changes = $(selected).attr(attrs);
     $.each(changes, function(attr, val) {
@@ -1557,8 +1557,8 @@
       y: $(gsvg).attr('y') || 0
     };
   }
-  
-  // if we haven't created an initial array in polygon/polyline/path, then 
+
+  // 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 = $.extend(true, {}, changes);
@@ -1568,7 +1568,7 @@
   }
   // save the start transform value too
   initial["transform"] = start_transform ? start_transform : "";
-  
+
   // if it's a regular group, we have special processing to flatten transforms
   if ((selected.tagName == "g" && !gsvg) || selected.tagName == "a") {
     var box = svgedit.utilities.getBBox(selected),
@@ -1576,8 +1576,8 @@
       newcenter = transformPoint(box.x+box.width/2, box.y+box.height/2,
               transformListToTransform(tlist).matrix),
       m = svgroot.createSVGMatrix();
-    
-    
+
+
     // temporarily strip off the rotate and save the old center
     var gangle = getRotationAngle(selected);
     if (gangle) {
@@ -1609,17 +1609,17 @@
     }
 
     // first, if it was a scale then the second-last transform will be it
-    if (N >= 3 && tlist.getItem(N-2).type == 3 && 
-      tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) 
+    if (N >= 3 && tlist.getItem(N-2).type == 3 &&
+      tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2)
     {
       operation = 3; // scale
-    
+
       // if the children are unrotated, pass the scale down directly
       // otherwise pass the equivalent matrix() down directly
       var tm = tlist.getItem(N-3).matrix,
         sm = tlist.getItem(N-2).matrix,
         tmn = tlist.getItem(N-1).matrix;
-    
+
       var children = selected.childNodes;
       var c = children.length;
       while (c--) {
@@ -1649,7 +1649,7 @@
 //              childTlist.appendItem(translateOrigin);
 //            }
 //          }
-        
+
           var angle = getRotationAngle(child);
           var old_start_transform = start_transform;
           var childxforms = [];
@@ -1664,10 +1664,10 @@
           // if not rotated or skewed, push the [T][S][-T] down to the child
           else {
             // update the transform list with translate,scale,translate
-            
+
             // slide the [T][S][-T] from the front to the back
             // [T][S][-T][M] = [M][T2][S2][-T2]
-            
+
             // (only bringing [-T] to the right of [M])
             // [T][S][-T][M] = [T][S][M][-T2]
             // [-T2] = [M_inv][-T][M]
@@ -1676,7 +1676,7 @@
             var t2 = svgroot.createSVGMatrix();
             t2.e = -t2n.e;
             t2.f = -t2n.f;
-            
+
             // [T][S][-T][M] = [M][T2][S2][-T2]
             // [S2] = [T2_inv][M_inv][T][S][-T][M][-T2_inv]
             var s2 = matrixMultiply(t2.inverse(), m.inverse(), tm, sm, tmn, m, t2n.inverse());
@@ -1697,8 +1697,8 @@
 //            logMatrix(scale.matrix);
           } // not rotated
           batchCmd.addSubCommand( recalculateDimensions(child) );
-          // TODO: If any <use> have this group as a parent and are 
-          // referencing this child, then we need to impose a reverse 
+          // TODO: If any <use> have this group as a parent and are
+          // referencing this child, then we need to impose a reverse
           // scale on it so that when it won't get double-translated
 //            var uses = selected.getElementsByTagNameNS(svgns, "use");
 //            var href = "#"+child.id;
@@ -1728,19 +1728,19 @@
       e2t.setMatrix(m);
       tlist.clear();
       tlist.appendItem(e2t);
-    }     
-    // next, check if the first transform was a translate 
+    }
+    // next, check if the first transform was a translate
     // if we had [ T1 ] [ M ] we want to transform this into [ M ] [ T2 ]
     // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ]
-    else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && 
-      tlist.getItem(0).type == 2) 
+    else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) &&
+      tlist.getItem(0).type == 2)
     {
       operation = 2; // translate
       var T_M = transformListToTransform(tlist).matrix;
       tlist.removeItem(0);
       var M_inv = transformListToTransform(tlist).matrix.inverse();
       var M2 = matrixMultiply( M_inv, T_M );
-      
+
       tx = M2.e;
       ty = M2.f;
 
@@ -1748,13 +1748,13 @@
         // we pass the translates down to the individual children
         var children = selected.childNodes;
         var c = children.length;
-        
+
         var clipPaths_done = [];
-        
+
         while (c--) {
           var child = children.item(c);
           if (child.nodeType == 1) {
-          
+
             // Check if child has clip-path
             if(child.getAttribute('clip-path')) {
               // tx, ty
@@ -1762,12 +1762,12 @@
               if(clipPaths_done.indexOf(attr) === -1) {
                 updateClipPath(attr, tx, ty);
                 clipPaths_done.push(attr);
-              }             
+              }
             }
 
             var old_start_transform = start_transform;
             start_transform = child.getAttribute("transform");
-            
+
             var childTlist = getTransformList(child);
             // some children might not have a transform (<metadata>, <defs>, etc)
             if (childTlist) {
@@ -1779,7 +1779,7 @@
                 childTlist.appendItem(newxlate);
               }
               batchCmd.addSubCommand( recalculateDimensions(child) );
-              // If any <use> have this group as a parent and are 
+              // If any <use> have this group as a parent and are
               // referencing this child, then impose a reverse translate on it
               // so that when it won't get double-translated
               var uses = selected.getElementsByTagNameNS(svgns, "use");
@@ -1798,9 +1798,9 @@
             }
           }
         }
-        
+
         clipPaths_done = [];
-        
+
         start_transform = old_start_transform;
       }
     }
@@ -1817,18 +1817,18 @@
           var old_start_transform = start_transform;
           start_transform = child.getAttribute("transform");
           var childTlist = getTransformList(child);
-          
+
           if (!childTlist) continue;
-          
+
           var em = matrixMultiply(m, transformListToTransform(childTlist).matrix);
           var e2m = svgroot.createSVGTransform();
           e2m.setMatrix(em);
           childTlist.clear();
           childTlist.appendItem(e2m,0);
-          
+
           batchCmd.addSubCommand( recalculateDimensions(child) );
           start_transform = old_start_transform;
-          
+
           // Convert stroke
           // TODO: Find out if this should actually happen somewhere else
           var sw = child.getAttribute("stroke-width");
@@ -1855,9 +1855,9 @@
       if (tlist.numberOfItems == 0) {
         selected.removeAttribute("transform");
       }
-      return null;      
+      return null;
     }
-    
+
     // if it was a translate, put back the rotate at the new center
     if (operation == 2) {
       if (gangle) {
@@ -1865,7 +1865,7 @@
           x: oldcenter.x + first_m.e,
           y: oldcenter.y + first_m.f
         };
-      
+
         var newRot = svgroot.createSVGTransform();
         newRot.setRotate(gangle,newcenter.x,newcenter.y);
         if(tlist.numberOfItems) {
@@ -1914,7 +1914,7 @@
           }
         }
       }
-      
+
       if (gangle) {
         if(tlist.numberOfItems) {
           tlist.insertItemBefore(rnew, 0);
@@ -1934,9 +1934,9 @@
     // but we still may need to recalculate them (see issue 595).
     // TODO: Figure out how to get BBox from these elements in case they
     // have a rotation transform
-    
+
     if(!box && selected.tagName != 'path') return null;
-    
+
 
     var m = svgroot.createSVGMatrix(),
       // temporarily strip off the rotate and save the old center
@@ -1945,7 +1945,7 @@
       var oldcenter = {x: box.x+box.width/2, y: box.y+box.height/2},
       newcenter = transformPoint(box.x+box.width/2, box.y+box.height/2,
               transformListToTransform(tlist).matrix);
-    
+
       var a = angle * Math.PI / 180;
       if ( Math.abs(a) > (1.0e-10) ) {
         var s = Math.sin(a)/(1 - Math.cos(a));
@@ -1965,11 +1965,11 @@
         }
       }
     }
-    
+
     // 2 = translate, 3 = scale, 4 = rotate, 1 = matrix imposition
     var operation = 0;
     var N = tlist.numberOfItems;
-    
+
     // Check if it has a gradient with userSpaceOnUse, in which case
     // adjust it by recalculating the matrix transform.
     // TODO: Make this work in Webkit using svgedit.transformlist.SVGTransformList
@@ -1992,14 +1992,14 @@
       }
     }
 
-    // first, if it was a scale of a non-skewed element, then the second-last  
+    // first, if it was a scale of a non-skewed element, then the second-last
     // transform will be the [S]
     // if we had [M][T][S][T] we want to extract the matrix equivalent of
     // [T][S][T] and push it down to the element
-    if (N >= 3 && tlist.getItem(N-2).type == 3 && 
-      tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) 
-      
-      // Removed this so a <use> with a given [T][S][T] would convert to a matrix. 
+    if (N >= 3 && tlist.getItem(N-2).type == 3 &&
+      tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2)
+
+      // Removed this so a <use> with a given [T][S][T] would convert to a matrix.
       // Is that bad?
       //  && selected.nodeName != "use"
     {
@@ -2019,12 +2019,12 @@
       tlist.appendItem(e2t);
       // reset the matrix so that the element is not re-mapped
       m = svgroot.createSVGMatrix();
-    } // if we had [R][T][S][-T][M], then this was a rotated matrix-element  
+    } // if we had [R][T][S][-T][M], then this was a rotated matrix-element
     // if we had [T1][M] we want to transform this into [M][T2]
-    // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2] 
+    // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2]
     // down to the element
-    else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && 
-      tlist.getItem(0).type == 2) 
+    else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) &&
+      tlist.getItem(0).type == 2)
     {
       operation = 2; // translate
       var oldxlate = tlist.getItem(0).matrix,
@@ -2069,7 +2069,7 @@
       if (angle) {
         var newRot = svgroot.createSVGTransform();
         newRot.setRotate(angle,newcenter.x,newcenter.y);
-        
+
         if(tlist.numberOfItems) {
           tlist.insertItemBefore(newRot, 0);
         } else {
@@ -2081,12 +2081,12 @@
       }
       return null;
     }
-    
+
     // if it was a translate or resize, we need to remap the element and absorb the xform
     if (operation == 1 || operation == 2 || operation == 3) {
       remapElement(selected,changes,m);
     } // if we are remapping
-    
+
     // if it was a translate, put back the rotate at the new center
     if (operation == 2) {
       if (angle) {
@@ -2119,7 +2119,7 @@
       var rnew_inv = rnew.matrix.inverse();
       var m_inv = m.inverse();
       var extrat = matrixMultiply(m_inv, rnew_inv, rold, m);
-    
+
       remapElement(selected,changes,extrat);
       if (angle) {
         if(tlist.numberOfItems) {
@@ -2135,9 +2135,9 @@
   if (tlist.numberOfItems == 0) {
     selected.removeAttribute("transform");
   }
-  
+
   batchCmd.addSubCommand(new ChangeElementCommand(selected, initial));
-  
+
   return batchCmd;
 };
 
@@ -2148,7 +2148,7 @@
 
 // Function: clearSelection
 // Clears the selection.  The 'selected' handler is then called.
-// Parameters: 
+// Parameters:
 // noCall - Optional boolean that when true does not call the "selected" handler
 var clearSelection = this.clearSelection = function(noCall) {
   if (selectedElements[0] != null) {
@@ -2177,9 +2177,9 @@
   if (elemsToAdd.length == 0) { return; }
   // find the first null in our selectedElements array
   var j = 0;
-  
+
   while (j < selectedElements.length) {
-    if (selectedElements[j] == null) { 
+    if (selectedElements[j] == null) {
       break;
     }
     ++j;
@@ -2192,7 +2192,7 @@
     if (!elem || !svgedit.utilities.getBBox(elem)) continue;
 
     if(elem.tagName === 'a' && elem.childNodes.length === 1) {
-      // Make "a" element's child be the selected element 
+      // Make "a" element's child be the selected element
       elem = elem.firstChild;
     }
 
@@ -2205,7 +2205,7 @@
 //      if (j == 0) selectedBBoxes[0] = svgedit.utilities.getBBox(elem);
       j++;
       var sel = selectorManager.requestSelector(elem);
-  
+
       if (selectedElements.length > 1) {
         sel.showGrips(false);
       }
@@ -2220,12 +2220,12 @@
 
   selectedElements.sort(function(a,b) {
     if(a && b && a.compareDocumentPosition) {
-      return 3 - (b.compareDocumentPosition(a) & 6);  
+      return 3 - (b.compareDocumentPosition(a) & 6);
     } else if(a == null) {
       return 1;
     }
   });
-  
+
   // Make sure first elements are not null
   while(selectedElements[0] == null) selectedElements.shift(0);
 };
@@ -2285,10 +2285,10 @@
 
 // Function: getMouseTarget
 // Gets the desired element from a mouse event
-// 
+//
 // Parameters:
 // evt - Event object from the mouse event
-// 
+//
 // Returns:
 // DOM element we want
 var getMouseTarget = this.getMouseTarget = function(evt) {
@@ -2296,33 +2296,33 @@
     return null;
   }
   var mouse_target = evt.target;
-  
+
   // if it was a <use>, Opera and WebKit return the SVGElementInstance
   if (mouse_target.correspondingUseElement) mouse_target = mouse_target.correspondingUseElement;
-  
+
   // for foreign content, go up until we find the foreignObject
-  // WebKit browsers set the mouse target to the svgcanvas div 
-  if ([mathns, htmlns].indexOf(mouse_target.namespaceURI) >= 0 && 
-    mouse_target.id != "svgcanvas") 
+  // WebKit browsers set the mouse target to the svgcanvas div
+  if ([mathns, htmlns].indexOf(mouse_target.namespaceURI) >= 0 &&
+    mouse_target.id != "svgcanvas")
   {
     while (mouse_target.nodeName != "foreignObject") {
       mouse_target = mouse_target.parentNode;
       if(!mouse_target) return svgroot;
     }
   }
-  
+
   // Get the desired mouse_target with jQuery selector-fu
   // If it's root-like, select the root
   var current_layer = getCurrentDrawing().getCurrentLayer();
   if([svgroot, container, svgcontent, current_layer].indexOf(mouse_target) >= 0) {
     return svgroot;
   }
-  
+
   var $target = $(mouse_target);
 
   // If it's a selection grip, return the grip parent
   if($target.closest('#selectorParentGroup').length) {
-    // While we could instead have just returned mouse_target, 
+    // While we could instead have just returned mouse_target,
     // this makes it easier to indentify as being a selector grip
     return selectorManager.selectorParentGroup;
   }
@@ -2330,8 +2330,8 @@
   while (mouse_target.parentNode && mouse_target.parentNode !== (current_group || current_layer)) {
     mouse_target = mouse_target.parentNode;
   }
-  
-//  
+
+//
 //  // go up until we hit a child of a layer
 //  while (mouse_target.parentNode.parentNode.tagName == 'g') {
 //    mouse_target = mouse_target.parentNode;
@@ -2341,7 +2341,7 @@
 //  if (mouse_target.nodeName.toLowerCase() == "div") {
 //    mouse_target = svgroot;
 //  }
-  
+
   return mouse_target;
 };
 
@@ -2359,7 +2359,7 @@
       maxx: null,
       maxy: null
     };
-  
+
   // - when we are in a create mode, the element is added to the canvas
   //   but the action is not recorded until mousing up
   // - when we are in select mode, select the element, remember the position
@@ -2374,7 +2374,7 @@
     var pt = transformPoint( evt.pageX, evt.pageY, root_sctm ),
       mouse_x = pt.x * current_zoom,
       mouse_y = pt.y * current_zoom;
-      
+
 
     evt.preventDefault();
 
@@ -2382,15 +2382,15 @@
       current_mode = "select";
       lastClickPoint = pt;
     }
-    
+
     var x = mouse_x / current_zoom,
       y = mouse_y / current_zoom,
       mouse_target = getMouseTarget(evt);
-    
+
     if(mouse_target.tagName === 'a' && mouse_target.childNodes.length === 1) {
       mouse_target = mouse_target.firstChild;
     }
-    
+
     // real_x/y ignores grid-snap value
     var real_x = r_start_x = start_x = x;
     var real_y = r_start_y = start_y = y;
@@ -2402,9 +2402,9 @@
       start_y = snapToGrid(start_y);
     }
 
-    // if it is a selector grip, then it must be a single element selected, 
+    // if it is a selector grip, then it must be a single element selected,
     // set the mouse_target to that and update the mode to rotate/resize
-    
+
     if (mouse_target == selectorManager.selectorParentGroup && selectedElements[0] != null) {
       var grip = evt.target;
       var griptype = elData(grip, "type");
@@ -2420,7 +2420,7 @@
       }
       mouse_target = selectedElements[0];
     }
-    
+
     start_transform = mouse_target.getAttribute("transform");
     var tlist = getTransformList(mouse_target);
     switch (current_mode) {
@@ -2428,11 +2428,11 @@
         started = true;
         current_resize_mode = "none";
         if(right_click) started = false;
-        
+
         if (mouse_target != svgroot) {
           // if this element is not yet selected, clear selection and select it
           if (selectedElements.indexOf(mouse_target) == -1) {
-            // only clear selection if shift is not pressed (otherwise, add 
+            // only clear selection if shift is not pressed (otherwise, add
             // element to selection)
             if (!evt.shiftKey) {
               // No need to do the call here as it will be done on addToSelection
@@ -2443,7 +2443,7 @@
             pathActions.clear();
           }
           // else if it's a path, go into pathedit mode in mouseup
-          
+
           if(!right_click) {
             // insert a dummy transform so if the element(s) are moved it will have
             // a transform to use for its translate
@@ -2466,11 +2466,11 @@
           }
           r_start_x *= current_zoom;
           r_start_y *= current_zoom;
-//          console.log('p',[evt.pageX, evt.pageY]);          
-//          console.log('c',[evt.clientX, evt.clientY]);  
-//          console.log('o',[evt.offsetX, evt.offsetY]);  
+//          console.log('p',[evt.pageX, evt.pageY]);
+//          console.log('c',[evt.clientX, evt.clientY]);
+//          console.log('o',[evt.offsetX, evt.offsetY]);
 //          console.log('s',[start_x, start_y]);
-          
+
           assignAttributes(rubberBox, {
             'x': r_start_x,
             'y': r_start_y,
@@ -2480,7 +2480,7 @@
           }, 100);
         }
         break;
-      case "zoom": 
+      case "zoom":
         started = true;
         if (rubberBox == null) {
           rubberBox = selectorManager.getRubberBandBox();
@@ -2497,7 +2497,7 @@
         started = true;
         start_x = x;
         start_y = y;
-        
+
         // Getting the BBox from the selection box, since we know we
         // want to orient around it
         init_bbox = svgedit.utilities.getBBox($('#selectedBox0')[0]);
@@ -2509,7 +2509,7 @@
         // append three dummy transforms to the tlist so that
         // we can translate,scale,translate in mousemove
         var pos = getRotationAngle(mouse_target)?1:0;
-        
+
         if(hasMatrixTransform(tlist)) {
           tlist.insertItemBefore(svgroot.createSVGTransform(), pos);
           tlist.insertItemBefore(svgroot.createSVGTransform(), pos);
@@ -2518,7 +2518,7 @@
           tlist.appendItem(svgroot.createSVGTransform());
           tlist.appendItem(svgroot.createSVGTransform());
           tlist.appendItem(svgroot.createSVGTransform());
-          
+
           if(svgedit.browser.supportsNonScalingStroke()) {
             //Handle crash for newer Webkit: https://code.google.com/p/svg-edit/issues/detail?id=904
             //Chromium issue: https://code.google.com/p/chromium/issues/detail?id=114625
@@ -2701,27 +2701,27 @@
         // This could occur in an extension
         break;
     }
-    
+
     var ext_result = runExtensions("mouseDown", {
       event: evt,
       start_x: start_x,
       start_y: start_y,
       selectedElements: selectedElements
     }, true);
-    
+
     $.each(ext_result, function(i, r) {
       if(r && r.started) {
         started = true;
       }
     });
     if (current_mode) {
-      document.getElementById("workarea").className = 
+      document.getElementById("workarea").className =
         (current_mode == "resize")
         ? evt.target.style.cursor
         : current_mode
       }
   };
-  
+
   // in this function we do not record any state changes yet (but we do update
   // any elements that are still being created, moved or resized on the canvas)
   var mouseMove = function(evt) {
@@ -2743,23 +2743,23 @@
     }
 
     evt.preventDefault();
-    
+
     switch (current_mode)
     {
       case "select":
         // we temporarily use a translate on the element(s) being dragged
-        // this transform is removed upon mousing up and the element is 
+        // this transform is removed upon mousing up and the element is
         // relocated to the new location
         if (selectedElements[0] !== null) {
           var dx = x - start_x;
           var dy = y - start_y;
-          
+
           if(curConfig.gridSnapping){
             dx = snapToGrid(dx);
             dy = snapToGrid(dy);
           }
-          
-          if(evt.shiftKey) { 
+
+          if(evt.shiftKey) {
             var xya = snapToAngle(start_x,start_y,x,y); x=xya.x; y=xya.y;
          }
           if (dx != 0 || dy != 0) {
@@ -2790,13 +2790,13 @@
               } else {
                 tlist.appendItem(xform);
               }
-              
+
               // update our internal bbox that we're tracking while dragging
               selectorManager.requestSelector(selected).resize();
             }
 
             //duplicate only once
-            // alt drag = create a clone and save the reference             
+            // alt drag = create a clone and save the reference
             if(evt.altKey) {
               //clone doesn't exist yet
               if (!canvas.addClones) {
@@ -2812,14 +2812,14 @@
                 window.addEventListener("keyup", canvas.removeClones)
               }
             }
-      
+
             call("transition", selectedElements);
           }
-          
 
 
-          
-          
+
+
+
         }
         break;
       case "multiselect":
@@ -2839,7 +2839,7 @@
         var elemsToRemove = [], elemsToAdd = [],
           newList = getIntersectionList(),
           len = selectedElements.length;
-        
+
         for (var i = 0; i < len; ++i) {
           var ind = newList.indexOf(selectedElements[i]);
           if (ind == -1) {
@@ -2849,16 +2849,16 @@
             newList[ind] = null;
           }
         }
-        
+
         len = newList.length;
         for (i = 0; i < len; ++i) { if (newList[i]) elemsToAdd.push(newList[i]); }
-        
-        if (elemsToRemove.length > 0) 
+
+        if (elemsToRemove.length > 0)
           canvas.removeFromSelection(elemsToRemove);
-        
-        if (elemsToAdd.length > 0) 
+
+        if (elemsToAdd.length > 0)
           addToSelection(elemsToAdd);
-          
+
         break;
       case "resize":
         // we track the resize bounding box and translate/scale the selected element
@@ -2866,10 +2866,10 @@
         // the shape's coordinates
         var tlist = getTransformList(selected),
           hasMatrix = hasMatrixTransform(tlist),
-          box = hasMatrix ? init_bbox : svgedit.utilities.getBBox(selected), 
+          box = hasMatrix ? init_bbox : svgedit.utilities.getBBox(selected),
           left=box.x, top=box.y, width=box.width,
           height=box.height, dx=(x-start_x), dy=(y-start_y);
-        
+
         if(curConfig.gridSnapping){
           dx = snapToGrid(dx);
           dy = snapToGrid(dy);
@@ -2893,24 +2893,24 @@
         }
         if(current_resize_mode.indexOf("e")==-1 && current_resize_mode.indexOf("w")==-1) {
           dx = 0;
-        }       
-        
+        }
+
         var ts = null,
           tx = 0, ty = 0,
-          sy = height ? (height+dy)/height : 1, 
+          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") >= 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") >= 0) {
           sx = width ? (width-dx)/width : 1;
           tx = width;
         }
-        
+
         // update the transform list with translate,scale,translate
         var translateOrigin = svgroot.createSVGTransform(),
           scale = svgroot.createSVGTransform(),
@@ -2929,7 +2929,7 @@
           else sy = sx;
         }
         scale.setScale(sx,sy);
-        
+
         translateBack.setTranslate(left+tx,top+ty);
         if(hasMatrix) {
           var diff = angle?1:0;
@@ -2944,9 +2944,9 @@
         }
 
         selectorManager.requestSelector(selected).resize();
-        
+
         call("transition", selectedElements);
-        
+
         break;
       case "zoom":
         real_x *= current_zoom;
@@ -2956,7 +2956,7 @@
           'y': Math.min(r_start_y*current_zoom, real_y),
           'width': Math.abs(real_x - r_start_x*current_zoom),
           'height': Math.abs(real_y - r_start_y*current_zoom)
-        },100);     
+        },100);
         break;
       case "text":
         assignAttributes(shape,{
@@ -2971,10 +2971,10 @@
         }
 
         var x2 = x;
-        var y2 = y;         
+        var y2 = y;
 
         if(evt.shiftKey) { var xya = snapToAngle(start_x,start_y,x2,y2); x2=xya.x; y2=xya.y; }
-        
+
         shape.setAttributeNS(null, "x2", x2);
         shape.setAttributeNS(null, "y2", y2);
         break;
@@ -2998,11 +2998,11 @@
         }
         if (evt.altKey){
           w *=2;
-          h *=2; 
+          h *=2;
           new_x = start_x - w/2;
           new_y = start_y - h/2;
         }
-  
+
         if(curConfig.gridSnapping){
           w = snapToGrid(w);
           h = snapToGrid(h);
@@ -3016,7 +3016,7 @@
           'x': new_x,
           'y': new_y
         },1000);
-        
+
         break;
       case "circle":
         var c = $(shape).attr(["cx", "cy"]);
@@ -3042,7 +3042,7 @@
         if (evt.shiftKey) {
           ry = rx
           cy = (y > start_y) ? start_y + rx : start_y - rx
-          
+
         }
         if (evt.altKey) {
           cx = start_x
@@ -3072,7 +3072,7 @@
       case "pathedit":
         x *= current_zoom;
         y *= current_zoom;
-        
+
         if(curConfig.gridSnapping){
           x = snapToGrid(x);
           y = snapToGrid(y);
@@ -3091,7 +3091,7 @@
           var xya = snapToAngle(x1,y1,x,y);
           x=xya.x; y=xya.y;
         }
-        
+
         if(rubberBox && rubberBox.getAttribute('display') !== 'none') {
           real_x *= current_zoom;
           real_y *= current_zoom;
@@ -3100,10 +3100,10 @@
             'y': Math.min(r_start_y*current_zoom, real_y),
             'width': Math.abs(real_x - r_start_x*current_zoom),
             'height': Math.abs(real_y - r_start_y*current_zoom)
-          },100); 
+          },100);
         }
         pathActions.mouseMove(evt, x, y);
-        
+
         break;
       case "textedit":
         x *= current_zoom;
@@ -3116,13 +3116,13 @@
 //              'height': Math.abs(y-start_y)
 //            },100);
 //          }
-        
+
         textActions.mouseMove(mouse_x, mouse_y);
-        
+
         break;
       case "rotate":
         var box = svgedit.utilities.getBBox(selected),
-          cx = box.x + box.width/2, 
+          cx = box.x + box.width/2,
           cy = box.y + box.height/2,
           m = getMatrix(selected),
           center = transformPoint(cx,cy,m);
@@ -3150,7 +3150,7 @@
       default:
         break;
     }
-    
+
     runExtensions("mouseMove", {
       event: evt,
       mouse_x: mouse_x,
@@ -3159,16 +3159,16 @@
     });
 
   }; // mouseMove()
-  
-  
+
+
   /* mouseover mode
   var mouseOver = function(evt) {
-    
+
     if(canvas.spaceKey || evt.button === 1 || current_mode != "select") return;
     evt.stopPropagation();
     mouse_target = getMouseTarget(evt);
     if (svghover.lastChild) svghover.removeChild(svghover.lastChild);
-    
+
     if (mouse_target.id == "svgroot") return
     switch (mouse_target.nodeName) {
       case "polyline":
@@ -3176,7 +3176,7 @@
       case "path":
       case "ellipse":
       case "rect":
-          var clone = mouse_target.cloneNode(true); 
+          var clone = mouse_target.cloneNode(true);
           clone.setAttribute("stroke", "#c00")
           clone.setAttribute("stroke-width", "1")
           clone.setAttribute("stroke-opacity", "1")
@@ -3184,13 +3184,13 @@
           clone.setAttribute("fill", "none")
           hover_group.appendChild(clone);
       break;
-        
+
       default:
       break;
     }
   }
   */
-  
+
   // - in create mode, the element's opacity is set properly, we create an InsertElementCommand
   //   and store it on the Undo stack
   // - in move/resize mode, the element's attributes which were affected by the move/resize are
@@ -3256,7 +3256,7 @@
               cur_text.font_family = selected.getAttribute("font-family");
             }
             selectorManager.requestSelector(selected).showGrips(true);
-            
+
             // This shouldn't be necessary as it was done on mouseDown...
 //              call("selected", [selected]);
           }
@@ -3264,8 +3264,8 @@
           recalculateAllSelectedDimensions();
 
           // if it was being dragged/resized
-          r_start_x = r_start_x; 
-          r_start_y = r_start_y; 
+          r_start_x = r_start_x;
+          r_start_y = r_start_y;
           var difference_x = Math.abs(real_x-r_start_x);
           var difference_y = Math.abs(real_y-r_start_y);
 
@@ -3292,7 +3292,7 @@
               }
             }
           } // no change in mouse position
-          
+
           // Remove non-scaling stroke
           if(svgedit.browser.supportsNonScalingStroke()) {
             var elem = selectedElements[0];
@@ -3401,7 +3401,7 @@
         element = null;
         // continue to be set to true so that mouseMove happens
         started = true;
-        
+
         var res = pathActions.mouseUp(evt, element, mouse_x, mouse_y);
         element = res.element;
         keep = res.keep;
@@ -3421,7 +3421,7 @@
         element = null;
         current_mode = "select";
         var batchCmd = canvas.undoMgr.finishUndoableChange();
-        if (!batchCmd.isEmpty()) { 
+        if (!batchCmd.isEmpty()) {
           addCommandToHistory(batchCmd);
         }
         // perform recalculation to weed out any stray identity transforms that might get stuck
@@ -3432,13 +3432,13 @@
         // This could occur in an extension
         break;
     }
-    
+
     var ext_result = runExtensions("mouseUp", {
       event: evt,
       mouse_x: mouse_x,
       mouse_y: mouse_y
     }, true);
-    
+
     $.each(ext_result, function(i, r) {
       if(r) {
         keep = r.keep || keep;
@@ -3446,37 +3446,37 @@
         started = r.started || started;
       }
     });
-    
+
     if (!keep && element != null) {
       getCurrentDrawing().releaseId(getId());
       element.parentNode.removeChild(element);
       element = null;
-      
+
       var t = evt.target;
-      
-      // if this element is in a group, go up until we reach the top-level group 
+
+      // 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 <a> elements
       while (t.parentNode.parentNode.tagName == "g") {
         t = t.parentNode;
       }
-      // if we are not in the middle of creating a path, and we've clicked on some shape, 
+      // if we are not in the middle of creating a path, and we've clicked on some shape,
       // then go to Select mode.
       // WebKit returns <div> when the canvas is clicked, Firefox/Opera return <svg>
       if ( (current_mode != "path" || !drawn_path) &&
         t.parentNode.id != "selectorParentGroup" &&
-        t.id != "svgcanvas" && t.id != "svgroot") 
+        t.id != "svgcanvas" && t.id != "svgroot")
       {
         // switch into "select" mode if we've clicked on an element
         canvas.setMode("select");
         selectOnly([t], true);
       }
-      
+
     } else if (element != null) {
       canvas.addedNew = true;
-      
+
       if(useUnit) svgedit.units.convertAttrs(element);
-      
+
       var ani_dur = .2, c_ani;
       if(opac_ani.beginElement && element.getAttribute('opacity') != cur_shape.opacity) {
         c_ani = $(opac_ani).clone().attr({
@@ -3490,7 +3490,7 @@
       } else {
         ani_dur = 0;
       }
-      
+
       // Ideally this would be done on the endEvent of the animation,
       // but that doesn't seem to be supported in Webkit
       setTimeout(function() {
@@ -3508,14 +3508,14 @@
         // we create the insert command that is stored on the stack
         // undo means to call cmd.unapply(), redo means to call cmd.apply()
         addCommandToHistory(new InsertElementCommand(element));
-        
+
         call("changed",[element]);
       }, ani_dur * 1000);
     }
-    
+
     start_transform = null;
   };
-  
+
   var dblClick = function(evt) {
     var evt_target = evt.target;
     var parent = evt_target.parentNode;
@@ -3523,16 +3523,16 @@
     var tagName = mouse_target.tagName;
 
     if(parent === current_group) return;
-    
+
     if(tagName === 'text' && current_mode !== 'textedit') {
       var pt = transformPoint( evt.pageX, evt.pageY, root_sctm );
       textActions.select(mouse_target, pt.x, pt.y);
     }
-    
+
     if((tagName === "g" || tagName === "a") && getRotationAngle(mouse_target)) {
-      // TODO: Allow method of in-group editing without having to do 
+      // TODO: Allow method of in-group editing without having to do
       // this (similar to editing rotated paths)
-    
+
       // Ungroup and regroup
       pushGroupProperties(mouse_target);
       mouse_target = selectedElements[0];
@@ -3542,7 +3542,7 @@
     if(current_group) {
       leaveContext();
     }
-    
+
     if((parent.tagName !== 'g' && parent.tagName !== 'a') ||
       parent === getCurrentDrawing().getCurrentLayer() ||
       mouse_target === selectorManager.selectorParentGroup)
@@ -3558,12 +3558,12 @@
     e.preventDefault();
     return false;
   };
-  
+
   // Added mouseup to the container here.
   // TODO(codedread): Figure out why after the Closure compiler, the window mouseup is ignored.
   $(container).mousedown(mouseDown).mousemove(mouseMove).click(handleLinkInCanvas).dblclick(dblClick).mouseup(mouseUp);
 //  $(window).mouseup(mouseUp);
-  
+
   $(container).bind("mousewheel DOMMouseScroll", function(e){
     if(!e.shiftKey) return;
     e.preventDefault();
@@ -3589,14 +3589,14 @@
       if (e.detail > 0) {
         bbox.factor = .5;
       } else if (e.detail < 0) {
-        bbox.factor = 2;      
-      }       
+        bbox.factor = 2;
+      }
     }
-    
+
     if(!bbox.factor) return;
     call("zoomed", bbox);
   });
-  
+
 }());
 
 // Function: preventClickDefault
@@ -3621,11 +3621,11 @@
   var matrix;
   var last_x, last_y;
   var allow_dbl;
-  
+
   function setCursor(index) {
     var empty = (textinput.value === "");
     $(textinput).focus();
-  
+
     if(!arguments.length) {
       if(empty) {
         index = 0;
@@ -3634,7 +3634,7 @@
         index = textinput.selectionEnd;
       }
     }
-    
+
     var charbb;
     charbb = chardata[index];
     if(!empty) {
@@ -3650,7 +3650,7 @@
       });
       cursor = getElem("selectorParentGroup").appendChild(cursor);
     }
-    
+
     if(!blinker) {
       blinker = setInterval(function() {
         var show = (cursor.getAttribute('display') === 'none');
@@ -3658,11 +3658,11 @@
       }, 600);
 
     }
-    
-    
+
+
     var start_pt = ptToScreen(charbb.x, textbb.y);
     var end_pt = ptToScreen(charbb.x, (textbb.y + textbb.height));
-    
+
     assignAttributes(cursor, {
       x1: start_pt.x,
       y1: start_pt.y,
@@ -3671,20 +3671,20 @@
       visibility: 'visible',
       display: 'inline'
     });
-    
+
     if(selblock) selblock.setAttribute('d', 'M 0 0');
   }
-  
+
   function setSelection(start, end, skipInput) {
     if(start === end) {
       setCursor(end);
       return;
     }
-  
+
     if(!skipInput) {
       textinput.setSelectionRange(start, end);
     }
-    
+
     selblock = getElem("text_selectblock");
     if (!selblock) {
 
@@ -3698,30 +3698,30 @@
       getElem("selectorParentGroup").appendChild(selblock);
     }
 
-    
+
     var startbb = chardata[start];
-    
+
     var endbb = chardata[end];
-    
+
     cursor.setAttribute('visibility', 'hidden');
-    
+
     var tl = ptToScreen(startbb.x, textbb.y),
       tr = ptToScreen(startbb.x + (endbb.x - startbb.x), textbb.y),
       bl = ptToScreen(startbb.x, textbb.y + textbb.height),
       br = ptToScreen(startbb.x + (endbb.x - startbb.x), textbb.y + textbb.height);
-    
-    
+
+
     var dstr = "M" + tl.x + "," + tl.y
           + " L" + tr.x + "," + tr.y
           + " " + br.x + "," + br.y
           + " " + bl.x + "," + bl.y + "z";
-    
+
     assignAttributes(selblock, {
       d: dstr,
       'display': 'inline'
     });
   }
-  
+
   function getIndexFromPoint(mouse_x, mouse_y) {
     // Position cursor here
     var pt = svgroot.createSVGPoint();
@@ -3748,62 +3748,62 @@
     }
     return charpos;
   }
-  
+
   function setCursorFromPoint(mouse_x, mouse_y) {
     setCursor(getIndexFromPoint(mouse_x, mouse_y));
   }
-  
+
   function setEndSelectionFromPoint(x, y, apply) {
     var i1 = textinput.selectionStart;
     var i2 = getIndexFromPoint(x, y);
-    
+
     var start = Math.min(i1, i2);
     var end = Math.max(i1, i2);
     setSelection(start, end, !apply);
   }
-    
+
   function screenToPt(x_in, y_in) {
     var out = {
       x: x_in,
       y: y_in
     }
-    
+
     out.x /= current_zoom;
-    out.y /= current_zoom;      
+    out.y /= current_zoom;
 
     if(matrix) {
       var pt = transformPoint(out.x, out.y, matrix.inverse());
       out.x = pt.x;
       out.y = pt.y;
     }
-    
+
     return out;
-  } 
-  
+  }
+
   function ptToScreen(x_in, y_in) {
     var out = {
       x: x_in,
       y: y_in
     }
-    
+
     if(matrix) {
       var pt = transformPoint(out.x, out.y, matrix);
       out.x = pt.x;
       out.y = pt.y;
     }
-    
+
     out.x *= current_zoom;
     out.y *= current_zoom;
-    
+
     return out;
   }
-  
+
   function hideCursor() {
     if(cursor) {
       cursor.setAttribute('visibility', 'hidden');
     }
   }
-  
+
   function selectAll(evt) {
     setSelection(0, curtext.textContent.length);
     $(this).unbind(evt);
@@ -3811,25 +3811,25 @@
 
   function selectWord(evt) {
     if(!allow_dbl || !curtext) return;
-  
+
     var ept = transformPoint( evt.pageX, evt.pageY, root_sctm ),
       mouse_x = ept.x * current_zoom,
       mouse_y = ept.y * current_zoom;
     var pt = screenToPt(mouse_x, mouse_y);
-    
+
     var index = getIndexFromPoint(pt.x, pt.y);
     var str = curtext.textContent;
     var first = str.substr(0, index).replace(/[a-z0-9]+$/i, '').length;
     var m = str.substr(index).match(/^[a-z0-9]+/i);
     var last = (m?m[0].length:0) + index;
     setSelection(first, last);
-    
+
     // Set tripleclick
     $(evt.target).click(selectAll);
     setTimeout(function() {
       $(evt.target).unbind('click', selectAll);
     }, 300);
-    
+
   }
 
   return {
@@ -3843,27 +3843,27 @@
     },
     mouseDown: function(evt, mouse_target, start_x, start_y) {
       var pt = screenToPt(start_x, start_y);
-    
+
       textinput.focus();
       setCursorFromPoint(pt.x, pt.y);
       last_x = start_x;
       last_y = start_y;
-      
+
       // TODO: Find way to block native selection
     },
     mouseMove: function(mouse_x, mouse_y) {
       var pt = screenToPt(mouse_x, mouse_y);
       setEndSelectionFromPoint(pt.x, pt.y);
-    },      
+    },
     mouseUp: function(evt, mouse_x, mouse_y) {
       var pt = screenToPt(mouse_x, mouse_y);
-      
+
       setEndSelectionFromPoint(pt.x, pt.y, true);
-      
-      // TODO: Find a way to make this work: Use transformed BBox instead of evt.target 
+
+      // TODO: Find a way to make this work: Use transformed BBox instead of evt.target
 //        if(last_x === mouse_x && last_y === mouse_y
 //          && !svgedit.math.rectsIntersect(transbb, {x: pt.x, y: pt.y, width:0, height:0})) {
-//          textActions.toSelectMode(true);       
+//          textActions.toSelectMode(true);
 //        }
 
       if(
@@ -3883,21 +3883,21 @@
       allow_dbl = false;
       current_mode = "textedit";
       selectorManager.requestSelector(curtext).showGrips(false);
-    
+
       // Make selector group accept clicks
       var sel = selectorManager.requestSelector(curtext).selectorRect;
-      
+
       textActions.init();
 
       $(curtext).css('cursor', 'text');
-      
+
       if(!arguments.length) {
         setCursor();
       } else {
         var pt = screenToPt(x, y);
         setCursorFromPoint(pt.x, pt.y);
       }
-      
+
       setTimeout(function() {
         allow_dbl = true;
       }, 300);
@@ -3909,11 +3909,11 @@
       if(selblock) $(selblock).attr('display','none');
       if(cursor) $(cursor).attr('visibility','hidden');
       $(curtext).css('cursor', 'move');
-      
+
       if(selectElem) {
         clearSelection();
         $(curtext).css('cursor', 'move');
-        
+
         call("selected", [curtext]);
         addToSelection([curtext], true);
       }
@@ -3921,11 +3921,11 @@
         // No content, so delete
         canvas.deleteSelectedElements();
       }
-      
+
       $(textinput).blur();
-      
+
       curtext = false;
-      
+
 //        if(svgedit.browser.supportsEditableText()) {
 //          curtext.removeAttribute('editable');
 //        }
@@ -3946,47 +3946,47 @@
 //          curtext.select();
 //          return;
 //        }
-    
+
       if(!curtext.parentNode) {
         // Result of the ffClone, need to get correct element
         curtext = selectedElements[0];
         selectorManager.requestSelector(curtext).showGrips(false);
       }
-      
+
       var str = curtext.textContent;
       var len = str.length;
-      
+
       var xform = curtext.getAttribute('transform');
 
       textbb = svgedit.utilities.getBBox(curtext);
-      
+
       matrix = xform?getMatrix(curtext):null;
 
       chardata = Array(len);
       textinput.focus();
-      
+
       $(curtext).unbind('dblclick', selectWord).dblclick(selectWord);
-      
+
       if(!len) {
         var end = {x: textbb.x + (textbb.width/2), width: 0};
       }
-      
+
       for(var i=0; i<len; i++) {
         var start = curtext.getStartPositionOfChar(i);
         var end = curtext.getEndPositionOfChar(i);
-        
+
         if(!svgedit.browser.supportsGoodTextCharPos()) {
           var offset = canvas.contentW * current_zoom;
           start.x -= offset;
           end.x -= offset;
-          
+
           start.x /= current_zoom;
           end.x /= current_zoom;
         }
-        
+
         // Get a "bbox" equivalent for each character. Uses the
         // bbox data of the actual text for y, height purposes
-        
+
         // TODO: Decide if y, width and height are actually necessary
         chardata[i] = {
           x: start.x,
@@ -3995,7 +3995,7 @@
           height: textbb.height
         };
       }
-      
+
       // Add a last bbox for cursor at end of text
       chardata.push({
         x: end.x,
@@ -4010,11 +4010,11 @@
 // Group: Path edit functions
 // Functions relating to editing path elements
 var pathActions = canvas.pathActions = function() {
-  
+
   var subpath = false;
   var current_path;
   var newPoint, firstCtrl;
-  
+
   function resetD(p) {
     p.setAttribute("d", pathActions.convertPath(p));
   }
@@ -4050,9 +4050,9 @@
       }
       // TODO: Correct this:
       pathActions.canDeleteNodes = true;
-      
+
       pathActions.closed_subpath = this.subpathIsClosed(this.selected_pts[0]);
-      
+
       call("selected", grips);
     }
 
@@ -4062,7 +4062,7 @@
     stretchy = null;
 
   this.lastCtrlPoint = [0, 0];
-  
+
   // This function converts a polyline (created by the fh_path tool) into
   // a path element and coverts every three line segments into a single bezier
   // curve in an attempt to smooth out the free-hand
@@ -4071,13 +4071,13 @@
     var N = points.numberOfItems;
     if (N >= 4) {
       // loop through every 3 points and convert to a cubic bezier curve segment
-      // 
-      // NOTE: this is cheating, it means that every 3 points has the potential to 
+      //
+      // NOTE: this is cheating, it means that every 3 points has the potential to
       // be a corner instead of treating each point in an equal manner.  In general,
       // this technique does not look that good.
-      // 
+      //
       // I am open to better ideas!
-      // 
+      //
       // Reading:
       // - http://www.efg2.com/Lab/Graphics/Jean-YvesQueinecBezierCurves.htm
       // - http://www.codeproject.com/KB/graphics/BezierSpline.aspx?msg=2956963
@@ -4090,7 +4090,7 @@
         var ct1 = points.getItem(i);
         var ct2 = points.getItem(i+1);
         var end = points.getItem(i+2);
-        
+
         // if the previous segment had a control point, we want to smooth out
         // the control points on both sides
         if (prevCtlPt) {
@@ -4103,9 +4103,9 @@
             ct1 = newpts[1];
           }
         }
-        
+
         d.push([ct1.x,ct1.y,ct2.x,ct2.y,end.x,end.y].join(','));
-        
+
         curpos = end;
         prevCtlPt = ct2;
       }
@@ -4137,12 +4137,12 @@
       if(current_mode === "path") {
         mouse_x = start_x;
         mouse_y = start_y;
-        
+
         var x = mouse_x/current_zoom,
           y = mouse_y/current_zoom,
           stretchy = getElem("path_stretch_line");
-        newPoint = [x, y];  
-        
+        newPoint = [x, y];
+
         if(curConfig.gridSnapping){
           x = snapToGrid(x);
           y = snapToGrid(y);
@@ -4163,9 +4163,9 @@
         stretchy.setAttribute("display", "inline");
 
         this.stretchy = stretchy;
-        
+
         var keep = null;
-        
+
         // if pts array is empty, create path element with M at current point
         if (!drawn_path) {
           d_attr = "M" + x + "," + y + " ";
@@ -4200,15 +4200,15 @@
               break;
             }
           }
-          
+
           // get path element that we are in the process of creating
           var id = getId();
-        
+
           // Remove previous path object if previously created
           svgedit.path.removePath_(id);
-          
+
           var newpath = getElem(id);
-          
+
           var len = seglist.numberOfItems;
           // if we clicked on an existing point, then we are done this path, commit it
           // (i,i+1) are the x,y that were clicked on
@@ -4224,7 +4224,7 @@
               var abs_y = seglist.getItem(0).y;
               var grip_x = svgedit.path.first_grip ? svgedit.path.first_grip[0]/current_zoom : seglist.getItem(0).x;
               var grip_y = svgedit.path.first_grip ? svgedit.path.first_grip[1]/current_zoom : seglist.getItem(0).y;
-              
+
 
               var s_seg = stretchy.pathSegList.getItem(1);
               if(s_seg.pathSegType === 4) {
@@ -4247,7 +4247,7 @@
               return keep;
             }
             $(stretchy).remove();
-            
+
             // this will signal to commit the path
             element = newpath;
             drawn_path = null;
@@ -4257,7 +4257,7 @@
               if(svgedit.path.path.matrix) {
                 remapElement(newpath, {}, svgedit.path.path.matrix.inverse());
               }
-            
+
               var new_d = newpath.getAttribute("d");
               var orig_d = $(svgedit.path.path.elem).attr("d");
               $(svgedit.path.path.elem).attr("d", orig_d + new_d);
@@ -4286,7 +4286,7 @@
             var lastx = last.x, lasty = last.y;
 
             if(evt.shiftKey) { var xya = snapToAngle(lastx,lasty,x,y); x=xya.x; y=xya.y; }
-            
+
             // Use the segment defined by stretchy
             var s_seg = stretchy.pathSegList.getItem(1);
             if(s_seg.pathSegType === 4) {
@@ -4301,12 +4301,12 @@
                 s_seg.y2 / current_zoom
               );
             }
-            
+
             drawn_path.pathSegList.appendItem(newseg);
-            
+
             x *= current_zoom;
             y *= current_zoom;
-            
+
             // update everything to the latest point
             stretchy.setAttribute('d', ['M', x, y, x, y].join(' '));
             var pointGrip1 = svgedit.path.addCtrlGrip('1c1');
@@ -4337,20 +4337,20 @@
         }
         return;
       }
-      
+
       // TODO: Make sure current_path isn't null at this point
       if(!svgedit.path.path) return;
-      
+
       svgedit.path.path.storeD();
-      
+
       var id = evt.target.id;
       if (id.substr(0,14) == "pathpointgrip_") {
         // Select this point
         var cur_pt = svgedit.path.path.cur_pt = parseInt(id.substr(14));
         svgedit.path.path.dragging = [start_x, start_y];
         var seg = svgedit.path.path.segs[cur_pt];
-        
-        // only clear selection if shift is not pressed (otherwise, add 
+
+        // only clear selection if shift is not pressed (otherwise, add
         // node to selection)
         if (!evt.shiftKey) {
           if(svgedit.path.path.selected_pts.length <= 1 || !seg.selected) {
@@ -4364,7 +4364,7 @@
         }
       } else if(id.indexOf("ctrlpointgrip_") == 0) {
         svgedit.path.path.dragging = [start_x, start_y];
-        
+
         var parts = id.split('_')[1].split('c');
         var cur_pt = parts[0]-0;
         var ctrl_num = parts[1]-0;
@@ -4467,7 +4467,7 @@
         if(!drawn_path) return;
         var seglist = drawn_path.pathSegList;
         var index = seglist.numberOfItems - 1;
-        var pointGrip1 = svgedit.path.addCtrlGrip('1c1'); 
+        var pointGrip1 = svgedit.path.addCtrlGrip('1c1');
         var pointGrip2 = svgedit.path.addCtrlGrip('0c2');
 
         if(newPoint) {
@@ -4484,20 +4484,20 @@
 
           var pt_x = newPoint[0];
           var pt_y = newPoint[1];
-          
+
           // set curve
           var seg = seglist.getItem(index);
           var cur_x = mouse_x / current_zoom;
           var cur_y = mouse_y / current_zoom;
           var alt_x = (is_linked) ?  (pt_x + (pt_x - cur_x)) : current_pointGrip2_x;
           var alt_y = (is_linked) ?  (pt_y + (pt_y - cur_y)) : current_pointGrip2_y;
-          
-          
+
+
           pointGrip2.setAttribute('cx', alt_x * current_zoom);
           pointGrip2.setAttribute('cy', alt_y * current_zoom);
           pointGrip2.setAttribute('display', 'inline');
-          
-          
+
+
           var ctrlLine = svgedit.path.getCtrlLine(1);
           var ctrlLine2 = svgedit.path.getCtrlLine(2);
           assignAttributes(ctrlLine, {
@@ -4507,7 +4507,7 @@
             y2: pt_y * current_zoom,
             display: 'inline'
           });
-          
+
 
           assignAttributes(ctrlLine2, {
             x1: alt_x * current_zoom,
@@ -4522,11 +4522,11 @@
             firstCtrl = [mouse_x, mouse_y];
           } else {
             var last_x, last_y;
-            
+
             var last = seglist.getItem(index - 1);
             var last_x = last.x;
             var last_y = last.y
-  
+
             if(last.pathSegType === 6) {
               last_x += (last_x - last.x2);
               last_y += (last_y - last.y2);
@@ -4552,7 +4552,7 @@
             if(prev.pathSegType === 6) {
               var prev_x = this.lastCtrlPoint[0]/current_zoom || prev.x + (prev.x - prev.x2);
               var prev_y = this.lastCtrlPoint[1]/current_zoom || prev.y + (prev.y - prev.y2);
-              svgedit.path.replacePathSeg(6, 1, [mouse_x, mouse_y, prev_x * current_zoom, prev_y * current_zoom, lastgripx, lastgripy], stretchy);              
+              svgedit.path.replacePathSeg(6, 1, [mouse_x, mouse_y, prev_x * current_zoom, prev_y * current_zoom, lastgripx, lastgripy], stretchy);
             } else if(firstCtrl) {
               svgedit.path.replacePathSeg(6, 1, [mouse_x, mouse_y, firstCtrl[0], firstCtrl[1], mouse_x, mouse_y], stretchy);
             } else {
@@ -4592,7 +4592,7 @@
           if(!seg.next && !seg.prev) return;
           var item = seg.item;
           var rbb = rubberBox.getBBox();
-          
+
           var pt = svgedit.path.getGripPt(seg);
           var pt_bb = {
             x: pt.x,
@@ -4600,7 +4600,7 @@
             width: 0,
             height: 0
           };
-        
+
           var sel = svgedit.math.rectsIntersect(rbb, pt_bb);
 
           this.select(sel);
@@ -4609,7 +4609,7 @@
         });
 
       }
-    }, 
+    },
     mouseUp: function(evt, element, mouse_x, mouse_y) {
       var lastpointgrip = getElem('ctrlpointgrip_1c1');
       var firstpointgrip = getElem('ctrlpointgrip_0c2');
@@ -4639,34 +4639,34 @@
           element: element
         }
       }
-      
+
       // Edit mode
-      
+
       if (svgedit.path.path.dragging) {
         var last_pt = svgedit.path.path.cur_pt;
 
         svgedit.path.path.dragging = false;
         svgedit.path.path.dragctrl = false;
         svgedit.path.path.update();
-        
-      
+
+
         if(hasMoved) {
           svgedit.path.path.endChanges("Move path point(s)");
-        } 
-        
+        }
+
         if(!evt.shiftKey && !hasMoved) {
           svgedit.path.path.selectPt(last_pt);
-        } 
+        }
       }
       else if(rubberBox && rubberBox.getAttribute('display') != 'none') {
         // Done with multi-node-select
         rubberBox.setAttribute("display", "none");
-        
+
         if(rubberBox.getAttribute('width') <= 2 && rubberBox.getAttribute('height') <= 2) {
           pathActions.toSelectMode(evt.target);
         }
-        
-      // else, move back to select mode 
+
+      // else, move back to select mode
       } else {
         pathActions.toSelectMode(evt.target);
       }
@@ -4686,12 +4686,12 @@
       svgedit.path.path.show(false);
       current_path = false;
       clearSelection();
-      
+
       if(svgedit.path.path.matrix) {
         // Rotated, so may need to re-calculate the center
         svgedit.path.recalcRotatedPath();
       }
-      
+
       if(selPath) {
         call("selected", [elem]);
         addToSelection([elem], true);
@@ -4715,14 +4715,14 @@
       } // going into pathedit mode
       else {
         current_path = target;
-      } 
+      }
     },
     reorient: function() {
       var elem = selectedElements[0];
       if(!elem) return;
       var angle = getRotationAngle(elem);
       if(angle == 0) return;
-      
+
       var batchCmd = new BatchCommand("Reorient path");
       var changes = {
         d: elem.getAttribute('d'),
@@ -4731,18 +4731,18 @@
       batchCmd.addSubCommand(new ChangeElementCommand(elem, changes));
       clearSelection();
       this.resetOrientation(elem);
-      
+
       addCommandToHistory(batchCmd);
 
       // Set matrix to null
-      svgedit.path.getPath_(elem).show(false).matrix = null; 
+      svgedit.path.getPath_(elem).show(false).matrix = null;
 
       this.clear();
-  
+
       addToSelection([elem], true);
       call("changed", selectedElements);
     },
-    
+
     clear: function(remove) {
       current_path = null;
       if (drawn_path) {
@@ -4764,11 +4764,11 @@
       tlist.clear();
       path.removeAttribute("transform");
       var segList = path.pathSegList;
-      
+
       // Opera/win/non-EN throws an error here.
       // TODO: Find out why!
       // Presumed fixed in Opera 10.5, so commented out for now
-      
+
 //      try {
         var len = segList.numberOfItems;
 //      } catch(err) {
@@ -4794,7 +4794,7 @@
         });
         svgedit.path.replacePathSeg(type, i, pts, path);
       }
-      
+
       reorientGrads(path, m);
 
 
@@ -4814,16 +4814,16 @@
         y: seg.item.y,
         type: seg.type
       };
-    }, 
+    },
     linkControlPoints: function(linkPoints) {
       svgedit.path.setLinkControlPoints(linkPoints);
     },
     clonePathNode: function() {
       svgedit.path.path.storeD();
-      
+
       var sel_pts = svgedit.path.path.selected_pts;
       var segs = svgedit.path.path.segs;
-      
+
       var i = sel_pts.length;
       var nums = [];
 
@@ -4833,7 +4833,7 @@
         nums.push(pt + i);
         nums.push(pt + i + 1);
       }
-      
+
       svgedit.path.path.init().addPtsToSelection(nums);
 
       svgedit.path.path.endChanges("Clone path node(s)");
@@ -4842,14 +4842,14 @@
       var sel_pts = svgedit.path.path.selected_pts;
       // Only allow one selected node for now
       if(sel_pts.length !== 1) return;
-      
+
       var elem = svgedit.path.path.elem;
       var list = elem.pathSegList;
 
       var len = list.numberOfItems;
 
       var index = sel_pts[0];
-      
+
       var open_pt = null;
       var start_item = null;
 
@@ -4869,7 +4869,7 @@
           return false;
         }
       });
-      
+
       if(open_pt == null) {
         // Single path, so close last seg
         open_pt = svgedit.path.path.segs.length - 1;
@@ -4877,10 +4877,10 @@
 
       if(open_pt !== false) {
         // Close this path
-        
+
         // Create a line going to the previous "M"
         var newseg = elem.createSVGPathSegLinetoAbs(start_item.x, start_item.y);
-      
+
         var closer = elem.createSVGPathSegClosePath();
         if(open_pt == svgedit.path.path.segs.length) {
           list.appendItem(newseg);
@@ -4889,30 +4889,30 @@
           svgedit.path.insertItemBefore(elem, closer, open_pt);
           svgedit.path.insertItemBefore(elem, newseg, open_pt);
         }
-        
+
         svgedit.path.path.init().selectPt(open_pt+1);
         return;
       }
-      
-      
+
+
 
       // M 1,1 L 2,2 L 3,3 L 1,1 z // open at 2,2
       // M 2,2 L 3,3 L 1,1
-      
-      // M 1,1 L 2,2 L 1,1 z M 4,4 L 5,5 L6,6 L 5,5 z 
-      // M 1,1 L 2,2 L 1,1 z [M 4,4] L 5,5 L(M)6,6 L 5,5 z 
-      
+
+      // M 1,1 L 2,2 L 1,1 z M 4,4 L 5,5 L6,6 L 5,5 z
+      // M 1,1 L 2,2 L 1,1 z [M 4,4] L 5,5 L(M)6,6 L 5,5 z
+
       var seg = svgedit.path.path.segs[index];
-      
+
       if(seg.mate) {
         list.removeItem(index); // Removes last "L"
         list.removeItem(index); // Removes the "Z"
         svgedit.path.path.init().selectPt(index - 1);
         return;
       }
-      
+
       var last_m, z_seg;
-      
+
       // Find this sub-path's closing point and remove
       for(var i=0; i<list.numberOfItems; i++) {
         var item = list.getItem(i);
@@ -4931,26 +4931,26 @@
           break;
         }
       }
-      
+
       var num = (index - last_m) - 1;
-      
+
       while(num--) {
         svgedit.path.insertItemBefore(elem, list.getItem(last_m), z_seg);
       }
-      
+
       var pt = list.getItem(last_m);
-      
+
       // Make this point the new "M"
       svgedit.path.replacePathSeg(2, last_m, [pt.x, pt.y]);
-      
+
       var i = index
-      
+
       svgedit.path.path.init().selectPt(0);
     },
     deletePathNode: function() {
       if(!pathActions.canDeleteNodes) return;
       svgedit.path.path.storeD();
-      
+
       var sel_pts = svgedit.path.path.selected_pts;
       var i = sel_pts.length;
 
@@ -4958,12 +4958,12 @@
         var pt = sel_pts[i];
         svgedit.path.path.deleteSeg(pt);
       }
-      
+
       // Cleanup
       var cleanup = function() {
         var segList = svgedit.path.path.elem.pathSegList;
         var len = segList.numberOfItems;
-        
+
         var remItems = function(pos, count) {
           while(count--) {
             segList.removeItem(pos);
@@ -4971,7 +4971,7 @@
         }
 
         if(len <= 1) return true;
-        
+
         while(len--) {
           var item = segList.getItem(len);
           if(item.pathSegType === 1) {
@@ -4990,12 +4990,12 @@
           } else if(item.pathSegType === 2) {
             if(len > 0) {
               var prev_type = segList.getItem(len-1).pathSegType;
-              // Path has M M  
+              // Path has M M
               if(prev_type === 2) {
                 remItems(len-1, 1);
                 cleanup();
                 break;
-              // Entire path ends with Z M 
+              // Entire path ends with Z M
               } else if(prev_type === 1 && segList.numberOfItems-1 === len) {
                 remItems(len, 1);
                 cleanup();
@@ -5003,23 +5003,23 @@
               }
             }
           }
-        } 
+        }
         return false;
       }
-      
+
       cleanup();
-      
+
       // Completely delete a path with 1 or 0 segments
       if(svgedit.path.path.elem.pathSegList.numberOfItems <= 1) {
         canvas.setMode("select")
         canvas.deleteSelectedElements();
         return;
       }
-      
+
       svgedit.path.path.init();
-      
+
       svgedit.path.path.clearSelection();
-      
+
       // TODO: Find right way to select point now
       // path.selectPt(sel_pt);
       if(window.opera) { // Opera repaints incorrectly
@@ -5034,14 +5034,14 @@
     moveNode: function(attr, newValue) {
       var sel_pts = svgedit.path.path.selected_pts;
       if(!sel_pts.length) return;
-      
+
       svgedit.path.path.storeD();
-      
+
       // Get first selected point
       var seg = svgedit.path.path.segs[sel_pts[0]];
       var diff = {x:0, y:0};
       diff[attr] = newValue - seg.item[attr];
-      
+
       seg.move(diff.x, diff.y);
       svgedit.path.path.endChanges("Move path point");
     },
@@ -5057,7 +5057,7 @@
         if(item.pathSegType === 2) {
           last_m = item;
         }
-        
+
         if(item.pathSegType === 1) {
           var prev = segList.getItem(i-1);
           if(prev.x != last_m.x || prev.y != last_m.y) {
@@ -5068,7 +5068,7 @@
             pathActions.fixEnd(elem);
             break;
           }
-          
+
         }
       }
       if(svgedit.browser.isWebkit()) resetD(elem);
@@ -5080,7 +5080,7 @@
       var curx = 0, cury = 0;
       var d = "";
       var last_m = null;
-      
+
       for (var i = 0; i < len; ++i) {
         var seg = segList.getItem(i);
         // if these properties are not in the segment, set them to zero
@@ -5090,10 +5090,10 @@
           y1 = seg.y1 || 0,
           x2 = seg.x2 || 0,
           y2 = seg.y2 || 0;
-  
+
         var type = seg.pathSegType;
         var letter = pathMap[type]['to'+(toRel?'Lower':'Upper')+'Case']();
-        
+
         var addToD = function(pnts, more, last) {
           var str = '';
           var more = more?' '+more.join(' '):'';
@@ -5103,7 +5103,7 @@
           });
           d += letter + pnts.join(' ') + more + last;
         }
-        
+
         switch (type) {
           case 1: // z,Z closepath (Z/z)
             d += "z";
@@ -5143,12 +5143,12 @@
             y -= cury;
           case 5: // relative line (l)
           case 3: // relative move (m)
-            // If the last segment was a "z", this must be relative to 
+            // If the last segment was a "z", this must be relative to
             if(last_m && segList.getItem(i-1).pathSegType === 1 && !toRel) {
               curx = last_m[0];
               cury = last_m[1];
             }
-          
+
           case 19: // relative smooth quad (t)
             if(toRel) {
               curx += x;
@@ -5160,7 +5160,7 @@
               cury = y;
             }
             if(type === 3) last_m = [curx, cury];
-            
+
             addToD([[x,y]]);
             break;
           case 6: // absolute cubic (C)
@@ -5181,7 +5181,7 @@
           case 8: // absolute quad (Q)
             x -= curx; x1 -= curx;
             y -= cury; y1 -= cury;
-          case 9: // relative quad (q) 
+          case 9: // relative quad (q)
             if(toRel) {
               curx += x;
               cury += y;
@@ -5241,23 +5241,23 @@
 // Function: removeUnusedDefElems
 // Looks at DOM elements inside the <defs> to see if they are referred to,
 // removes them from the DOM if they are not.
-// 
+//
 // Returns:
 // The amount of elements that were removed
 var removeUnusedDefElems = this.removeUnusedDefElems = function() {
   var defs = svgcontent.getElementsByTagNameNS(svgns, "defs");
   if(!defs || !defs.length) return 0;
-  
+
 //  if(!defs.firstChild) return;
-  
+
   var defelem_uses = [],
     numRemoved = 0;
   var attrs = ['fill', 'stroke', 'filter', 'marker-start', 'marker-mid', 'marker-end'];
   var alen = attrs.length;
-  
+
   var all_els = svgcontent.getElementsByTagNameNS(svgns, '*');
   var all_len = all_els.length;
-  
+
   for(var i=0; i<all_len; i++) {
     var el = all_els[i];
     for(var j = 0; j < alen; j++) {
@@ -5268,14 +5268,14 @@
         }
       }
     }
-    
+
     // gradients can refer to other gradients
     var href = getHref(el);
     if (href && href.indexOf('#') === 0) {
       defelem_uses.push(href.substr(1));
     }
   };
-  
+
   var defelems = $(defs).find("linearGradient, radialGradient, filter, marker, svg, symbol");
     defelem_ids = [],
     i = defelems.length;
@@ -5294,34 +5294,34 @@
 }
 
 // Function: svgCanvasToString
-// Main function to set up the SVG content for output 
+// Main function to set up the SVG content for output
 //
-// Returns: 
+// Returns:
 // String containing the SVG image for output
 this.svgCanvasToString = function() {
   // keep calling it until there are none to remove
   while (removeUnusedDefElems() > 0) {};
-  
+
   pathActions.clear(true);
-  
+
   // Keep SVG-Edit comment on top
   $.each(svgcontent.childNodes, function(i, node) {
     if(i && node.nodeType === 8 && node.data.indexOf('Created with') >= 0) {
       svgcontent.insertBefore(node, svgcontent.firstChild);
     }
   });
-  
+
   // Move out of in-group editing mode
   if(current_group) {
     leaveContext();
     selectOnly([current_group]);
   }
-  
+
   //hide grid, otherwise shows a black canvas
   $('#canvasGrid').attr('display', 'none');
-  
+
   var naked_svgs = [];
-  
+
   // Unwrap gsvg if it has no special attributes (only id and style)
   $(svgcontent).find('g:data(gsvg)').each(function() {
     var attrs = this.attributes;
@@ -5339,25 +5339,25 @@
     }
   });
   var output = this.svgToString(svgcontent, 0);
-  
+
   // Rewrap gsvg
   if(naked_svgs.length) {
     $(naked_svgs).each(function() {
       groupSvgElem(this);
     });
   }
-  
+
   return output;
 };
 
 // Function: svgToString
 // Sub function ran on each SVG element to convert it to a string as desired
-// 
-// Parameters: 
+//
+// Parameters:
 // elem - The SVG element to convert
 // indent - Integer with the amount of spaces to indent this tag
 //
-// Returns: 
+// Returns:
 // String with the given element as an SVG tag
 this.svgToString = function(elem, indent) {
   var out = new Array(), toXml = svgedit.utilities.toXml;
@@ -5370,13 +5370,13 @@
       attr,
       i,
       childs = elem.childNodes;
-    
+
     for (var i=0; i<indent; i++) out.push(" ");
     out.push("<"); out.push(elem.nodeName);
     if(elem.id === 'svgcontent') {
       // Process root element separately
       var res = getResolution();
-      
+
       var vb = "";
       // TODO: Allow this by dividing all values by current baseVal
       // Note that this also means we should properly deal with this on import
@@ -5385,20 +5385,20 @@
 //        var unit_m = svgedit.units.getTypeMap()[unit];
 //        res.w = svgedit.units.shortFloat(res.w / unit_m)
 //        res.h = svgedit.units.shortFloat(res.h / unit_m)
-//        vb = ' viewBox="' + [0, 0, res.w, res.h].join(' ') + '"';       
+//        vb = ' viewBox="' + [0, 0, res.w, res.h].join(' ') + '"';
 //        res.w += unit;
 //        res.h += unit;
 //      }
-      
+
       if(unit !== "px") {
         res.w = svgedit.units.convertUnit(res.w, unit) + unit;
         res.h = svgedit.units.convertUnit(res.h, unit) + unit;
       }
-      
+
       out.push(' width="' + res.w + '" height="' + res.h + '"' + vb + ' xmlns="'+svgns+'"');
-      
+
       var nsuris = {};
-      
+
       // Check elements for namespaces, add if found
       $(elem).find('*').andSelf().each(function() {
         var el = this;
@@ -5410,22 +5410,22 @@
           }
         });
       });
-      
+
       var i = attrs.length;
       var attr_names = ['width','height','xmlns','x','y','viewBox','id','overflow'];
       while (i--) {
         attr = attrs.item(i);
         var attrVal = toXml(attr.nodeValue);
-        
+
         // Namespaces have already been dealt with, so skip
         if(attr.nodeName.indexOf('xmlns:') === 0) continue;
 
         // only serialize attributes we don't use internally
-        if (attrVal != "" && attr_names.indexOf(attr.localName) == -1) 
+        if (attrVal != "" && attr_names.indexOf(attr.localName) == -1)
         {
 
           if(!attr.namespaceURI || nsMap[attr.namespaceURI]) {
-            out.push(' '); 
+            out.push(' ');
             out.push(attr.nodeName); out.push("=\"");
             out.push(attrVal); out.push("\"");
           }
@@ -5434,7 +5434,7 @@
     } else {
       // Skip empty defs
       if(elem.nodeName === 'defs' && !elem.firstChild) return;
-    
+
       var moz_attrs = ['-moz-math-font-style', '_moz-math-font-style'];
       for (var i=attrs.length-1; i>=0; i--) {
         attr = attrs.item(i);
@@ -5444,25 +5444,25 @@
         if (attrVal != "") {
           if(attrVal.indexOf('pointer-events') === 0) continue;
           if(attr.localName === "class" && attrVal.indexOf('se_') === 0) continue;
-          out.push(" "); 
+          out.push(" ");
           if(attr.localName === 'd') attrVal = pathActions.convertPath(elem, true);
           if(!isNaN(attrVal)) {
             attrVal = svgedit.units.shortFloat(attrVal);
           } else if(unit_re.test(attrVal)) {
             attrVal = svgedit.units.shortFloat(attrVal) + unit;
           }
-          
-          // Embed images when saving 
+
+          // Embed images when saving
           if(save_options.apply
-            && elem.nodeName === 'image' 
+            && elem.nodeName === 'image'
             && attr.localName === 'href'
             && save_options.images
-            && save_options.images === 'embed') 
+            && save_options.images === 'embed')
           {
             var img = encodableImages[attrVal];
             if(img) attrVal = img;
           }
-          
+
           // map various namespaces to our fixed namespace prefixes
           // (the default xmlns attribute itself does not get a prefix)
           if(!attr.namespaceURI || attr.namespaceURI == svgns || nsMap[attr.namespaceURI]) {
@@ -5477,7 +5477,7 @@
       out.push(">");
       indent++;
       var bOneLine = false;
-      
+
       for (var i=0; i<childs.length; i++)
       {
         var child = childs.item(i);
@@ -5525,7 +5525,7 @@
 // Function: embedImage
 // Converts a given image file to a data URL when possible, then runs a given callback
 //
-// Parameters: 
+// Parameters:
 // val - String with the path/URL of the image
 // callback - Optional function to run when image data is found, supplies the
 // result (data URL or false) as first parameter.
@@ -5567,7 +5567,7 @@
 // This function also includes the XML prolog.  Clients of the SvgCanvas bind their save
 // function to the 'saved' event.
 //
-// Returns: 
+// Returns:
 // Nothing
 this.save = function(opts) {
   // remove the selected outline before serializing
@@ -5575,7 +5575,7 @@
   // Update save options if provided
   if(opts) $.extend(save_options, opts);
   save_options.apply = true;
-  
+
   // no need for doctype, see http://jwatt.org/svg/authoring/#doctype-declaration
   var str = this.svgCanvasToString();
   if (svgedit.browser.supportsBlobs()) {
@@ -5589,15 +5589,15 @@
 };
 
 // Function: rasterExport
-// Generates a PNG Data URL based on the current image, then calls "exported" 
+// Generates a PNG Data URL based on the current image, then calls "exported"
 // with an object including the string and any issues found
 this.rasterExport = function() {
   // remove the selected outline before serializing
   clearSelection();
-  
-  // Check for known CanVG issues 
+
+  // Check for known CanVG issues
   var issues = [];
-  
+
   // Selector and notice
   var issue_list = {
     'feGaussianBlur': uiStrings.exportNoBlur,
@@ -5605,12 +5605,12 @@
     '[stroke-dasharray]': uiStrings.exportNoDashArray
   };
   var content = $(svgcontent);
-  
+
   // Add font/text check if Canvas Text API is not implemented
   if(!("font" in $('<canvas>')[0].getContext('2d'))) {
     issue_list['text'] = uiStrings.exportNoText;
   }
-  
+
   $.each(issue_list, function(sel, descr) {
     if(content.find(sel).length) {
       issues.push(descr);
@@ -5634,7 +5634,7 @@
 // Function: randomizeIds
 // This function determines whether to use a nonce in the prefix, when
 // generating IDs for future documents in SVG-Edit.
-// 
+//
 //  Parameters:
 //   an opional boolean, which, if true, adds a nonce to the prefix. Thus
 //     svgCanvas.randomizeIds()  <==> svgCanvas.randomizeIds(true)
@@ -5662,11 +5662,11 @@
   //
   // <marker id='se_marker_end_svg_7'/>
   // <polyline id='svg_7' se:connector='svg_1 svg_6' marker-end='url(#se_marker_end_svg_7)'/>
-  // 
+  //
   // Problem #1: if svg_1 gets renamed, we do not update the polyline's se:connector attribute
   // Problem #2: if the polyline svg_7 gets renamed, we do not update the marker id nor the polyline's marker-end attribute
   var ref_elems = ["filter", "linearGradient", "pattern", "radialGradient", "symbol", "textPath", "use"];
-  
+
   svgedit.utilities.walkTree(g, function(n) {
     // if it's an element node
     if (n.nodeType == 1) {
@@ -5679,7 +5679,7 @@
         }
         ids[n.id]["elem"] = n;
       }
-      
+
       // now search for all attributes on this element that might refer
       // to other elements
       $.each(ref_attrs,function(i,attr) {
@@ -5697,7 +5697,7 @@
           }
         }
       });
-      
+
       // check xlink:href now
       var href = svgedit.utilities.getHref(n);
       // TODO: what if an <image> or <a> element refers to an element internally?
@@ -5711,20 +5711,20 @@
           }
           ids[refid]["hrefs"].push(n);
         }
-      }           
+      }
     }
   });
-  
+
   // in ids, we now have a map of ids, elements and attributes, let's re-identify
   for (var oldid in ids) {
     if (!oldid) continue;
     var elem = ids[oldid]["elem"];
     if (elem) {
       var newid = getNextId();
-      
+
       // assign element its new id
       elem.id = newid;
-      
+
       // remap all url() attributes
       var attrs = ids[oldid]["attrs"];
       var j = attrs.length;
@@ -5732,7 +5732,7 @@
         var attr = attrs[j];
         attr.ownerElement.setAttribute(attr.name, "url(#" + newid + ")");
       }
-      
+
       // remap all href attributes
       var hreffers = ids[oldid]["hrefs"];
       var k = hreffers.length;
@@ -5748,11 +5748,11 @@
 // Assigns reference data for each use element
 var setUseData = this.setUseData = function(parent) {
   var elems = $(parent);
-  
+
   if(parent.tagName !== 'use') {
     elems = elems.find('use');
   }
-  
+
   elems.each(function() {
     var id = getHref(this).substr(1);
     var ref_elem = getElem(id);
@@ -5774,38 +5774,38 @@
       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
       var elems = $(svgcontent).find('[fill="url(#' + grad.id + ')"],[stroke="url(#' + grad.id + ')"]');
       if(!elems.length) return;
-      
+
       // get object's bounding box
       var bb = svgedit.utilities.getBBox(elems[0]);
-      
+
       // This will occur if the element is inside a <defs> or a <symbol>,
       // in which we shouldn't need to convert anyway.
       if(!bb) return;
-      
+
       if(grad.tagName === 'linearGradient') {
         var g_coords = $(grad).attr(['x1', 'y1', 'x2', 'y2']);
-        
+
         // If has transform, convert
         var tlist = grad.gradientTransform.baseVal;
         if(tlist && tlist.numberOfItems > 0) {
           var m = transformListToTransform(tlist).matrix;
           var pt1 = transformPoint(g_coords.x1, g_coords.y1, m);
           var pt2 = transformPoint(g_coords.x2, g_coords.y2, m);
-          
+
           g_coords.x1 = pt1.x;
           g_coords.y1 = pt1.y;
           g_coords.x2 = pt2.x;
           g_coords.y2 = pt2.y;
           grad.removeAttribute('gradientTransform');
         }
-        
+
         $(grad).attr({
           x1: (g_coords.x1 - bb.x) / bb.width,
           y1: (g_coords.y1 - bb.y) / bb.height,
@@ -5814,26 +5814,26 @@
         });
         grad.removeAttribute('gradientUnits');
       } else {
-        // Note: radialGradient elements cannot be easily converted 
+        // Note: radialGradient elements cannot be easily converted
         // because userSpaceOnUse will keep circular gradients, while
         // objectBoundingBox will x/y scale the gradient according to
-        // its bbox. 
-        
+        // its bbox.
+
         // For now we'll do nothing, though we should probably have
-        // the gradient be updated as the element is moved, as 
+        // the gradient be updated as the element is moved, as
         // inkscape/illustrator do.
-      
+
 //                var g_coords = $(grad).attr(['cx', 'cy', 'r']);
-//                
+//
 //            $(grad).attr({
 //              cx: (g_coords.cx - bb.x) / bb.width,
 //              cy: (g_coords.cy - bb.y) / bb.height,
 //              r: g_coords.r
 //            });
-//            
+//
 //                grad.removeAttribute('gradientUnits');
       }
-      
+
 
     }
   });
@@ -5846,19 +5846,19 @@
     elem = selectedElements[0];
   }
   var $elem = $(elem);
-  
+
   var batchCmd = new BatchCommand();
-  
+
   var ts;
-  
+
   if($elem.data('gsvg')) {
     // Use the gsvg as the new group
     var svg = elem.firstChild;
     var pt = $(svg).attr(['x', 'y']);
-    
+
     $(elem.firstChild.firstChild).unwrap();
     $(elem).removeData('gsvg');
-    
+
     var tlist = getTransformList(elem);
     var xform = svgroot.createSVGTransform();
     xform.setTranslate(pt.x, pt.y);
@@ -5867,61 +5867,61 @@
     call("selected", [elem]);
   } else if($elem.data('symbol')) {
     elem = $elem.data('symbol');
-    
+
     ts = $elem.attr('transform');
     var pos = $elem.attr(['x','y']);
 
     var vb = elem.getAttribute('viewBox');
-    
+
     if(vb) {
       var nums = vb.split(' ');
       pos.x -= +nums[0];
       pos.y -= +nums[1];
     }
-    
+
     // Not ideal, but works
     ts += " translate(" + (pos.x || 0) + "," + (pos.y || 0) + ")";
-    
+
     var prev = $elem.prev();
-    
+
     // Remove <use> element
     batchCmd.addSubCommand(new RemoveElementCommand($elem[0], $elem[0].nextSibling, $elem[0].parentNode));
     $elem.remove();
-    
+
     // See if other elements reference this symbol
     var has_more = $(svgcontent).find('use:data(symbol)').length;
-      
+
     var g = svgdoc.createElementNS(svgns, "g");
     var childs = elem.childNodes;
-    
+
     for(var i = 0; i < childs.length; i++) {
       g.appendChild(childs[i].cloneNode(true));
     }
-    
+
     // Duplicate the gradients for Gecko, since they weren't included in the <symbol>
     if(svgedit.browser.isGecko()) {
       var dupeGrads = $(findDefs()).children('linearGradient,radialGradient,pattern').clone();
       $(g).append(dupeGrads);
     }
-    
+
     if (ts) {
       g.setAttribute("transform", ts);
     }
-    
+
     var parent = elem.parentNode;
-    
+
     uniquifyElems(g);
-    
+
     // Put the dupe gradients back into <defs> (after uniquifying them)
     if(svgedit.browser.isGecko()) {
       $(findDefs()).append( $(g).find('linearGradient,radialGradient,pattern') );
     }
-  
+
     // now give the g itself a new id
     g.id = getNextId();
-    
+
     prev.after(g);
-    
+
     if(parent) {
       if(!has_more) {
         // remove symbol/svg element
@@ -5931,39 +5931,39 @@
       }
       batchCmd.addSubCommand(new InsertElementCommand(g));
     }
-    
+
     setUseData(g);
-    
+
     if(svgedit.browser.isGecko()) {
       convertGradients(findDefs());
     } else {
       convertGradients(g);
     }
-    
+
     // recalculate dimensions on the top-level children so that unnecessary transforms
     // are removed
     svgedit.utilities.walkTreePost(g, function(n){try{recalculateDimensions(n)}catch(e){console.log(e)}});
-    
+
     // Give ID for any visible element missing one
     $(g).find(visElems).each(function() {
       if(!this.id) this.id = getNextId();
     });
-    
+
     selectOnly([g]);
-    
+
     var cm = pushGroupProperties(g, true);
     if(cm) {
       batchCmd.addSubCommand(cm);
     }
 
     addCommandToHistory(batchCmd);
-    
+
   } else {
     console.log('Unexpected element to ungroup:', elem);
   }
 }
 
-//   
+//
 // Function: setSvgString
 // This function sets the current drawing as the input SVG XML.
 //
@@ -5985,7 +5985,7 @@
     var nextSibling = svgcontent.nextSibling;
     var oldzoom = svgroot.removeChild(svgcontent);
     batchCmd.addSubCommand(new RemoveElementCommand(oldzoom, nextSibling, svgroot));
-  
+
     // set new svg document
     // If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode()
     if(svgdoc.adoptNode) {
@@ -5994,12 +5994,12 @@
     else {
       svgcontent = svgdoc.importNode(newDoc.documentElement, true);
     }
-    
+
     svgroot.appendChild(svgcontent);
     var content = $(svgcontent);
-    
+
     canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent, idprefix);
-    
+
     // retrieve or set the nonce
     var nonce = getCurrentDrawing().getNonce();
     if (nonce) {
@@ -6007,7 +6007,7 @@
     } else {
       call("unsetnonce");
     }
-    
+
     // change image href vals if possible
     content.find('image').each(function() {
       var image = this;
@@ -6026,14 +6026,14 @@
       // Add to encodableImages if it loads
       canvas.embedImage(val);
     });
-  
+
     // Wrap child SVGs in group elements
     content.find('svg').each(function() {
       // Skip if it's in a <defs>
       if($(this).closest('defs').length) return;
-    
+
       uniquifyElems(this);
-    
+
       // Check if it already has a gsvg group
       var pa = this.parentNode;
       if(pa.childNodes.length === 1 && pa.nodeName === 'g') {
@@ -6043,29 +6043,29 @@
         groupSvgElem(this);
       }
     });
-    
+
     // Put all paint elems in defs
-    
+
     content.find('linearGradient, radialGradient, pattern').appendTo(findDefs());
-    
+
     // Set ref element for <use> elements
-    
+
     // TODO: This should also be done if the object is re-added through "redo"
     setUseData(content);
-    
+
     convertGradients(content[0]);
-    
+
     // recalculate dimensions on the top-level children so that unnecessary transforms
     // are removed
     svgedit.utilities.walkTreePost(svgcontent, function(n){try{recalculateDimensions(n)}catch(e){console.log(e)}});
-    
+
     var attrs = {
       id: 'svgcontent',
       overflow: curConfig.show_outside_canvas?'visible':'hidden'
     };
-    
+
     var percs = false;
-    
+
     // determine proper size
     if (content.attr("viewBox")) {
       var vb = content.attr("viewBox").split(' ');
@@ -6077,9 +6077,9 @@
       $.each(['width', 'height'], function(i, dim) {
         // Set to 100 if not given
         var val = content.attr(dim);
-        
+
         if(!val) val = '100%';
-        
+
         if((val+'').substr(-1) === "%") {
           // Use user units if percentage given
           percs = true;
@@ -6088,31 +6088,31 @@
         }
       });
     }
-    
+
     // identify layers
     identifyLayers();
-    
+
     // Give ID for any visible layer children missing one
     content.children().find(visElems).each(function() {
       if(!this.id) this.id = getNextId();
     });
-    
+
     // Percentage width/height, so let's base it on visible elements
     if(percs) {
       var bb = getStrokedBBox();
       attrs.width = bb.width + bb.x;
       attrs.height = bb.height + bb.y;
     }
-    
-    // Just in case negative numbers are given or 
+
+    // Just in case negative numbers are given or
     // result from the percs calculation
     if(attrs.width <= 0) attrs.width = 200;
     if(attrs.height <= 0) attrs.height = 200;
-    
+
     content.attr(attrs);
     this.contentW = attrs['width'];
     this.contentH = attrs['height'];
-    
+
     $("#canvas_width").val(this.contentW)
     $("#canvas_height").val(this.contentH)
     var background = $("#canvas_background")
@@ -6131,16 +6131,16 @@
     // update root to the correct size
     var changes = content.attr(["width", "height"]);
     batchCmd.addSubCommand(new ChangeElementCommand(svgroot, changes));
-    
+
     // reset zoom
     current_zoom = 1;
-    
+
     // reset transform lists
     svgedit.transformlist.resetListMap();
     clearSelection();
     svgedit.path.clearData();
     svgroot.appendChild(selectorManager.selectorParentGroup);
-    
+
     addCommandToHistory(batchCmd);
     call("changed", [svgcontent]);
   } catch(e) {
@@ -6165,7 +6165,7 @@
 
     opts = { alpha: opac };
     opts[refElem.tagName] = refElem;
-  } 
+  }
   else if (color.indexOf("#") === 0) {
     opts = {
       alpha: opac,
@@ -6190,19 +6190,19 @@
 //
 // Returns:
 // This function returns false if the import was unsuccessful, true otherwise.
-// TODO: 
+// TODO:
 // * properly handle if namespace is introduced by imported content (must add to svgcontent
 // and update all prefixes in the imported node)
 // * properly handle recalculating dimensions, recalculateDimensions() doesn't handle
-// arbitrary transform lists, but makes some assumptions about how the transform list 
+// arbitrary transform lists, but makes some assumptions about how the transform list
 // was obtained
-// * import should happen in top-left of current zoomed viewport  
+// * import should happen in top-left of current zoomed viewport
 this.importSvgString = function(xmlString) {
 
   try {
     // Get unique ID
     var uid = svgedit.utilities.encode64(xmlString.length + xmlString).substr(0,32);
-    
+
     var useExisting = false;
 
     // Look for symbol and make sure symbol exists in image
@@ -6211,18 +6211,18 @@
         useExisting = true;
       }
     }
-    
+
     var batchCmd = new BatchCommand("Import SVG");
-  
+
     if(useExisting) {
       var symbol = import_ids[uid].symbol;
       var ts = import_ids[uid].xform;
     } else {
       // convert string into XML document
       var newDoc = svgedit.utilities.text2xml(xmlString);
-  
+
       this.prepareSvg(newDoc);
-  
+
       // import new svg document into our document
       var svg;
       // If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode()
@@ -6232,9 +6232,9 @@
       else {
         svg = svgdoc.importNode(newDoc.documentElement, true);
       }
-      
+
       uniquifyElems(svg);
-      
+
       var innerw = convertToNum('width', svg.getAttribute("width")),
         innerh = convertToNum('height', svg.getAttribute("height")),
         innervb = svg.getAttribute("viewBox"),
@@ -6242,32 +6242,32 @@
         vb = innervb ? innervb.split(" ") : [0,0,innerw,innerh];
       for (var j = 0; j < 4; ++j)
         vb[j] = +(vb[j]);
-  
+
       // TODO: properly handle preserveAspectRatio
       var canvasw = +svgcontent.getAttribute("width"),
         canvash = +svgcontent.getAttribute("height");
       // imported content should be 1/3 of the canvas on its largest dimension
-      
+
       if (innerh > innerw) {
         var ts = "scale(" + (canvash/3)/vb[3] + ")";
       }
       else {
         var ts = "scale(" + (canvash/3)/vb[2] + ")";
       }
-      
+
       // Hack to make recalculateDimensions understand how to scale
       ts = "translate(0) " + ts + " translate(0)";
-      
+
       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);
@@ -6278,31 +6278,31 @@
         symbol.setAttribute(attr.nodeName, attr.nodeValue);
       }
       symbol.id = getNextId();
-      
+
       // Store data
       import_ids[uid] = {
         symbol: symbol,
         xform: ts
       }
-      
+
       findDefs().appendChild(symbol);
       batchCmd.addSubCommand(new InsertElementCommand(symbol));
     }
-    
-    
+
+
     var use_el = svgdoc.createElementNS(svgns, "use");
     use_el.id = getNextId();
     setHref(use_el, "#" + symbol.id);
-    
+
     (current_group || getCurrentDrawing().getCurrentLayer()).appendChild(use_el);
     batchCmd.addSubCommand(new InsertElementCommand(use_el));
     clearSelection();
-    
+
     use_el.setAttribute("transform", ts);
     recalculateDimensions(use_el);
     $(use_el).data('symbol', symbol).data('ref', symbol);
     addToSelection([use_el]);
-    
+
     // TODO: Find way to add this in a recalculateDimensions-parsable way
 //        if (vb[0] != 0 || vb[1] != 0)
 //          ts = "translate(" + (-vb[0]) + "," + (-vb[1]) + ") " + ts;
@@ -6330,7 +6330,7 @@
 };
 
 // Function: createLayer
-// Creates a new top-level layer in the drawing with the given name, sets the current layer 
+// Creates a new top-level layer in the drawing with the given name, sets the current layer
 // to it, and then clears the selection  This function then calls the 'changed' handler.
 // This is an undoable action.
 //
@@ -6366,7 +6366,7 @@
     if(ch.localName == 'title') continue;
     new_layer.appendChild(copyElem(ch));
   }
-  
+
   clearSelection();
   identifyLayers();
 
@@ -6377,7 +6377,7 @@
 };
 
 // Function: deleteCurrentLayer
-// Deletes the current layer from the drawing and then clears the selection. This function 
+// Deletes the current layer from the drawing and then clears the selection. This function
 // then calls the 'changed' handler.  This is an undoable action.
 this.deleteCurrentLayer = function() {
   var current_layer = getCurrentDrawing().getCurrentLayer();
@@ -6414,11 +6414,11 @@
 };
 
 // Function: renameCurrentLayer
-// Renames the current layer. If the layer name is not valid (i.e. unique), then this function 
+// Renames the current layer. If the layer name is not valid (i.e. unique), then this function
 // does nothing and returns false, otherwise it returns true. This is an undo-able action.
-// 
+//
 // Parameters:
-// newname - the new name you want to give the current layer.  This name must be unique 
+// newname - the new name you want to give the current layer.  This name must be unique
 // among all layer names.
 //
 // Returns:
@@ -6437,14 +6437,14 @@
       }
       var oldname = drawing.getLayerName(i);
       drawing.all_layers[i][0] = svgedit.utilities.toXml(newname);
-    
+
       // now change the underlying title element contents
       var len = oldLayer.childNodes.length;
       for (var i = 0; i < len; ++i) {
         var child = oldLayer.childNodes.item(i);
         // found the <title> element, now append all the
         if (child && child.tagName == "title") {
-          // wipe out old name 
+          // wipe out old name
           while (child.firstChild) { child.removeChild(child.firstChild); }
           child.textContent = newname;
 
@@ -6461,14 +6461,14 @@
 };
 
 // Function: setCurrentLayerPosition
-// Changes the position of the current layer to the new value. If the new index is not valid, 
+// Changes the position of the current layer to the new value. If the new index is not valid,
 // this function does nothing and returns false, otherwise it returns true. This is an
 // undo-able action.
 //
 // Parameters:
 // newpos - The zero-based index of the new position of the layer.  This should be between
 // 0 and (number of layers - 1)
-// 
+//
 // Returns:
 // true if the current layer position was changed, false otherwise.
 this.setCurrentLayerPosition = function(newpos) {
@@ -6479,7 +6479,7 @@
     }
     // some unknown error condition (current_layer not in all_layers)
     if (oldpos == drawing.getNumLayers()) { return false; }
-    
+
     if (oldpos != newpos) {
       // if our new position is below us, we need to insert before the node after newpos
       var refLayer = null;
@@ -6495,19 +6495,19 @@
       }
       svgcontent.insertBefore(drawing.current_layer, refLayer);
       addCommandToHistory(new MoveElementCommand(drawing.current_layer, oldNextSibling, svgcontent));
-      
+
       identifyLayers();
       canvas.setCurrentLayer(drawing.getLayerName(newpos));
-      
+
       return true;
     }
   }
-  
+
   return false;
 };
 
 // Function: setLayerVisibility
-// Sets the visibility of the layer. If the layer name is not valid, this function return 
+// Sets the visibility of the layer. If the layer name is not valid, this function return
 // false, otherwise it returns true. This is an undo-able action.
 //
 // Parameters:
@@ -6526,17 +6526,17 @@
   } else {
     return false;
   }
-  
+
   if (layer == drawing.getCurrentLayer()) {
     clearSelection();
     pathActions.clear();
   }
-//    call("changed", [selected]);  
+//    call("changed", [selected]);
   return true;
 };
 
 // Function: moveSelectedToLayer
-// Moves the selected elements to layername. If the name is not a valid layer name, then false 
+// Moves the selected elements to layername. If the name is not a valid layer name, then false
 // is returned.  Otherwise it returns true. This is an undo-able action.
 //
 // Parameters:
@@ -6555,9 +6555,9 @@
     }
   }
   if (!layer) return false;
-  
+
   var batchCmd = new BatchCommand("Move Elements to Layer");
-  
+
   // loop for each selected element and move it
   var selElems = selectedElements;
   var i = selElems.length;
@@ -6570,9 +6570,9 @@
     layer.appendChild(elem);
     batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldLayer));
   }
-  
+
   addCommandToHistory(batchCmd);
-  
+
   return true;
 };
 
@@ -6598,19 +6598,19 @@
     prev.appendChild(ch);
     batchCmd.addSubCommand(new MoveElementCommand(ch, oldNextSibling, drawing.current_layer));
   }
-  
+
   // Remove current layer
   svgcontent.removeChild(drawing.current_layer);
-  
+
   if(!skipHistory) {
     clearSelection();
     identifyLayers();
 
     call("changed", [svgcontent]);
-    
+
     addCommandToHistory(batchCmd);
   }
-  
+
   drawing.current_layer = prev;
   return batchCmd;
 }
@@ -6622,7 +6622,7 @@
   while($(svgcontent).children('g').length > 1) {
     batchCmd.addSubCommand(canvas.mergeLayer(true));
   }
-  
+
   clearSelection();
   identifyLayers();
   call("changed", [svgcontent]);
@@ -6637,7 +6637,7 @@
   if(len) {
     for(var i = 0; i < len; i++) {
       var elem = disabled_elems[i];
-      
+
       var orig = elData(elem, 'orig_opac');
       if(orig !== 1) {
         elem.setAttribute('opacity', orig);
@@ -6663,7 +6663,7 @@
 
   // Edit inside this group
   current_group = elem;
-  
+
   // Disable other elements
   $(elem).parentsUntil('#svgcontent').andSelf().siblings().each(function() {
     var opac = this.getAttribute('opacity') || 1;
@@ -6695,7 +6695,7 @@
 
   // create empty first layer
   canvas.createLayer("Layer 1");
-  
+
   // clear the undo stack
   canvas.undoMgr.resetUndoStack();
 
@@ -6729,10 +6729,10 @@
 var getResolution = this.getResolution = function() {
 //    var vb = svgcontent.getAttribute("viewBox").split(' ');
 //    return {'w':vb[2], 'h':vb[3], 'zoom': current_zoom};
-  
+
   var width = svgcontent.getAttribute("width")/current_zoom;
   var height = svgcontent.getAttribute("height")/current_zoom;
-  
+
   return {
     'w': width,
     'h': height,
@@ -6789,11 +6789,11 @@
 this.setGroupTitle = function(val) {
   var elem = selectedElements[0];
   elem = $(elem).data('gsvg') || elem;
-  
+
   var ts = $(elem).children('title');
-  
+
   var batchCmd = new BatchCommand("Set Label");
-  
+
   if(!val.length) {
     // Remove title element
     var tsNextSibling = ts.nextSibling;
@@ -6829,9 +6829,9 @@
 // newtitle - String with the new title
 this.setDocumentTitle = function(newtitle) {
   var childs = svgcontent.childNodes, doc_title = false, old_title = '';
-  
+
   var batchCmd = new BatchCommand("Change Image Title");
-  
+
   for (var i=0; i<childs.length; i++) {
     if(childs[i].nodeName == 'title') {
       doc_title = childs[i];
@@ -6842,8 +6842,8 @@
   if(!doc_title) {
     doc_title = svgdoc.createElementNS(svgns, "title");
     svgcontent.insertBefore(doc_title, svgcontent.firstChild);
-  } 
-  
+  }
+
   if(newtitle.length) {
     doc_title.textContent = newtitle;
   } else {
@@ -6869,13 +6869,13 @@
 // Function: setResolution
 // Changes the document's dimensions to the given size
 //
-// Parameters: 
-// x - Number with the width of the new dimensions in user units. 
+// Parameters:
+// x - Number with the width of the new dimensions in user units.
 // Can also be the string "fit" to indicate "fit to content"
-// y - Number with the height of the new dimensions in user units. 
+// y - Number with the height of the new dimensions in user units.
 //
 // Returns:
-// Boolean to indicate if resolution change was succesful. 
+// Boolean to indicate if resolution change was succesful.
 // It will fail on "fit to content" option with no content to fit to.
 this.setResolution = function(x, y) {
   var res = getResolution();
@@ -6885,7 +6885,7 @@
   if(x == 'fit') {
     // Get bounding box
     var bbox = getStrokedBBox();
-    
+
     if(bbox) {
       batchCmd = new BatchCommand("Fit Canvas to Content");
       var visEls = getVisibleElements();
@@ -6895,11 +6895,11 @@
         dx.push(bbox.x*-1);
         dy.push(bbox.y*-1);
       });
-      
+
       var cmd = canvas.moveSelectedElements(dx, dy, true);
       batchCmd.addSubCommand(cmd);
       clearSelection();
-      
+
       x = Math.round(bbox.width);
       y = Math.round(bbox.height);
     } else {
@@ -6913,17 +6913,17 @@
 
     x = convertToNum('width', x);
     y = convertToNum('height', y);
-    
+
     svgcontent.setAttribute('width', x);
     svgcontent.setAttribute('height', y);
-    
+
     this.contentW = x;
     this.contentH = y;
     batchCmd.addSubCommand(new ChangeElementCommand(svgcontent, {"width":w, "height":h}));
 
     svgcontent.setAttribute("viewBox", [0, 0, x/current_zoom, y/current_zoom].join(' '));
     batchCmd.addSubCommand(new ChangeElementCommand(svgcontent, {"viewBox": ["0 0", w, h].join(' ')}));
-  
+
     addCommandToHistory(batchCmd);
     background = document.getElementById("canvas_background");
     if (background) {
@@ -6946,9 +6946,9 @@
 
 // Function: setBBoxZoom
 // Sets the zoom level on the canvas-side based on the given value
-// 
+//
 // Parameters:
-// val - Bounding box object to zoom to or string indicating zoom option 
+// val - Bounding box object to zoom to or string indicating zoom option
 // editor_w - Integer with the editor's workarea box's width
 // editor_h - Integer with the editor's workarea box's height
 this.setBBoxZoom = function(val, editor_w, editor_h) {
@@ -6957,12 +6957,12 @@
   var calcZoom = function(bb) {
     if(!bb) return false;
     var w_zoom = Math.round((editor_w / bb.width)*100 * spacer)/100;
-    var h_zoom = Math.round((editor_h / bb.height)*100 * spacer)/100; 
+    var h_zoom = Math.round((editor_h / bb.height)*100 * spacer)/100;
     var zoomlevel = Math.min(w_zoom,h_zoom);
     canvas.setZoom(zoomlevel);
     return {'zoom': zoomlevel, 'bbox': bb};
   }
-  
+
   if(typeof val == 'object') {
     bb = val;
     if(bb.width == 0 || bb.height == 0) {
@@ -7025,7 +7025,7 @@
 // Parameters:
 // name - String with the new mode to change to
 this.setMode = function(name) {
-  
+
   pathActions.clear();
   textActions.clear();
   $("#workarea").attr("class", name);
@@ -7043,7 +7043,7 @@
 
 // Function: setColor
 // Change the current stroke/fill color/gradient value
-// 
+//
 // Parameters:
 // type - String indicating fill or stroke
 // val - The value to set the stroke attribute to
@@ -7073,7 +7073,7 @@
     if (!preventUndo) {
       changeSelectedAttribute(type, val, elems);
       call("changed", elems);
-    } else 
+    } else
       changeSelectedAttributeNoUndo(type, val, elems);
   }
 }
@@ -7147,22 +7147,22 @@
       if (grad.getAttribute('x1') != og.getAttribute('x1') ||
         grad.getAttribute('y1') != og.getAttribute('y1') ||
         grad.getAttribute('x2') != og.getAttribute('x2') ||
-        grad.getAttribute('y2') != og.getAttribute('y2')) 
+        grad.getAttribute('y2') != og.getAttribute('y2'))
       {
         continue;
       }
     } else {
       var grad_attrs = $(grad).attr(rad_attrs);
       var og_attrs = $(og).attr(rad_attrs);
-      
+
       var diff = false;
       $.each(rad_attrs, function(i, attr) {
         if(grad_attrs[attr] != og_attrs[attr]) diff = true;
       });
-      
+
       if(diff) continue;
     }
-    
+
     // else could be a duplicate, iterate through stops
     var stops = grad.getElementsByTagNameNS(svgns, "stop");
     var ostops = og.getElementsByTagNameNS(svgns, "stop");
@@ -7178,7 +7178,7 @@
 
       if (stop.getAttribute('offset') != ostop.getAttribute('offset') ||
         stop.getAttribute('stop-opacity') != ostop.getAttribute('stop-opacity') ||
-        stop.getAttribute('stop-color') != ostop.getAttribute('stop-color')) 
+        stop.getAttribute('stop-color') != ostop.getAttribute('stop-color'))
       {
         break;
       }
@@ -7204,28 +7204,28 @@
         var y1 = grad.getAttribute('y1') || 0;
         var x2 = grad.getAttribute('x2') || 1;
         var y2 = grad.getAttribute('y2') || 0;
-        
+
         // Convert to USOU points
         x1 = (bb.width * x1) + bb.x;
         y1 = (bb.height * y1) + bb.y;
         x2 = (bb.width * x2) + bb.x;
         y2 = (bb.height * y2) + bb.y;
-      
+
         // Transform those points
         var pt1 = transformPoint(x1, y1, m);
         var pt2 = transformPoint(x2, y2, m);
-        
+
         // Convert back to BB points
         var g_coords = {};
-        
+
         g_coords.x1 = (pt1.x - bb.x) / bb.width;
         g_coords.y1 = (pt1.y - bb.y) / bb.height;
         g_coords.x2 = (pt2.x - bb.x) / bb.width;
         g_coords.y2 = (pt2.y - bb.y) / bb.height;
-    
+
         var newgrad = grad.cloneNode(true);
         $(newgrad).attr(g_coords);
-  
+
         newgrad.id = getNextId();
         findDefs().appendChild(newgrad);
         elem.setAttribute(type, 'url(#' + newgrad.id + ')');
@@ -7237,7 +7237,7 @@
 // Function: setPaint
 // Set a color/gradient to a fill/stroke
 //
-// Parameters: 
+// Parameters:
 // type - String with "fill" or "stroke"
 // paint - The jGraduate paint object to apply
 this.setPaint = function(type, paint) {
@@ -7248,13 +7248,13 @@
   cur_properties[type + '_paint'] = p;
   switch ( p.type ) {
     case "solidColor":
-      
+
       if (p.solidColor != "none" && p.solidColor != "#none") {
         this.setColor(type, "#"+p.solidColor)
       }
       else {
         this.setColor(type, "none");
-        var selector = (type == "fill") ? "#fill_color rect" : "#stroke_color rect" 
+        var selector = (type == "fill") ? "#fill_color rect" : "#stroke_color rect"
         document.querySelector(selector).setAttribute('fill', 'none');
       }
       break;
@@ -7273,7 +7273,7 @@
 //  // make a copy
 //  var p = new $.jGraduate.Paint(p);
 //  this.setStrokeOpacity(p.alpha/100);
-// 
+//
 //  // now set the current paint object
 //  cur_properties.stroke_paint = p;
 //  switch ( p.type ) {
@@ -7283,17 +7283,17 @@
 //    case "linearGradient"
 //    case "radialGradient"
 //      canvas.strokeGrad = p[p.type];
-//      setGradient(type); 
+//      setGradient(type);
 //    default:
 // //     console.log("none!");
 //  }
 // };
-// 
+//
 // this.setFillPaint = function(p, addGrad) {
 //  // make a copy
 //  var p = new $.jGraduate.Paint(p);
 //  this.setFillOpacity(p.alpha/100, true);
-// 
+//
 //  // now set the current paint object
 //  cur_properties.fill_paint = p;
 //  if (p.type == "solidColor") {
@@ -7301,11 +7301,11 @@
 //  }
 //  else if(p.type == "linearGradient") {
 //    canvas.fillGrad = p.linearGradient;
-//    if(addGrad) setGradient(); 
+//    if(addGrad) setGradient();
 //  }
 //  else if(p.type == "radialGradient") {
 //    canvas.fillGrad = p.radialGradient;
-//    if(addGrad) setGradient(); 
+//    if(addGrad) setGradient();
 //  }
 //  else {
 // //     console.log("none!");
@@ -7330,7 +7330,7 @@
     return;
   }
   cur_properties.stroke_width = val;
-  
+
   var elems = [];
   var i = selectedElements.length;
   while (i--) {
@@ -7338,10 +7338,10 @@
     if (elem) {
       if (elem.tagName == "g")
         svgedit.utilities.walkTree(elem, function(e){if(e.nodeName!="g") elems.push(e);});
-      else 
+      else
         elems.push(elem);
     }
-  }   
+  }
   if (elems.length > 0) {
     changeSelectedAttribute("stroke-width", val, elems);
     call("changed", selectedElements);
@@ -7363,10 +7363,10 @@
     if (elem) {
       if (elem.tagName == "g")
         svgedit.utilities.walkTree(elem, function(e){if(e.nodeName!="g") elems.push(e);});
-      else 
+      else
         elems.push(elem);
     }
-  }   
+  }
   if (elems.length > 0) {
     changeSelectedAttribute(attr, val, elems);
     call("changed", selectedElements);
@@ -7427,7 +7427,7 @@
 this.getBlur = function(elem) {
   var val = 0;
 //    var elem = selectedElements[0];
-  
+
   if(elem) {
     var filter_url = elem.getAttribute('filter');
     if(filter_url) {
@@ -7444,7 +7444,7 @@
   var cur_command = null;
   var filter = null;
   var filterHidden = false;
-  
+
   // Function: setBlurNoUndo
   // Sets the stdDeviation blur value on the selected element without being undoable
   //
@@ -7473,12 +7473,12 @@
       canvas.setBlurOffsets(filter, val);
     }
   }
-  
+
   function finishChange() {
     var bCmd = canvas.undoMgr.finishUndoableChange();
     cur_command.addSubCommand(bCmd);
     addCommandToHistory(cur_command);
-    cur_command = null; 
+    cur_command = null;
     filter = null;
   }
 
@@ -7509,7 +7509,7 @@
     }
   }
 
-  // Function: setBlur 
+  // Function: setBlur
   // Adds/updates the blur filter to the selected element
   //
   // Parameters:
@@ -7520,16 +7520,16 @@
       finishChange();
       return;
     }
-  
+
     // Looks for associated blur, creates one if not found
     var elem = selectedElements[0];
     var elem_id = elem.id;
     filter = getElem(elem_id + '_blur');
-    
+
     val -= 0;
-    
+
     var batchCmd = new BatchCommand();
-    
+
     // Blur found!
     if(filter) {
       if(val === 0) {
@@ -7543,33 +7543,33 @@
           "stdDeviation": val
         }
       });
-      
+
       filter = addSvgElementFromJson({ "element": "filter",
         "attr": {
           "id": elem_id + '_blur'
         }
       });
-      
+
       filter.appendChild(newblur);
       findDefs().appendChild(filter);
-      
+
       batchCmd.addSubCommand(new InsertElementCommand(filter));
     }
 
     var changes = {filter: elem.getAttribute('filter')};
-    
+
     if(val === 0) {
       elem.removeAttribute("filter");
       batchCmd.addSubCommand(new ChangeElementCommand(elem, changes));
       return;
     } else {
       changeSelectedAttribute("filter", 'url(#' + elem_id + '_blur)');
-      
+
       batchCmd.addSubCommand(new ChangeElementCommand(elem, changes));
-      
+
       canvas.setBlurOffsets(filter, val);
     }
-    
+
     cur_command = batchCmd;
     canvas.undoMgr.beginUndoableChange("stdDeviation", [filter?filter.firstChild:null]);
     if(complete) {
@@ -7710,18 +7710,18 @@
 // Function: setImageURL
 // Sets the new image URL for the selected image element. Updates its size if
 // a new URL is given
-// 
+//
 // Parameters:
 // val - String with the image URL/path
 this.setImageURL = function(val) {
   var elem = selectedElements[0];
   if(!elem) return;
-  
+
   var attrs = $(elem).attr(['width', 'height']);
   var setsize = (!attrs.width || !attrs.height);
 
   var cur_href = getHref(elem);
-  
+
   // Do nothing if no URL change or size change
   if(cur_href !== val) {
     setsize = true;
@@ -7737,14 +7737,14 @@
   if(setsize) {
     $(new Image()).load(function() {
       var changes = $(elem).attr(['width', 'height']);
-    
+
       $(elem).attr({
         width: this.width,
         height: this.height
       });
-      
+
       selectorManager.requestSelector(elem).resize();
-      
+
       batchCmd.addSubCommand(new ChangeElementCommand(elem, changes));
       addCommandToHistory(batchCmd);
       call("changed", [elem]);
@@ -7756,7 +7756,7 @@
 
 // Function: setLinkURL
 // Sets the new link URL for the selected anchor element.
-// 
+//
 // Parameters:
 // val - String with the link URL/path
 this.setLinkURL = function(val) {
@@ -7771,11 +7771,11 @@
       return;
     }
   }
-  
+
   var cur_href = getHref(elem);
-  
+
   if(cur_href === val) return;
-  
+
   var batchCmd = new BatchCommand("Change Link URL");
 
   setHref(elem, val);
@@ -7789,14 +7789,14 @@
 
 // Function elementAreSame
 // Checks if all the selected Elements are the same type
-// 
+//
 // Parameters:
 // None
 
 this.elementsAreSame = function(elements) {
   if (!elements.length || elements[0] == null) return null
   else {
-    var isSameElement = function(el) { 
+    var isSameElement = function(el) {
       if (el && selectedElements[0])
         return (el.nodeName == selectedElements[0].nodeName);
       else return null;
@@ -7808,7 +7808,7 @@
 
 // Function: setRectRadius
 // Sets the rx & ry values to the selected rect element to change its corner radius
-// 
+//
 // Parameters:
 // val - The new radius
 this.setRectRadius = function(val) {
@@ -7830,7 +7830,7 @@
 // Wraps the selected element(s) in an anchor element or converts group to one
 this.makeHyperlink = function(url) {
   canvas.groupSelectedElements('a', url);
-  
+
   // TODO: If element is a single "g", convert to "a"
   //  if(selectedElements.length > 1 && selectedElements[1]) {
 
@@ -7844,7 +7844,7 @@
 // Group: Element manipulation
 
 // Function: setSegType
-// Sets the new segment type to the selected segment(s). 
+// Sets the new segment type to the selected segment(s).
 //
 // Parameters:
 // new_type - Integer with the new segment type
@@ -7857,7 +7857,7 @@
 // Function: convertToPath
 // Convert selected element to a path, or get the BBox of an element-as-path
 //
-// Parameters: 
+// Parameters:
 // elem - The DOM element to be converted
 // getBBox - Boolean on whether or not to only return the path's BBox
 //
@@ -7872,11 +7872,11 @@
     });
     return;
   }
-  
+
   if(!getBBox) {
     var batchCmd = new BatchCommand("Convert element to Path");
   }
-  
+
   var attrs = getBBox?{}:{
     "fill": cur_shape.fill,
     "fill-opacity": cur_shape.fill_opacity,
@@ -7889,7 +7889,7 @@
     "opacity": cur_shape.opacity,
     "visibility":"hidden"
   };
-  
+
   // any attribute on the element not covered by the above
   // TODO: make this list global so that we can properly maintain it
   // TODO: what about @transform, @clip-rule, @fill-rule, etc?
@@ -7898,17 +7898,17 @@
       attrs[this] = elem.getAttribute(this);
     }
   });
-  
+
   var path = addSvgElementFromJson({
     "element": "path",
     "attr": attrs
   });
-  
+
   var eltrans = elem.getAttribute("transform");
   if(eltrans) {
     path.setAttribute("transform",eltrans);
   }
-  
+
   var id = elem.id;
   var parent = elem.parentNode;
   if(elem.nextSibling) {
@@ -7916,9 +7916,9 @@
   } else {
     parent.appendChild(path);
   }
-  
+
   var d = '';
-  
+
   var joinSegs = function(segs) {
     $.each(segs, function(j, seg) {
       var l = seg[0], pts = seg[1];
@@ -7940,7 +7940,7 @@
     if(elem.tagName == 'circle') {
       rx = ry = $(elem).attr('r');
     }
-  
+
     joinSegs([
       ['M',[(cx-rx),(cy)]],
       ['C',[(cx-rx),(cy-ry/num), (cx-rx/num),(cy-ry), (cx),(cy-ry)]],
@@ -7997,14 +7997,14 @@
     path.parentNode.removeChild(path);
     break;
   }
-  
+
   if(d) {
     path.setAttribute('d',d);
   }
-  
+
   if(!getBBox) {
     // Replace the current element with the converted one
-    
+
     // Reorient if it has a matrix
     if(eltrans) {
       var tlist = getTransformList(path);
@@ -8012,7 +8012,7 @@
         pathActions.resetOrientation(path);
       }
     }
-    
+
     var nextSibling = elem.nextSibling;
     batchCmd.addSubCommand(new RemoveElementCommand(elem, nextSibling, parent));
     batchCmd.addSubCommand(new InsertElementCommand(path));
@@ -8022,9 +8022,9 @@
     path.setAttribute('id', id);
     path.removeAttribute("visibility");
     addToSelection([path], true);
-    
+
     addCommandToHistory(batchCmd);
-    
+
   } else {
     // Get the correct BBox of the new path, then discard it
     pathActions.resetOrientation(path);
@@ -8042,8 +8042,8 @@
 
 // Function: changeSelectedAttributeNoUndo
 // This function makes the changes to the elements. It does not add the change
-// to the history stack. 
-// 
+// to the history stack.
+//
 // Parameters:
 // attr - String with the attribute name
 // newValue - String or number with the new attribute value
@@ -8099,7 +8099,7 @@
           },0);
         }
         // if this element was rotated, and we changed the position of this element
-        // we need to update the rotational transform attribute 
+        // we need to update the rotational transform attribute
         var angle = getRotationAngle(elem);
         if (angle != 0 && attr != "transform") {
           var tlist = getTransformList(elem);
@@ -8130,7 +8130,7 @@
 // If you want to change all selectedElements, ignore the elems argument.
 // If you want to change only a subset of selectedElements, then send the
 // subset to this function in the elems argument.
-// 
+//
 // Parameters:
 // attr - String with the attribute name
 // newValue - String or number with the new attribute value
@@ -8143,13 +8143,13 @@
   changeSelectedAttributeNoUndo(attr, val, elems);
 
   var batchCmd = canvas.undoMgr.finishUndoableChange();
-  if (!batchCmd.isEmpty()) { 
+  if (!batchCmd.isEmpty()) {
     addCommandToHistory(batchCmd);
   }
 };
 
 // Function: deleteSelectedElements
-// Removes all selected elements from the DOM and adds the change to the 
+// Removes all selected elements from the DOM and adds the change to the
 // history stack
 this.deleteSelectedElements = function() {
   var batchCmd = new BatchCommand("Delete Elements");
@@ -8161,19 +8161,19 @@
 
     var parent = selected.parentNode;
     var t = selected;
-    
+
     // this will unselect the element and remove the selectedOutline
     selectorManager.releaseSelector(t);
-    
+
     // Remove the path if present.
     svgedit.path.removePath_(t.id);
-    
+
     // Get the parent if it's a single-child anchor
     if(parent.tagName === 'a' && parent.childNodes.length === 1) {
       t = parent;
       parent = parent.parentNode;
     }
-    
+
     var nextSibling = t.nextSibling;
     var elem = parent.removeChild(t);
     selectedCopy.push(selected); //for the copy
@@ -8186,7 +8186,7 @@
 };
 
 // Function: cutSelectedElements
-// Removes all selected elements from the DOM and adds the change to the 
+// Removes all selected elements from the DOM and adds the change to the
 // history stack. Remembers removed elements on the clipboard
 
 // TODO: Combine similar code with deleteSelectedElements
@@ -8216,7 +8216,7 @@
   if (!batchCmd.isEmpty()) addCommandToHistory(batchCmd);
   call("changed", selectedCopy);
   clearSelection();
-  
+
   canvas.clipBoard = selectedCopy;
 };
 
@@ -8230,10 +8230,10 @@
   var cb = canvas.clipBoard;
   var len = cb.length;
   if(!len) return;
-  
+
   var pasted = [];
   var batchCmd = new BatchCommand('Paste elements');
-  
+
   // Move elements to lastClickPoint
 
   while (len--) {
@@ -8249,9 +8249,9 @@
   }
   svgCanvas.clearSelection();
   setTimeout(function(){selectOnly(pasted)},100);
-  
 
-  
+
+
   addCommandToHistory(batchCmd);
   call("changed", pasted);
 }
@@ -8259,12 +8259,12 @@
 // Function: groupSelectedElements
 // Wraps all the selected elements in a group (g) element
 
-// Parameters: 
+// Parameters:
 // type - type of element to group into, defaults to <g>
 this.groupSelectedElements = function(type) {
   if(!type) type = 'g';
   var cmd_str = '';
-  
+
   switch ( type ) {
     case "a":
       cmd_str = "Make hyperlink";
@@ -8278,9 +8278,9 @@
       cmd_str = "Group Elements";
       break;
   }
-  
+
   var batchCmd = new BatchCommand(cmd_str);
-  
+
   // create and insert the group element
   var g = addSvgElementFromJson({
               "element": type,
@@ -8292,24 +8292,24 @@
     setHref(g, url);
   }
   batchCmd.addSubCommand(new InsertElementCommand(g));
-  
+
   // now move all children into the group
   var i = selectedElements.length;
   while (i--) {
     var elem = selectedElements[i];
     if (elem == null) continue;
-    
+
     if (elem.parentNode.tagName === 'a' && elem.parentNode.childNodes.length === 1) {
       elem = elem.parentNode;
     }
-    
+
     var oldNextSibling = elem.nextSibling;
     var oldParent = elem.parentNode;
     g.appendChild(elem);
-    batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent));      
+    batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent));
   }
   if (!batchCmd.isEmpty()) addCommandToHistory(batchCmd);
-  
+
   // update selection
   selectOnly([g], true);
 };
@@ -8326,27 +8326,27 @@
 
   var glist = getTransformList(g);
   var m = transformListToTransform(glist).matrix;
-  
+
   var batchCmd = new BatchCommand("Push group properties");
 
   // TODO: get all fill/stroke properties from the group that we are about to destroy
-  // "fill", "fill-opacity", "fill-rule", "stroke", "stroke-dasharray", "stroke-dashoffset", 
-  // "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", 
+  // "fill", "fill-opacity", "fill-rule", "stroke", "stroke-dasharray", "stroke-dashoffset",
+  // "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity",
   // "stroke-width"
   // and then for each child, if they do not have the attribute (or the value is 'inherit')
   // then set the child's attribute
-  
+
   var i = 0;
   var gangle = getRotationAngle(g);
-  
+
   var gattrs = $(g).attr(['filter', 'opacity']);
   var gfilter, gblur;
-  
+
   for(var i = 0; i < len; i++) {
     var elem = children[i];
-    
+
     if(elem.nodeType !== 1) continue;
-    
+
     if(gattrs.opacity !== null && gattrs.opacity !== 1) {
       var c_opac = elem.getAttribute('opacity') || 1;
       var new_opac = Math.round((elem.getAttribute('opacity') || 1) * gattrs.opacity * 100)/100;
@@ -8363,7 +8363,7 @@
       } else if(cblur === 0) {
         cblur = gblur;
       }
-      
+
       // If child has no current filter, get group's filter or clone it.
       if(!orig_cblur) {
         // Set group's filter to use first child's ID
@@ -8379,28 +8379,28 @@
       }
 
       // Change this in future for different filters
-      var suffix = (gfilter.firstChild.tagName === 'feGaussianBlur')?'blur':'filter'; 
+      var suffix = (gfilter.firstChild.tagName === 'feGaussianBlur')?'blur':'filter';
       gfilter.id = elem.id + '_' + suffix;
       changeSelectedAttribute('filter', 'url(#' + gfilter.id + ')', [elem]);
-      
-      // Update blur value 
+
+      // Update blur value
       if(cblur) {
         changeSelectedAttribute('stdDeviation', cblur, [gfilter.firstChild]);
         canvas.setBlurOffsets(gfilter, cblur);
       }
     }
-    
+
     var chtlist = getTransformList(elem);
 
     // Don't process gradient transforms
     if(~elem.tagName.indexOf('Gradient')) chtlist = null;
-    
+
     // Hopefully not a problem to add this. Necessary for elements like <desc/>
     if(!chtlist) continue;
-    
+
     // Apparently <defs> can get get a transformlist, but we don't want it to have one!
     if(elem.tagName === 'defs') continue;
-    
+
     if (glist.numberOfItems) {
       // TODO: if the group's transform is just a rotate, we can always transfer the
       // rotate() down to the children (collapsing consecutive rotates and factoring
@@ -8408,43 +8408,43 @@
       if (gangle && glist.numberOfItems == 1) {
         // [Rg] [Rc] [Mc]
         // we want [Tr] [Rc2] [Mc] where:
-        //  - [Rc2] is at the child's current center but has the 
+        //  - [Rc2] is at the child's current center but has the
         //    sum of the group and child's rotation angles
-        //  - [Tr] is the equivalent translation that this child 
+        //  - [Tr] is the equivalent translation that this child
         //    undergoes if the group wasn't there
-        
+
         // [Tr] = [Rg] [Rc] [Rc2_inv]
-        
+
         // get group's rotation matrix (Rg)
         var rgm = glist.getItem(0).matrix;
-        
+
         // get child's rotation matrix (Rc)
         var rcm = svgroot.createSVGMatrix();
         var cangle = getRotationAngle(elem);
         if (cangle) {
           rcm = chtlist.getItem(0).matrix;
         }
-        
+
         // get child's old center of rotation
         var cbox = svgedit.utilities.getBBox(elem);
         var ceqm = transformListToTransform(chtlist).matrix;
         var coldc = transformPoint(cbox.x+cbox.width/2, cbox.y+cbox.height/2,ceqm);
-        
+
         // sum group and child's angles
         var sangle = gangle + cangle;
-        
+
         // get child's rotation at the old center (Rc2_inv)
         var r2 = svgroot.createSVGTransform();
         r2.setRotate(sangle, coldc.x, coldc.y);
-        
+
         // calculate equivalent translate
         var trm = matrixMultiply(rgm, rcm, r2.matrix.inverse());
-        
+
         // set up tlist
         if (cangle) {
           chtlist.removeItem(0);
         }
-        
+
         if (sangle) {
           if(chtlist.numberOfItems) {
             chtlist.insertItemBefore(r2, 0);
@@ -8464,9 +8464,9 @@
         }
       }
       else { // more complicated than just a rotate
-      
+
         // transfer the group's transform down to each child and then
-        // call recalculateDimensions()       
+        // call recalculateDimensions()
         var oldxform = elem.getAttribute("transform");
         var changes = {};
         changes["transform"] = oldxform ? oldxform : "";
@@ -8486,16 +8486,16 @@
     }
   }
 
-  
+
   // remove transform and make it undo-able
   if (xform) {
     var changes = {};
     changes["transform"] = xform;
     g.setAttribute("transform", "");
-    g.removeAttribute("transform");       
+    g.removeAttribute("transform");
     batchCmd.addSubCommand(new ChangeElementCommand(g, changes));
   }
-  
+
   if (undoable && !batchCmd.isEmpty()) {
     return batchCmd;
   }
@@ -8523,25 +8523,25 @@
   if(parents_a.length) {
     g = parents_a[0];
   }
-  
+
   // Look for parent "a"
   if (g.tagName === "g" || g.tagName === "a") {
-    
+
     var batchCmd = new BatchCommand("Ungroup Elements");
     var cmd = pushGroupProperties(g, true);
     if(cmd) batchCmd.addSubCommand(cmd);
-    
+
     var parent = g.parentNode;
     var anchor = g.nextSibling;
     var children = new Array(g.childNodes.length);
-    
+
     var i = 0;
-    
+
     while (g.firstChild) {
       var elem = g.firstChild;
       var oldNextSibling = elem.nextSibling;
       var oldParent = elem.parentNode;
-      
+
       // Remove child title elements
       if(elem.tagName === 'title') {
         var nextSibling = elem.nextSibling;
@@ -8549,21 +8549,21 @@
         oldParent.removeChild(elem);
         continue;
       }
-      
+
       children[i++] = elem = parent.insertBefore(elem, anchor);
       batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent));
     }
 
-    // remove the group from the selection      
+    // remove the group from the selection
     clearSelection();
-    
+
     // delete the group element (but make undo-able)
     var gNextSibling = g.nextSibling;
     g = parent.removeChild(g);
     batchCmd.addSubCommand(new RemoveElementCommand(g, gNextSibling, parent));
 
     if (!batchCmd.isEmpty()) addCommandToHistory(batchCmd);
-    
+
     // update selection
     addToSelection(children);
   }
@@ -8591,7 +8591,7 @@
 };
 
 // Function: moveToBottomSelectedElement
-// Repositions the selected element to the top in the DOM to appear under 
+// Repositions the selected element to the top in the DOM to appear under
 // other elements
 this.moveToBottomSelectedElement = function() {
   var selected = selectedElements.filter(Boolean).reverse();
@@ -8624,7 +8624,7 @@
 // Moves the select element up or down the stack, based on the visibly
 // intersecting elements
 //
-// Parameters: 
+// Parameters:
 // dir - String that's either 'Up' or 'Down'
 this.moveUpDownSelected = function(dir) {
   var selected = selectedElements.filter(Boolean);
@@ -8648,7 +8648,7 @@
       return false;
     });
     if(!closest) return;
-    
+
     var t = selected;
     var oldParent = t.parentNode;
     var oldNextSibling = t.nextSibling;
@@ -8664,7 +8664,7 @@
 };
 
 // Function: moveSelectedElements
-// Moves selected elements on the X/Y axis 
+// Moves selected elements on the X/Y axis
 //
 // Parameters:
 // dx - Float with the distance to move on the x-axis
@@ -8688,14 +8688,14 @@
     if (selected != null) {
 //      if (i==0)
 //        selectedBBoxes[0] = svgedit.utilities.getBBox(selected);
-      
+
 //      var b = {};
 //      for(var j in selectedBBoxes[i]) b[j] = selectedBBoxes[i][j];
 //      selectedBBoxes[i] = b;
-      
+
       var xform = svgroot.createSVGTransform();
       var tlist = getTransformList(selected);
-      
+
       // dx and dy could be arrays
       if (dx.constructor == Array) {
 //        if (i==0) {
@@ -8716,12 +8716,12 @@
       } else {
         tlist.appendItem(xform);
       }
-      
+
       var cmd = recalculateDimensions(selected);
       if (cmd) {
         batchCmd.addSubCommand(cmd);
       }
-      
+
       selectorManager.requestSelector(selected).resize();
     }
   }
@@ -8734,7 +8734,7 @@
 };
 
 // Function: cloneSelectedElements
-// Create deep DOM copies (clones) of all selected elements and move them slightly 
+// Create deep DOM copies (clones) of all selected elements and move them slightly
 // from their originals
 this.cloneSelectedElements = function(x,y, drag) {
   var batchCmd = new BatchCommand("Clone Elements");
@@ -8753,7 +8753,7 @@
   clones = []
   while (i--) {
     // clone each element and replace it within copiedElements
-    var elem = copiedElements[i] 
+    var elem = copiedElements[i]
     var clone = copyElem(copiedElements[i]);
     var parent = (current_group || getCurrentDrawing().getCurrentLayer())
     if (drag) {
@@ -8769,7 +8769,7 @@
     clones.push(clone)
     batchCmd.addSubCommand(new InsertElementCommand(clone));
   }
-  
+
   if (!batchCmd.isEmpty()) {
     addToSelection(copiedElements.reverse()); // Need to reverse for correct selection-adding
     if (!drag) this.moveSelectedElements(x,y,false);
@@ -8783,7 +8783,7 @@
 //
 // Parameters:
 // type - String with single character indicating the alignment type
-// relative_to - String that must be one of the following: 
+// relative_to - String that must be one of the following:
 // "selected", "largest", "smallest", "page"
 this.alignSelectedElements = function(type, relative_to) {
   var bboxes = [], angles = [];
@@ -8795,7 +8795,7 @@
     if (selectedElements[i] == null) break;
     var elem = selectedElements[i];
     bboxes[i] = getStrokedBBox([elem]);
-    
+
     // now bbox is axis-aligned and handles rotation
     switch (relative_to) {
       case 'smallest':
@@ -8874,13 +8874,13 @@
 this.contentH = getResolution().h;
 
 // Function: updateCanvas
-// Updates the editor canvas width/height/position after a zoom has occurred 
+// Updates the editor canvas width/height/position after a zoom has occurred
 //
 // Parameters:
 // w - Float with the new width
 // h - Float with the new height
 //
-// Returns: 
+// Returns:
 // Object with the following values:
 // * x - The canvas' new x coordinate
 // * y - The canvas' new y coordinate
@@ -8904,7 +8904,7 @@
     'y': y,
     "viewBox" : "0 0 " + this.contentW + " " + this.contentH
   });
-  
+
   assignAttributes(bg, {
     width: svgcontent.getAttribute('width'),
     height: svgcontent.getAttribute('height'),
@@ -8919,9 +8919,9 @@
       'height': '100%'
     });
   }
-  
+
   selectorManager.selectorParentGroup.setAttribute("transform","translate(" + x + "," + y + ")");
-  
+
   return {x:x, y:y, old_x:old_x, old_y:old_y, d_x:x - old_x, d_y:y - old_y};
 }
 
@@ -8976,12 +8976,12 @@
           num = 0;
         } else if(num < 0) {
           num = all_elems.length-1;
-        } 
+        }
         elem = all_elems[num];
         break;
-      } 
+      }
     }
-  }   
+  }
   selectOnly([elem], true);
   call("selected", selectedElements);
 }
@@ -8989,7 +8989,7 @@
 this.clear();
 
 
-// DEPRECATED: getPrivateMethods 
+// DEPRECATED: getPrivateMethods
 // Since all methods are/should be public somehow, this function should be removed
 
 // Being able to access private methods publicly seems wrong somehow,

--
Gitblit v1.9.1