From 4043c6e537f4d9f8a6ca1dcfe1a0b70e160f415d Mon Sep 17 00:00:00 2001 From: codedread Date: Thu, 17 May 2018 21:02:30 -0700 Subject: [PATCH] Change all tab indentations to 2sp indentation. Addresses issue #37 --- editor/browser.js | 170 +- editor/config-sample.js | 184 +- editor/contextmenu.js | 60 +- editor/coords.js | 510 +- editor/draw.js | 704 +-- editor/embedapi-dom.js | 122 +- editor/embedapi.html | 20 +- editor/embedapi.js | 202 +- editor/history.js | 552 +- editor/historyrecording.js | 70 +- editor/layer.js | 148 +- editor/math.js | 158 +- editor/path.js | 1414 ++--- editor/pathseg.js | 1534 +++--- editor/recalculate.js | 1390 ++--- editor/sanitize.js | 402 +- editor/select.js | 716 +-- editor/svg-editor.css | 1518 ++--- editor/svg-editor.html | 1216 ++-- editor/svg-editor.js | 10282 +++++++++++++++++----------------- editor/svgcanvas.js | 10420 +++++++++++++++++------------------ editor/svgedit.js | 32 +- editor/svgtransformlist.js | 420 +- editor/svgutils.js | 1782 +++--- editor/touch.js | 46 +- editor/units.js | 310 +- 26 files changed, 17191 insertions(+), 17191 deletions(-) diff --git a/editor/browser.js b/editor/browser.js index 8b5e5b4a..04a484a5 100644 --- a/editor/browser.js +++ b/editor/browser.js @@ -16,20 +16,20 @@ 'use strict'; if (!svgedit.browser) { - svgedit.browser = {}; + svgedit.browser = {}; } // alias var NS = svgedit.NS; var supportsSvg_ = (function () { - return !!document.createElementNS && !!document.createElementNS(NS.SVG, 'svg').createSVGRect; + return !!document.createElementNS && !!document.createElementNS(NS.SVG, 'svg').createSVGRect; }()); svgedit.browser.supportsSvg = function () { return supportsSvg_; }; if (!svgedit.browser.supportsSvg()) { - window.location = 'browser-not-supported.html'; - return; + window.location = 'browser-not-supported.html'; + return; } var userAgent = navigator.userAgent; @@ -46,120 +46,120 @@ var isMac_ = userAgent.indexOf('Macintosh') >= 0; var isTouch_ = 'ontouchstart' in window; var supportsSelectors_ = (function () { - return !!svg.querySelector; + return !!svg.querySelector; }()); var supportsXpath_ = (function () { - return !!document.evaluate; + return !!document.evaluate; }()); // segList functions (for FF1.5 and 2.0) var supportsPathReplaceItem_ = (function () { - var path = document.createElementNS(NS.SVG, 'path'); - path.setAttribute('d', 'M0,0 10,10'); - var seglist = path.pathSegList; - var seg = path.createSVGPathSegLinetoAbs(5, 5); - try { - seglist.replaceItem(seg, 1); - return true; - } catch (err) {} - return false; + var path = document.createElementNS(NS.SVG, 'path'); + path.setAttribute('d', 'M0,0 10,10'); + var seglist = path.pathSegList; + var seg = path.createSVGPathSegLinetoAbs(5, 5); + try { + seglist.replaceItem(seg, 1); + return true; + } catch (err) {} + return false; }()); var supportsPathInsertItemBefore_ = (function () { - var path = document.createElementNS(NS.SVG, 'path'); - path.setAttribute('d', 'M0,0 10,10'); - var seglist = path.pathSegList; - var seg = path.createSVGPathSegLinetoAbs(5, 5); - try { - seglist.insertItemBefore(seg, 1); - return true; - } catch (err) {} - return false; + var path = document.createElementNS(NS.SVG, 'path'); + path.setAttribute('d', 'M0,0 10,10'); + var seglist = path.pathSegList; + var seg = path.createSVGPathSegLinetoAbs(5, 5); + try { + seglist.insertItemBefore(seg, 1); + return true; + } catch (err) {} + return false; }()); // text character positioning (for IE9) var supportsGoodTextCharPos_ = (function () { - var svgroot = document.createElementNS(NS.SVG, 'svg'); - var svgcontent = document.createElementNS(NS.SVG, 'svg'); - document.documentElement.appendChild(svgroot); - svgcontent.setAttribute('x', 5); - svgroot.appendChild(svgcontent); - var text = document.createElementNS(NS.SVG, 'text'); - text.textContent = 'a'; - svgcontent.appendChild(text); - var pos = text.getStartPositionOfChar(0).x; - document.documentElement.removeChild(svgroot); - return (pos === 0); + var svgroot = document.createElementNS(NS.SVG, 'svg'); + var svgcontent = document.createElementNS(NS.SVG, 'svg'); + document.documentElement.appendChild(svgroot); + svgcontent.setAttribute('x', 5); + svgroot.appendChild(svgcontent); + var text = document.createElementNS(NS.SVG, 'text'); + text.textContent = 'a'; + svgcontent.appendChild(text); + var pos = text.getStartPositionOfChar(0).x; + document.documentElement.removeChild(svgroot); + return (pos === 0); }()); var supportsPathBBox_ = (function () { - var svgcontent = document.createElementNS(NS.SVG, 'svg'); - document.documentElement.appendChild(svgcontent); - var path = document.createElementNS(NS.SVG, 'path'); - path.setAttribute('d', 'M0,0 C0,0 10,10 10,0'); - svgcontent.appendChild(path); - var bbox = path.getBBox(); - document.documentElement.removeChild(svgcontent); - return (bbox.height > 4 && bbox.height < 5); + var svgcontent = document.createElementNS(NS.SVG, 'svg'); + document.documentElement.appendChild(svgcontent); + var path = document.createElementNS(NS.SVG, 'path'); + path.setAttribute('d', 'M0,0 C0,0 10,10 10,0'); + svgcontent.appendChild(path); + var bbox = path.getBBox(); + document.documentElement.removeChild(svgcontent); + return (bbox.height > 4 && bbox.height < 5); }()); // Support for correct bbox sizing on groups with horizontal/vertical lines var supportsHVLineContainerBBox_ = (function () { - var svgcontent = document.createElementNS(NS.SVG, 'svg'); - document.documentElement.appendChild(svgcontent); - var path = document.createElementNS(NS.SVG, 'path'); - path.setAttribute('d', 'M0,0 10,0'); - var path2 = document.createElementNS(NS.SVG, 'path'); - path2.setAttribute('d', 'M5,0 15,0'); - var g = document.createElementNS(NS.SVG, 'g'); - g.appendChild(path); - g.appendChild(path2); - svgcontent.appendChild(g); - var bbox = g.getBBox(); - document.documentElement.removeChild(svgcontent); - // Webkit gives 0, FF gives 10, Opera (correctly) gives 15 - return (bbox.width === 15); + var svgcontent = document.createElementNS(NS.SVG, 'svg'); + document.documentElement.appendChild(svgcontent); + var path = document.createElementNS(NS.SVG, 'path'); + path.setAttribute('d', 'M0,0 10,0'); + var path2 = document.createElementNS(NS.SVG, 'path'); + path2.setAttribute('d', 'M5,0 15,0'); + var g = document.createElementNS(NS.SVG, 'g'); + g.appendChild(path); + g.appendChild(path2); + svgcontent.appendChild(g); + var bbox = g.getBBox(); + document.documentElement.removeChild(svgcontent); + // Webkit gives 0, FF gives 10, Opera (correctly) gives 15 + return (bbox.width === 15); }()); var supportsEditableText_ = (function () { - // TODO: Find better way to check support for this - return isOpera_; + // TODO: Find better way to check support for this + return isOpera_; }()); var supportsGoodDecimals_ = (function () { - // Correct decimals on clone attributes (Opera < 10.5/win/non-en) - var rect = document.createElementNS(NS.SVG, 'rect'); - rect.setAttribute('x', 0.1); - var crect = rect.cloneNode(false); - var retValue = (crect.getAttribute('x').indexOf(',') === -1); - if (!retValue) { - $.alert('NOTE: This version of Opera is known to contain bugs in SVG-edit.\n' + - 'Please upgrade to the latest version in which the problems have been fixed.'); - } - return retValue; + // Correct decimals on clone attributes (Opera < 10.5/win/non-en) + var rect = document.createElementNS(NS.SVG, 'rect'); + rect.setAttribute('x', 0.1); + var crect = rect.cloneNode(false); + var retValue = (crect.getAttribute('x').indexOf(',') === -1); + if (!retValue) { + $.alert('NOTE: This version of Opera is known to contain bugs in SVG-edit.\n' + + 'Please upgrade to the latest version in which the problems have been fixed.'); + } + return retValue; }()); var supportsNonScalingStroke_ = (function () { - var rect = document.createElementNS(NS.SVG, 'rect'); - rect.setAttribute('style', 'vector-effect:non-scaling-stroke'); - return rect.style.vectorEffect === 'non-scaling-stroke'; + var rect = document.createElementNS(NS.SVG, 'rect'); + rect.setAttribute('style', 'vector-effect:non-scaling-stroke'); + return rect.style.vectorEffect === 'non-scaling-stroke'; }()); var supportsNativeSVGTransformLists_ = (function () { - var rect = document.createElementNS(NS.SVG, 'rect'); - var rxform = rect.transform.baseVal; - var t1 = svg.createSVGTransform(); - rxform.appendItem(t1); - var r1 = rxform.getItem(0); - return r1 instanceof SVGTransform && t1 instanceof SVGTransform && - r1.type === t1.type && r1.angle === t1.angle && - r1.matrix.a === t1.matrix.a && - r1.matrix.b === t1.matrix.b && - r1.matrix.c === t1.matrix.c && - r1.matrix.d === t1.matrix.d && - r1.matrix.e === t1.matrix.e && - r1.matrix.f === t1.matrix.f; + var rect = document.createElementNS(NS.SVG, 'rect'); + var rxform = rect.transform.baseVal; + var t1 = svg.createSVGTransform(); + rxform.appendItem(t1); + var r1 = rxform.getItem(0); + return r1 instanceof SVGTransform && t1 instanceof SVGTransform && + r1.type === t1.type && r1.angle === t1.angle && + r1.matrix.a === t1.matrix.a && + r1.matrix.b === t1.matrix.b && + r1.matrix.c === t1.matrix.c && + r1.matrix.d === t1.matrix.d && + r1.matrix.e === t1.matrix.e && + r1.matrix.f === t1.matrix.f; }()); // Public API diff --git a/editor/config-sample.js b/editor/config-sample.js index fc8c24f6..39ad9095 100644 --- a/editor/config-sample.js +++ b/editor/config-sample.js @@ -21,86 +21,86 @@ See svg-editor.js for documentation on using setConfig(). // URL OVERRIDE CONFIG svgEditor.setConfig({ - /** - To override the ability for URLs to set URL-based SVG content, - uncomment the following: - */ - // preventURLContentLoading: true, - /** - To override the ability for URLs to set other configuration (including - extension config), uncomment the following: - */ - // preventAllURLConfig: true, - /** - To override the ability for URLs to set their own extensions, - uncomment the following (note that if setConfig() is used in - extension code, it will still be additive to extensions, - however): - */ - // lockExtensions: true, + /** + To override the ability for URLs to set URL-based SVG content, + uncomment the following: + */ + // preventURLContentLoading: true, + /** + To override the ability for URLs to set other configuration (including + extension config), uncomment the following: + */ + // preventAllURLConfig: true, + /** + To override the ability for URLs to set their own extensions, + uncomment the following (note that if setConfig() is used in + extension code, it will still be additive to extensions, + however): + */ + // lockExtensions: true, }); svgEditor.setConfig({ - /* - Provide default values here which differ from that of the editor but - which the URL can override - */ + /* + Provide default values here which differ from that of the editor but + which the URL can override + */ }, {allowInitialUserOverride: true}); // EXTENSION CONFIG svgEditor.setConfig({ - extensions: [ - // 'ext-overview_window.js', 'ext-markers.js', 'ext-connector.js', 'ext-eyedropper.js', 'ext-shapes.js', 'ext-imagelib.js', 'ext-grid.js', 'ext-polygon.js', 'ext-star.js', 'ext-panning.js', 'ext-storage.js' - ] - // , noDefaultExtensions: false, // noDefaultExtensions can only be meaningfully used in config.js or in the URL + extensions: [ + // 'ext-overview_window.js', 'ext-markers.js', 'ext-connector.js', 'ext-eyedropper.js', 'ext-shapes.js', 'ext-imagelib.js', 'ext-grid.js', 'ext-polygon.js', 'ext-star.js', 'ext-panning.js', 'ext-storage.js' + ] + // , noDefaultExtensions: false, // noDefaultExtensions can only be meaningfully used in config.js or in the URL }); // OTHER CONFIG svgEditor.setConfig({ - // canvasName: 'default', - // canvas_expansion: 3, - // initFill: { - // color: 'FF0000', // solid red - // opacity: 1 - // }, - // initStroke: { - // width: 5, - // color: '000000', // solid black - // opacity: 1 - // }, - // initOpacity: 1, - // colorPickerCSS: null, - // initTool: 'select', - // exportWindowType: 'new', // 'same' - // wireframe: false, - // showlayers: false, - // no_save_warning: false, - // PATH CONFIGURATION - // imgPath: 'images/', - // langPath: 'locale/', - // extPath: 'extensions/', - // jGraduatePath: 'jgraduate/images/', - /* - Uncomment the following to allow at least same domain (embedded) access, - including file:// access. - Setting as `['*']` would allow any domain to access but would be unsafe to - data privacy and integrity. - */ - // allowedOrigins: [window.location.origin || 'null'], // May be 'null' (as a string) when used as a file:// URL - // DOCUMENT PROPERTIES - // dimensions: [640, 480], - // EDITOR OPTIONS - // gridSnapping: false, - // gridColor: '#000', - // baseUnit: 'px', - // snappingStep: 10, - // showRulers: true, - // EXTENSION-RELATED (GRID) - // showGrid: false, // Set by ext-grid.js - // EXTENSION-RELATED (STORAGE) - // noStorageOnLoad: false, // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage - // forceStorage: false, // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not - // emptyStorageOnDecline: true, // Used by ext-storage.js; empty any prior storage if the user declines to store + // canvasName: 'default', + // canvas_expansion: 3, + // initFill: { + // color: 'FF0000', // solid red + // opacity: 1 + // }, + // initStroke: { + // width: 5, + // color: '000000', // solid black + // opacity: 1 + // }, + // initOpacity: 1, + // colorPickerCSS: null, + // initTool: 'select', + // exportWindowType: 'new', // 'same' + // wireframe: false, + // showlayers: false, + // no_save_warning: false, + // PATH CONFIGURATION + // imgPath: 'images/', + // langPath: 'locale/', + // extPath: 'extensions/', + // jGraduatePath: 'jgraduate/images/', + /* + Uncomment the following to allow at least same domain (embedded) access, + including file:// access. + Setting as `['*']` would allow any domain to access but would be unsafe to + data privacy and integrity. + */ + // allowedOrigins: [window.location.origin || 'null'], // May be 'null' (as a string) when used as a file:// URL + // DOCUMENT PROPERTIES + // dimensions: [640, 480], + // EDITOR OPTIONS + // gridSnapping: false, + // gridColor: '#000', + // baseUnit: 'px', + // snappingStep: 10, + // showRulers: true, + // EXTENSION-RELATED (GRID) + // showGrid: false, // Set by ext-grid.js + // EXTENSION-RELATED (STORAGE) + // noStorageOnLoad: false, // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage + // forceStorage: false, // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not + // emptyStorageOnDecline: true, // Used by ext-storage.js; empty any prior storage if the user declines to store }); // PREF CHANGES @@ -118,29 +118,29 @@ As with configuration, one may use allowInitialUserOverride, but are hard-coded here regardless of URL or prior user storage setting. */ svgEditor.setConfig( - { - // lang: '', // Set dynamically within locale.js if not previously set - // iconsize: '', // Will default to 's' if the window height is smaller than the minimum height and 'm' otherwise - /** - * When showing the preferences dialog, svg-editor.js currently relies - * on curPrefs instead of $.pref, so allowing an override for bkgd_color - * means that this value won't have priority over block auto-detection as - * far as determining which color shows initially in the preferences - * dialog (though it can be changed and saved). - */ - // bkgd_color: '#FFF', - // bkgd_url: '', - // img_save: 'embed', - // Only shows in UI as far as alert notices - // save_notice_done: false, - // export_notice_done: false - } + { + // lang: '', // Set dynamically within locale.js if not previously set + // iconsize: '', // Will default to 's' if the window height is smaller than the minimum height and 'm' otherwise + /** + * When showing the preferences dialog, svg-editor.js currently relies + * on curPrefs instead of $.pref, so allowing an override for bkgd_color + * means that this value won't have priority over block auto-detection as + * far as determining which color shows initially in the preferences + * dialog (though it can be changed and saved). + */ + // bkgd_color: '#FFF', + // bkgd_url: '', + // img_save: 'embed', + // Only shows in UI as far as alert notices + // save_notice_done: false, + // export_notice_done: false + } ); svgEditor.setConfig( - { - // Indicate pref settings here if you wish to allow user storage or URL settings - // to be able to override your default preferences (unless other config options - // have already explicitly prevented one or the other) - }, - {allowInitialUserOverride: true} + { + // Indicate pref settings here if you wish to allow user storage or URL settings + // to be able to override your default preferences (unless other config options + // have already explicitly prevented one or the other) + }, + {allowInitialUserOverride: true} ); diff --git a/editor/contextmenu.js b/editor/contextmenu.js index 986c462c..0df08ab4 100644 --- a/editor/contextmenu.js +++ b/editor/contextmenu.js @@ -13,51 +13,51 @@ var svgedit = svgedit || {}; // eslint-disable-line no-use-before-define (function () { var self = this; if (!svgedit.contextmenu) { - svgedit.contextmenu = {}; + svgedit.contextmenu = {}; } self.contextMenuExtensions = {}; var menuItemIsValid = function (menuItem) { - return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action === 'function'; + return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action === 'function'; }; var addContextMenuItem = function (menuItem) { - // menuItem: {id, label, shortcut, action} - if (!menuItemIsValid(menuItem)) { - console.error('Menu items must be defined and have at least properties: id, label, action, where action must be a function'); - return; - } - if (menuItem.id in self.contextMenuExtensions) { - console.error('Cannot add extension "' + menuItem.id + '", an extension by that name already exists"'); - return; - } - // Register menuItem action, see below for deferred menu dom injection - console.log('Registed contextmenu item: {id:' + menuItem.id + ', label:' + menuItem.label + '}'); - self.contextMenuExtensions[menuItem.id] = menuItem; - // TODO: Need to consider how to handle custom enable/disable behavior + // menuItem: {id, label, shortcut, action} + if (!menuItemIsValid(menuItem)) { + console.error('Menu items must be defined and have at least properties: id, label, action, where action must be a function'); + return; + } + if (menuItem.id in self.contextMenuExtensions) { + console.error('Cannot add extension "' + menuItem.id + '", an extension by that name already exists"'); + return; + } + // Register menuItem action, see below for deferred menu dom injection + console.log('Registed contextmenu item: {id:' + menuItem.id + ', label:' + menuItem.label + '}'); + self.contextMenuExtensions[menuItem.id] = menuItem; + // TODO: Need to consider how to handle custom enable/disable behavior }; var hasCustomHandler = function (handlerKey) { - return self.contextMenuExtensions[handlerKey] && true; + return self.contextMenuExtensions[handlerKey] && true; }; var getCustomHandler = function (handlerKey) { - return self.contextMenuExtensions[handlerKey].action; + return self.contextMenuExtensions[handlerKey].action; }; var injectExtendedContextMenuItemIntoDom = function (menuItem) { - if (Object.keys(self.contextMenuExtensions).length === 0) { - // all menuItems appear at the bottom of the menu in their own container. - // if this is the first extension menu we need to add the separator. - $('#cmenu_canvas').append("
  • "); - } - var shortcut = menuItem.shortcut || ''; - $('#cmenu_canvas').append("
  • " + - menuItem.label + "" + - shortcut + '
  • '); + if (Object.keys(self.contextMenuExtensions).length === 0) { + // all menuItems appear at the bottom of the menu in their own container. + // if this is the first extension menu we need to add the separator. + $('#cmenu_canvas').append("
  • "); + } + var shortcut = menuItem.shortcut || ''; + $('#cmenu_canvas').append("
  • " + + menuItem.label + "" + + shortcut + '
  • '); }; // Defer injection to wait out initial menu processing. This probably goes away once all context // menu behavior is brought here. svgEditor.ready(function () { - var menuItem; - for (menuItem in self.contextMenuExtensions) { - injectExtendedContextMenuItemIntoDom(self.contextMenuExtensions[menuItem]); - } + var menuItem; + for (menuItem in self.contextMenuExtensions) { + injectExtendedContextMenuItemIntoDom(self.contextMenuExtensions[menuItem]); + } }); svgedit.contextmenu.resetCustomMenus = function () { self.contextMenuExtensions = {}; }; svgedit.contextmenu.add = addContextMenuItem; diff --git a/editor/coords.js b/editor/coords.js index 760e1e4c..682f2273 100644 --- a/editor/coords.js +++ b/editor/coords.js @@ -22,12 +22,12 @@ var svgedit = svgedit || {}; // eslint-disable-line no-use-before-define 'use strict'; if (!svgedit.coords) { - svgedit.coords = {}; + svgedit.coords = {}; } // 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', - 'H', 'h', 'V', 'v', 'S', 's', 'T', 't']; + 'H', 'h', 'V', 'v', 'S', 's', 'T', 't']; /** * @typedef editorContext @@ -41,7 +41,7 @@ var editorContext_ = null; * @param {editorContext} editorContext */ svgedit.coords.init = function (editorContext) { - editorContext_ = editorContext; + editorContext_ = editorContext; }; /** @@ -51,266 +51,266 @@ svgedit.coords.init = function (editorContext) { * @param {SVGMatrix} m - Matrix object to use for remapping coordinates */ svgedit.coords.remapElement = function (selected, changes, m) { - var i, type, - remap = function (x, y) { return svgedit.math.transformPoint(x, y, m); }, - scalew = function (w) { return m.a * w; }, - scaleh = function (h) { return m.d * h; }, - doSnapping = editorContext_.getGridSnapping() && selected.parentNode.parentNode.localName === 'svg', - finishUp = function () { - var o; - if (doSnapping) { - for (o in changes) { - changes[o] = svgedit.utilities.snapToGrid(changes[o]); - } - } - svgedit.utilities.assignAttributes(selected, changes, 1000, true); - }, - box = svgedit.utilities.getBBox(selected); + var i, type, + remap = function (x, y) { return svgedit.math.transformPoint(x, y, m); }, + scalew = function (w) { return m.a * w; }, + scaleh = function (h) { return m.d * h; }, + doSnapping = editorContext_.getGridSnapping() && selected.parentNode.parentNode.localName === 'svg', + finishUp = function () { + var o; + if (doSnapping) { + for (o in changes) { + changes[o] = svgedit.utilities.snapToGrid(changes[o]); + } + } + svgedit.utilities.assignAttributes(selected, changes, 1000, true); + }, + box = svgedit.utilities.getBBox(selected); - for (i = 0; i < 2; i++) { - type = i === 0 ? 'fill' : 'stroke'; - var attrVal = selected.getAttribute(type); - if (attrVal && attrVal.indexOf('url(') === 0) { - if (m.a < 0 || m.d < 0) { - var grad = svgedit.utilities.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)); - } + for (i = 0; i < 2; i++) { + type = i === 0 ? 'fill' : 'stroke'; + var attrVal = selected.getAttribute(type); + if (attrVal && attrVal.indexOf('url(') === 0) { + if (m.a < 0 || m.d < 0) { + var grad = svgedit.utilities.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'); - var y2 = newgrad.getAttribute('y2'); - newgrad.setAttribute('y1', -(y1 - 1)); - newgrad.setAttribute('y2', -(y2 - 1)); - } - newgrad.id = editorContext_.getDrawing().getNextId(); - svgedit.utilities.findDefs().appendChild(newgrad); - selected.setAttribute(type, 'url(#' + newgrad.id + ')'); - } + if (m.d < 0) { + // flip y + var y1 = newgrad.getAttribute('y1'); + var y2 = newgrad.getAttribute('y2'); + newgrad.setAttribute('y1', -(y1 - 1)); + newgrad.setAttribute('y2', -(y2 - 1)); + } + newgrad.id = editorContext_.getDrawing().getNextId(); + svgedit.utilities.findDefs().appendChild(newgrad); + selected.setAttribute(type, 'url(#' + newgrad.id + ')'); + } - // Not really working :( - // if (selected.tagName === 'path') { - // reorientGrads(selected, m); - // } - } - } + // Not really working :( + // if (selected.tagName === 'path') { + // reorientGrads(selected, m); + // } + } + } - var elName = selected.tagName; - var chlist, mt; - if (elName === 'g' || elName === 'text' || elName === 'tspan' || 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)) { - // [T][M] = [M][T'] - // therefore [T'] = [M_inv][T][M] - var existing = svgedit.math.transformListToTransform(selected).matrix, - tNew = svgedit.math.matrixMultiply(existing.inverse(), m, existing); - changes.x = parseFloat(changes.x) + tNew.e; - changes.y = parseFloat(changes.y) + tNew.f; - } else { - // we just absorb all matrices into the element and don't do any remapping - chlist = svgedit.transformlist.getTransformList(selected); - mt = svgroot.createSVGTransform(); - mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m)); - chlist.clear(); - chlist.appendItem(mt); - } - } - var c, pt, pt1, pt2, len; - // now we have a set of changes and an applied reduced transform list - // we apply the changes directly to the DOM - switch (elName) { - 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 - chlist = svgedit.transformlist.getTransformList(selected); - mt = svgroot.createSVGTransform(); - mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m)); - chlist.clear(); - chlist.appendItem(mt); - } else { - 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); - changes.height = Math.abs(changes.height); - } - finishUp(); - break; - case 'ellipse': - c = remap(changes.cx, changes.cy); - changes.cx = c.x; - 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(); - break; - case 'circle': - c = remap(changes.cx, changes.cy); - changes.cx = c.x; - changes.cy = c.y; - // take the minimum of the new selected box's dimensions for the new circle radius - var tbox = svgedit.math.transformBox(box.x, box.y, box.width, box.height, m); - var w = tbox.tr.x - tbox.tl.x, h = tbox.bl.y - tbox.tl.y; - changes.r = Math.min(w / 2, h / 2); + var elName = selected.tagName; + var chlist, mt; + if (elName === 'g' || elName === 'text' || elName === 'tspan' || 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)) { + // [T][M] = [M][T'] + // therefore [T'] = [M_inv][T][M] + var existing = svgedit.math.transformListToTransform(selected).matrix, + tNew = svgedit.math.matrixMultiply(existing.inverse(), m, existing); + changes.x = parseFloat(changes.x) + tNew.e; + changes.y = parseFloat(changes.y) + tNew.f; + } else { + // we just absorb all matrices into the element and don't do any remapping + chlist = svgedit.transformlist.getTransformList(selected); + mt = svgroot.createSVGTransform(); + mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m)); + chlist.clear(); + chlist.appendItem(mt); + } + } + var c, pt, pt1, pt2, len; + // now we have a set of changes and an applied reduced transform list + // we apply the changes directly to the DOM + switch (elName) { + 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 + chlist = svgedit.transformlist.getTransformList(selected); + mt = svgroot.createSVGTransform(); + mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m)); + chlist.clear(); + chlist.appendItem(mt); + } else { + 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); + changes.height = Math.abs(changes.height); + } + finishUp(); + break; + case 'ellipse': + c = remap(changes.cx, changes.cy); + changes.cx = c.x; + 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(); + break; + case 'circle': + c = remap(changes.cx, changes.cy); + changes.cx = c.x; + changes.cy = c.y; + // take the minimum of the new selected box's dimensions for the new circle radius + var tbox = svgedit.math.transformBox(box.x, box.y, box.width, box.height, m); + var w = tbox.tr.x - tbox.tl.x, h = tbox.bl.y - tbox.tl.y; + changes.r = Math.min(w / 2, h / 2); - if (changes.r) { changes.r = Math.abs(changes.r); } - finishUp(); - break; - case 'line': - pt1 = remap(changes.x1, changes.y1); - pt2 = remap(changes.x2, changes.y2); - changes.x1 = pt1.x; - changes.y1 = pt1.y; - changes.x2 = pt2.x; - changes.y2 = pt2.y; - // deliberately fall through here - case 'text': - case 'tspan': - case 'use': - finishUp(); - break; - case 'g': - var gsvg = $(selected).data('gsvg'); - if (gsvg) { - svgedit.utilities.assignAttributes(gsvg, changes, 1000, true); - } - break; - case 'polyline': - case 'polygon': - len = changes.points.length; - for (i = 0; i < len; ++i) { - pt = changes.points[i]; - pt = remap(pt.x, pt.y); - changes.points[i].x = pt.x; - changes.points[i].y = pt.y; - } + if (changes.r) { changes.r = Math.abs(changes.r); } + finishUp(); + break; + case 'line': + pt1 = remap(changes.x1, changes.y1); + pt2 = remap(changes.x2, changes.y2); + changes.x1 = pt1.x; + changes.y1 = pt1.y; + changes.x2 = pt2.x; + changes.y2 = pt2.y; + // deliberately fall through here + case 'text': + case 'tspan': + case 'use': + finishUp(); + break; + case 'g': + var gsvg = $(selected).data('gsvg'); + if (gsvg) { + svgedit.utilities.assignAttributes(gsvg, changes, 1000, true); + } + break; + case 'polyline': + case 'polygon': + len = changes.points.length; + for (i = 0; i < len; ++i) { + pt = changes.points[i]; + pt = remap(pt.x, pt.y); + changes.points[i].x = pt.x; + changes.points[i].y = pt.y; + } - len = changes.points.length; - var pstr = ''; - for (i = 0; i < len; ++i) { - pt = changes.points[i]; - pstr += pt.x + ',' + pt.y + ' '; - } - selected.setAttribute('points', pstr); - break; - case 'path': - var seg; - var segList = selected.pathSegList; - len = segList.numberOfItems; - changes.d = []; - for (i = 0; i < len; ++i) { - seg = segList.getItem(i); - changes.d[i] = { - type: seg.pathSegType, - x: seg.x, - y: seg.y, - x1: seg.x1, - y1: seg.y1, - x2: seg.x2, - y2: seg.y2, - r1: seg.r1, - r2: seg.r2, - angle: seg.angle, - largeArcFlag: seg.largeArcFlag, - sweepFlag: seg.sweepFlag - }; - } + len = changes.points.length; + var pstr = ''; + for (i = 0; i < len; ++i) { + pt = changes.points[i]; + pstr += pt.x + ',' + pt.y + ' '; + } + selected.setAttribute('points', pstr); + break; + case 'path': + var seg; + var segList = selected.pathSegList; + len = segList.numberOfItems; + changes.d = []; + for (i = 0; i < len; ++i) { + seg = segList.getItem(i); + changes.d[i] = { + type: seg.pathSegType, + x: seg.x, + y: seg.y, + x1: seg.x1, + y1: seg.y1, + x2: seg.x2, + y2: seg.y2, + r1: seg.r1, + r2: seg.r2, + angle: seg.angle, + largeArcFlag: seg.largeArcFlag, + sweepFlag: seg.sweepFlag + }; + } - len = changes.d.length; - var firstseg = changes.d[0], - currentpt = remap(firstseg.x, firstseg.y); - changes.d[0].x = currentpt.x; - changes.d[0].y = currentpt.y; - for (i = 1; i < len; ++i) { - seg = changes.d[i]; - type = seg.type; - // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2 - // if relative, we want to scalew, scaleh - if (type % 2 === 0) { // absolute - var thisx = (seg.x !== undefined) ? seg.x : currentpt.x, // for V commands - thisy = (seg.y !== undefined) ? seg.y : currentpt.y; // for H commands - pt = remap(thisx, thisy); - pt1 = remap(seg.x1, seg.y1); - pt2 = remap(seg.x2, seg.y2); - seg.x = pt.x; - seg.y = pt.y; - seg.x1 = pt1.x; - seg.y1 = pt1.y; - seg.x2 = pt2.x; - seg.y2 = pt2.y; - seg.r1 = scalew(seg.r1); - seg.r2 = scaleh(seg.r2); - } else { // relative - seg.x = scalew(seg.x); - seg.y = scaleh(seg.y); - seg.x1 = scalew(seg.x1); - seg.y1 = scaleh(seg.y1); - seg.x2 = scalew(seg.x2); - seg.y2 = scaleh(seg.y2); - seg.r1 = scalew(seg.r1); - seg.r2 = scaleh(seg.r2); - } - } // for each segment + len = changes.d.length; + var firstseg = changes.d[0], + currentpt = remap(firstseg.x, firstseg.y); + changes.d[0].x = currentpt.x; + changes.d[0].y = currentpt.y; + for (i = 1; i < len; ++i) { + seg = changes.d[i]; + type = seg.type; + // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2 + // if relative, we want to scalew, scaleh + if (type % 2 === 0) { // absolute + var thisx = (seg.x !== undefined) ? seg.x : currentpt.x, // for V commands + thisy = (seg.y !== undefined) ? seg.y : currentpt.y; // for H commands + pt = remap(thisx, thisy); + pt1 = remap(seg.x1, seg.y1); + pt2 = remap(seg.x2, seg.y2); + seg.x = pt.x; + seg.y = pt.y; + seg.x1 = pt1.x; + seg.y1 = pt1.y; + seg.x2 = pt2.x; + seg.y2 = pt2.y; + seg.r1 = scalew(seg.r1); + seg.r2 = scaleh(seg.r2); + } else { // relative + seg.x = scalew(seg.x); + seg.y = scaleh(seg.y); + seg.x1 = scalew(seg.x1); + seg.y1 = scaleh(seg.y1); + seg.x2 = scalew(seg.x2); + seg.y2 = scaleh(seg.y2); + seg.r1 = scalew(seg.r1); + seg.r2 = scaleh(seg.r2); + } + } // for each segment - var dstr = ''; - len = changes.d.length; - for (i = 0; i < len; ++i) { - seg = changes.d[i]; - type = seg.type; - dstr += pathMap[type]; - switch (type) { - case 13: // relative horizontal line (h) - case 12: // absolute horizontal line (H) - dstr += seg.x + ' '; - break; - case 15: // relative vertical line (v) - case 14: // absolute vertical line (V) - dstr += seg.y + ' '; - break; - case 3: // relative move (m) - case 5: // relative line (l) - case 19: // relative smooth quad (t) - case 2: // absolute move (M) - case 4: // absolute line (L) - case 18: // absolute smooth quad (T) - dstr += seg.x + ',' + seg.y + ' '; - break; - case 7: // relative cubic (c) - case 6: // absolute cubic (C) - dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x2 + ',' + seg.y2 + ' ' + - seg.x + ',' + seg.y + ' '; - break; - case 9: // relative quad (q) - case 8: // absolute quad (Q) - dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x + ',' + seg.y + ' '; - break; - case 11: // relative elliptical arc (a) - case 10: // absolute elliptical arc (A) - dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + (+seg.largeArcFlag) + - ' ' + (+seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' '; - break; - case 17: // relative smooth cubic (s) - case 16: // absolute smooth cubic (S) - dstr += seg.x2 + ',' + seg.y2 + ' ' + seg.x + ',' + seg.y + ' '; - break; - } - } + var dstr = ''; + len = changes.d.length; + for (i = 0; i < len; ++i) { + seg = changes.d[i]; + type = seg.type; + dstr += pathMap[type]; + switch (type) { + case 13: // relative horizontal line (h) + case 12: // absolute horizontal line (H) + dstr += seg.x + ' '; + break; + case 15: // relative vertical line (v) + case 14: // absolute vertical line (V) + dstr += seg.y + ' '; + break; + case 3: // relative move (m) + case 5: // relative line (l) + case 19: // relative smooth quad (t) + case 2: // absolute move (M) + case 4: // absolute line (L) + case 18: // absolute smooth quad (T) + dstr += seg.x + ',' + seg.y + ' '; + break; + case 7: // relative cubic (c) + case 6: // absolute cubic (C) + dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x2 + ',' + seg.y2 + ' ' + + seg.x + ',' + seg.y + ' '; + break; + case 9: // relative quad (q) + case 8: // absolute quad (Q) + dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x + ',' + seg.y + ' '; + break; + case 11: // relative elliptical arc (a) + case 10: // absolute elliptical arc (A) + dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + (+seg.largeArcFlag) + + ' ' + (+seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' '; + break; + case 17: // relative smooth cubic (s) + case 16: // absolute smooth cubic (S) + dstr += seg.x2 + ',' + seg.y2 + ' ' + seg.x + ',' + seg.y + ' '; + break; + } + } - selected.setAttribute('d', dstr); - break; - } + selected.setAttribute('d', dstr); + break; + } }; }()); diff --git a/editor/draw.js b/editor/draw.js index 9799cd9f..7080b9f0 100644 --- a/editor/draw.js +++ b/editor/draw.js @@ -17,7 +17,7 @@ 'use strict'; if (!svgedit.draw) { - svgedit.draw = {}; + svgedit.draw = {}; } // alias var NS = svgedit.NS; @@ -25,9 +25,9 @@ var NS = svgedit.NS; var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use'.split(','); var RandomizeModes = { - LET_DOCUMENT_DECIDE: 0, - ALWAYS_RANDOMIZE: 1, - NEVER_RANDOMIZE: 2 + LET_DOCUMENT_DECIDE: 0, + ALWAYS_RANDOMIZE: 1, + NEVER_RANDOMIZE: 2 }; var randomizeIds = RandomizeModes.LET_DOCUMENT_DECIDE; @@ -38,15 +38,15 @@ var randomizeIds = RandomizeModes.LET_DOCUMENT_DECIDE; * @param {svgedit.draw.Drawing} currentDrawing */ svgedit.draw.randomizeIds = function (enableRandomization, currentDrawing) { - randomizeIds = enableRandomization === false - ? RandomizeModes.NEVER_RANDOMIZE - : RandomizeModes.ALWAYS_RANDOMIZE; + randomizeIds = enableRandomization === false + ? RandomizeModes.NEVER_RANDOMIZE + : RandomizeModes.ALWAYS_RANDOMIZE; - if (randomizeIds === RandomizeModes.ALWAYS_RANDOMIZE && !currentDrawing.getNonce()) { - currentDrawing.setNonce(Math.floor(Math.random() * 100001)); - } else if (randomizeIds === RandomizeModes.NEVER_RANDOMIZE && currentDrawing.getNonce()) { - currentDrawing.clearNonce(); - } + if (randomizeIds === RandomizeModes.ALWAYS_RANDOMIZE && !currentDrawing.getNonce()) { + currentDrawing.setNonce(Math.floor(Math.random() * 100001)); + } else if (randomizeIds === RandomizeModes.NEVER_RANDOMIZE && currentDrawing.getNonce()) { + currentDrawing.clearNonce(); + } }; /** @@ -57,72 +57,72 @@ svgedit.draw.randomizeIds = function (enableRandomization, currentDrawing) { * @param {String=svg_} [optIdPrefix] - The ID prefix to use. */ svgedit.draw.Drawing = function (svgElem, optIdPrefix) { - if (!svgElem || !svgElem.tagName || !svgElem.namespaceURI || - svgElem.tagName !== 'svg' || svgElem.namespaceURI !== NS.SVG) { - throw new Error('Error: svgedit.draw.Drawing instance initialized without a element'); - } + if (!svgElem || !svgElem.tagName || !svgElem.namespaceURI || + svgElem.tagName !== 'svg' || svgElem.namespaceURI !== NS.SVG) { + throw new Error('Error: svgedit.draw.Drawing instance initialized without a element'); + } - /** - * The SVG DOM Element that represents this drawing. - * @type {SVGSVGElement} - */ - this.svgElem_ = svgElem; + /** + * The SVG DOM Element that represents this drawing. + * @type {SVGSVGElement} + */ + this.svgElem_ = svgElem; - /** - * The latest object number used in this drawing. - * @type {number} - */ - this.obj_num = 0; + /** + * The latest object number used in this drawing. + * @type {number} + */ + this.obj_num = 0; - /** - * The prefix to prepend to each element id in the drawing. - * @type {String} - */ - this.idPrefix = optIdPrefix || 'svg_'; + /** + * The prefix to prepend to each element id in the drawing. + * @type {String} + */ + this.idPrefix = optIdPrefix || 'svg_'; - /** - * An array of released element ids to immediately reuse. - * @type {Array.} - */ - this.releasedNums = []; + /** + * An array of released element ids to immediately reuse. + * @type {Array.} + */ + this.releasedNums = []; - /** - * The z-ordered array of Layer objects. Each layer has a name - * and group element. - * The first layer is the one at the bottom of the rendering. - * @type {Array.} - */ - this.all_layers = []; + /** + * The z-ordered array of Layer objects. Each layer has a name + * and group element. + * The first layer is the one at the bottom of the rendering. + * @type {Array.} + */ + this.all_layers = []; - /** - * Map of all_layers by name. - * - * Note: Layers are ordered, but referenced externally by name; so, we need both container - * types depending on which function is called (i.e. all_layers and layer_map). - * - * @type {Object.} - */ - this.layer_map = {}; + /** + * Map of all_layers by name. + * + * Note: Layers are ordered, but referenced externally by name; so, we need both container + * types depending on which function is called (i.e. all_layers and layer_map). + * + * @type {Object.} + */ + this.layer_map = {}; - /** - * The current layer being used. - * @type {Layer} - */ - this.current_layer = null; + /** + * The current layer being used. + * @type {Layer} + */ + this.current_layer = null; - /** - * The nonce to use to uniquely identify elements across drawings. - * @type {!String} - */ - this.nonce_ = ''; - var n = this.svgElem_.getAttributeNS(NS.SE, 'nonce'); - // If already set in the DOM, use the nonce throughout the document - // else, if randomizeIds(true) has been called, create and set the nonce. - if (!!n && randomizeIds !== RandomizeModes.NEVER_RANDOMIZE) { - this.nonce_ = n; - } else if (randomizeIds === RandomizeModes.ALWAYS_RANDOMIZE) { - this.setNonce(Math.floor(Math.random() * 100001)); - } + /** + * The nonce to use to uniquely identify elements across drawings. + * @type {!String} + */ + this.nonce_ = ''; + var n = this.svgElem_.getAttributeNS(NS.SE, 'nonce'); + // If already set in the DOM, use the nonce throughout the document + // else, if randomizeIds(true) has been called, create and set the nonce. + if (!!n && randomizeIds !== RandomizeModes.NEVER_RANDOMIZE) { + this.nonce_ = n; + } else if (randomizeIds === RandomizeModes.ALWAYS_RANDOMIZE) { + this.setNonce(Math.floor(Math.random() * 100001)); + } }; /** @@ -130,44 +130,44 @@ svgedit.draw.Drawing = function (svgElem, optIdPrefix) { * @returns {Element} SVG element within the root SVGSVGElement */ svgedit.draw.Drawing.prototype.getElem_ = function (id) { - if (this.svgElem_.querySelector) { - // querySelector lookup - return this.svgElem_.querySelector('#' + id); - } - // jQuery lookup: twice as slow as xpath in FF - return $(this.svgElem_).find('[id=' + id + ']')[0]; + if (this.svgElem_.querySelector) { + // querySelector lookup + return this.svgElem_.querySelector('#' + id); + } + // jQuery lookup: twice as slow as xpath in FF + return $(this.svgElem_).find('[id=' + id + ']')[0]; }; /** * @returns {SVGSVGElement} */ svgedit.draw.Drawing.prototype.getSvgElem = function () { - return this.svgElem_; + return this.svgElem_; }; /** * @returns {!string|number} The previously set nonce */ svgedit.draw.Drawing.prototype.getNonce = function () { - return this.nonce_; + return this.nonce_; }; /** * @param {!string|number} n The nonce to set */ svgedit.draw.Drawing.prototype.setNonce = function (n) { - this.svgElem_.setAttributeNS(NS.XMLNS, 'xmlns:se', NS.SE); - this.svgElem_.setAttributeNS(NS.SE, 'se:nonce', n); - this.nonce_ = n; + this.svgElem_.setAttributeNS(NS.XMLNS, 'xmlns:se', NS.SE); + this.svgElem_.setAttributeNS(NS.SE, 'se:nonce', n); + this.nonce_ = n; }; /** * Clears any previously set nonce */ svgedit.draw.Drawing.prototype.clearNonce = function () { - // We deliberately leave any se:nonce attributes alone, - // we just don't use it to randomize ids. - this.nonce_ = ''; + // We deliberately leave any se:nonce attributes alone, + // we just don't use it to randomize ids. + this.nonce_ = ''; }; /** @@ -175,9 +175,9 @@ svgedit.draw.Drawing.prototype.clearNonce = function () { * @return {String} The latest object Id. */ svgedit.draw.Drawing.prototype.getId = function () { - return this.nonce_ - ? this.idPrefix + this.nonce_ + '_' + this.obj_num - : this.idPrefix + this.obj_num; + return this.nonce_ + ? this.idPrefix + this.nonce_ + '_' + this.obj_num + : this.idPrefix + this.obj_num; }; /** @@ -185,35 +185,35 @@ svgedit.draw.Drawing.prototype.getId = function () { * @return {String} The next object Id to use. */ svgedit.draw.Drawing.prototype.getNextId = function () { - var oldObjNum = this.obj_num; - var restoreOldObjNum = false; + var oldObjNum = this.obj_num; + var restoreOldObjNum = false; - // If there are any released numbers in the release stack, - // use the last one instead of the next obj_num. - // We need to temporarily use obj_num as that is what getId() depends on. - if (this.releasedNums.length > 0) { - this.obj_num = this.releasedNums.pop(); - restoreOldObjNum = true; - } else { - // If we are not using a released id, then increment the obj_num. - this.obj_num++; - } + // If there are any released numbers in the release stack, + // use the last one instead of the next obj_num. + // We need to temporarily use obj_num as that is what getId() depends on. + if (this.releasedNums.length > 0) { + this.obj_num = this.releasedNums.pop(); + restoreOldObjNum = true; + } else { + // If we are not using a released id, then increment the obj_num. + this.obj_num++; + } - // Ensure the ID does not exist. - var id = this.getId(); - while (this.getElem_(id)) { - if (restoreOldObjNum) { - this.obj_num = oldObjNum; - restoreOldObjNum = false; - } - this.obj_num++; - id = this.getId(); - } - // Restore the old object number if required. - if (restoreOldObjNum) { - this.obj_num = oldObjNum; - } - return id; + // Ensure the ID does not exist. + var id = this.getId(); + while (this.getElem_(id)) { + if (restoreOldObjNum) { + this.obj_num = oldObjNum; + restoreOldObjNum = false; + } + this.obj_num++; + id = this.getId(); + } + // Restore the old object number if required. + if (restoreOldObjNum) { + this.obj_num = oldObjNum; + } + return id; }; /** @@ -224,24 +224,24 @@ svgedit.draw.Drawing.prototype.getNextId = function () { * @returns {boolean} True if the id was valid to be released, false otherwise. */ svgedit.draw.Drawing.prototype.releaseId = function (id) { - // confirm if this is a valid id for this Document, else return false - var front = this.idPrefix + (this.nonce_ ? this.nonce_ + '_' : ''); - if (typeof id !== 'string' || id.indexOf(front) !== 0) { - return false; - } - // extract the obj_num of this id - var num = parseInt(id.substr(front.length), 10); + // confirm if this is a valid id for this Document, else return false + var front = this.idPrefix + (this.nonce_ ? this.nonce_ + '_' : ''); + if (typeof id !== 'string' || id.indexOf(front) !== 0) { + return false; + } + // extract the obj_num of this id + var num = parseInt(id.substr(front.length), 10); - // if we didn't get a positive number or we already released this number - // then return false. - if (typeof num !== 'number' || num <= 0 || this.releasedNums.indexOf(num) !== -1) { - return false; - } + // if we didn't get a positive number or we already released this number + // then return false. + if (typeof num !== 'number' || num <= 0 || this.releasedNums.indexOf(num) !== -1) { + return false; + } - // push the released number into the released queue - this.releasedNums.push(num); + // push the released number into the released queue + this.releasedNums.push(num); - return true; + return true; }; /** @@ -249,7 +249,7 @@ svgedit.draw.Drawing.prototype.releaseId = function (id) { * @returns {integer} The number of layers in the current drawing. */ svgedit.draw.Drawing.prototype.getNumLayers = function () { - return this.all_layers.length; + return this.all_layers.length; }; /** @@ -257,7 +257,7 @@ svgedit.draw.Drawing.prototype.getNumLayers = function () { * @param {string} name - The layer name to check */ svgedit.draw.Drawing.prototype.hasLayer = function (name) { - return this.layer_map[name] !== undefined; + return this.layer_map[name] !== undefined; }; /** @@ -266,14 +266,14 @@ svgedit.draw.Drawing.prototype.hasLayer = function (name) { * @returns {string} The name of the ith layer (or the empty string if none found) */ svgedit.draw.Drawing.prototype.getLayerName = function (i) { - return i >= 0 && i < this.getNumLayers() ? this.all_layers[i].getName() : ''; + return i >= 0 && i < this.getNumLayers() ? this.all_layers[i].getName() : ''; }; /** * @returns {SVGGElement} The SVGGElement representing the current layer. */ svgedit.draw.Drawing.prototype.getCurrentLayer = function () { - return this.current_layer ? this.current_layer.getGroup() : null; + return this.current_layer ? this.current_layer.getGroup() : null; }; /** @@ -281,8 +281,8 @@ svgedit.draw.Drawing.prototype.getCurrentLayer = function () { * @returns {SVGGElement} The SVGGElement representing the named layer or null. */ svgedit.draw.Drawing.prototype.getLayerByName = function (name) { - var layer = this.layer_map[name]; - return layer ? layer.getGroup() : null; + var layer = this.layer_map[name]; + return layer ? layer.getGroup() : null; }; /** @@ -291,7 +291,7 @@ svgedit.draw.Drawing.prototype.getLayerByName = function (name) { * @returns {string} The name of the currently active layer (or the empty string if none found). */ svgedit.draw.Drawing.prototype.getCurrentLayerName = function () { - return this.current_layer ? this.current_layer.getName() : ''; + return this.current_layer ? this.current_layer.getName() : ''; }; /** @@ -301,16 +301,16 @@ svgedit.draw.Drawing.prototype.getCurrentLayerName = function () { * @returns {string|null} The new name if changed; otherwise, null. */ svgedit.draw.Drawing.prototype.setCurrentLayerName = function (name, hrService) { - var finalName = null; - if (this.current_layer) { - var oldName = this.current_layer.getName(); - finalName = this.current_layer.setName(name, hrService); - if (finalName) { - delete this.layer_map[oldName]; - this.layer_map[finalName] = this.current_layer; - } - } - return finalName; + var finalName = null; + if (this.current_layer) { + var oldName = this.current_layer.getName(); + finalName = this.current_layer.setName(name, hrService); + if (finalName) { + delete this.layer_map[oldName]; + this.layer_map[finalName] = this.current_layer; + } + } + return finalName; }; /** @@ -319,89 +319,89 @@ svgedit.draw.Drawing.prototype.setCurrentLayerName = function (name, hrService) * @returns {Object} If the name was changed, returns {title:SVGGElement, previousName:string}; otherwise null. */ svgedit.draw.Drawing.prototype.setCurrentLayerPosition = function (newpos) { - var layerCount = this.getNumLayers(); - if (!this.current_layer || newpos < 0 || newpos >= layerCount) { - return null; - } + var layerCount = this.getNumLayers(); + if (!this.current_layer || newpos < 0 || newpos >= layerCount) { + return null; + } - var oldpos; - for (oldpos = 0; oldpos < layerCount; ++oldpos) { - if (this.all_layers[oldpos] === this.current_layer) { break; } - } - // some unknown error condition (current_layer not in all_layers) - if (oldpos === layerCount) { return null; } + var oldpos; + for (oldpos = 0; oldpos < layerCount; ++oldpos) { + if (this.all_layers[oldpos] === this.current_layer) { break; } + } + // some unknown error condition (current_layer not in all_layers) + if (oldpos === layerCount) { return null; } - if (oldpos !== newpos) { - // if our new position is below us, we need to insert before the node after newpos - var refGroup = null; - var currentGroup = this.current_layer.getGroup(); - var oldNextSibling = currentGroup.nextSibling; - if (newpos > oldpos) { - if (newpos < layerCount - 1) { - refGroup = this.all_layers[newpos + 1].getGroup(); - } - // if our new position is above us, we need to insert before the node at newpos - } else { - refGroup = this.all_layers[newpos].getGroup(); - } - this.svgElem_.insertBefore(currentGroup, refGroup); + if (oldpos !== newpos) { + // if our new position is below us, we need to insert before the node after newpos + var refGroup = null; + var currentGroup = this.current_layer.getGroup(); + var oldNextSibling = currentGroup.nextSibling; + if (newpos > oldpos) { + if (newpos < layerCount - 1) { + refGroup = this.all_layers[newpos + 1].getGroup(); + } + // if our new position is above us, we need to insert before the node at newpos + } else { + refGroup = this.all_layers[newpos].getGroup(); + } + this.svgElem_.insertBefore(currentGroup, refGroup); - this.identifyLayers(); - this.setCurrentLayer(this.getLayerName(newpos)); + this.identifyLayers(); + this.setCurrentLayer(this.getLayerName(newpos)); - return { - currentGroup: currentGroup, - oldNextSibling: oldNextSibling - }; - } - return null; + return { + currentGroup: currentGroup, + oldNextSibling: oldNextSibling + }; + } + return null; }; svgedit.draw.Drawing.prototype.mergeLayer = function (hrService) { - var currentGroup = this.current_layer.getGroup(); - var prevGroup = $(currentGroup).prev()[0]; - if (!prevGroup) { return; } + var currentGroup = this.current_layer.getGroup(); + var prevGroup = $(currentGroup).prev()[0]; + if (!prevGroup) { return; } - hrService.startBatchCommand('Merge Layer'); + hrService.startBatchCommand('Merge Layer'); - var layerNextSibling = currentGroup.nextSibling; - hrService.removeElement(currentGroup, layerNextSibling, this.svgElem_); + var layerNextSibling = currentGroup.nextSibling; + hrService.removeElement(currentGroup, layerNextSibling, this.svgElem_); - while (currentGroup.firstChild) { - var child = currentGroup.firstChild; - if (child.localName === 'title') { - hrService.removeElement(child, child.nextSibling, currentGroup); - currentGroup.removeChild(child); - continue; - } - var oldNextSibling = child.nextSibling; - prevGroup.appendChild(child); - hrService.moveElement(child, oldNextSibling, currentGroup); - } + while (currentGroup.firstChild) { + var child = currentGroup.firstChild; + if (child.localName === 'title') { + hrService.removeElement(child, child.nextSibling, currentGroup); + currentGroup.removeChild(child); + continue; + } + var oldNextSibling = child.nextSibling; + prevGroup.appendChild(child); + hrService.moveElement(child, oldNextSibling, currentGroup); + } - // Remove current layer's group - this.current_layer.removeGroup(); - // Remove the current layer and set the previous layer as the new current layer - var index = this.all_layers.indexOf(this.current_layer); - if (index > 0) { - var name = this.current_layer.getName(); - this.current_layer = this.all_layers[index - 1]; - this.all_layers.splice(index, 1); - delete this.layer_map[name]; - } + // Remove current layer's group + this.current_layer.removeGroup(); + // Remove the current layer and set the previous layer as the new current layer + var index = this.all_layers.indexOf(this.current_layer); + if (index > 0) { + var name = this.current_layer.getName(); + this.current_layer = this.all_layers[index - 1]; + this.all_layers.splice(index, 1); + delete this.layer_map[name]; + } - hrService.endBatchCommand(); + hrService.endBatchCommand(); }; svgedit.draw.Drawing.prototype.mergeAllLayers = function (hrService) { - // Set the current layer to the last layer. - this.current_layer = this.all_layers[this.all_layers.length - 1]; + // Set the current layer to the last layer. + this.current_layer = this.all_layers[this.all_layers.length - 1]; - hrService.startBatchCommand('Merge all Layers'); - while (this.all_layers.length > 1) { - this.mergeLayer(hrService); - } - hrService.endBatchCommand(); + hrService.startBatchCommand('Merge all Layers'); + while (this.all_layers.length > 1) { + this.mergeLayer(hrService); + } + hrService.endBatchCommand(); }; /** @@ -412,16 +412,16 @@ svgedit.draw.Drawing.prototype.mergeAllLayers = function (hrService) { * @returns {boolean} true if the current layer was switched, otherwise false */ svgedit.draw.Drawing.prototype.setCurrentLayer = function (name) { - var layer = this.layer_map[name]; - if (layer) { - if (this.current_layer) { - this.current_layer.deactivate(); - } - this.current_layer = layer; - this.current_layer.activate(); - return true; - } - return false; + var layer = this.layer_map[name]; + if (layer) { + if (this.current_layer) { + this.current_layer.deactivate(); + } + this.current_layer = layer; + this.current_layer.activate(); + return true; + } + return false; }; /** @@ -430,12 +430,12 @@ svgedit.draw.Drawing.prototype.setCurrentLayer = function (name) { * @returns {SVGGElement} The SVGGElement of the layer removed or null. */ svgedit.draw.Drawing.prototype.deleteCurrentLayer = function () { - if (this.current_layer && this.getNumLayers() > 1) { - var oldLayerGroup = this.current_layer.removeGroup(); - this.identifyLayers(); - return oldLayerGroup; - } - return null; + if (this.current_layer && this.getNumLayers() > 1) { + var oldLayerGroup = this.current_layer.removeGroup(); + this.identifyLayers(); + return oldLayerGroup; + } + return null; }; /** @@ -444,13 +444,13 @@ svgedit.draw.Drawing.prototype.deleteCurrentLayer = function () { * @returns {string} The layer name or empty string. */ function findLayerNameInGroup (group) { - var name = $('title', group).text(); + var name = $('title', group).text(); - // Hack for Opera 10.60 - if (!name && svgedit.browser.isOpera() && group.querySelectorAll) { - name = $(group.querySelectorAll('title')).text(); - } - return name; + // Hack for Opera 10.60 + if (!name && svgedit.browser.isOpera() && group.querySelectorAll) { + name = $(group.querySelectorAll('title')).text(); + } + return name; } /** @@ -459,10 +459,10 @@ function findLayerNameInGroup (group) { * @returns {string} - The new name. */ function getNewLayerName (existingLayerNames) { - var i = 1; - // TODO(codedread): What about internationalization of "Layer"? - while (existingLayerNames.indexOf(('Layer ' + i)) >= 0) { i++; } - return 'Layer ' + i; + var i = 1; + // TODO(codedread): What about internationalization of "Layer"? + while (existingLayerNames.indexOf(('Layer ' + i)) >= 0) { i++; } + return 'Layer ' + i; } /** @@ -470,46 +470,46 @@ function getNewLayerName (existingLayerNames) { * top-most layer (last child of this drawing). */ svgedit.draw.Drawing.prototype.identifyLayers = function () { - this.all_layers = []; - this.layer_map = {}; - var numchildren = this.svgElem_.childNodes.length; - // loop through all children of SVG element - var orphans = [], layernames = []; - var layer = null; - var childgroups = false; - for (var i = 0; i < numchildren; ++i) { - var child = this.svgElem_.childNodes.item(i); - // for each g, find its layer name - if (child && child.nodeType === 1) { - if (child.tagName === 'g') { - childgroups = true; - var name = findLayerNameInGroup(child); - if (name) { - layernames.push(name); - layer = new svgedit.draw.Layer(name, child); - this.all_layers.push(layer); - this.layer_map[name] = layer; - } else { - // if group did not have a name, it is an orphan - orphans.push(child); - } - } else if (~visElems.indexOf(child.nodeName)) { - // Child is "visible" (i.e. not a or element), so it is an orphan - orphans.push(child); - } - } - } + this.all_layers = []; + this.layer_map = {}; + var numchildren = this.svgElem_.childNodes.length; + // loop through all children of SVG element + var orphans = [], layernames = []; + var layer = null; + var childgroups = false; + for (var i = 0; i < numchildren; ++i) { + var child = this.svgElem_.childNodes.item(i); + // for each g, find its layer name + if (child && child.nodeType === 1) { + if (child.tagName === 'g') { + childgroups = true; + var name = findLayerNameInGroup(child); + if (name) { + layernames.push(name); + layer = new svgedit.draw.Layer(name, child); + this.all_layers.push(layer); + this.layer_map[name] = layer; + } else { + // if group did not have a name, it is an orphan + orphans.push(child); + } + } else if (~visElems.indexOf(child.nodeName)) { + // Child is "visible" (i.e. not a or <defs> element), so it is an orphan + orphans.push(child); + } + } + } - // If orphans or no layers found, create a new layer and add all the orphans to it - if (orphans.length > 0 || !childgroups) { - layer = new svgedit.draw.Layer(getNewLayerName(layernames), null, this.svgElem_); - layer.appendChildren(orphans); - this.all_layers.push(layer); - this.layer_map[name] = layer; - } else { - layer.activate(); - } - this.current_layer = layer; + // If orphans or no layers found, create a new layer and add all the orphans to it + if (orphans.length > 0 || !childgroups) { + layer = new svgedit.draw.Layer(getNewLayerName(layernames), null, this.svgElem_); + layer.appendChildren(orphans); + this.all_layers.push(layer); + this.layer_map[name] = layer; + } else { + layer.activate(); + } + this.current_layer = layer; }; /** @@ -521,27 +521,27 @@ svgedit.draw.Drawing.prototype.identifyLayers = function () { * also the current layer of this drawing. */ svgedit.draw.Drawing.prototype.createLayer = function (name, hrService) { - if (this.current_layer) { - this.current_layer.deactivate(); - } - // Check for duplicate name. - if (name === undefined || name === null || name === '' || this.layer_map[name]) { - name = getNewLayerName(Object.keys(this.layer_map)); - } + if (this.current_layer) { + this.current_layer.deactivate(); + } + // Check for duplicate name. + if (name === undefined || name === null || name === '' || this.layer_map[name]) { + name = getNewLayerName(Object.keys(this.layer_map)); + } - // Crate new layer and add to DOM as last layer - var layer = new svgedit.draw.Layer(name, null, this.svgElem_); - // Like to assume hrService exists, but this is backwards compatible with old version of createLayer. - if (hrService) { - hrService.startBatchCommand('Create Layer'); - hrService.insertElement(layer.getGroup()); - hrService.endBatchCommand(); - } + // Crate new layer and add to DOM as last layer + var layer = new svgedit.draw.Layer(name, null, this.svgElem_); + // Like to assume hrService exists, but this is backwards compatible with old version of createLayer. + if (hrService) { + hrService.startBatchCommand('Create Layer'); + hrService.insertElement(layer.getGroup()); + hrService.endBatchCommand(); + } - this.all_layers.push(layer); - this.layer_map[name] = layer; - this.current_layer = layer; - return layer.getGroup(); + this.all_layers.push(layer); + this.layer_map[name] = layer; + this.current_layer = layer; + return layer.getGroup(); }; /** @@ -552,43 +552,43 @@ svgedit.draw.Drawing.prototype.createLayer = function (name, hrService) { * also the current layer of this drawing. */ svgedit.draw.Drawing.prototype.cloneLayer = function (name, hrService) { - if (!this.current_layer) { return null; } - this.current_layer.deactivate(); - // Check for duplicate name. - if (name === undefined || name === null || name === '' || this.layer_map[name]) { - name = getNewLayerName(Object.keys(this.layer_map)); - } + if (!this.current_layer) { return null; } + this.current_layer.deactivate(); + // Check for duplicate name. + if (name === undefined || name === null || name === '' || this.layer_map[name]) { + name = getNewLayerName(Object.keys(this.layer_map)); + } - // Create new group and add to DOM just after current_layer - var currentGroup = this.current_layer.getGroup(); - var layer = new svgedit.draw.Layer(name, currentGroup, this.svgElem_); - var group = layer.getGroup(); + // Create new group and add to DOM just after current_layer + var currentGroup = this.current_layer.getGroup(); + var layer = new svgedit.draw.Layer(name, currentGroup, this.svgElem_); + var group = layer.getGroup(); - // Clone children - var children = currentGroup.childNodes; - var index; - for (index = 0; index < children.length; index++) { - var ch = children[index]; - if (ch.localName === 'title') { continue; } - group.appendChild(this.copyElem(ch)); - } + // Clone children + var children = currentGroup.childNodes; + var index; + for (index = 0; index < children.length; index++) { + var ch = children[index]; + if (ch.localName === 'title') { continue; } + group.appendChild(this.copyElem(ch)); + } - if (hrService) { - hrService.startBatchCommand('Duplicate Layer'); - hrService.insertElement(group); - hrService.endBatchCommand(); - } + if (hrService) { + hrService.startBatchCommand('Duplicate Layer'); + hrService.insertElement(group); + hrService.endBatchCommand(); + } - // Update layer containers and current_layer. - index = this.all_layers.indexOf(this.current_layer); - if (index >= 0) { - this.all_layers.splice(index + 1, 0, layer); - } else { - this.all_layers.push(layer); - } - this.layer_map[name] = layer; - this.current_layer = layer; - return group; + // Update layer containers and current_layer. + index = this.all_layers.indexOf(this.current_layer); + if (index >= 0) { + this.all_layers.splice(index + 1, 0, layer); + } else { + this.all_layers.push(layer); + } + this.layer_map[name] = layer; + this.current_layer = layer; + return group; }; /** @@ -598,8 +598,8 @@ svgedit.draw.Drawing.prototype.cloneLayer = function (name, hrService) { * @returns {boolean} The visibility state of the layer, or false if the layer name was invalid. */ svgedit.draw.Drawing.prototype.getLayerVisibility = function (layername) { - var layer = this.layer_map[layername]; - return layer ? layer.isVisible() : false; + var layer = this.layer_map[layername]; + return layer ? layer.isVisible() : false; }; /** @@ -612,13 +612,13 @@ svgedit.draw.Drawing.prototype.getLayerVisibility = function (layername) { * layername was valid, otherwise null. */ svgedit.draw.Drawing.prototype.setLayerVisibility = function (layername, bVisible) { - if (typeof bVisible !== 'boolean') { - return null; - } - var layer = this.layer_map[layername]; - if (!layer) { return null; } - layer.setVisible(bVisible); - return layer.getGroup(); + if (typeof bVisible !== 'boolean') { + return null; + } + var layer = this.layer_map[layername]; + if (!layer) { return null; } + layer.setVisible(bVisible); + return layer.getGroup(); }; /** @@ -628,9 +628,9 @@ svgedit.draw.Drawing.prototype.setLayerVisibility = function (layername, bVisibl * if layername is not a valid layer */ svgedit.draw.Drawing.prototype.getLayerOpacity = function (layername) { - var layer = this.layer_map[layername]; - if (!layer) { return null; } - return layer.getOpacity(); + var layer = this.layer_map[layername]; + if (!layer) { return null; } + return layer.getOpacity(); }; /** @@ -641,13 +641,13 @@ svgedit.draw.Drawing.prototype.getLayerOpacity = function (layername) { * @param {number} opacity - A float value in the range 0.0-1.0 */ svgedit.draw.Drawing.prototype.setLayerOpacity = function (layername, opacity) { - if (typeof opacity !== 'number' || opacity < 0.0 || opacity > 1.0) { - return; - } - var layer = this.layer_map[layername]; - if (layer) { - layer.setOpacity(opacity); - } + if (typeof opacity !== 'number' || opacity < 0.0 || opacity > 1.0) { + return; + } + var layer = this.layer_map[layername]; + if (layer) { + layer.setOpacity(opacity); + } }; /** @@ -656,8 +656,8 @@ svgedit.draw.Drawing.prototype.setLayerOpacity = function (layername, opacity) { * @returns {Element} */ svgedit.draw.Drawing.prototype.copyElem = function (el) { - var self = this; - var getNextIdClosure = function () { return self.getNextId(); }; - return svgedit.utilities.copyElem(el, getNextIdClosure); + var self = this; + var getNextIdClosure = function () { return self.getNextId(); }; + return svgedit.utilities.copyElem(el, getNextIdClosure); }; }()); diff --git a/editor/embedapi-dom.js b/editor/embedapi-dom.js index 098912df..33c661ff 100644 --- a/editor/embedapi-dom.js +++ b/editor/embedapi-dom.js @@ -5,76 +5,76 @@ var initEmbed; // eslint-disable-line no-unused-vars // Todo: Get rid of frame.contentWindow dependencies so can be more easily adjusted to work cross-domain $(function () { - 'use strict'; + 'use strict'; - var svgCanvas = null; - var frame; + var svgCanvas = null; + var frame; - initEmbed = function () { - var doc, mainButton; - svgCanvas = new EmbeddedSVGEdit(frame); - // Hide main button, as we will be controlling new, load, save, etc. from the host document - doc = frame.contentDocument || frame.contentWindow.document; - mainButton = doc.getElementById('main_button'); - mainButton.style.display = 'none'; - }; + initEmbed = function () { + var doc, mainButton; + svgCanvas = new EmbeddedSVGEdit(frame); + // Hide main button, as we will be controlling new, load, save, etc. from the host document + doc = frame.contentDocument || frame.contentWindow.document; + mainButton = doc.getElementById('main_button'); + mainButton.style.display = 'none'; + }; - function handleSvgData (data, error) { - if (error) { - alert('error ' + error); - } else { - alert('Congratulations. Your SVG string is back in the host page, do with it what you will\n\n' + data); - } - } + function handleSvgData (data, error) { + if (error) { + alert('error ' + error); + } else { + alert('Congratulations. Your SVG string is back in the host page, do with it what you will\n\n' + data); + } + } - function loadSvg () { - var svgexample = '<svg width="640" height="480" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><g><title>Layer 1'; - svgCanvas.setSvgString(svgexample); - } + function loadSvg () { + var svgexample = 'Layer 1'; + svgCanvas.setSvgString(svgexample); + } - function saveSvg () { - svgCanvas.getSvgString()(handleSvgData); - } + function saveSvg () { + svgCanvas.getSvgString()(handleSvgData); + } - function exportPNG () { - var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage; + function exportPNG () { + var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage; - var exportWindow = window.open( - 'data:text/html;charset=utf-8,' + encodeURIComponent('' + str + '

    ' + str + '

    '), - 'svg-edit-exportWindow' - ); - svgCanvas.rasterExport('PNG', null, exportWindow.name); - } + var exportWindow = window.open( + 'data:text/html;charset=utf-8,' + encodeURIComponent('' + str + '

    ' + str + '

    '), + 'svg-edit-exportWindow' + ); + svgCanvas.rasterExport('PNG', null, exportWindow.name); + } - function exportPDF () { - var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage; + function exportPDF () { + var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage; - /** - // If you want to handle the PDF blob yourself, do as follows - svgCanvas.bind('exportedPDF', function (win, data) { - alert(data.dataurlstring); - }); - svgCanvas.exportPDF(); // Accepts two args: optionalWindowName supplied back to bound exportPDF handler and optionalOutputType (defaults to dataurlstring) - return; - */ + /** + // If you want to handle the PDF blob yourself, do as follows + svgCanvas.bind('exportedPDF', function (win, data) { + alert(data.dataurlstring); + }); + svgCanvas.exportPDF(); // Accepts two args: optionalWindowName supplied back to bound exportPDF handler and optionalOutputType (defaults to dataurlstring) + return; + */ - var exportWindow = window.open( - 'data:text/html;charset=utf-8,' + encodeURIComponent('' + str + '

    ' + str + '

    '), - 'svg-edit-exportWindow' - ); - svgCanvas.exportPDF(exportWindow.name); - } + var exportWindow = window.open( + 'data:text/html;charset=utf-8,' + encodeURIComponent('' + str + '

    ' + str + '

    '), + 'svg-edit-exportWindow' + ); + svgCanvas.exportPDF(exportWindow.name); + } - // Add event handlers - $('#load').click(loadSvg); - $('#save').click(saveSvg); - $('#exportPNG').click(exportPNG); - $('#exportPDF').click(exportPDF); - $('body').append( - $('' - ) - ); - frame = document.getElementById('svgedit'); + // Add event handlers + $('#load').click(loadSvg); + $('#save').click(saveSvg); + $('#exportPNG').click(exportPNG); + $('#exportPDF').click(exportPDF); + $('body').append( + $('' + ) + ); + frame = document.getElementById('svgedit'); }); diff --git a/editor/embedapi.html b/editor/embedapi.html index 04e176a4..95f45572 100644 --- a/editor/embedapi.html +++ b/editor/embedapi.html @@ -1,17 +1,17 @@  - - Embed API - - - + + Embed API + + + - - - - -
    + + + + +
    diff --git a/editor/embedapi.js b/editor/embedapi.js index a996e9b7..af61b41f 100644 --- a/editor/embedapi.js +++ b/editor/embedapi.js @@ -10,11 +10,11 @@ var svgCanvas = new EmbeddedSVGEdit(window.frames.svgedit); svgCanvas.setSvgString('string') - Or if a callback is needed: svgCanvas.setSvgString('string')(function(data, error){ - if (error){ - // There was an error - } else{ - // Handle data - } + if (error){ + // There was an error + } else{ + // Handle data + } }) Everything is done with the same API as the real svg-edit, @@ -42,15 +42,15 @@ blah.clearSelection('woot', 'blah', 1337, [1, 2, 3, 4, 5, 'moo'], -42, {a: 'tree var cbid = 0; function getCallbackSetter (d) { - return function () { - var t = this, // New callback - args = [].slice.call(arguments), - cbid = t.send(d, args, function () {}); // The callback (currently it's nothing, but will be set later) + return function () { + var t = this, // New callback + args = [].slice.call(arguments), + cbid = t.send(d, args, function () {}); // The callback (currently it's nothing, but will be set later) - return function (newcallback) { - t.callbacks[cbid] = newcallback; // Set callback - }; - }; + return function (newcallback) { + t.callbacks[cbid] = newcallback; // Set callback + }; + }; } /* @@ -59,38 +59,38 @@ function getCallbackSetter (d) { * of same domain control */ function addCallback (t, data) { - var result = data.result || data.error, - cbid = data.id; - if (t.callbacks[cbid]) { - if (data.result) { - t.callbacks[cbid](result); - } else { - t.callbacks[cbid](result, 'error'); - } - } + var result = data.result || data.error, + cbid = data.id; + if (t.callbacks[cbid]) { + if (data.result) { + t.callbacks[cbid](result); + } else { + t.callbacks[cbid](result, 'error'); + } + } } function messageListener (e) { - // We accept and post strings as opposed to objects for the sake of IE9 support; this - // will most likely be changed in the future - if (typeof e.data !== 'string') { - return; - } - var allowedOrigins = this.allowedOrigins, - data = e.data && JSON.parse(e.data); - if (!data || typeof data !== 'object' || data.namespace !== 'svg-edit' || - e.source !== this.frame.contentWindow || - (allowedOrigins.indexOf('*') === -1 && allowedOrigins.indexOf(e.origin) === -1) - ) { - return; - } - addCallback(this, data); + // We accept and post strings as opposed to objects for the sake of IE9 support; this + // will most likely be changed in the future + if (typeof e.data !== 'string') { + return; + } + var allowedOrigins = this.allowedOrigins, + data = e.data && JSON.parse(e.data); + if (!data || typeof data !== 'object' || data.namespace !== 'svg-edit' || + e.source !== this.frame.contentWindow || + (allowedOrigins.indexOf('*') === -1 && allowedOrigins.indexOf(e.origin) === -1) + ) { + return; + } + addCallback(this, data); } function getMessageListener (t) { - return function (e) { - messageListener.call(t, e); - }; + return function (e) { + messageListener.call(t, e); + }; } /** @@ -100,77 +100,77 @@ function getMessageListener (t) { * If supplied, it should probably be the same as svgEditor's allowedOrigins */ function EmbeddedSVGEdit (frame, allowedOrigins) { - if (!(this instanceof EmbeddedSVGEdit)) { // Allow invocation without 'new' keyword - return new EmbeddedSVGEdit(frame); - } - this.allowedOrigins = allowedOrigins || []; - // Initialize communication - this.frame = frame; - this.callbacks = {}; - // List of functions extracted with this: - // Run in firebug on http://svg-edit.googlecode.com/svn/trunk/docs/files/svgcanvas-js.html + if (!(this instanceof EmbeddedSVGEdit)) { // Allow invocation without 'new' keyword + return new EmbeddedSVGEdit(frame); + } + this.allowedOrigins = allowedOrigins || []; + // Initialize communication + this.frame = frame; + this.callbacks = {}; + // List of functions extracted with this: + // Run in firebug on http://svg-edit.googlecode.com/svn/trunk/docs/files/svgcanvas-js.html - // for (var i=0,q=[],f = document.querySelectorAll('div.CFunction h3.CTitle a'); i < f.length; i++) { q.push(f[i].name); }; q - // var functions = ['clearSelection', 'addToSelection', 'removeFromSelection', 'open', 'save', 'getSvgString', 'setSvgString', - // 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility', - // 'moveSelectedToLayer', 'clear']; + // for (var i=0,q=[],f = document.querySelectorAll('div.CFunction h3.CTitle a'); i < f.length; i++) { q.push(f[i].name); }; q + // var functions = ['clearSelection', 'addToSelection', 'removeFromSelection', 'open', 'save', 'getSvgString', 'setSvgString', + // 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility', + // 'moveSelectedToLayer', 'clear']; - // Newer, well, it extracts things that aren't documented as well. All functions accessible through the normal thingy can now be accessed though the API - // var svgCanvas = frame.contentWindow.svgCanvas; - // var l = []; for (var i in svgCanvas){ if (typeof svgCanvas[i] == 'function') { l.push(i);} }; - // alert("['" + l.join("', '") + "']"); - // Run in svgedit itself - var i, - functions = [ - 'clearSvgContentElement', 'setIdPrefix', 'getCurrentDrawing', 'addSvgElementFromJson', 'getTransformList', 'matrixMultiply', 'hasMatrixTransform', 'transformListToTransform', 'convertToNum', 'findDefs', 'getUrlFromAttr', 'getHref', 'setHref', 'getBBox', 'getRotationAngle', 'getElem', 'getRefElem', 'assignAttributes', 'cleanupElement', 'remapElement', 'recalculateDimensions', 'sanitizeSvg', 'runExtensions', 'addExtension', 'round', 'getIntersectionList', 'getStrokedBBox', 'getVisibleElements', 'getVisibleElementsAndBBoxes', 'groupSvgElem', 'getId', 'getNextId', 'call', 'bind', 'prepareSvg', 'setRotationAngle', 'recalculateAllSelectedDimensions', 'clearSelection', 'addToSelection', 'selectOnly', 'removeFromSelection', 'selectAllInCurrentLayer', 'getMouseTarget', 'removeUnusedDefElems', 'svgCanvasToString', 'svgToString', 'embedImage', 'setGoodImage', 'open', 'save', 'rasterExport', 'exportPDF', 'getSvgString', 'randomizeIds', 'uniquifyElems', 'setUseData', 'convertGradients', 'convertToGroup', 'setSvgString', 'importSvgString', 'identifyLayers', 'createLayer', 'cloneLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility', 'moveSelectedToLayer', 'mergeLayer', 'mergeAllLayers', 'leaveContext', 'setContext', 'clear', 'linkControlPoints', 'getContentElem', 'getRootElem', 'getSelectedElems', 'getResolution', 'getZoom', 'getVersion', 'setUiStrings', 'setConfig', 'getTitle', 'setGroupTitle', 'getDocumentTitle', 'setDocumentTitle', 'getEditorNS', 'setResolution', 'getOffset', 'setBBoxZoom', 'setZoom', 'getMode', 'setMode', 'getColor', 'setColor', 'setGradient', 'setPaint', 'setStrokePaint', 'setFillPaint', 'getStrokeWidth', 'setStrokeWidth', 'setStrokeAttr', 'getStyle', 'getOpacity', 'setOpacity', 'getFillOpacity', 'getStrokeOpacity', 'setPaintOpacity', 'getPaintOpacity', 'getBlur', 'setBlurNoUndo', 'setBlurOffsets', 'setBlur', 'getBold', 'setBold', 'getItalic', 'setItalic', 'getFontFamily', 'setFontFamily', 'setFontColor', 'getFontColor', 'getFontSize', 'setFontSize', 'getText', 'setTextContent', 'setImageURL', 'setLinkURL', 'setRectRadius', 'makeHyperlink', 'removeHyperlink', 'setSegType', 'convertToPath', 'changeSelectedAttribute', 'deleteSelectedElements', 'cutSelectedElements', 'copySelectedElements', 'pasteElements', 'groupSelectedElements', 'pushGroupProperties', 'ungroupSelectedElement', 'moveToTopSelectedElement', 'moveToBottomSelectedElement', 'moveUpDownSelected', 'moveSelectedElements', 'cloneSelectedElements', 'alignSelectedElements', 'updateCanvas', 'setBackground', 'cycleElement', 'getPrivateMethods', 'zoomChanged', 'ready' - ]; + // Newer, well, it extracts things that aren't documented as well. All functions accessible through the normal thingy can now be accessed though the API + // var svgCanvas = frame.contentWindow.svgCanvas; + // var l = []; for (var i in svgCanvas){ if (typeof svgCanvas[i] == 'function') { l.push(i);} }; + // alert("['" + l.join("', '") + "']"); + // Run in svgedit itself + var i, + functions = [ + 'clearSvgContentElement', 'setIdPrefix', 'getCurrentDrawing', 'addSvgElementFromJson', 'getTransformList', 'matrixMultiply', 'hasMatrixTransform', 'transformListToTransform', 'convertToNum', 'findDefs', 'getUrlFromAttr', 'getHref', 'setHref', 'getBBox', 'getRotationAngle', 'getElem', 'getRefElem', 'assignAttributes', 'cleanupElement', 'remapElement', 'recalculateDimensions', 'sanitizeSvg', 'runExtensions', 'addExtension', 'round', 'getIntersectionList', 'getStrokedBBox', 'getVisibleElements', 'getVisibleElementsAndBBoxes', 'groupSvgElem', 'getId', 'getNextId', 'call', 'bind', 'prepareSvg', 'setRotationAngle', 'recalculateAllSelectedDimensions', 'clearSelection', 'addToSelection', 'selectOnly', 'removeFromSelection', 'selectAllInCurrentLayer', 'getMouseTarget', 'removeUnusedDefElems', 'svgCanvasToString', 'svgToString', 'embedImage', 'setGoodImage', 'open', 'save', 'rasterExport', 'exportPDF', 'getSvgString', 'randomizeIds', 'uniquifyElems', 'setUseData', 'convertGradients', 'convertToGroup', 'setSvgString', 'importSvgString', 'identifyLayers', 'createLayer', 'cloneLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility', 'moveSelectedToLayer', 'mergeLayer', 'mergeAllLayers', 'leaveContext', 'setContext', 'clear', 'linkControlPoints', 'getContentElem', 'getRootElem', 'getSelectedElems', 'getResolution', 'getZoom', 'getVersion', 'setUiStrings', 'setConfig', 'getTitle', 'setGroupTitle', 'getDocumentTitle', 'setDocumentTitle', 'getEditorNS', 'setResolution', 'getOffset', 'setBBoxZoom', 'setZoom', 'getMode', 'setMode', 'getColor', 'setColor', 'setGradient', 'setPaint', 'setStrokePaint', 'setFillPaint', 'getStrokeWidth', 'setStrokeWidth', 'setStrokeAttr', 'getStyle', 'getOpacity', 'setOpacity', 'getFillOpacity', 'getStrokeOpacity', 'setPaintOpacity', 'getPaintOpacity', 'getBlur', 'setBlurNoUndo', 'setBlurOffsets', 'setBlur', 'getBold', 'setBold', 'getItalic', 'setItalic', 'getFontFamily', 'setFontFamily', 'setFontColor', 'getFontColor', 'getFontSize', 'setFontSize', 'getText', 'setTextContent', 'setImageURL', 'setLinkURL', 'setRectRadius', 'makeHyperlink', 'removeHyperlink', 'setSegType', 'convertToPath', 'changeSelectedAttribute', 'deleteSelectedElements', 'cutSelectedElements', 'copySelectedElements', 'pasteElements', 'groupSelectedElements', 'pushGroupProperties', 'ungroupSelectedElement', 'moveToTopSelectedElement', 'moveToBottomSelectedElement', 'moveUpDownSelected', 'moveSelectedElements', 'cloneSelectedElements', 'alignSelectedElements', 'updateCanvas', 'setBackground', 'cycleElement', 'getPrivateMethods', 'zoomChanged', 'ready' + ]; - // TODO: rewrite the following, it's pretty scary. - for (i = 0; i < functions.length; i++) { - this[functions[i]] = getCallbackSetter(functions[i]); - } + // TODO: rewrite the following, it's pretty scary. + for (i = 0; i < functions.length; i++) { + this[functions[i]] = getCallbackSetter(functions[i]); + } - // Older IE may need a polyfill for addEventListener, but so it would for SVG - window.addEventListener('message', getMessageListener(this), false); + // Older IE may need a polyfill for addEventListener, but so it would for SVG + window.addEventListener('message', getMessageListener(this), false); } EmbeddedSVGEdit.prototype.send = function (name, args, callback) { - var t = this; - cbid++; + var t = this; + cbid++; - this.callbacks[cbid] = callback; - setTimeout((function (cbid) { - return function () { // Delay for the callback to be set in case its synchronous - /* - * Todo: Handle non-JSON arguments and return values (undefined, - * nonfinite numbers, functions, and built-in objects like Date, - * RegExp), etc.? Allow promises instead of callbacks? Review - * SVG-Edit functions for whether JSON-able parameters can be - * made compatile with all API functionality - */ - // We accept and post strings for the sake of IE9 support - if (window.location.origin === t.frame.contentWindow.location.origin) { - // Although we do not really need this API if we are working same - // domain, it could allow us to write in a way that would work - // cross-domain as well, assuming we stick to the argument limitations - // of the current JSON-based communication API (e.g., not passing - // callbacks). We might be able to address these shortcomings; see - // the todo elsewhere in this file. - var message = {id: cbid}, - svgCanvas = t.frame.contentWindow.svgCanvas; - try { - message.result = svgCanvas[name].apply(svgCanvas, args); - } catch (err) { - message.error = err.message; - } - addCallback(t, message); - } else { // Requires the ext-xdomain-messaging.js extension - t.frame.contentWindow.postMessage(JSON.stringify({namespace: 'svgCanvas', id: cbid, name: name, args: args}), '*'); - } - }; - }(cbid)), 0); + this.callbacks[cbid] = callback; + setTimeout((function (cbid) { + return function () { // Delay for the callback to be set in case its synchronous + /* + * Todo: Handle non-JSON arguments and return values (undefined, + * nonfinite numbers, functions, and built-in objects like Date, + * RegExp), etc.? Allow promises instead of callbacks? Review + * SVG-Edit functions for whether JSON-able parameters can be + * made compatile with all API functionality + */ + // We accept and post strings for the sake of IE9 support + if (window.location.origin === t.frame.contentWindow.location.origin) { + // Although we do not really need this API if we are working same + // domain, it could allow us to write in a way that would work + // cross-domain as well, assuming we stick to the argument limitations + // of the current JSON-based communication API (e.g., not passing + // callbacks). We might be able to address these shortcomings; see + // the todo elsewhere in this file. + var message = {id: cbid}, + svgCanvas = t.frame.contentWindow.svgCanvas; + try { + message.result = svgCanvas[name].apply(svgCanvas, args); + } catch (err) { + message.error = err.message; + } + addCallback(t, message); + } else { // Requires the ext-xdomain-messaging.js extension + t.frame.contentWindow.postMessage(JSON.stringify({namespace: 'svgCanvas', id: cbid, name: name, args: args}), '*'); + } + }; + }(cbid)), 0); - return cbid; + return cbid; }; window.embedded_svg_edit = EmbeddedSVGEdit; // Export old, deprecated API diff --git a/editor/history.js b/editor/history.js index d546779b..b2970615 100644 --- a/editor/history.js +++ b/editor/history.js @@ -17,15 +17,15 @@ 'use strict'; if (!svgedit.history) { - svgedit.history = {}; + svgedit.history = {}; } // Group: Undo/Redo history management svgedit.history.HistoryEventTypes = { - BEFORE_APPLY: 'before_apply', - AFTER_APPLY: 'after_apply', - BEFORE_UNAPPLY: 'before_unapply', - AFTER_UNAPPLY: 'after_unapply' + BEFORE_APPLY: 'before_apply', + AFTER_APPLY: 'after_apply', + BEFORE_UNAPPLY: 'before_unapply', + AFTER_UNAPPLY: 'after_unapply' }; // var removedElements = {}; @@ -63,18 +63,18 @@ svgedit.history.HistoryEventTypes = { * @param {string} [text] - An optional string visible to user related to this change */ svgedit.history.MoveElementCommand = function (elem, oldNextSibling, oldParent, text) { - this.elem = elem; - this.text = text ? ('Move ' + elem.tagName + ' to ' + text) : ('Move ' + elem.tagName); - this.oldNextSibling = oldNextSibling; - this.oldParent = oldParent; - this.newNextSibling = elem.nextSibling; - this.newParent = elem.parentNode; + this.elem = elem; + this.text = text ? ('Move ' + elem.tagName + ' to ' + text) : ('Move ' + elem.tagName); + this.oldNextSibling = oldNextSibling; + this.oldParent = oldParent; + this.newNextSibling = elem.nextSibling; + this.newParent = elem.parentNode; }; svgedit.history.MoveElementCommand.type = function () { return 'svgedit.history.MoveElementCommand'; }; svgedit.history.MoveElementCommand.prototype.type = svgedit.history.MoveElementCommand.type; svgedit.history.MoveElementCommand.prototype.getText = function () { - return this.text; + return this.text; }; /** @@ -82,16 +82,16 @@ svgedit.history.MoveElementCommand.prototype.getText = function () { * @param {handleHistoryEvent: function} */ svgedit.history.MoveElementCommand.prototype.apply = function (handler) { - // TODO(codedread): Refactor this common event code into a base HistoryCommand class. - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); - } + // TODO(codedread): Refactor this common event code into a base HistoryCommand class. + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); + } - this.elem = this.newParent.insertBefore(this.elem, this.newNextSibling); + this.elem = this.newParent.insertBefore(this.elem, this.newNextSibling); - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); + } }; /** @@ -99,21 +99,21 @@ svgedit.history.MoveElementCommand.prototype.apply = function (handler) { * @param {handleHistoryEvent: function} */ svgedit.history.MoveElementCommand.prototype.unapply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); + } - this.elem = this.oldParent.insertBefore(this.elem, this.oldNextSibling); + this.elem = this.oldParent.insertBefore(this.elem, this.oldNextSibling); - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); + } }; // Function: svgedit.history.MoveElementCommand.elements // Returns array with element associated with this command svgedit.history.MoveElementCommand.prototype.elements = function () { - return [this.elem]; + return [this.elem]; }; // Class: svgedit.history.InsertElementCommand @@ -124,52 +124,52 @@ svgedit.history.MoveElementCommand.prototype.elements = function () { // elem - The newly added DOM element // text - An optional string visible to user related to this change svgedit.history.InsertElementCommand = function (elem, text) { - this.elem = elem; - this.text = text || ('Create ' + elem.tagName); - this.parent = elem.parentNode; - this.nextSibling = this.elem.nextSibling; + this.elem = elem; + this.text = text || ('Create ' + elem.tagName); + this.parent = elem.parentNode; + this.nextSibling = this.elem.nextSibling; }; svgedit.history.InsertElementCommand.type = function () { return 'svgedit.history.InsertElementCommand'; }; svgedit.history.InsertElementCommand.prototype.type = svgedit.history.InsertElementCommand.type; // Function: svgedit.history.InsertElementCommand.getText svgedit.history.InsertElementCommand.prototype.getText = function () { - return this.text; + return this.text; }; // Function: svgedit.history.InsertElementCommand.apply // Re-Inserts the new element svgedit.history.InsertElementCommand.prototype.apply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); + } - this.elem = this.parent.insertBefore(this.elem, this.nextSibling); + this.elem = this.parent.insertBefore(this.elem, this.nextSibling); - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); + } }; // Function: svgedit.history.InsertElementCommand.unapply // Removes the element svgedit.history.InsertElementCommand.prototype.unapply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); + } - this.parent = this.elem.parentNode; - this.elem = this.elem.parentNode.removeChild(this.elem); + this.parent = this.elem.parentNode; + this.elem = this.elem.parentNode.removeChild(this.elem); - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); + } }; // Function: svgedit.history.InsertElementCommand.elements // Returns array with element associated with this command svgedit.history.InsertElementCommand.prototype.elements = function () { - return [this.elem]; + return [this.elem]; }; // Class: svgedit.history.RemoveElementCommand @@ -182,62 +182,62 @@ svgedit.history.InsertElementCommand.prototype.elements = function () { // oldParent - The DOM element's parent // text - An optional string visible to user related to this change svgedit.history.RemoveElementCommand = function (elem, oldNextSibling, oldParent, text) { - this.elem = elem; - this.text = text || ('Delete ' + elem.tagName); - this.nextSibling = oldNextSibling; - this.parent = oldParent; + this.elem = elem; + this.text = text || ('Delete ' + elem.tagName); + this.nextSibling = oldNextSibling; + this.parent = oldParent; - // special hack for webkit: remove this element's entry in the svgTransformLists map - svgedit.transformlist.removeElementFromListMap(elem); + // special hack for webkit: remove this element's entry in the svgTransformLists map + svgedit.transformlist.removeElementFromListMap(elem); }; svgedit.history.RemoveElementCommand.type = function () { return 'svgedit.history.RemoveElementCommand'; }; svgedit.history.RemoveElementCommand.prototype.type = svgedit.history.RemoveElementCommand.type; // Function: svgedit.history.RemoveElementCommand.getText svgedit.history.RemoveElementCommand.prototype.getText = function () { - return this.text; + return this.text; }; // Function: RemoveElementCommand.apply // Re-removes the new element svgedit.history.RemoveElementCommand.prototype.apply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); + } - svgedit.transformlist.removeElementFromListMap(this.elem); - this.parent = this.elem.parentNode; - this.elem = this.parent.removeChild(this.elem); + svgedit.transformlist.removeElementFromListMap(this.elem); + this.parent = this.elem.parentNode; + this.elem = this.parent.removeChild(this.elem); - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); + } }; // Function: RemoveElementCommand.unapply // Re-adds the new element svgedit.history.RemoveElementCommand.prototype.unapply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); + } - svgedit.transformlist.removeElementFromListMap(this.elem); - if (this.nextSibling == null) { - if (window.console) { - console.log('Error: reference element was lost'); - } - } - this.parent.insertBefore(this.elem, this.nextSibling); + svgedit.transformlist.removeElementFromListMap(this.elem); + if (this.nextSibling == null) { + if (window.console) { + console.log('Error: reference element was lost'); + } + } + this.parent.insertBefore(this.elem, this.nextSibling); - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); + } }; // Function: RemoveElementCommand.elements // Returns array with element associated with this command svgedit.history.RemoveElementCommand.prototype.elements = function () { - return [this.elem]; + return [this.elem]; }; // Class: svgedit.history.ChangeElementCommand @@ -250,135 +250,135 @@ svgedit.history.RemoveElementCommand.prototype.elements = function () { // attrs - An object with the attributes to be changed and the values they had *before* the change // text - An optional string visible to user related to this change svgedit.history.ChangeElementCommand = function (elem, attrs, text) { - this.elem = elem; - this.text = text ? ('Change ' + elem.tagName + ' ' + text) : ('Change ' + elem.tagName); - this.newValues = {}; - this.oldValues = attrs; - var attr; - for (attr in attrs) { - if (attr === '#text') { - this.newValues[attr] = elem.textContent; - } else if (attr === '#href') { - this.newValues[attr] = svgedit.utilities.getHref(elem); - } else { - this.newValues[attr] = elem.getAttribute(attr); - } - } + this.elem = elem; + this.text = text ? ('Change ' + elem.tagName + ' ' + text) : ('Change ' + elem.tagName); + this.newValues = {}; + this.oldValues = attrs; + var attr; + for (attr in attrs) { + if (attr === '#text') { + this.newValues[attr] = elem.textContent; + } else if (attr === '#href') { + this.newValues[attr] = svgedit.utilities.getHref(elem); + } else { + this.newValues[attr] = elem.getAttribute(attr); + } + } }; svgedit.history.ChangeElementCommand.type = function () { return 'svgedit.history.ChangeElementCommand'; }; svgedit.history.ChangeElementCommand.prototype.type = svgedit.history.ChangeElementCommand.type; // Function: svgedit.history.ChangeElementCommand.getText svgedit.history.ChangeElementCommand.prototype.getText = function () { - return this.text; + return this.text; }; // Function: svgedit.history.ChangeElementCommand.apply // Performs the stored change action svgedit.history.ChangeElementCommand.prototype.apply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); + } - var bChangedTransform = false; - var attr; - for (attr in this.newValues) { - if (this.newValues[attr]) { - if (attr === '#text') { - this.elem.textContent = this.newValues[attr]; - } else if (attr === '#href') { - svgedit.utilities.setHref(this.elem, this.newValues[attr]); - } else { - this.elem.setAttribute(attr, this.newValues[attr]); - } - } else { - if (attr === '#text') { - this.elem.textContent = ''; - } else { - this.elem.setAttribute(attr, ''); - this.elem.removeAttribute(attr); - } - } + var bChangedTransform = false; + var attr; + for (attr in this.newValues) { + if (this.newValues[attr]) { + if (attr === '#text') { + this.elem.textContent = this.newValues[attr]; + } else if (attr === '#href') { + svgedit.utilities.setHref(this.elem, this.newValues[attr]); + } else { + this.elem.setAttribute(attr, this.newValues[attr]); + } + } else { + if (attr === '#text') { + this.elem.textContent = ''; + } else { + this.elem.setAttribute(attr, ''); + this.elem.removeAttribute(attr); + } + } - if (attr === 'transform') { bChangedTransform = true; } - } + if (attr === 'transform') { bChangedTransform = true; } + } - // relocate rotational transform, if necessary - if (!bChangedTransform) { - var angle = svgedit.utilities.getRotationAngle(this.elem); - if (angle) { - var bbox = this.elem.getBBox(); - var cx = bbox.x + bbox.width / 2, - cy = bbox.y + bbox.height / 2; - var rotate = ['rotate(', angle, ' ', cx, ',', cy, ')'].join(''); - if (rotate !== this.elem.getAttribute('transform')) { - this.elem.setAttribute('transform', rotate); - } - } - } + // relocate rotational transform, if necessary + if (!bChangedTransform) { + var angle = svgedit.utilities.getRotationAngle(this.elem); + if (angle) { + var bbox = this.elem.getBBox(); + var cx = bbox.x + bbox.width / 2, + cy = bbox.y + bbox.height / 2; + var rotate = ['rotate(', angle, ' ', cx, ',', cy, ')'].join(''); + if (rotate !== this.elem.getAttribute('transform')) { + this.elem.setAttribute('transform', rotate); + } + } + } - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); + } - return true; + return true; }; // Function: svgedit.history.ChangeElementCommand.unapply // Reverses the stored change action svgedit.history.ChangeElementCommand.prototype.unapply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); + } - var bChangedTransform = false; - var attr; - for (attr in this.oldValues) { - if (this.oldValues[attr]) { - if (attr === '#text') { - this.elem.textContent = this.oldValues[attr]; - } else if (attr === '#href') { - svgedit.utilities.setHref(this.elem, this.oldValues[attr]); - } else { - this.elem.setAttribute(attr, this.oldValues[attr]); - } - } else { - if (attr === '#text') { - this.elem.textContent = ''; - } else { - this.elem.removeAttribute(attr); - } - } - if (attr === 'transform') { bChangedTransform = true; } - } - // relocate rotational transform, if necessary - if (!bChangedTransform) { - var angle = svgedit.utilities.getRotationAngle(this.elem); - if (angle) { - var bbox = this.elem.getBBox(); - var cx = bbox.x + bbox.width / 2, - cy = bbox.y + bbox.height / 2; - var rotate = ['rotate(', angle, ' ', cx, ',', cy, ')'].join(''); - if (rotate !== this.elem.getAttribute('transform')) { - this.elem.setAttribute('transform', rotate); - } - } - } + var bChangedTransform = false; + var attr; + for (attr in this.oldValues) { + if (this.oldValues[attr]) { + if (attr === '#text') { + this.elem.textContent = this.oldValues[attr]; + } else if (attr === '#href') { + svgedit.utilities.setHref(this.elem, this.oldValues[attr]); + } else { + this.elem.setAttribute(attr, this.oldValues[attr]); + } + } else { + if (attr === '#text') { + this.elem.textContent = ''; + } else { + this.elem.removeAttribute(attr); + } + } + if (attr === 'transform') { bChangedTransform = true; } + } + // relocate rotational transform, if necessary + if (!bChangedTransform) { + var angle = svgedit.utilities.getRotationAngle(this.elem); + if (angle) { + var bbox = this.elem.getBBox(); + var cx = bbox.x + bbox.width / 2, + cy = bbox.y + bbox.height / 2; + var rotate = ['rotate(', angle, ' ', cx, ',', cy, ')'].join(''); + if (rotate !== this.elem.getAttribute('transform')) { + this.elem.setAttribute('transform', rotate); + } + } + } - // Remove transformlist to prevent confusion that causes bugs like 575. - svgedit.transformlist.removeElementFromListMap(this.elem); + // Remove transformlist to prevent confusion that causes bugs like 575. + svgedit.transformlist.removeElementFromListMap(this.elem); - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); + } - return true; + return true; }; // Function: ChangeElementCommand.elements // Returns array with element associated with this command svgedit.history.ChangeElementCommand.prototype.elements = function () { - return [this.elem]; + return [this.elem]; }; // TODO: create a 'typing' command object that tracks changes in text @@ -392,65 +392,65 @@ svgedit.history.ChangeElementCommand.prototype.elements = function () { // Parameters: // text - An optional string visible to user related to this change svgedit.history.BatchCommand = function (text) { - this.text = text || 'Batch Command'; - this.stack = []; + this.text = text || 'Batch Command'; + this.stack = []; }; svgedit.history.BatchCommand.type = function () { return 'svgedit.history.BatchCommand'; }; svgedit.history.BatchCommand.prototype.type = svgedit.history.BatchCommand.type; // Function: svgedit.history.BatchCommand.getText svgedit.history.BatchCommand.prototype.getText = function () { - return this.text; + return this.text; }; // Function: svgedit.history.BatchCommand.apply // Runs "apply" on all subcommands svgedit.history.BatchCommand.prototype.apply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); + } - var i, - len = this.stack.length; - for (i = 0; i < len; ++i) { - this.stack[i].apply(handler); - } + var i, + len = this.stack.length; + for (i = 0; i < len; ++i) { + this.stack[i].apply(handler); + } - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); + } }; // Function: svgedit.history.BatchCommand.unapply // Runs "unapply" on all subcommands svgedit.history.BatchCommand.prototype.unapply = function (handler) { - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); + } - var i; - for (i = this.stack.length - 1; i >= 0; i--) { - this.stack[i].unapply(handler); - } + var i; + for (i = this.stack.length - 1; i >= 0; i--) { + this.stack[i].unapply(handler); + } - if (handler) { - handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); - } + if (handler) { + handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); + } }; // Function: svgedit.history.BatchCommand.elements // Iterate through all our subcommands and returns all the elements we are changing svgedit.history.BatchCommand.prototype.elements = function () { - var elems = []; - var cmd = this.stack.length; - while (cmd--) { - var thisElems = this.stack[cmd].elements(); - var elem = thisElems.length; - while (elem--) { - if (elems.indexOf(thisElems[elem]) === -1) { elems.push(thisElems[elem]); } - } - } - return elems; + var elems = []; + var cmd = this.stack.length; + while (cmd--) { + var thisElems = this.stack[cmd].elements(); + var elem = thisElems.length; + while (elem--) { + if (elems.indexOf(thisElems[elem]) === -1) { elems.push(thisElems[elem]); } + } + } + return elems; }; // Function: svgedit.history.BatchCommand.addSubCommand @@ -459,13 +459,13 @@ svgedit.history.BatchCommand.prototype.elements = function () { // Parameters: // cmd - The undo command object to add svgedit.history.BatchCommand.prototype.addSubCommand = function (cmd) { - this.stack.push(cmd); + this.stack.push(cmd); }; // Function: svgedit.history.BatchCommand.isEmpty // Returns a boolean indicating whether or not the batch command is empty svgedit.history.BatchCommand.prototype.isEmpty = function () { - return this.stack.length === 0; + return this.stack.length === 0; }; // Class: svgedit.history.UndoManager @@ -473,67 +473,67 @@ svgedit.history.BatchCommand.prototype.isEmpty = function () { // historyEventHandler - an object that conforms to the HistoryEventHandler interface // (see above) svgedit.history.UndoManager = function (historyEventHandler) { - this.handler_ = historyEventHandler || null; - this.undoStackPointer = 0; - this.undoStack = []; + this.handler_ = historyEventHandler || null; + this.undoStackPointer = 0; + this.undoStack = []; - // this is the stack that stores the original values, the elements and - // the attribute name for begin/finish - this.undoChangeStackPointer = -1; - this.undoableChangeStack = []; + // this is the stack that stores the original values, the elements and + // the attribute name for begin/finish + this.undoChangeStackPointer = -1; + this.undoableChangeStack = []; }; // Function: svgedit.history.UndoManager.resetUndoStack // Resets the undo stack, effectively clearing the undo/redo history svgedit.history.UndoManager.prototype.resetUndoStack = function () { - this.undoStack = []; - this.undoStackPointer = 0; + this.undoStack = []; + this.undoStackPointer = 0; }; // Function: svgedit.history.UndoManager.getUndoStackSize // Returns: // Integer with the current size of the undo history stack svgedit.history.UndoManager.prototype.getUndoStackSize = function () { - return this.undoStackPointer; + return this.undoStackPointer; }; // Function: svgedit.history.UndoManager.getRedoStackSize // Returns: // Integer with the current size of the redo history stack svgedit.history.UndoManager.prototype.getRedoStackSize = function () { - return this.undoStack.length - this.undoStackPointer; + return this.undoStack.length - this.undoStackPointer; }; // Function: svgedit.history.UndoManager.getNextUndoCommandText // Returns: // String associated with the next undo command svgedit.history.UndoManager.prototype.getNextUndoCommandText = function () { - return this.undoStackPointer > 0 ? this.undoStack[this.undoStackPointer - 1].getText() : ''; + return this.undoStackPointer > 0 ? this.undoStack[this.undoStackPointer - 1].getText() : ''; }; // Function: svgedit.history.UndoManager.getNextRedoCommandText // Returns: // String associated with the next redo command svgedit.history.UndoManager.prototype.getNextRedoCommandText = function () { - return this.undoStackPointer < this.undoStack.length ? this.undoStack[this.undoStackPointer].getText() : ''; + return this.undoStackPointer < this.undoStack.length ? this.undoStack[this.undoStackPointer].getText() : ''; }; // Function: svgedit.history.UndoManager.undo // Performs an undo step svgedit.history.UndoManager.prototype.undo = function () { - if (this.undoStackPointer > 0) { - var cmd = this.undoStack[--this.undoStackPointer]; - cmd.unapply(this.handler_); - } + if (this.undoStackPointer > 0) { + var cmd = this.undoStack[--this.undoStackPointer]; + cmd.unapply(this.handler_); + } }; // Function: svgedit.history.UndoManager.redo // Performs a redo step svgedit.history.UndoManager.prototype.redo = function () { - if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) { - var cmd = this.undoStack[this.undoStackPointer++]; - cmd.apply(this.handler_); - } + if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) { + var cmd = this.undoStack[this.undoStackPointer++]; + cmd.apply(this.handler_); + } }; // Function: svgedit.history.UndoManager.addCommandToHistory @@ -542,18 +542,18 @@ svgedit.history.UndoManager.prototype.redo = function () { // Parameters: // cmd - The command object to add svgedit.history.UndoManager.prototype.addCommandToHistory = function (cmd) { - // FIXME: we MUST compress consecutive text changes to the same element - // (right now each keystroke is saved as a separate command that includes the - // entire text contents of the text element) - // TODO: consider limiting the history that we store here (need to do some slicing) + // FIXME: we MUST compress consecutive text changes to the same element + // (right now each keystroke is saved as a separate command that includes the + // entire text contents of the text element) + // TODO: consider limiting the history that we store here (need to do some slicing) - // if our stack pointer is not at the end, then we have to remove - // all commands after the pointer and insert the new command - if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) { - this.undoStack = this.undoStack.splice(0, this.undoStackPointer); - } - this.undoStack.push(cmd); - this.undoStackPointer = this.undoStack.length; + // if our stack pointer is not at the end, then we have to remove + // all commands after the pointer and insert the new command + if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) { + this.undoStack = this.undoStack.splice(0, this.undoStackPointer); + } + this.undoStack.push(cmd); + this.undoStackPointer = this.undoStack.length; }; // Function: svgedit.history.UndoManager.beginUndoableChange @@ -567,20 +567,20 @@ svgedit.history.UndoManager.prototype.addCommandToHistory = function (cmd) { // attrName - The name of the attribute being changed // elems - Array of DOM elements being changed svgedit.history.UndoManager.prototype.beginUndoableChange = function (attrName, elems) { - var p = ++this.undoChangeStackPointer; - var i = elems.length; - var oldValues = new Array(i), elements = new Array(i); - while (i--) { - var elem = elems[i]; - if (elem == null) { continue; } - elements[i] = elem; - oldValues[i] = elem.getAttribute(attrName); - } - this.undoableChangeStack[p] = { - 'attrName': attrName, - 'oldValues': oldValues, - 'elements': elements - }; + var p = ++this.undoChangeStackPointer; + var i = elems.length; + var oldValues = new Array(i), elements = new Array(i); + while (i--) { + var elem = elems[i]; + if (elem == null) { continue; } + elements[i] = elem; + oldValues[i] = elem.getAttribute(attrName); + } + this.undoableChangeStack[p] = { + 'attrName': attrName, + 'oldValues': oldValues, + 'elements': elements + }; }; // Function: svgedit.history.UndoManager.finishUndoableChange @@ -591,21 +591,21 @@ svgedit.history.UndoManager.prototype.beginUndoableChange = function (attrName, // Returns: // Batch command object with resulting changes svgedit.history.UndoManager.prototype.finishUndoableChange = function () { - var p = this.undoChangeStackPointer--; - var changeset = this.undoableChangeStack[p]; - var i = changeset.elements.length; - var attrName = changeset.attrName; - var batchCmd = new svgedit.history.BatchCommand('Change ' + attrName); - while (i--) { - var elem = changeset.elements[i]; - if (elem == null) { continue; } - var changes = {}; - changes[attrName] = changeset.oldValues[i]; - if (changes[attrName] !== elem.getAttribute(attrName)) { - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes, attrName)); - } - } - this.undoableChangeStack[p] = null; - return batchCmd; + var p = this.undoChangeStackPointer--; + var changeset = this.undoableChangeStack[p]; + var i = changeset.elements.length; + var attrName = changeset.attrName; + var batchCmd = new svgedit.history.BatchCommand('Change ' + attrName); + while (i--) { + var elem = changeset.elements[i]; + if (elem == null) { continue; } + var changes = {}; + changes[attrName] = changeset.oldValues[i]; + if (changes[attrName] !== elem.getAttribute(attrName)) { + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes, attrName)); + } + } + this.undoableChangeStack[p] = null; + return batchCmd; }; }()); diff --git a/editor/historyrecording.js b/editor/historyrecording.js index 9fdd0280..2f7a39b4 100644 --- a/editor/historyrecording.js +++ b/editor/historyrecording.js @@ -15,7 +15,7 @@ 'use strict'; if (!svgedit.history) { - svgedit.history = {}; + svgedit.history = {}; } var history = svgedit.history; @@ -58,9 +58,9 @@ var history = svgedit.history; * See singleton: HistoryRecordingService.NO_HISTORY */ var HistoryRecordingService = history.HistoryRecordingService = function (undoManager) { - this.undoManager_ = undoManager; - this.currentBatchCommand_ = null; - this.batchCommandStack_ = []; + this.undoManager_ = undoManager; + this.currentBatchCommand_ = null; + this.batchCommandStack_ = []; }; /** @@ -77,10 +77,10 @@ HistoryRecordingService.NO_HISTORY = new HistoryRecordingService(); * @returns {svgedit.history.HistoryRecordingService} */ HistoryRecordingService.prototype.startBatchCommand = function (text) { - if (!this.undoManager_) { return this; } - this.currentBatchCommand_ = new history.BatchCommand(text); - this.batchCommandStack_.push(this.currentBatchCommand_); - return this; + if (!this.undoManager_) { return this; } + this.currentBatchCommand_ = new history.BatchCommand(text); + this.batchCommandStack_.push(this.currentBatchCommand_); + return this; }; /** @@ -88,15 +88,15 @@ HistoryRecordingService.prototype.startBatchCommand = function (text) { * @returns {svgedit.history.HistoryRecordingService} */ HistoryRecordingService.prototype.endBatchCommand = function () { - if (!this.undoManager_) { return this; } - if (this.currentBatchCommand_) { - var batchCommand = this.currentBatchCommand_; - this.batchCommandStack_.pop(); - var length = this.batchCommandStack_.length; - this.currentBatchCommand_ = length ? this.batchCommandStack_[length - 1] : null; - this.addCommand_(batchCommand); - } - return this; + if (!this.undoManager_) { return this; } + if (this.currentBatchCommand_) { + var batchCommand = this.currentBatchCommand_; + this.batchCommandStack_.pop(); + var length = this.batchCommandStack_.length; + this.currentBatchCommand_ = length ? this.batchCommandStack_[length - 1] : null; + this.addCommand_(batchCommand); + } + return this; }; /** @@ -108,9 +108,9 @@ HistoryRecordingService.prototype.endBatchCommand = function () { * @returns {svgedit.history.HistoryRecordingService} */ HistoryRecordingService.prototype.moveElement = function (elem, oldNextSibling, oldParent, text) { - if (!this.undoManager_) { return this; } - this.addCommand_(new history.MoveElementCommand(elem, oldNextSibling, oldParent, text)); - return this; + if (!this.undoManager_) { return this; } + this.addCommand_(new history.MoveElementCommand(elem, oldNextSibling, oldParent, text)); + return this; }; /** @@ -120,9 +120,9 @@ HistoryRecordingService.prototype.moveElement = function (elem, oldNextSibling, * @returns {svgedit.history.HistoryRecordingService} */ HistoryRecordingService.prototype.insertElement = function (elem, text) { - if (!this.undoManager_) { return this; } - this.addCommand_(new history.InsertElementCommand(elem, text)); - return this; + if (!this.undoManager_) { return this; } + this.addCommand_(new history.InsertElementCommand(elem, text)); + return this; }; /** @@ -134,9 +134,9 @@ HistoryRecordingService.prototype.insertElement = function (elem, text) { * @returns {svgedit.history.HistoryRecordingService} */ HistoryRecordingService.prototype.removeElement = function (elem, oldNextSibling, oldParent, text) { - if (!this.undoManager_) { return this; } - this.addCommand_(new history.RemoveElementCommand(elem, oldNextSibling, oldParent, text)); - return this; + if (!this.undoManager_) { return this; } + this.addCommand_(new history.RemoveElementCommand(elem, oldNextSibling, oldParent, text)); + return this; }; /** @@ -147,9 +147,9 @@ HistoryRecordingService.prototype.removeElement = function (elem, oldNextSibling * @returns {svgedit.history.HistoryRecordingService} */ HistoryRecordingService.prototype.changeElement = function (elem, attrs, text) { - if (!this.undoManager_) { return this; } - this.addCommand_(new history.ChangeElementCommand(elem, attrs, text)); - return this; + if (!this.undoManager_) { return this; } + this.addCommand_(new history.ChangeElementCommand(elem, attrs, text)); + return this; }; /** @@ -159,11 +159,11 @@ HistoryRecordingService.prototype.changeElement = function (elem, attrs, text) { * @private */ HistoryRecordingService.prototype.addCommand_ = function (cmd) { - if (!this.undoManager_) { return this; } - if (this.currentBatchCommand_) { - this.currentBatchCommand_.addSubCommand(cmd); - } else { - this.undoManager_.addCommandToHistory(cmd); - } + if (!this.undoManager_) { return this; } + if (this.currentBatchCommand_) { + this.currentBatchCommand_.addSubCommand(cmd); + } else { + this.undoManager_.addCommandToHistory(cmd); + } }; }()); diff --git a/editor/layer.js b/editor/layer.js index b9f56239..197eee9f 100644 --- a/editor/layer.js +++ b/editor/layer.js @@ -17,7 +17,7 @@ 'use strict'; if (!svgedit.draw) { - svgedit.draw = {}; + svgedit.draw = {}; } var NS = svgedit.NS; @@ -39,29 +39,29 @@ var NS = svgedit.NS; * a new layer to the document. */ var Layer = svgedit.draw.Layer = function (name, group, svgElem) { - this.name_ = name; - this.group_ = svgElem ? null : group; + this.name_ = name; + this.group_ = svgElem ? null : group; - if (svgElem) { - // Create a group element with title and add it to the DOM. - var svgdoc = svgElem.ownerDocument; - this.group_ = svgdoc.createElementNS(NS.SVG, 'g'); - var layerTitle = svgdoc.createElementNS(NS.SVG, 'title'); - layerTitle.textContent = name; - this.group_.appendChild(layerTitle); - if (group) { - $(group).after(this.group_); - } else { - svgElem.appendChild(this.group_); - } - } + if (svgElem) { + // Create a group element with title and add it to the DOM. + var svgdoc = svgElem.ownerDocument; + this.group_ = svgdoc.createElementNS(NS.SVG, 'g'); + var layerTitle = svgdoc.createElementNS(NS.SVG, 'title'); + layerTitle.textContent = name; + this.group_.appendChild(layerTitle); + if (group) { + $(group).after(this.group_); + } else { + svgElem.appendChild(this.group_); + } + } - addLayerClass(this.group_); - svgedit.utilities.walkTree(this.group_, function (e) { - e.setAttribute('style', 'pointer-events:inherit'); - }); + addLayerClass(this.group_); + svgedit.utilities.walkTree(this.group_, function (e) { + e.setAttribute('style', 'pointer-events:inherit'); + }); - this.group_.setAttribute('style', svgElem ? 'pointer-events:all' : 'pointer-events:none'); + this.group_.setAttribute('style', svgElem ? 'pointer-events:all' : 'pointer-events:none'); }; /** @@ -79,7 +79,7 @@ Layer.CLASS_REGEX = new RegExp('(\\s|^)' + Layer.CLASS_NAME + '(\\s|$)'); * @returns {string} The layer name */ Layer.prototype.getName = function () { - return this.name_; + return this.name_; }; /** @@ -87,21 +87,21 @@ Layer.prototype.getName = function () { * @returns {SVGGElement} The layer SVG group */ Layer.prototype.getGroup = function () { - return this.group_; + return this.group_; }; /** * Active this layer so it takes pointer events. */ Layer.prototype.activate = function () { - this.group_.setAttribute('style', 'pointer-events:all'); + this.group_.setAttribute('style', 'pointer-events:all'); }; /** * Deactive this layer so it does NOT take pointer events. */ Layer.prototype.deactivate = function () { - this.group_.setAttribute('style', 'pointer-events:none'); + this.group_.setAttribute('style', 'pointer-events:none'); }; /** @@ -109,11 +109,11 @@ Layer.prototype.deactivate = function () { * @param {boolean} visible - If true, make visible; otherwise, hide it. */ Layer.prototype.setVisible = function (visible) { - var expected = visible === undefined || visible ? 'inline' : 'none'; - var oldDisplay = this.group_.getAttribute('display'); - if (oldDisplay !== expected) { - this.group_.setAttribute('display', expected); - } + var expected = visible === undefined || visible ? 'inline' : 'none'; + var oldDisplay = this.group_.getAttribute('display'); + if (oldDisplay !== expected) { + this.group_.setAttribute('display', expected); + } }; /** @@ -121,7 +121,7 @@ Layer.prototype.setVisible = function (visible) { * @returns {boolean} True if visible. */ Layer.prototype.isVisible = function () { - return this.group_.getAttribute('display') !== 'none'; + return this.group_.getAttribute('display') !== 'none'; }; /** @@ -129,11 +129,11 @@ Layer.prototype.isVisible = function () { * @returns {number} Opacity value. */ Layer.prototype.getOpacity = function () { - var opacity = this.group_.getAttribute('opacity'); - if (opacity === null || opacity === undefined) { - return 1; - } - return parseFloat(opacity); + var opacity = this.group_.getAttribute('opacity'); + if (opacity === null || opacity === undefined) { + return 1; + } + return parseFloat(opacity); }; /** @@ -142,9 +142,9 @@ Layer.prototype.getOpacity = function () { * @param {number} opacity - A float value in the range 0.0-1.0 */ Layer.prototype.setOpacity = function (opacity) { - if (typeof opacity === 'number' && opacity >= 0.0 && opacity <= 1.0) { - this.group_.setAttribute('opacity', opacity); - } + if (typeof opacity === 'number' && opacity >= 0.0 && opacity <= 1.0) { + this.group_.setAttribute('opacity', opacity); + } }; /** @@ -152,20 +152,20 @@ Layer.prototype.setOpacity = function (opacity) { * @param {SVGGElement} children - The children to append to this layer. */ Layer.prototype.appendChildren = function (children) { - for (var i = 0; i < children.length; ++i) { - this.group_.appendChild(children[i]); - } + for (var i = 0; i < children.length; ++i) { + this.group_.appendChild(children[i]); + } }; Layer.prototype.getTitleElement = function () { - var len = this.group_.childNodes.length; - for (var i = 0; i < len; ++i) { - var child = this.group_.childNodes.item(i); - if (child && child.tagName === 'title') { - return child; - } - } - return null; + var len = this.group_.childNodes.length; + for (var i = 0; i < len; ++i) { + var child = this.group_.childNodes.item(i); + if (child && child.tagName === 'title') { + return child; + } + } + return null; }; /** @@ -175,20 +175,20 @@ Layer.prototype.getTitleElement = function () { * @returns {string|null} The new name if changed; otherwise, null. */ Layer.prototype.setName = function (name, hrService) { - var previousName = this.name_; - name = svgedit.utilities.toXml(name); - // now change the underlying title element contents - var title = this.getTitleElement(); - if (title) { - $(title).empty(); - title.textContent = name; - this.name_ = name; - if (hrService) { - hrService.changeElement(title, {'#text': previousName}); - } - return this.name_; - } - return null; + var previousName = this.name_; + name = svgedit.utilities.toXml(name); + // now change the underlying title element contents + var title = this.getTitleElement(); + if (title) { + $(title).empty(); + title.textContent = name; + this.name_ = name; + if (hrService) { + hrService.changeElement(title, {'#text': previousName}); + } + return this.name_; + } + return null; }; /** @@ -197,10 +197,10 @@ Layer.prototype.setName = function (name, hrService) { * @returns {SVGGElement} The layer SVG group that was just removed. */ Layer.prototype.removeGroup = function () { - var parent = this.group_.parentNode; - var group = parent.removeChild(this.group_); - this.group_ = undefined; - return group; + var parent = this.group_.parentNode; + var group = parent.removeChild(this.group_); + this.group_ = undefined; + return group; }; /** @@ -210,11 +210,11 @@ Layer.prototype.removeGroup = function () { * @param {SVGGElement} elem - The SVG element to update */ function addLayerClass (elem) { - var classes = elem.getAttribute('class'); - if (classes === null || classes === undefined || classes.length === 0) { - elem.setAttribute('class', Layer.CLASS_NAME); - } else if (!Layer.CLASS_REGEX.test(classes)) { - elem.setAttribute('class', classes + ' ' + Layer.CLASS_NAME); - } + var classes = elem.getAttribute('class'); + if (classes === null || classes === undefined || classes.length === 0) { + elem.setAttribute('class', Layer.CLASS_NAME); + } else if (!Layer.CLASS_REGEX.test(classes)) { + elem.setAttribute('class', classes + ' ' + Layer.CLASS_NAME); + } } }()); diff --git a/editor/math.js b/editor/math.js index 466f76c0..1f95ec1f 100644 --- a/editor/math.js +++ b/editor/math.js @@ -24,7 +24,7 @@ 'use strict'; if (!svgedit.math) { - svgedit.math = {}; + svgedit.math = {}; } // Constants @@ -42,7 +42,7 @@ var svg = document.createElementNS(svgedit.NS.SVG, 'svg'); * @returns {Object} An x, y object representing the transformed point */ svgedit.math.transformPoint = function (x, y, m) { - return {x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f}; + return {x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f}; }; /** @@ -52,7 +52,7 @@ svgedit.math.transformPoint = function (x, y, m) { * @returns {boolean} Indicates whether or not the matrix is 1,0,0,1,0,0 */ svgedit.math.isIdentity = function (m) { - return (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0); + return (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0); }; /** @@ -62,20 +62,20 @@ svgedit.math.isIdentity = function (m) { * @returns {SVGMatrix} The matrix object resulting from the calculation */ svgedit.math.matrixMultiply = function (matr) { - var args = arguments, i = args.length, m = args[i - 1]; + var args = arguments, i = args.length, m = args[i - 1]; - while (i-- > 1) { - var m1 = args[i - 1]; - m = m1.multiply(m); - } - if (Math.abs(m.a) < NEAR_ZERO) { m.a = 0; } - if (Math.abs(m.b) < NEAR_ZERO) { m.b = 0; } - if (Math.abs(m.c) < NEAR_ZERO) { m.c = 0; } - if (Math.abs(m.d) < NEAR_ZERO) { m.d = 0; } - if (Math.abs(m.e) < NEAR_ZERO) { m.e = 0; } - if (Math.abs(m.f) < NEAR_ZERO) { m.f = 0; } + while (i-- > 1) { + var m1 = args[i - 1]; + m = m1.multiply(m); + } + if (Math.abs(m.a) < NEAR_ZERO) { m.a = 0; } + if (Math.abs(m.b) < NEAR_ZERO) { m.b = 0; } + if (Math.abs(m.c) < NEAR_ZERO) { m.c = 0; } + if (Math.abs(m.d) < NEAR_ZERO) { m.d = 0; } + if (Math.abs(m.e) < NEAR_ZERO) { m.e = 0; } + if (Math.abs(m.f) < NEAR_ZERO) { m.f = 0; } - return m; + return m; }; /** @@ -84,13 +84,13 @@ svgedit.math.matrixMultiply = function (matr) { * @returns {boolean} Whether or not a matrix transform was found */ svgedit.math.hasMatrixTransform = function (tlist) { - if (!tlist) { return false; } - var num = tlist.numberOfItems; - while (num--) { - var xform = tlist.getItem(num); - if (xform.type === 1 && !svgedit.math.isIdentity(xform.matrix)) { return true; } - } - return false; + if (!tlist) { return false; } + var num = tlist.numberOfItems; + while (num--) { + var xform = tlist.getItem(num); + if (xform.type === 1 && !svgedit.math.isIdentity(xform.matrix)) { return true; } + } + return false; }; /** @@ -112,30 +112,30 @@ svgedit.math.hasMatrixTransform = function (tlist) { * height - Float with the axis-aligned height coordinate */ svgedit.math.transformBox = function (l, t, w, h, m) { - var transformPoint = svgedit.math.transformPoint, + var transformPoint = svgedit.math.transformPoint, - tl = transformPoint(l, t, m), - tr = transformPoint((l + w), t, m), - bl = transformPoint(l, (t + h), m), - br = transformPoint((l + w), (t + h), m), + tl = transformPoint(l, t, m), + tr = transformPoint((l + w), t, m), + bl = transformPoint(l, (t + h), m), + br = transformPoint((l + w), (t + h), m), - minx = Math.min(tl.x, tr.x, bl.x, br.x), - maxx = Math.max(tl.x, tr.x, bl.x, br.x), - miny = Math.min(tl.y, tr.y, bl.y, br.y), - maxy = Math.max(tl.y, tr.y, bl.y, br.y); + minx = Math.min(tl.x, tr.x, bl.x, br.x), + maxx = Math.max(tl.x, tr.x, bl.x, br.x), + miny = Math.min(tl.y, tr.y, bl.y, br.y), + maxy = Math.max(tl.y, tr.y, bl.y, br.y); - return { - tl: tl, - tr: tr, - bl: bl, - br: br, - aabox: { - x: minx, - y: miny, - width: (maxx - minx), - height: (maxy - miny) - } - }; + return { + tl: tl, + tr: tr, + bl: bl, + br: br, + aabox: { + x: minx, + y: miny, + width: (maxx - minx), + height: (maxy - miny) + } + }; }; /** @@ -150,25 +150,25 @@ svgedit.math.transformBox = function (l, t, w, h, m) { * @returns {Object} A single matrix transform object */ svgedit.math.transformListToTransform = function (tlist, min, max) { - if (tlist == null) { - // Or should tlist = null have been prevented before this? - return svg.createSVGTransformFromMatrix(svg.createSVGMatrix()); - } - min = min || 0; - max = max || (tlist.numberOfItems - 1); - min = parseInt(min, 10); - max = parseInt(max, 10); - if (min > max) { var temp = max; max = min; min = temp; } - var m = svg.createSVGMatrix(); - var i; - for (i = min; i <= max; ++i) { - // if our indices are out of range, just use a harmless identity matrix - var mtom = (i >= 0 && i < tlist.numberOfItems - ? tlist.getItem(i).matrix - : svg.createSVGMatrix()); - m = svgedit.math.matrixMultiply(m, mtom); - } - return svg.createSVGTransformFromMatrix(m); + if (tlist == null) { + // Or should tlist = null have been prevented before this? + return svg.createSVGTransformFromMatrix(svg.createSVGMatrix()); + } + min = min || 0; + max = max || (tlist.numberOfItems - 1); + min = parseInt(min, 10); + max = parseInt(max, 10); + if (min > max) { var temp = max; max = min; min = temp; } + var m = svg.createSVGMatrix(); + var i; + for (i = min; i <= max; ++i) { + // if our indices are out of range, just use a harmless identity matrix + var mtom = (i >= 0 && i < tlist.numberOfItems + ? tlist.getItem(i).matrix + : svg.createSVGMatrix()); + m = svgedit.math.matrixMultiply(m, mtom); + } + return svg.createSVGTransformFromMatrix(m); }; /** @@ -177,8 +177,8 @@ svgedit.math.transformListToTransform = function (tlist, min, max) { * @returns {SVGMatrix} The matrix object associated with the element's transformlist */ svgedit.math.getMatrix = function (elem) { - var tlist = svgedit.transformlist.getTransformList(elem); - return svgedit.math.transformListToTransform(tlist).matrix; + var tlist = svgedit.transformlist.getTransformList(elem); + return svgedit.math.transformListToTransform(tlist).matrix; }; /** @@ -191,18 +191,18 @@ svgedit.math.getMatrix = function (elem) { * @returns {AngleCoord45} */ svgedit.math.snapToAngle = function (x1, y1, x2, y2) { - var snap = Math.PI / 4; // 45 degrees - var dx = x2 - x1; - var dy = y2 - y1; - var angle = Math.atan2(dy, dx); - var dist = Math.sqrt(dx * dx + dy * dy); - var snapangle = Math.round(angle / snap) * snap; + var snap = Math.PI / 4; // 45 degrees + var dx = x2 - x1; + var dy = y2 - y1; + var angle = Math.atan2(dy, dx); + var dist = Math.sqrt(dx * dx + dy * dy); + var snapangle = Math.round(angle / snap) * snap; - return { - x: x1 + dist * Math.cos(snapangle), - y: y1 + dist * Math.sin(snapangle), - a: snapangle - }; + return { + x: x1 + dist * Math.cos(snapangle), + y: y1 + dist * Math.sin(snapangle), + a: snapangle + }; }; /** @@ -212,9 +212,9 @@ svgedit.math.snapToAngle = function (x1, y1, x2, y2) { * @returns {boolean} True if rectangles intersect */ svgedit.math.rectsIntersect = function (r1, r2) { - return r2.x < (r1.x + r1.width) && - (r2.x + r2.width) > r1.x && - r2.y < (r1.y + r1.height) && - (r2.y + r2.height) > r1.y; + return r2.x < (r1.x + r1.width) && + (r2.x + r2.width) > r1.x && + r2.y < (r1.y + r1.height) && + (r2.y + r2.height) > r1.y; }; }()); diff --git a/editor/path.js b/editor/path.js index 46dca2e9..b8f4207f 100644 --- a/editor/path.js +++ b/editor/path.js @@ -19,25 +19,25 @@ 'use strict'; if (!svgedit.path) { - svgedit.path = {}; + svgedit.path = {}; } var NS = svgedit.NS; var uiStrings = { - 'pathNodeTooltip': 'Drag node to move it. Double-click node to change segment type', - 'pathCtrlPtTooltip': 'Drag control point to adjust curve properties' + 'pathNodeTooltip': 'Drag node to move it. Double-click node to change segment type', + 'pathCtrlPtTooltip': 'Drag control point to adjust curve properties' }; var segData = { - 2: ['x', 'y'], - 4: ['x', 'y'], - 6: ['x', 'y', 'x1', 'y1', 'x2', 'y2'], - 8: ['x', 'y', 'x1', 'y1'], - 10: ['x', 'y', 'r1', 'r2', 'angle', 'largeArcFlag', 'sweepFlag'], - 12: ['x'], - 14: ['y'], - 16: ['x', 'y', 'x2', 'y2'], - 18: ['x', 'y'] + 2: ['x', 'y'], + 4: ['x', 'y'], + 6: ['x', 'y', 'x1', 'y1', 'x2', 'y2'], + 8: ['x', 'y', 'x1', 'y1'], + 10: ['x', 'y', 'r1', 'r2', 'angle', 'largeArcFlag', 'sweepFlag'], + 12: ['x'], + 14: ['y'], + 16: ['x', 'y', 'x2', 'y2'], + 18: ['x', 'y'] }; var pathFuncs = []; @@ -49,7 +49,7 @@ var linkControlPts = true; var pathData = {}; svgedit.path.setLinkControlPoints = function (lcp) { - linkControlPts = lcp; + linkControlPts = lcp; }; svgedit.path.path = null; @@ -57,300 +57,300 @@ svgedit.path.path = null; var editorContext_ = null; svgedit.path.init = function (editorContext) { - editorContext_ = editorContext; + editorContext_ = editorContext; - pathFuncs = [0, 'ClosePath']; - var pathFuncsStrs = ['Moveto', 'Lineto', 'CurvetoCubic', 'CurvetoQuadratic', 'Arc', - 'LinetoHorizontal', 'LinetoVertical', 'CurvetoCubicSmooth', 'CurvetoQuadraticSmooth']; - $.each(pathFuncsStrs, function (i, s) { - pathFuncs.push(s + 'Abs'); - pathFuncs.push(s + 'Rel'); - }); + pathFuncs = [0, 'ClosePath']; + var pathFuncsStrs = ['Moveto', 'Lineto', 'CurvetoCubic', 'CurvetoQuadratic', 'Arc', + 'LinetoHorizontal', 'LinetoVertical', 'CurvetoCubicSmooth', 'CurvetoQuadraticSmooth']; + $.each(pathFuncsStrs, function (i, s) { + pathFuncs.push(s + 'Abs'); + pathFuncs.push(s + 'Rel'); + }); }; svgedit.path.insertItemBefore = function (elem, newseg, index) { - // Support insertItemBefore on paths for FF2 - var list = elem.pathSegList; + // Support insertItemBefore on paths for FF2 + var list = elem.pathSegList; - if (svgedit.browser.supportsPathInsertItemBefore()) { - list.insertItemBefore(newseg, index); - return; - } - var len = list.numberOfItems; - var arr = []; - var i; - for (i = 0; i < len; i++) { - var curSeg = list.getItem(i); - arr.push(curSeg); - } - list.clear(); - for (i = 0; i < len; i++) { - if (i === index) { // index + 1 - list.appendItem(newseg); - } - list.appendItem(arr[i]); - } + if (svgedit.browser.supportsPathInsertItemBefore()) { + list.insertItemBefore(newseg, index); + return; + } + var len = list.numberOfItems; + var arr = []; + var i; + for (i = 0; i < len; i++) { + var curSeg = list.getItem(i); + arr.push(curSeg); + } + list.clear(); + for (i = 0; i < len; i++) { + if (i === index) { // index + 1 + list.appendItem(newseg); + } + list.appendItem(arr[i]); + } }; // TODO: See if this should just live in replacePathSeg svgedit.path.ptObjToArr = function (type, segItem) { - var arr = segData[type], len = arr.length; - var i, out = []; - for (i = 0; i < len; i++) { - out[i] = segItem[arr[i]]; - } - return out; + var arr = segData[type], len = arr.length; + var i, out = []; + for (i = 0; i < len; i++) { + out[i] = segItem[arr[i]]; + } + return out; }; svgedit.path.getGripPt = function (seg, altPt) { - var out = { - x: altPt ? altPt.x : seg.item.x, - y: altPt ? altPt.y : seg.item.y - }, path = seg.path; + var out = { + x: altPt ? altPt.x : seg.item.x, + y: altPt ? altPt.y : seg.item.y + }, path = seg.path; - if (path.matrix) { - var pt = svgedit.math.transformPoint(out.x, out.y, path.matrix); - out = pt; - } + if (path.matrix) { + var pt = svgedit.math.transformPoint(out.x, out.y, path.matrix); + out = pt; + } - out.x *= editorContext_.getCurrentZoom(); - out.y *= editorContext_.getCurrentZoom(); + out.x *= editorContext_.getCurrentZoom(); + out.y *= editorContext_.getCurrentZoom(); - return out; + return out; }; svgedit.path.getPointFromGrip = function (pt, path) { - var out = { - x: pt.x, - y: pt.y - }; + var out = { + x: pt.x, + y: pt.y + }; - if (path.matrix) { - pt = svgedit.math.transformPoint(out.x, out.y, path.imatrix); - out.x = pt.x; - out.y = pt.y; - } + if (path.matrix) { + pt = svgedit.math.transformPoint(out.x, out.y, path.imatrix); + out.x = pt.x; + out.y = pt.y; + } - out.x /= editorContext_.getCurrentZoom(); - out.y /= editorContext_.getCurrentZoom(); + out.x /= editorContext_.getCurrentZoom(); + out.y /= editorContext_.getCurrentZoom(); - return out; + return out; }; svgedit.path.addPointGrip = function (index, x, y) { - // create the container of all the point grips - var pointGripContainer = svgedit.path.getGripContainer(); + // create the container of all the point grips + var pointGripContainer = svgedit.path.getGripContainer(); - var pointGrip = svgedit.utilities.getElem('pathpointgrip_' + index); - // create it - if (!pointGrip) { - pointGrip = document.createElementNS(NS.SVG, 'circle'); - svgedit.utilities.assignAttributes(pointGrip, { - 'id': 'pathpointgrip_' + index, - 'display': 'none', - 'r': 4, - 'fill': '#0FF', - 'stroke': '#00F', - 'stroke-width': 2, - 'cursor': 'move', - 'style': 'pointer-events:all', - 'xlink:title': uiStrings.pathNodeTooltip - }); - pointGrip = pointGripContainer.appendChild(pointGrip); + var pointGrip = svgedit.utilities.getElem('pathpointgrip_' + index); + // create it + if (!pointGrip) { + pointGrip = document.createElementNS(NS.SVG, 'circle'); + svgedit.utilities.assignAttributes(pointGrip, { + 'id': 'pathpointgrip_' + index, + 'display': 'none', + 'r': 4, + 'fill': '#0FF', + 'stroke': '#00F', + 'stroke-width': 2, + 'cursor': 'move', + 'style': 'pointer-events:all', + 'xlink:title': uiStrings.pathNodeTooltip + }); + pointGrip = pointGripContainer.appendChild(pointGrip); - var grip = $('#pathpointgrip_' + index); - grip.dblclick(function () { - if (svgedit.path.path) { - svgedit.path.path.setSegType(); - } - }); - } - if (x && y) { - // set up the point grip element and display it - svgedit.utilities.assignAttributes(pointGrip, { - 'cx': x, - 'cy': y, - 'display': 'inline' - }); - } - return pointGrip; + var grip = $('#pathpointgrip_' + index); + grip.dblclick(function () { + if (svgedit.path.path) { + svgedit.path.path.setSegType(); + } + }); + } + if (x && y) { + // set up the point grip element and display it + svgedit.utilities.assignAttributes(pointGrip, { + 'cx': x, + 'cy': y, + 'display': 'inline' + }); + } + return pointGrip; }; svgedit.path.getGripContainer = function () { - var c = svgedit.utilities.getElem('pathpointgrip_container'); - if (!c) { - var parent = svgedit.utilities.getElem('selectorParentGroup'); - c = parent.appendChild(document.createElementNS(NS.SVG, 'g')); - c.id = 'pathpointgrip_container'; - } - return c; + var c = svgedit.utilities.getElem('pathpointgrip_container'); + if (!c) { + var parent = svgedit.utilities.getElem('selectorParentGroup'); + c = parent.appendChild(document.createElementNS(NS.SVG, 'g')); + c.id = 'pathpointgrip_container'; + } + return c; }; svgedit.path.addCtrlGrip = function (id) { - var pointGrip = svgedit.utilities.getElem('ctrlpointgrip_' + id); - if (pointGrip) { return pointGrip; } + var pointGrip = svgedit.utilities.getElem('ctrlpointgrip_' + id); + if (pointGrip) { return pointGrip; } - pointGrip = document.createElementNS(NS.SVG, 'circle'); - svgedit.utilities.assignAttributes(pointGrip, { - 'id': 'ctrlpointgrip_' + id, - 'display': 'none', - 'r': 4, - 'fill': '#0FF', - 'stroke': '#55F', - 'stroke-width': 1, - 'cursor': 'move', - 'style': 'pointer-events:all', - 'xlink:title': uiStrings.pathCtrlPtTooltip - }); - svgedit.path.getGripContainer().appendChild(pointGrip); - return pointGrip; + pointGrip = document.createElementNS(NS.SVG, 'circle'); + svgedit.utilities.assignAttributes(pointGrip, { + 'id': 'ctrlpointgrip_' + id, + 'display': 'none', + 'r': 4, + 'fill': '#0FF', + 'stroke': '#55F', + 'stroke-width': 1, + 'cursor': 'move', + 'style': 'pointer-events:all', + 'xlink:title': uiStrings.pathCtrlPtTooltip + }); + svgedit.path.getGripContainer().appendChild(pointGrip); + return pointGrip; }; svgedit.path.getCtrlLine = function (id) { - var ctrlLine = svgedit.utilities.getElem('ctrlLine_' + id); - if (ctrlLine) { return ctrlLine; } + var ctrlLine = svgedit.utilities.getElem('ctrlLine_' + id); + if (ctrlLine) { return ctrlLine; } - ctrlLine = document.createElementNS(NS.SVG, 'line'); - svgedit.utilities.assignAttributes(ctrlLine, { - 'id': 'ctrlLine_' + id, - 'stroke': '#555', - 'stroke-width': 1, - 'style': 'pointer-events:none' - }); - svgedit.path.getGripContainer().appendChild(ctrlLine); - return ctrlLine; + ctrlLine = document.createElementNS(NS.SVG, 'line'); + svgedit.utilities.assignAttributes(ctrlLine, { + 'id': 'ctrlLine_' + id, + 'stroke': '#555', + 'stroke-width': 1, + 'style': 'pointer-events:none' + }); + svgedit.path.getGripContainer().appendChild(ctrlLine); + return ctrlLine; }; svgedit.path.getPointGrip = function (seg, update) { - var index = seg.index; - var pointGrip = svgedit.path.addPointGrip(index); + var index = seg.index; + var pointGrip = svgedit.path.addPointGrip(index); - if (update) { - var pt = svgedit.path.getGripPt(seg); - svgedit.utilities.assignAttributes(pointGrip, { - 'cx': pt.x, - 'cy': pt.y, - 'display': 'inline' - }); - } + if (update) { + var pt = svgedit.path.getGripPt(seg); + svgedit.utilities.assignAttributes(pointGrip, { + 'cx': pt.x, + 'cy': pt.y, + 'display': 'inline' + }); + } - return pointGrip; + return pointGrip; }; svgedit.path.getControlPoints = function (seg) { - var item = seg.item; - var index = seg.index; - if (!('x1' in item) || !('x2' in item)) { return null; } - var cpt = {}; - /* var pointGripContainer = */ svgedit.path.getGripContainer(); + var item = seg.item; + var index = seg.index; + if (!('x1' in item) || !('x2' in item)) { return null; } + var cpt = {}; + /* var pointGripContainer = */ svgedit.path.getGripContainer(); - // Note that this is intentionally not seg.prev.item - var prev = svgedit.path.path.segs[index - 1].item; + // Note that this is intentionally not seg.prev.item + var prev = svgedit.path.path.segs[index - 1].item; - var segItems = [prev, item]; + var segItems = [prev, item]; - var i; - for (i = 1; i < 3; i++) { - var id = index + 'c' + i; + var i; + for (i = 1; i < 3; i++) { + var id = index + 'c' + i; - var ctrlLine = cpt['c' + i + '_line'] = svgedit.path.getCtrlLine(id); + var ctrlLine = cpt['c' + i + '_line'] = svgedit.path.getCtrlLine(id); - var pt = svgedit.path.getGripPt(seg, {x: item['x' + i], y: item['y' + i]}); - var gpt = svgedit.path.getGripPt(seg, {x: segItems[i - 1].x, y: segItems[i - 1].y}); + var pt = svgedit.path.getGripPt(seg, {x: item['x' + i], y: item['y' + i]}); + var gpt = svgedit.path.getGripPt(seg, {x: segItems[i - 1].x, y: segItems[i - 1].y}); - svgedit.utilities.assignAttributes(ctrlLine, { - 'x1': pt.x, - 'y1': pt.y, - 'x2': gpt.x, - 'y2': gpt.y, - 'display': 'inline' - }); + svgedit.utilities.assignAttributes(ctrlLine, { + 'x1': pt.x, + 'y1': pt.y, + 'x2': gpt.x, + 'y2': gpt.y, + 'display': 'inline' + }); - cpt['c' + i + '_line'] = ctrlLine; + cpt['c' + i + '_line'] = ctrlLine; - // create it - var pointGrip = cpt['c' + i] = svgedit.path.addCtrlGrip(id); + // create it + var pointGrip = cpt['c' + i] = svgedit.path.addCtrlGrip(id); - svgedit.utilities.assignAttributes(pointGrip, { - 'cx': pt.x, - 'cy': pt.y, - 'display': 'inline' - }); - cpt['c' + i] = pointGrip; - } - return cpt; + svgedit.utilities.assignAttributes(pointGrip, { + 'cx': pt.x, + 'cy': pt.y, + 'display': 'inline' + }); + cpt['c' + i] = pointGrip; + } + return cpt; }; // This replaces the segment at the given index. Type is given as number. svgedit.path.replacePathSeg = function (type, index, pts, elem) { - var path = elem || svgedit.path.path.elem; + var path = elem || svgedit.path.path.elem; - var func = 'createSVGPathSeg' + pathFuncs[type]; - var seg = path[func].apply(path, pts); + var func = 'createSVGPathSeg' + pathFuncs[type]; + var seg = path[func].apply(path, pts); - if (svgedit.browser.supportsPathReplaceItem()) { - path.pathSegList.replaceItem(seg, index); - } else { - var segList = path.pathSegList; - var len = segList.numberOfItems; - var arr = []; - var i; - for (i = 0; i < len; i++) { - var curSeg = segList.getItem(i); - arr.push(curSeg); - } - segList.clear(); - for (i = 0; i < len; i++) { - if (i === index) { - segList.appendItem(seg); - } else { - segList.appendItem(arr[i]); - } - } - } + if (svgedit.browser.supportsPathReplaceItem()) { + path.pathSegList.replaceItem(seg, index); + } else { + var segList = path.pathSegList; + var len = segList.numberOfItems; + var arr = []; + var i; + for (i = 0; i < len; i++) { + var curSeg = segList.getItem(i); + arr.push(curSeg); + } + segList.clear(); + for (i = 0; i < len; i++) { + if (i === index) { + segList.appendItem(seg); + } else { + segList.appendItem(arr[i]); + } + } + } }; svgedit.path.getSegSelector = function (seg, update) { - var index = seg.index; - var segLine = svgedit.utilities.getElem('segline_' + index); - if (!segLine) { - var pointGripContainer = svgedit.path.getGripContainer(); - // create segline - segLine = document.createElementNS(NS.SVG, 'path'); - svgedit.utilities.assignAttributes(segLine, { - 'id': 'segline_' + index, - 'display': 'none', - 'fill': 'none', - 'stroke': '#0FF', - 'stroke-width': 2, - 'style': 'pointer-events:none', - 'd': 'M0,0 0,0' - }); - pointGripContainer.appendChild(segLine); - } + var index = seg.index; + var segLine = svgedit.utilities.getElem('segline_' + index); + if (!segLine) { + var pointGripContainer = svgedit.path.getGripContainer(); + // create segline + segLine = document.createElementNS(NS.SVG, 'path'); + svgedit.utilities.assignAttributes(segLine, { + 'id': 'segline_' + index, + 'display': 'none', + 'fill': 'none', + 'stroke': '#0FF', + 'stroke-width': 2, + 'style': 'pointer-events:none', + 'd': 'M0,0 0,0' + }); + pointGripContainer.appendChild(segLine); + } - if (update) { - var prev = seg.prev; - if (!prev) { - segLine.setAttribute('display', 'none'); - return segLine; - } + if (update) { + var prev = seg.prev; + if (!prev) { + segLine.setAttribute('display', 'none'); + return segLine; + } - var pt = svgedit.path.getGripPt(prev); - // Set start point - svgedit.path.replacePathSeg(2, 0, [pt.x, pt.y], segLine); + var pt = svgedit.path.getGripPt(prev); + // Set start point + svgedit.path.replacePathSeg(2, 0, [pt.x, pt.y], segLine); - var pts = svgedit.path.ptObjToArr(seg.type, seg.item, true); - var i; - for (i = 0; i < pts.length; i += 2) { - pt = svgedit.path.getGripPt(seg, {x: pts[i], y: pts[i + 1]}); - pts[i] = pt.x; - pts[i + 1] = pt.y; - } + var pts = svgedit.path.ptObjToArr(seg.type, seg.item, true); + var i; + for (i = 0; i < pts.length; i += 2) { + pt = svgedit.path.getGripPt(seg, {x: pts[i], y: pts[i + 1]}); + pts[i] = pt.x; + pts[i + 1] = pt.y; + } - svgedit.path.replacePathSeg(seg.type, 1, pts, segLine); - } - return segLine; + svgedit.path.replacePathSeg(seg.type, 1, pts, segLine); + } + return segLine; }; // Function: smoothControlPoints @@ -364,571 +364,571 @@ svgedit.path.getSegSelector = function (seg, update) { // Returns: // Array of two "smoothed" point objects svgedit.path.smoothControlPoints = function (ct1, ct2, pt) { - // each point must not be the origin - var x1 = ct1.x - pt.x, - y1 = ct1.y - pt.y, - x2 = ct2.x - pt.x, - y2 = ct2.y - pt.y; + // each point must not be the origin + var x1 = ct1.x - pt.x, + y1 = ct1.y - pt.y, + x2 = ct2.x - pt.x, + y2 = ct2.y - pt.y; - if ((x1 !== 0 || y1 !== 0) && (x2 !== 0 || y2 !== 0)) { - var anglea = Math.atan2(y1, x1), - angleb = Math.atan2(y2, x2), - r1 = Math.sqrt(x1 * x1 + y1 * y1), - r2 = Math.sqrt(x2 * x2 + y2 * y2), - nct1 = editorContext_.getSVGRoot().createSVGPoint(), - nct2 = editorContext_.getSVGRoot().createSVGPoint(); - if (anglea < 0) { anglea += 2 * Math.PI; } - if (angleb < 0) { angleb += 2 * Math.PI; } + if ((x1 !== 0 || y1 !== 0) && (x2 !== 0 || y2 !== 0)) { + var anglea = Math.atan2(y1, x1), + angleb = Math.atan2(y2, x2), + r1 = Math.sqrt(x1 * x1 + y1 * y1), + r2 = Math.sqrt(x2 * x2 + y2 * y2), + nct1 = editorContext_.getSVGRoot().createSVGPoint(), + nct2 = editorContext_.getSVGRoot().createSVGPoint(); + if (anglea < 0) { anglea += 2 * Math.PI; } + if (angleb < 0) { angleb += 2 * Math.PI; } - var angleBetween = Math.abs(anglea - angleb), - angleDiff = Math.abs(Math.PI - angleBetween) / 2; + var angleBetween = Math.abs(anglea - angleb), + angleDiff = Math.abs(Math.PI - angleBetween) / 2; - var newAnglea, newAngleb; - if (anglea - angleb > 0) { - newAnglea = angleBetween < Math.PI ? (anglea + angleDiff) : (anglea - angleDiff); - newAngleb = angleBetween < Math.PI ? (angleb - angleDiff) : (angleb + angleDiff); - } else { - newAnglea = angleBetween < Math.PI ? (anglea - angleDiff) : (anglea + angleDiff); - newAngleb = angleBetween < Math.PI ? (angleb + angleDiff) : (angleb - angleDiff); - } + var newAnglea, newAngleb; + if (anglea - angleb > 0) { + newAnglea = angleBetween < Math.PI ? (anglea + angleDiff) : (anglea - angleDiff); + newAngleb = angleBetween < Math.PI ? (angleb - angleDiff) : (angleb + angleDiff); + } else { + newAnglea = angleBetween < Math.PI ? (anglea - angleDiff) : (anglea + angleDiff); + newAngleb = angleBetween < Math.PI ? (angleb + angleDiff) : (angleb - angleDiff); + } - // rotate the points - nct1.x = r1 * Math.cos(newAnglea) + pt.x; - nct1.y = r1 * Math.sin(newAnglea) + pt.y; - nct2.x = r2 * Math.cos(newAngleb) + pt.x; - nct2.y = r2 * Math.sin(newAngleb) + pt.y; + // rotate the points + nct1.x = r1 * Math.cos(newAnglea) + pt.x; + nct1.y = r1 * Math.sin(newAnglea) + pt.y; + nct2.x = r2 * Math.cos(newAngleb) + pt.x; + nct2.y = r2 * Math.sin(newAngleb) + pt.y; - return [nct1, nct2]; - } - return undefined; + return [nct1, nct2]; + } + return undefined; }; svgedit.path.Segment = function (index, item) { - this.selected = false; - this.index = index; - this.item = item; - this.type = item.pathSegType; + this.selected = false; + this.index = index; + this.item = item; + this.type = item.pathSegType; - this.ctrlpts = []; - this.ptgrip = null; - this.segsel = null; + this.ctrlpts = []; + this.ptgrip = null; + this.segsel = null; }; svgedit.path.Segment.prototype.showCtrlPts = function (y) { - var i; - for (i in this.ctrlpts) { - if (this.ctrlpts.hasOwnProperty(i)) { - this.ctrlpts[i].setAttribute('display', y ? 'inline' : 'none'); - } - } + var i; + for (i in this.ctrlpts) { + if (this.ctrlpts.hasOwnProperty(i)) { + this.ctrlpts[i].setAttribute('display', y ? 'inline' : 'none'); + } + } }; svgedit.path.Segment.prototype.selectCtrls = function (y) { - $('#ctrlpointgrip_' + this.index + 'c1, #ctrlpointgrip_' + this.index + 'c2') - .attr('fill', y ? '#0FF' : '#EEE'); + $('#ctrlpointgrip_' + this.index + 'c1, #ctrlpointgrip_' + this.index + 'c2') + .attr('fill', y ? '#0FF' : '#EEE'); }; svgedit.path.Segment.prototype.show = function (y) { - if (this.ptgrip) { - this.ptgrip.setAttribute('display', y ? 'inline' : 'none'); - this.segsel.setAttribute('display', y ? 'inline' : 'none'); - // Show/hide all control points if available - this.showCtrlPts(y); - } + if (this.ptgrip) { + this.ptgrip.setAttribute('display', y ? 'inline' : 'none'); + this.segsel.setAttribute('display', y ? 'inline' : 'none'); + // Show/hide all control points if available + this.showCtrlPts(y); + } }; svgedit.path.Segment.prototype.select = function (y) { - if (this.ptgrip) { - this.ptgrip.setAttribute('stroke', y ? '#0FF' : '#00F'); - this.segsel.setAttribute('display', y ? 'inline' : 'none'); - if (this.ctrlpts) { - this.selectCtrls(y); - } - this.selected = y; - } + if (this.ptgrip) { + this.ptgrip.setAttribute('stroke', y ? '#0FF' : '#00F'); + this.segsel.setAttribute('display', y ? 'inline' : 'none'); + if (this.ctrlpts) { + this.selectCtrls(y); + } + this.selected = y; + } }; svgedit.path.Segment.prototype.addGrip = function () { - this.ptgrip = svgedit.path.getPointGrip(this, true); - this.ctrlpts = svgedit.path.getControlPoints(this, true); - this.segsel = svgedit.path.getSegSelector(this, true); + this.ptgrip = svgedit.path.getPointGrip(this, true); + this.ctrlpts = svgedit.path.getControlPoints(this, true); + this.segsel = svgedit.path.getSegSelector(this, true); }; svgedit.path.Segment.prototype.update = function (full) { - if (this.ptgrip) { - var pt = svgedit.path.getGripPt(this); - svgedit.utilities.assignAttributes(this.ptgrip, { - 'cx': pt.x, - 'cy': pt.y - }); + if (this.ptgrip) { + var pt = svgedit.path.getGripPt(this); + svgedit.utilities.assignAttributes(this.ptgrip, { + 'cx': pt.x, + 'cy': pt.y + }); - svgedit.path.getSegSelector(this, true); + svgedit.path.getSegSelector(this, true); - if (this.ctrlpts) { - if (full) { - this.item = svgedit.path.path.elem.pathSegList.getItem(this.index); - this.type = this.item.pathSegType; - } - svgedit.path.getControlPoints(this); - } - // this.segsel.setAttribute('display', y?'inline':'none'); - } + if (this.ctrlpts) { + if (full) { + this.item = svgedit.path.path.elem.pathSegList.getItem(this.index); + this.type = this.item.pathSegType; + } + svgedit.path.getControlPoints(this); + } + // this.segsel.setAttribute('display', y?'inline':'none'); + } }; svgedit.path.Segment.prototype.move = function (dx, dy) { - var curPts, item = this.item; + var curPts, item = this.item; - if (this.ctrlpts) { - curPts = [item.x += dx, item.y += dy, - item.x1, item.y1, item.x2 += dx, item.y2 += dy]; - } else { - curPts = [item.x += dx, item.y += dy]; - } + if (this.ctrlpts) { + curPts = [item.x += dx, item.y += dy, + item.x1, item.y1, item.x2 += dx, item.y2 += dy]; + } else { + curPts = [item.x += dx, item.y += dy]; + } - svgedit.path.replacePathSeg(this.type, this.index, curPts); + svgedit.path.replacePathSeg(this.type, this.index, curPts); - if (this.next && this.next.ctrlpts) { - var next = this.next.item; - var nextPts = [next.x, next.y, - next.x1 += dx, next.y1 += dy, next.x2, next.y2]; - svgedit.path.replacePathSeg(this.next.type, this.next.index, nextPts); - } + if (this.next && this.next.ctrlpts) { + var next = this.next.item; + var nextPts = [next.x, next.y, + next.x1 += dx, next.y1 += dy, next.x2, next.y2]; + svgedit.path.replacePathSeg(this.next.type, this.next.index, nextPts); + } - if (this.mate) { - // The last point of a closed subpath has a 'mate', - // which is the 'M' segment of the subpath - item = this.mate.item; - var pts = [item.x += dx, item.y += dy]; - svgedit.path.replacePathSeg(this.mate.type, this.mate.index, pts); - // Has no grip, so does not need 'updating'? - } + if (this.mate) { + // The last point of a closed subpath has a 'mate', + // which is the 'M' segment of the subpath + item = this.mate.item; + var pts = [item.x += dx, item.y += dy]; + svgedit.path.replacePathSeg(this.mate.type, this.mate.index, pts); + // Has no grip, so does not need 'updating'? + } - this.update(true); - if (this.next) { this.next.update(true); } + this.update(true); + if (this.next) { this.next.update(true); } }; svgedit.path.Segment.prototype.setLinked = function (num) { - var seg, anum, pt; - if (num === 2) { - anum = 1; - seg = this.next; - if (!seg) { return; } - pt = this.item; - } else { - anum = 2; - seg = this.prev; - if (!seg) { return; } - pt = seg.item; - } + var seg, anum, pt; + if (num === 2) { + anum = 1; + seg = this.next; + if (!seg) { return; } + pt = this.item; + } else { + anum = 2; + seg = this.prev; + if (!seg) { return; } + pt = seg.item; + } - var item = seg.item; - item['x' + anum] = pt.x + (pt.x - this.item['x' + num]); - item['y' + anum] = pt.y + (pt.y - this.item['y' + num]); + var item = seg.item; + item['x' + anum] = pt.x + (pt.x - this.item['x' + num]); + item['y' + anum] = pt.y + (pt.y - this.item['y' + num]); - var pts = [item.x, item.y, - item.x1, item.y1, - item.x2, item.y2]; + var pts = [item.x, item.y, + item.x1, item.y1, + item.x2, item.y2]; - svgedit.path.replacePathSeg(seg.type, seg.index, pts); - seg.update(true); + svgedit.path.replacePathSeg(seg.type, seg.index, pts); + seg.update(true); }; svgedit.path.Segment.prototype.moveCtrl = function (num, dx, dy) { - var item = this.item; - item['x' + num] += dx; - item['y' + num] += dy; + var item = this.item; + item['x' + num] += dx; + item['y' + num] += dy; - var pts = [item.x, item.y, - item.x1, item.y1, item.x2, item.y2]; + var pts = [item.x, item.y, + item.x1, item.y1, item.x2, item.y2]; - svgedit.path.replacePathSeg(this.type, this.index, pts); - this.update(true); + svgedit.path.replacePathSeg(this.type, this.index, pts); + this.update(true); }; svgedit.path.Segment.prototype.setType = function (newType, pts) { - svgedit.path.replacePathSeg(newType, this.index, pts); - this.type = newType; - this.item = svgedit.path.path.elem.pathSegList.getItem(this.index); - this.showCtrlPts(newType === 6); - this.ctrlpts = svgedit.path.getControlPoints(this); - this.update(true); + svgedit.path.replacePathSeg(newType, this.index, pts); + this.type = newType; + this.item = svgedit.path.path.elem.pathSegList.getItem(this.index); + this.showCtrlPts(newType === 6); + this.ctrlpts = svgedit.path.getControlPoints(this); + this.update(true); }; svgedit.path.Path = function (elem) { - if (!elem || elem.tagName !== 'path') { - throw new Error('svgedit.path.Path constructed without a element'); - } + if (!elem || elem.tagName !== 'path') { + throw new Error('svgedit.path.Path constructed without a element'); + } - this.elem = elem; - this.segs = []; - this.selected_pts = []; - svgedit.path.path = this; + this.elem = elem; + this.segs = []; + this.selected_pts = []; + svgedit.path.path = this; - this.init(); + this.init(); }; // Reset path data svgedit.path.Path.prototype.init = function () { - // Hide all grips, etc + // Hide all grips, etc - // fixed, needed to work on all found elements, not just first - $(svgedit.path.getGripContainer()).find('*').each(function () { - $(this).attr('display', 'none'); - }); + // fixed, needed to work on all found elements, not just first + $(svgedit.path.getGripContainer()).find('*').each(function () { + $(this).attr('display', 'none'); + }); - var segList = this.elem.pathSegList; - var len = segList.numberOfItems; - this.segs = []; - this.selected_pts = []; - this.first_seg = null; + var segList = this.elem.pathSegList; + var len = segList.numberOfItems; + this.segs = []; + this.selected_pts = []; + this.first_seg = null; - // Set up segs array - var i; - for (i = 0; i < len; i++) { - var item = segList.getItem(i); - var segment = new svgedit.path.Segment(i, item); - segment.path = this; - this.segs.push(segment); - } + // Set up segs array + var i; + for (i = 0; i < len; i++) { + var item = segList.getItem(i); + var segment = new svgedit.path.Segment(i, item); + segment.path = this; + this.segs.push(segment); + } - var segs = this.segs; - var startI = null; + var segs = this.segs; + var startI = null; - for (i = 0; i < len; i++) { - var seg = segs[i]; - var nextSeg = (i + 1) >= len ? null : segs[i + 1]; - var prevSeg = (i - 1) < 0 ? null : segs[i - 1]; - var startSeg; - if (seg.type === 2) { - if (prevSeg && prevSeg.type !== 1) { - // New sub-path, last one is open, - // so add a grip to last sub-path's first point - startSeg = segs[startI]; - startSeg.next = segs[startI + 1]; - startSeg.next.prev = startSeg; - startSeg.addGrip(); - } - // Remember that this is a starter seg - startI = i; - } else if (nextSeg && nextSeg.type === 1) { - // This is the last real segment of a closed sub-path - // Next is first seg after "M" - seg.next = segs[startI + 1]; + for (i = 0; i < len; i++) { + var seg = segs[i]; + var nextSeg = (i + 1) >= len ? null : segs[i + 1]; + var prevSeg = (i - 1) < 0 ? null : segs[i - 1]; + var startSeg; + if (seg.type === 2) { + if (prevSeg && prevSeg.type !== 1) { + // New sub-path, last one is open, + // so add a grip to last sub-path's first point + startSeg = segs[startI]; + startSeg.next = segs[startI + 1]; + startSeg.next.prev = startSeg; + startSeg.addGrip(); + } + // Remember that this is a starter seg + startI = i; + } else if (nextSeg && nextSeg.type === 1) { + // This is the last real segment of a closed sub-path + // Next is first seg after "M" + seg.next = segs[startI + 1]; - // First seg after "M"'s prev is this - seg.next.prev = seg; - seg.mate = segs[startI]; - seg.addGrip(); - if (this.first_seg == null) { - this.first_seg = seg; - } - } else if (!nextSeg) { - if (seg.type !== 1) { - // Last seg, doesn't close so add a grip - // to last sub-path's first point - startSeg = segs[startI]; - startSeg.next = segs[startI + 1]; - startSeg.next.prev = startSeg; - startSeg.addGrip(); - seg.addGrip(); + // First seg after "M"'s prev is this + seg.next.prev = seg; + seg.mate = segs[startI]; + seg.addGrip(); + if (this.first_seg == null) { + this.first_seg = seg; + } + } else if (!nextSeg) { + if (seg.type !== 1) { + // Last seg, doesn't close so add a grip + // to last sub-path's first point + startSeg = segs[startI]; + startSeg.next = segs[startI + 1]; + startSeg.next.prev = startSeg; + startSeg.addGrip(); + seg.addGrip(); - if (!this.first_seg) { - // Open path, so set first as real first and add grip - this.first_seg = segs[startI]; - } - } - } else if (seg.type !== 1) { - // Regular segment, so add grip and its "next" - seg.addGrip(); + if (!this.first_seg) { + // Open path, so set first as real first and add grip + this.first_seg = segs[startI]; + } + } + } else if (seg.type !== 1) { + // Regular segment, so add grip and its "next" + seg.addGrip(); - // Don't set its "next" if it's an "M" - if (nextSeg && nextSeg.type !== 2) { - seg.next = nextSeg; - seg.next.prev = seg; - } - } - } - return this; + // Don't set its "next" if it's an "M" + if (nextSeg && nextSeg.type !== 2) { + seg.next = nextSeg; + seg.next.prev = seg; + } + } + } + return this; }; svgedit.path.Path.prototype.eachSeg = function (fn) { - var i; - var len = this.segs.length; - for (i = 0; i < len; i++) { - var ret = fn.call(this.segs[i], i); - if (ret === false) { break; } - } + var i; + var len = this.segs.length; + for (i = 0; i < len; i++) { + var ret = fn.call(this.segs[i], i); + if (ret === false) { break; } + } }; svgedit.path.Path.prototype.addSeg = function (index) { - // Adds a new segment - var seg = this.segs[index]; - if (!seg.prev) { return; } + // Adds a new segment + var seg = this.segs[index]; + if (!seg.prev) { return; } - var prev = seg.prev; - var newseg, newX, newY; - switch (seg.item.pathSegType) { - case 4: - newX = (seg.item.x + prev.item.x) / 2; - newY = (seg.item.y + prev.item.y) / 2; - newseg = this.elem.createSVGPathSegLinetoAbs(newX, newY); - break; - case 6: // make it a curved segment to preserve the shape (WRS) - // http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm#Geometric_interpretation - var p0x = (prev.item.x + seg.item.x1) / 2; - var p1x = (seg.item.x1 + seg.item.x2) / 2; - var p2x = (seg.item.x2 + seg.item.x) / 2; - var p01x = (p0x + p1x) / 2; - var p12x = (p1x + p2x) / 2; - newX = (p01x + p12x) / 2; - var p0y = (prev.item.y + seg.item.y1) / 2; - var p1y = (seg.item.y1 + seg.item.y2) / 2; - var p2y = (seg.item.y2 + seg.item.y) / 2; - var p01y = (p0y + p1y) / 2; - var p12y = (p1y + p2y) / 2; - newY = (p01y + p12y) / 2; - newseg = this.elem.createSVGPathSegCurvetoCubicAbs(newX, newY, p0x, p0y, p01x, p01y); - var pts = [seg.item.x, seg.item.y, p12x, p12y, p2x, p2y]; - svgedit.path.replacePathSeg(seg.type, index, pts); - break; - } + var prev = seg.prev; + var newseg, newX, newY; + switch (seg.item.pathSegType) { + case 4: + newX = (seg.item.x + prev.item.x) / 2; + newY = (seg.item.y + prev.item.y) / 2; + newseg = this.elem.createSVGPathSegLinetoAbs(newX, newY); + break; + case 6: // make it a curved segment to preserve the shape (WRS) + // http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm#Geometric_interpretation + var p0x = (prev.item.x + seg.item.x1) / 2; + var p1x = (seg.item.x1 + seg.item.x2) / 2; + var p2x = (seg.item.x2 + seg.item.x) / 2; + var p01x = (p0x + p1x) / 2; + var p12x = (p1x + p2x) / 2; + newX = (p01x + p12x) / 2; + var p0y = (prev.item.y + seg.item.y1) / 2; + var p1y = (seg.item.y1 + seg.item.y2) / 2; + var p2y = (seg.item.y2 + seg.item.y) / 2; + var p01y = (p0y + p1y) / 2; + var p12y = (p1y + p2y) / 2; + newY = (p01y + p12y) / 2; + newseg = this.elem.createSVGPathSegCurvetoCubicAbs(newX, newY, p0x, p0y, p01x, p01y); + var pts = [seg.item.x, seg.item.y, p12x, p12y, p2x, p2y]; + svgedit.path.replacePathSeg(seg.type, index, pts); + break; + } - svgedit.path.insertItemBefore(this.elem, newseg, index); + svgedit.path.insertItemBefore(this.elem, newseg, index); }; svgedit.path.Path.prototype.deleteSeg = function (index) { - var seg = this.segs[index]; - var list = this.elem.pathSegList; + var seg = this.segs[index]; + var list = this.elem.pathSegList; - seg.show(false); - var next = seg.next; - var pt; - if (seg.mate) { - // Make the next point be the "M" point - pt = [next.item.x, next.item.y]; - svgedit.path.replacePathSeg(2, next.index, pt); + seg.show(false); + var next = seg.next; + var pt; + if (seg.mate) { + // Make the next point be the "M" point + pt = [next.item.x, next.item.y]; + svgedit.path.replacePathSeg(2, next.index, pt); - // Reposition last node - svgedit.path.replacePathSeg(4, seg.index, pt); + // Reposition last node + svgedit.path.replacePathSeg(4, seg.index, pt); - list.removeItem(seg.mate.index); - } else if (!seg.prev) { - // First node of open path, make next point the M - // var item = seg.item; - pt = [next.item.x, next.item.y]; - svgedit.path.replacePathSeg(2, seg.next.index, pt); - list.removeItem(index); - } else { - list.removeItem(index); - } + list.removeItem(seg.mate.index); + } else if (!seg.prev) { + // First node of open path, make next point the M + // var item = seg.item; + pt = [next.item.x, next.item.y]; + svgedit.path.replacePathSeg(2, seg.next.index, pt); + list.removeItem(index); + } else { + list.removeItem(index); + } }; svgedit.path.Path.prototype.subpathIsClosed = function (index) { - var closed = false; - // Check if subpath is already open - svgedit.path.path.eachSeg(function (i) { - if (i <= index) { return true; } - if (this.type === 2) { - // Found M first, so open - return false; - } - if (this.type === 1) { - // Found Z first, so closed - closed = true; - return false; - } - }); + var closed = false; + // Check if subpath is already open + svgedit.path.path.eachSeg(function (i) { + if (i <= index) { return true; } + if (this.type === 2) { + // Found M first, so open + return false; + } + if (this.type === 1) { + // Found Z first, so closed + closed = true; + return false; + } + }); - return closed; + return closed; }; svgedit.path.Path.prototype.removePtFromSelection = function (index) { - var pos = this.selected_pts.indexOf(index); - if (pos === -1) { - return; - } - this.segs[index].select(false); - this.selected_pts.splice(pos, 1); + var pos = this.selected_pts.indexOf(index); + if (pos === -1) { + return; + } + this.segs[index].select(false); + this.selected_pts.splice(pos, 1); }; svgedit.path.Path.prototype.clearSelection = function () { - this.eachSeg(function () { - // 'this' is the segment here - this.select(false); - }); - this.selected_pts = []; + this.eachSeg(function () { + // 'this' is the segment here + this.select(false); + }); + this.selected_pts = []; }; svgedit.path.Path.prototype.storeD = function () { - this.last_d = this.elem.getAttribute('d'); + this.last_d = this.elem.getAttribute('d'); }; svgedit.path.Path.prototype.show = function (y) { - // Shows this path's segment grips - this.eachSeg(function () { - // 'this' is the segment here - this.show(y); - }); - if (y) { - this.selectPt(this.first_seg.index); - } - return this; + // Shows this path's segment grips + this.eachSeg(function () { + // 'this' is the segment here + this.show(y); + }); + if (y) { + this.selectPt(this.first_seg.index); + } + return this; }; // Move selected points svgedit.path.Path.prototype.movePts = function (dx, dy) { - var i = this.selected_pts.length; - while (i--) { - var seg = this.segs[this.selected_pts[i]]; - seg.move(dx, dy); - } + var i = this.selected_pts.length; + while (i--) { + var seg = this.segs[this.selected_pts[i]]; + seg.move(dx, dy); + } }; svgedit.path.Path.prototype.moveCtrl = function (dx, dy) { - var seg = this.segs[this.selected_pts[0]]; - seg.moveCtrl(this.dragctrl, dx, dy); - if (linkControlPts) { - seg.setLinked(this.dragctrl); - } + var seg = this.segs[this.selected_pts[0]]; + seg.moveCtrl(this.dragctrl, dx, dy); + if (linkControlPts) { + seg.setLinked(this.dragctrl); + } }; svgedit.path.Path.prototype.setSegType = function (newType) { - this.storeD(); - var i = this.selected_pts.length; - var text; - while (i--) { - var selPt = this.selected_pts[i]; + this.storeD(); + var i = this.selected_pts.length; + var text; + while (i--) { + var selPt = this.selected_pts[i]; - // Selected seg - var cur = this.segs[selPt]; - var prev = cur.prev; - if (!prev) { continue; } + // Selected seg + var cur = this.segs[selPt]; + var prev = cur.prev; + if (!prev) { continue; } - if (!newType) { // double-click, so just toggle - text = 'Toggle Path Segment Type'; + if (!newType) { // double-click, so just toggle + text = 'Toggle Path Segment Type'; - // Toggle segment to curve/straight line - var oldType = cur.type; + // Toggle segment to curve/straight line + var oldType = cur.type; - newType = (oldType === 6) ? 4 : 6; - } + newType = (oldType === 6) ? 4 : 6; + } - newType = Number(newType); + newType = Number(newType); - var curX = cur.item.x; - var curY = cur.item.y; - var prevX = prev.item.x; - var prevY = prev.item.y; - var points; - switch (newType) { - case 6: - if (cur.olditem) { - var old = cur.olditem; - points = [curX, curY, old.x1, old.y1, old.x2, old.y2]; - } else { - var diffX = curX - prevX; - var diffY = curY - prevY; - // get control points from straight line segment - /* - var ct1x = (prevX + (diffY/2)); - var ct1y = (prevY - (diffX/2)); - var ct2x = (curX + (diffY/2)); - var ct2y = (curY - (diffX/2)); - */ - // create control points on the line to preserve the shape (WRS) - var ct1x = (prevX + (diffX / 3)); - var ct1y = (prevY + (diffY / 3)); - var ct2x = (curX - (diffX / 3)); - var ct2y = (curY - (diffY / 3)); - points = [curX, curY, ct1x, ct1y, ct2x, ct2y]; - } - break; - case 4: - points = [curX, curY]; + var curX = cur.item.x; + var curY = cur.item.y; + var prevX = prev.item.x; + var prevY = prev.item.y; + var points; + switch (newType) { + case 6: + if (cur.olditem) { + var old = cur.olditem; + points = [curX, curY, old.x1, old.y1, old.x2, old.y2]; + } else { + var diffX = curX - prevX; + var diffY = curY - prevY; + // get control points from straight line segment + /* + var ct1x = (prevX + (diffY/2)); + var ct1y = (prevY - (diffX/2)); + var ct2x = (curX + (diffY/2)); + var ct2y = (curY - (diffX/2)); + */ + // create control points on the line to preserve the shape (WRS) + var ct1x = (prevX + (diffX / 3)); + var ct1y = (prevY + (diffY / 3)); + var ct2x = (curX - (diffX / 3)); + var ct2y = (curY - (diffY / 3)); + points = [curX, curY, ct1x, ct1y, ct2x, ct2y]; + } + break; + case 4: + points = [curX, curY]; - // Store original prevve segment nums - cur.olditem = cur.item; - break; - } + // Store original prevve segment nums + cur.olditem = cur.item; + break; + } - cur.setType(newType, points); - } - svgedit.path.path.endChanges(text); + cur.setType(newType, points); + } + svgedit.path.path.endChanges(text); }; svgedit.path.Path.prototype.selectPt = function (pt, ctrlNum) { - this.clearSelection(); - if (pt == null) { - this.eachSeg(function (i) { - // 'this' is the segment here. - if (this.prev) { - pt = i; - } - }); - } - this.addPtsToSelection(pt); - if (ctrlNum) { - this.dragctrl = ctrlNum; + this.clearSelection(); + if (pt == null) { + this.eachSeg(function (i) { + // 'this' is the segment here. + if (this.prev) { + pt = i; + } + }); + } + this.addPtsToSelection(pt); + if (ctrlNum) { + this.dragctrl = ctrlNum; - if (linkControlPts) { - this.segs[pt].setLinked(ctrlNum); - } - } + if (linkControlPts) { + this.segs[pt].setLinked(ctrlNum); + } + } }; // Update position of all points svgedit.path.Path.prototype.update = function () { - var elem = this.elem; - if (svgedit.utilities.getRotationAngle(elem)) { - this.matrix = svgedit.math.getMatrix(elem); - this.imatrix = this.matrix.inverse(); - } else { - this.matrix = null; - this.imatrix = null; - } + var elem = this.elem; + if (svgedit.utilities.getRotationAngle(elem)) { + this.matrix = svgedit.math.getMatrix(elem); + this.imatrix = this.matrix.inverse(); + } else { + this.matrix = null; + this.imatrix = null; + } - this.eachSeg(function (i) { - this.item = elem.pathSegList.getItem(i); - this.update(); - }); + this.eachSeg(function (i) { + this.item = elem.pathSegList.getItem(i); + this.update(); + }); - return this; + return this; }; svgedit.path.getPath_ = function (elem) { - var p = pathData[elem.id]; - if (!p) { - p = pathData[elem.id] = new svgedit.path.Path(elem); - } - return p; + var p = pathData[elem.id]; + if (!p) { + p = pathData[elem.id] = new svgedit.path.Path(elem); + } + return p; }; svgedit.path.removePath_ = function (id) { - if (id in pathData) { delete pathData[id]; } + if (id in pathData) { delete pathData[id]; } }; var newcx, newcy, oldcx, oldcy, angle; var getRotVals = function (x, y) { - var dx = x - oldcx; - var dy = y - oldcy; + var dx = x - oldcx; + var dy = y - oldcy; - // rotate the point around the old center - var r = Math.sqrt(dx * dx + dy * dy); - var theta = Math.atan2(dy, dx) + angle; - dx = r * Math.cos(theta) + oldcx; - dy = r * Math.sin(theta) + oldcy; + // rotate the point around the old center + var r = Math.sqrt(dx * dx + dy * dy); + var theta = Math.atan2(dy, dx) + angle; + dx = r * Math.cos(theta) + oldcx; + dy = r * Math.sin(theta) + oldcy; - // dx,dy should now hold the actual coordinates of each - // point after being rotated + // dx,dy should now hold the actual coordinates of each + // point after being rotated - // now we want to rotate them around the new center in the reverse direction - dx -= newcx; - dy -= newcy; + // now we want to rotate them around the new center in the reverse direction + dx -= newcx; + dy -= newcy; - r = Math.sqrt(dx * dx + dy * dy); - theta = Math.atan2(dy, dx) - angle; + r = Math.sqrt(dx * dx + dy * dy); + theta = Math.atan2(dy, dx) - angle; - return {'x': r * Math.cos(theta) + newcx, - 'y': r * Math.sin(theta) + newcy}; + return {'x': r * Math.cos(theta) + newcx, + 'y': r * Math.sin(theta) + newcy}; }; // If the path was rotated, we must now pay the piper: @@ -939,59 +939,59 @@ var getRotVals = function (x, y) { // TODO: This is still using ye olde transform methods, can probably // be optimized or even taken care of by recalculateDimensions svgedit.path.recalcRotatedPath = function () { - var currentPath = svgedit.path.path.elem; - angle = svgedit.utilities.getRotationAngle(currentPath, true); - if (!angle) { return; } - // selectedBBoxes[0] = svgedit.path.path.oldbbox; - var box = svgedit.utilities.getBBox(currentPath), - oldbox = svgedit.path.path.oldbbox; // selectedBBoxes[0], - oldcx = oldbox.x + oldbox.width / 2; - oldcy = oldbox.y + oldbox.height / 2; - newcx = box.x + box.width / 2; - newcy = box.y + box.height / 2; + var currentPath = svgedit.path.path.elem; + angle = svgedit.utilities.getRotationAngle(currentPath, true); + if (!angle) { return; } + // selectedBBoxes[0] = svgedit.path.path.oldbbox; + var box = svgedit.utilities.getBBox(currentPath), + oldbox = svgedit.path.path.oldbbox; // selectedBBoxes[0], + oldcx = oldbox.x + oldbox.width / 2; + oldcy = oldbox.y + oldbox.height / 2; + newcx = box.x + box.width / 2; + newcy = box.y + box.height / 2; - // un-rotate the new center to the proper position - var dx = newcx - oldcx, - dy = newcy - oldcy, - r = Math.sqrt(dx * dx + dy * dy), - theta = Math.atan2(dy, dx) + angle; + // un-rotate the new center to the proper position + var dx = newcx - oldcx, + dy = newcy - oldcy, + r = Math.sqrt(dx * dx + dy * dy), + theta = Math.atan2(dy, dx) + angle; - newcx = r * Math.cos(theta) + oldcx; - newcy = r * Math.sin(theta) + oldcy; + newcx = r * Math.cos(theta) + oldcx; + newcy = r * Math.sin(theta) + oldcy; - var list = currentPath.pathSegList, - i = list.numberOfItems; - while (i) { - i -= 1; - var seg = list.getItem(i), - type = seg.pathSegType; - if (type === 1) { continue; } + var list = currentPath.pathSegList, + i = list.numberOfItems; + while (i) { + i -= 1; + var seg = list.getItem(i), + type = seg.pathSegType; + if (type === 1) { continue; } - var rvals = getRotVals(seg.x, seg.y), - points = [rvals.x, rvals.y]; - if (seg.x1 != null && seg.x2 != null) { - var cVals1 = getRotVals(seg.x1, seg.y1); - var cVals2 = getRotVals(seg.x2, seg.y2); - points.splice(points.length, 0, cVals1.x, cVals1.y, cVals2.x, cVals2.y); - } - svgedit.path.replacePathSeg(type, i, points); - } // loop for each point + var rvals = getRotVals(seg.x, seg.y), + points = [rvals.x, rvals.y]; + if (seg.x1 != null && seg.x2 != null) { + var cVals1 = getRotVals(seg.x1, seg.y1); + var cVals2 = getRotVals(seg.x2, seg.y2); + points.splice(points.length, 0, cVals1.x, cVals1.y, cVals2.x, cVals2.y); + } + svgedit.path.replacePathSeg(type, i, points); + } // loop for each point - box = svgedit.utilities.getBBox(currentPath); - // selectedBBoxes[0].x = box.x; selectedBBoxes[0].y = box.y; - // selectedBBoxes[0].width = box.width; selectedBBoxes[0].height = box.height; + box = svgedit.utilities.getBBox(currentPath); + // selectedBBoxes[0].x = box.x; selectedBBoxes[0].y = box.y; + // selectedBBoxes[0].width = box.width; selectedBBoxes[0].height = box.height; - // now we must set the new transform to be rotated around the new center - var Rnc = svgroot.createSVGTransform(), - tlist = svgedit.transformlist.getTransformList(currentPath); - Rnc.setRotate((angle * 180.0 / Math.PI), newcx, newcy); - tlist.replaceItem(Rnc, 0); + // now we must set the new transform to be rotated around the new center + var Rnc = svgroot.createSVGTransform(), + tlist = svgedit.transformlist.getTransformList(currentPath); + Rnc.setRotate((angle * 180.0 / Math.PI), newcx, newcy); + tlist.replaceItem(Rnc, 0); }; // ==================================== // Public API starts here svgedit.path.clearData = function () { - pathData = {}; + pathData = {}; }; }()); diff --git a/editor/pathseg.js b/editor/pathseg.js index 648721d2..b267c973 100644 --- a/editor/pathseg.js +++ b/editor/pathseg.js @@ -9,361 +9,361 @@ (function () { 'use strict'; if (!('SVGPathSeg' in window)) { - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg - window.SVGPathSeg = function (type, typeAsLetter, owningPathSegList) { - this.pathSegType = type; - this.pathSegTypeAsLetter = typeAsLetter; - this._owningPathSegList = owningPathSegList; - }; + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg + window.SVGPathSeg = function (type, typeAsLetter, owningPathSegList) { + this.pathSegType = type; + this.pathSegTypeAsLetter = typeAsLetter; + this._owningPathSegList = owningPathSegList; + }; - window.SVGPathSeg.prototype.classname = 'SVGPathSeg'; + window.SVGPathSeg.prototype.classname = 'SVGPathSeg'; - window.SVGPathSeg.PATHSEG_UNKNOWN = 0; - window.SVGPathSeg.PATHSEG_CLOSEPATH = 1; - window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2; - window.SVGPathSeg.PATHSEG_MOVETO_REL = 3; - window.SVGPathSeg.PATHSEG_LINETO_ABS = 4; - window.SVGPathSeg.PATHSEG_LINETO_REL = 5; - window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; - window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; - window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; - window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; - window.SVGPathSeg.PATHSEG_ARC_ABS = 10; - window.SVGPathSeg.PATHSEG_ARC_REL = 11; - window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; - window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; - window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; - window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; - window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; - window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; - window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; - window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; + window.SVGPathSeg.PATHSEG_UNKNOWN = 0; + window.SVGPathSeg.PATHSEG_CLOSEPATH = 1; + window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2; + window.SVGPathSeg.PATHSEG_MOVETO_REL = 3; + window.SVGPathSeg.PATHSEG_LINETO_ABS = 4; + window.SVGPathSeg.PATHSEG_LINETO_REL = 5; + window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; + window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; + window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; + window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; + window.SVGPathSeg.PATHSEG_ARC_ABS = 10; + window.SVGPathSeg.PATHSEG_ARC_REL = 11; + window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; + window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; + window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; + window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; + window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; + window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; + window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; + window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; - // Notify owning PathSegList on any changes so they can be synchronized back to the path element. - window.SVGPathSeg.prototype._segmentChanged = function () { - if (this._owningPathSegList) { - this._owningPathSegList.segmentChanged(this); - } - }; + // Notify owning PathSegList on any changes so they can be synchronized back to the path element. + window.SVGPathSeg.prototype._segmentChanged = function () { + if (this._owningPathSegList) { + this._owningPathSegList.segmentChanged(this); + } + }; - window.SVGPathSegClosePath = function (owningPathSegList) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, 'z', owningPathSegList); - }; - window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegClosePath.prototype.toString = function () { return '[object SVGPathSegClosePath]'; }; - window.SVGPathSegClosePath.prototype._asPathString = function () { return this.pathSegTypeAsLetter; }; - window.SVGPathSegClosePath.prototype.clone = function () { return new window.SVGPathSegClosePath(undefined); }; + window.SVGPathSegClosePath = function (owningPathSegList) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, 'z', owningPathSegList); + }; + window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegClosePath.prototype.toString = function () { return '[object SVGPathSegClosePath]'; }; + window.SVGPathSegClosePath.prototype._asPathString = function () { return this.pathSegTypeAsLetter; }; + window.SVGPathSegClosePath.prototype.clone = function () { return new window.SVGPathSegClosePath(undefined); }; - window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, 'M', owningPathSegList); - this._x = x; - this._y = y; - }; - window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegMovetoAbs.prototype.toString = function () { return '[object SVGPathSegMovetoAbs]'; }; - window.SVGPathSegMovetoAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegMovetoAbs.prototype.clone = function () { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y); }; - Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, 'M', owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegMovetoAbs.prototype.toString = function () { return '[object SVGPathSegMovetoAbs]'; }; + window.SVGPathSegMovetoAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegMovetoAbs.prototype.clone = function () { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y); }; + Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, 'm', owningPathSegList); - this._x = x; - this._y = y; - }; - window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegMovetoRel.prototype.toString = function () { return '[object SVGPathSegMovetoRel]'; }; - window.SVGPathSegMovetoRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegMovetoRel.prototype.clone = function () { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y); }; - Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, 'm', owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegMovetoRel.prototype.toString = function () { return '[object SVGPathSegMovetoRel]'; }; + window.SVGPathSegMovetoRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegMovetoRel.prototype.clone = function () { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y); }; + Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, 'L', owningPathSegList); - this._x = x; - this._y = y; - }; - window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegLinetoAbs.prototype.toString = function () { return '[object SVGPathSegLinetoAbs]'; }; - window.SVGPathSegLinetoAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegLinetoAbs.prototype.clone = function () { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y); }; - Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, 'L', owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoAbs.prototype.toString = function () { return '[object SVGPathSegLinetoAbs]'; }; + window.SVGPathSegLinetoAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegLinetoAbs.prototype.clone = function () { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y); }; + Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, 'l', owningPathSegList); - this._x = x; - this._y = y; - }; - window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegLinetoRel.prototype.toString = function () { return '[object SVGPathSegLinetoRel]'; }; - window.SVGPathSegLinetoRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegLinetoRel.prototype.clone = function () { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y); }; - Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, 'l', owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoRel.prototype.toString = function () { return '[object SVGPathSegLinetoRel]'; }; + window.SVGPathSegLinetoRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegLinetoRel.prototype.clone = function () { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y); }; + Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, 'C', owningPathSegList); - this._x = x; - this._y = y; - this._x1 = x1; - this._y1 = y1; - this._x2 = x2; - this._y2 = y2; - }; - window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicAbs]'; }; - window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }; - Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, 'C', owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + }; + window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicAbs]'; }; + window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }; + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, 'c', owningPathSegList); - this._x = x; - this._y = y; - this._x1 = x1; - this._y1 = y1; - this._x2 = x2; - this._y2 = y2; - }; - window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegCurvetoCubicRel.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicRel]'; }; - window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegCurvetoCubicRel.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }; - Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, 'c', owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + }; + window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoCubicRel.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicRel]'; }; + window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegCurvetoCubicRel.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }; + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, 'Q', owningPathSegList); - this._x = x; - this._y = y; - this._x1 = x1; - this._y1 = y1; - }; - window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticAbs]'; }; - window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); }; - Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, 'Q', owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + }; + window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticAbs]'; }; + window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); }; + Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, 'q', owningPathSegList); - this._x = x; - this._y = y; - this._x1 = x1; - this._y1 = y1; - }; - window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticRel]'; }; - window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); }; - Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, 'q', owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + }; + window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticRel]'; }; + window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); }; + Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, 'A', owningPathSegList); - this._x = x; - this._y = y; - this._r1 = r1; - this._r2 = r2; - this._angle = angle; - this._largeArcFlag = largeArcFlag; - this._sweepFlag = sweepFlag; - }; - window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegArcAbs.prototype.toString = function () { return '[object SVGPathSegArcAbs]'; }; - window.SVGPathSegArcAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegArcAbs.prototype.clone = function () { return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }; - Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r1', { get: function () { return this._r1; }, set: function (r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r2', { get: function () { return this._r2; }, set: function (r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'angle', { get: function () { return this._angle; }, set: function (angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'largeArcFlag', { get: function () { return this._largeArcFlag; }, set: function (largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'sweepFlag', { get: function () { return this._sweepFlag; }, set: function (sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, 'A', owningPathSegList); + this._x = x; + this._y = y; + this._r1 = r1; + this._r2 = r2; + this._angle = angle; + this._largeArcFlag = largeArcFlag; + this._sweepFlag = sweepFlag; + }; + window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegArcAbs.prototype.toString = function () { return '[object SVGPathSegArcAbs]'; }; + window.SVGPathSegArcAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegArcAbs.prototype.clone = function () { return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }; + Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r1', { get: function () { return this._r1; }, set: function (r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r2', { get: function () { return this._r2; }, set: function (r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'angle', { get: function () { return this._angle; }, set: function (angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'largeArcFlag', { get: function () { return this._largeArcFlag; }, set: function (largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'sweepFlag', { get: function () { return this._sweepFlag; }, set: function (sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, 'a', owningPathSegList); - this._x = x; - this._y = y; - this._r1 = r1; - this._r2 = r2; - this._angle = angle; - this._largeArcFlag = largeArcFlag; - this._sweepFlag = sweepFlag; - }; - window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegArcRel.prototype.toString = function () { return '[object SVGPathSegArcRel]'; }; - window.SVGPathSegArcRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegArcRel.prototype.clone = function () { return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }; - Object.defineProperty(window.SVGPathSegArcRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r1', { get: function () { return this._r1; }, set: function (r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r2', { get: function () { return this._r2; }, set: function (r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcRel.prototype, 'angle', { get: function () { return this._angle; }, set: function (angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcRel.prototype, 'largeArcFlag', { get: function () { return this._largeArcFlag; }, set: function (largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegArcRel.prototype, 'sweepFlag', { get: function () { return this._sweepFlag; }, set: function (sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, 'a', owningPathSegList); + this._x = x; + this._y = y; + this._r1 = r1; + this._r2 = r2; + this._angle = angle; + this._largeArcFlag = largeArcFlag; + this._sweepFlag = sweepFlag; + }; + window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegArcRel.prototype.toString = function () { return '[object SVGPathSegArcRel]'; }; + window.SVGPathSegArcRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegArcRel.prototype.clone = function () { return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }; + Object.defineProperty(window.SVGPathSegArcRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r1', { get: function () { return this._r1; }, set: function (r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r2', { get: function () { return this._r2; }, set: function (r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, 'angle', { get: function () { return this._angle; }, set: function (angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, 'largeArcFlag', { get: function () { return this._largeArcFlag; }, set: function (largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, 'sweepFlag', { get: function () { return this._sweepFlag; }, set: function (sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, 'H', owningPathSegList); - this._x = x; - }; - window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () { return '[object SVGPathSegLinetoHorizontalAbs]'; }; - window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x; }; - window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x); }; - Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, 'H', owningPathSegList); + this._x = x; + }; + window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () { return '[object SVGPathSegLinetoHorizontalAbs]'; }; + window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x; }; + window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x); }; + Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, 'h', owningPathSegList); - this._x = x; - }; - window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () { return '[object SVGPathSegLinetoHorizontalRel]'; }; - window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x; }; - window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x); }; - Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, 'h', owningPathSegList); + this._x = x; + }; + window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () { return '[object SVGPathSegLinetoHorizontalRel]'; }; + window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x; }; + window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x); }; + Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, 'V', owningPathSegList); - this._y = y; - }; - window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () { return '[object SVGPathSegLinetoVerticalAbs]'; }; - window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._y; }; - window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y); }; - Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, 'V', owningPathSegList); + this._y = y; + }; + window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () { return '[object SVGPathSegLinetoVerticalAbs]'; }; + window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._y; }; + window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y); }; + Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, 'v', owningPathSegList); - this._y = y; - }; - window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegLinetoVerticalRel.prototype.toString = function () { return '[object SVGPathSegLinetoVerticalRel]'; }; - window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._y; }; - window.SVGPathSegLinetoVerticalRel.prototype.clone = function () { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y); }; - Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, 'v', owningPathSegList); + this._y = y; + }; + window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoVerticalRel.prototype.toString = function () { return '[object SVGPathSegLinetoVerticalRel]'; }; + window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._y; }; + window.SVGPathSegLinetoVerticalRel.prototype.clone = function () { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y); }; + Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, 'S', owningPathSegList); - this._x = x; - this._y = y; - this._x2 = x2; - this._y2 = y2; - }; - window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicSmoothAbs]'; }; - window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); }; - Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, 'S', owningPathSegList); + this._x = x; + this._y = y; + this._x2 = x2; + this._y2 = y2; + }; + window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicSmoothAbs]'; }; + window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); }; + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, 's', owningPathSegList); - this._x = x; - this._y = y; - this._x2 = x2; - this._y2 = y2; - }; - window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicSmoothRel]'; }; - window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); }; - Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, 's', owningPathSegList); + this._x = x; + this._y = y; + this._x2 = x2; + this._y2 = y2; + }; + window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicSmoothRel]'; }; + window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); }; + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, 'T', owningPathSegList); - this._x = x; - this._y = y; - }; - window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticSmoothAbs]'; }; - window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); }; - Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, 'T', owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticSmoothAbs]'; }; + window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); }; + Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) { - window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, 't', owningPathSegList); - this._x = x; - this._y = y; - }; - window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); - window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticSmoothRel]'; }; - window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; - window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); }; - Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); + window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, 't', owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticSmoothRel]'; }; + window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; + window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); }; + Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - // Add createSVGPathSeg* functions to window.SVGPathElement. - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement. - window.SVGPathElement.prototype.createSVGPathSegClosePath = function () { return new window.SVGPathSegClosePath(undefined); }; - window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y); }; - window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y); }; - window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y); }; - window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y); }; - window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); }; - window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); }; - window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); }; - window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); }; - window.SVGPathElement.prototype.createSVGPathSegArcAbs = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }; - window.SVGPathElement.prototype.createSVGPathSegArcRel = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }; - window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x); }; - window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x); }; - window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y); }; - window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) { return new window.SVGPathSegLinetoVerticalRel(undefined, y); }; - window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); }; - window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); }; - window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); }; - window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); }; + // Add createSVGPathSeg* functions to window.SVGPathElement. + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement. + window.SVGPathElement.prototype.createSVGPathSegClosePath = function () { return new window.SVGPathSegClosePath(undefined); }; + window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y); }; + window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y); }; + window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y); }; + window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y); }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); }; + window.SVGPathElement.prototype.createSVGPathSegArcAbs = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }; + window.SVGPathElement.prototype.createSVGPathSegArcRel = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }; + window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x); }; + window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x); }; + window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y); }; + window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) { return new window.SVGPathSegLinetoVerticalRel(undefined, y); }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); }; - if (!('getPathSegAtLength' in window.SVGPathElement.prototype)) { - // Add getPathSegAtLength to SVGPathElement. - // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength - // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm. - window.SVGPathElement.prototype.getPathSegAtLength = function (distance) { - if (distance === undefined || !isFinite(distance)) { - throw new Error('Invalid arguments.'); - } + if (!('getPathSegAtLength' in window.SVGPathElement.prototype)) { + // Add getPathSegAtLength to SVGPathElement. + // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength + // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm. + window.SVGPathElement.prototype.getPathSegAtLength = function (distance) { + if (distance === undefined || !isFinite(distance)) { + throw new Error('Invalid arguments.'); + } - var measurementElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - measurementElement.setAttribute('d', this.getAttribute('d')); - var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1; + var measurementElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + measurementElement.setAttribute('d', this.getAttribute('d')); + var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1; - // If the path is empty, return 0. - if (lastPathSegment <= 0) { - return 0; - } + // If the path is empty, return 0. + if (lastPathSegment <= 0) { + return 0; + } - do { - measurementElement.pathSegList.removeItem(lastPathSegment); - if (distance > measurementElement.getTotalLength()) { - break; - } - lastPathSegment--; - } while (lastPathSegment > 0); - return lastPathSegment; - }; - } + do { + measurementElement.pathSegList.removeItem(lastPathSegment); + if (distance > measurementElement.getTotalLength()) { + break; + } + lastPathSegment--; + } while (lastPathSegment > 0); + return lastPathSegment; + }; + } } // Checking for SVGPathSegList in window checks for the case of an implementation without the @@ -372,508 +372,508 @@ if (!('SVGPathSeg' in window)) { // SVGPathSegList API (e.g., appendItem). In this case we need to re-implement the entire API // so the polyfill data (i.e., _list) is used throughout. if (!('SVGPathSegList' in window) || !('appendItem' in window.SVGPathSegList.prototype)) { - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList - window.SVGPathSegList = function (pathElement) { - this._pathElement = pathElement; - this._list = this._parsePath(this._pathElement.getAttribute('d')); + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList + window.SVGPathSegList = function (pathElement) { + this._pathElement = pathElement; + this._list = this._parsePath(this._pathElement.getAttribute('d')); - // Use a MutationObserver to catch changes to the path's "d" attribute. - this._mutationObserverConfig = { 'attributes': true, 'attributeFilter': ['d'] }; - this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this)); - this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); - }; + // Use a MutationObserver to catch changes to the path's "d" attribute. + this._mutationObserverConfig = { 'attributes': true, 'attributeFilter': ['d'] }; + this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this)); + this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); + }; - window.SVGPathSegList.prototype.classname = 'SVGPathSegList'; + window.SVGPathSegList.prototype.classname = 'SVGPathSegList'; - Object.defineProperty(window.SVGPathSegList.prototype, 'numberOfItems', { - get: function () { - this._checkPathSynchronizedToList(); - return this._list.length; - }, - enumerable: true - }); + Object.defineProperty(window.SVGPathSegList.prototype, 'numberOfItems', { + get: function () { + this._checkPathSynchronizedToList(); + return this._list.length; + }, + enumerable: true + }); - // Add the pathSegList accessors to window.SVGPathElement. - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData - Object.defineProperty(window.SVGPathElement.prototype, 'pathSegList', { - get: function () { - if (!this._pathSegList) { - this._pathSegList = new window.SVGPathSegList(this); - } - return this._pathSegList; - }, - enumerable: true - }); - // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList. - Object.defineProperty(window.SVGPathElement.prototype, 'normalizedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); - Object.defineProperty(window.SVGPathElement.prototype, 'animatedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); - Object.defineProperty(window.SVGPathElement.prototype, 'animatedNormalizedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); + // Add the pathSegList accessors to window.SVGPathElement. + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData + Object.defineProperty(window.SVGPathElement.prototype, 'pathSegList', { + get: function () { + if (!this._pathSegList) { + this._pathSegList = new window.SVGPathSegList(this); + } + return this._pathSegList; + }, + enumerable: true + }); + // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList. + Object.defineProperty(window.SVGPathElement.prototype, 'normalizedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); + Object.defineProperty(window.SVGPathElement.prototype, 'animatedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); + Object.defineProperty(window.SVGPathElement.prototype, 'animatedNormalizedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); - // Process any pending mutations to the path element and update the list as needed. - // This should be the first call of all public functions and is needed because - // MutationObservers are not synchronous so we can have pending asynchronous mutations. - window.SVGPathSegList.prototype._checkPathSynchronizedToList = function () { - this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords()); - }; + // Process any pending mutations to the path element and update the list as needed. + // This should be the first call of all public functions and is needed because + // MutationObservers are not synchronous so we can have pending asynchronous mutations. + window.SVGPathSegList.prototype._checkPathSynchronizedToList = function () { + this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords()); + }; - window.SVGPathSegList.prototype._updateListFromPathMutations = function (mutationRecords) { - if (!this._pathElement) { - return; - } - var hasPathMutations = false; - mutationRecords.forEach(function (record) { - if (record.attributeName === 'd') { - hasPathMutations = true; - } - }); - if (hasPathMutations) { - this._list = this._parsePath(this._pathElement.getAttribute('d')); - } - }; + window.SVGPathSegList.prototype._updateListFromPathMutations = function (mutationRecords) { + if (!this._pathElement) { + return; + } + var hasPathMutations = false; + mutationRecords.forEach(function (record) { + if (record.attributeName === 'd') { + hasPathMutations = true; + } + }); + if (hasPathMutations) { + this._list = this._parsePath(this._pathElement.getAttribute('d')); + } + }; - // Serialize the list and update the path's 'd' attribute. - window.SVGPathSegList.prototype._writeListToPath = function () { - this._pathElementMutationObserver.disconnect(); - this._pathElement.setAttribute('d', window.SVGPathSegList._pathSegArrayAsString(this._list)); - this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); - }; + // Serialize the list and update the path's 'd' attribute. + window.SVGPathSegList.prototype._writeListToPath = function () { + this._pathElementMutationObserver.disconnect(); + this._pathElement.setAttribute('d', window.SVGPathSegList._pathSegArrayAsString(this._list)); + this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); + }; - // When a path segment changes the list needs to be synchronized back to the path element. - window.SVGPathSegList.prototype.segmentChanged = function (pathSeg) { - this._writeListToPath(); - }; + // When a path segment changes the list needs to be synchronized back to the path element. + window.SVGPathSegList.prototype.segmentChanged = function (pathSeg) { + this._writeListToPath(); + }; - window.SVGPathSegList.prototype.clear = function () { - this._checkPathSynchronizedToList(); + window.SVGPathSegList.prototype.clear = function () { + this._checkPathSynchronizedToList(); - this._list.forEach(function (pathSeg) { - pathSeg._owningPathSegList = null; - }); - this._list = []; - this._writeListToPath(); - }; + this._list.forEach(function (pathSeg) { + pathSeg._owningPathSegList = null; + }); + this._list = []; + this._writeListToPath(); + }; - window.SVGPathSegList.prototype.initialize = function (newItem) { - this._checkPathSynchronizedToList(); + window.SVGPathSegList.prototype.initialize = function (newItem) { + this._checkPathSynchronizedToList(); - this._list = [newItem]; - newItem._owningPathSegList = this; - this._writeListToPath(); - return newItem; - }; + this._list = [newItem]; + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + }; - window.SVGPathSegList.prototype._checkValidIndex = function (index) { - if (isNaN(index) || index < 0 || index >= this.numberOfItems) { - throw new Error('INDEX_SIZE_ERR'); - } - }; + window.SVGPathSegList.prototype._checkValidIndex = function (index) { + if (isNaN(index) || index < 0 || index >= this.numberOfItems) { + throw new Error('INDEX_SIZE_ERR'); + } + }; - window.SVGPathSegList.prototype.getItem = function (index) { - this._checkPathSynchronizedToList(); + window.SVGPathSegList.prototype.getItem = function (index) { + this._checkPathSynchronizedToList(); - this._checkValidIndex(index); - return this._list[index]; - }; + this._checkValidIndex(index); + return this._list[index]; + }; - window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) { - this._checkPathSynchronizedToList(); + window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) { + this._checkPathSynchronizedToList(); - // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. - if (index > this.numberOfItems) { - index = this.numberOfItems; - } - if (newItem._owningPathSegList) { - // SVG2 spec says to make a copy. - newItem = newItem.clone(); - } - this._list.splice(index, 0, newItem); - newItem._owningPathSegList = this; - this._writeListToPath(); - return newItem; - }; + // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. + if (index > this.numberOfItems) { + index = this.numberOfItems; + } + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._list.splice(index, 0, newItem); + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + }; - window.SVGPathSegList.prototype.replaceItem = function (newItem, index) { - this._checkPathSynchronizedToList(); + window.SVGPathSegList.prototype.replaceItem = function (newItem, index) { + this._checkPathSynchronizedToList(); - if (newItem._owningPathSegList) { - // SVG2 spec says to make a copy. - newItem = newItem.clone(); - } - this._checkValidIndex(index); - this._list[index] = newItem; - newItem._owningPathSegList = this; - this._writeListToPath(); - return newItem; - }; + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._checkValidIndex(index); + this._list[index] = newItem; + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + }; - window.SVGPathSegList.prototype.removeItem = function (index) { - this._checkPathSynchronizedToList(); + window.SVGPathSegList.prototype.removeItem = function (index) { + this._checkPathSynchronizedToList(); - this._checkValidIndex(index); - var item = this._list[index]; - this._list.splice(index, 1); - this._writeListToPath(); - return item; - }; + this._checkValidIndex(index); + var item = this._list[index]; + this._list.splice(index, 1); + this._writeListToPath(); + return item; + }; - window.SVGPathSegList.prototype.appendItem = function (newItem) { - this._checkPathSynchronizedToList(); + window.SVGPathSegList.prototype.appendItem = function (newItem) { + this._checkPathSynchronizedToList(); - if (newItem._owningPathSegList) { - // SVG2 spec says to make a copy. - newItem = newItem.clone(); - } - this._list.push(newItem); - newItem._owningPathSegList = this; - // TODO: Optimize this to just append to the existing attribute. - this._writeListToPath(); - return newItem; - }; + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._list.push(newItem); + newItem._owningPathSegList = this; + // TODO: Optimize this to just append to the existing attribute. + this._writeListToPath(); + return newItem; + }; - window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) { - var string = ''; - var first = true; - pathSegArray.forEach(function (pathSeg) { - if (first) { - first = false; - string += pathSeg._asPathString(); - } else { - string += ' ' + pathSeg._asPathString(); - } - }); - return string; - }; + window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) { + var string = ''; + var first = true; + pathSegArray.forEach(function (pathSeg) { + if (first) { + first = false; + string += pathSeg._asPathString(); + } else { + string += ' ' + pathSeg._asPathString(); + } + }); + return string; + }; - // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. - window.SVGPathSegList.prototype._parsePath = function (string) { - if (!string || string.length === 0) { - return []; - } + // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. + window.SVGPathSegList.prototype._parsePath = function (string) { + if (!string || string.length === 0) { + return []; + } - var owningPathSegList = this; + var owningPathSegList = this; - var Builder = function () { - this.pathSegList = []; - }; + var Builder = function () { + this.pathSegList = []; + }; - Builder.prototype.appendSegment = function (pathSeg) { - this.pathSegList.push(pathSeg); - }; + Builder.prototype.appendSegment = function (pathSeg) { + this.pathSegList.push(pathSeg); + }; - var Source = function (string) { - this._string = string; - this._currentIndex = 0; - this._endIndex = this._string.length; - this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN; + var Source = function (string) { + this._string = string; + this._currentIndex = 0; + this._endIndex = this._string.length; + this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN; - this._skipOptionalSpaces(); - }; + this._skipOptionalSpaces(); + }; - Source.prototype._isCurrentSpace = function () { - var character = this._string[this._currentIndex]; - return character <= ' ' && (character === ' ' || character === '\n' || character === '\t' || character === '\r' || character === '\f'); - }; + Source.prototype._isCurrentSpace = function () { + var character = this._string[this._currentIndex]; + return character <= ' ' && (character === ' ' || character === '\n' || character === '\t' || character === '\r' || character === '\f'); + }; - Source.prototype._skipOptionalSpaces = function () { - while (this._currentIndex < this._endIndex && this._isCurrentSpace()) { - this._currentIndex++; - } - return this._currentIndex < this._endIndex; - }; + Source.prototype._skipOptionalSpaces = function () { + while (this._currentIndex < this._endIndex && this._isCurrentSpace()) { + this._currentIndex++; + } + return this._currentIndex < this._endIndex; + }; - Source.prototype._skipOptionalSpacesOrDelimiter = function () { - if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) !== ',') { - return false; - } - if (this._skipOptionalSpaces()) { - if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) === ',') { - this._currentIndex++; - this._skipOptionalSpaces(); - } - } - return this._currentIndex < this._endIndex; - }; + Source.prototype._skipOptionalSpacesOrDelimiter = function () { + if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) !== ',') { + return false; + } + if (this._skipOptionalSpaces()) { + if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) === ',') { + this._currentIndex++; + this._skipOptionalSpaces(); + } + } + return this._currentIndex < this._endIndex; + }; - Source.prototype.hasMoreData = function () { - return this._currentIndex < this._endIndex; - }; + Source.prototype.hasMoreData = function () { + return this._currentIndex < this._endIndex; + }; - Source.prototype.peekSegmentType = function () { - var lookahead = this._string[this._currentIndex]; - return this._pathSegTypeFromChar(lookahead); - }; + Source.prototype.peekSegmentType = function () { + var lookahead = this._string[this._currentIndex]; + return this._pathSegTypeFromChar(lookahead); + }; - Source.prototype._pathSegTypeFromChar = function (lookahead) { - switch (lookahead) { - case 'Z': - case 'z': - return window.SVGPathSeg.PATHSEG_CLOSEPATH; - case 'M': - return window.SVGPathSeg.PATHSEG_MOVETO_ABS; - case 'm': - return window.SVGPathSeg.PATHSEG_MOVETO_REL; - case 'L': - return window.SVGPathSeg.PATHSEG_LINETO_ABS; - case 'l': - return window.SVGPathSeg.PATHSEG_LINETO_REL; - case 'C': - return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS; - case 'c': - return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL; - case 'Q': - return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS; - case 'q': - return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL; - case 'A': - return window.SVGPathSeg.PATHSEG_ARC_ABS; - case 'a': - return window.SVGPathSeg.PATHSEG_ARC_REL; - case 'H': - return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS; - case 'h': - return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL; - case 'V': - return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS; - case 'v': - return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL; - case 'S': - return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; - case 's': - return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL; - case 'T': - return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; - case 't': - return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; - default: - return window.SVGPathSeg.PATHSEG_UNKNOWN; - } - }; + Source.prototype._pathSegTypeFromChar = function (lookahead) { + switch (lookahead) { + case 'Z': + case 'z': + return window.SVGPathSeg.PATHSEG_CLOSEPATH; + case 'M': + return window.SVGPathSeg.PATHSEG_MOVETO_ABS; + case 'm': + return window.SVGPathSeg.PATHSEG_MOVETO_REL; + case 'L': + return window.SVGPathSeg.PATHSEG_LINETO_ABS; + case 'l': + return window.SVGPathSeg.PATHSEG_LINETO_REL; + case 'C': + return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS; + case 'c': + return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL; + case 'Q': + return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS; + case 'q': + return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL; + case 'A': + return window.SVGPathSeg.PATHSEG_ARC_ABS; + case 'a': + return window.SVGPathSeg.PATHSEG_ARC_REL; + case 'H': + return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS; + case 'h': + return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL; + case 'V': + return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS; + case 'v': + return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL; + case 'S': + return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; + case 's': + return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL; + case 'T': + return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; + case 't': + return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; + default: + return window.SVGPathSeg.PATHSEG_UNKNOWN; + } + }; - Source.prototype._nextCommandHelper = function (lookahead, previousCommand) { - // Check for remaining coordinates in the current command. - if ((lookahead === '+' || lookahead === '-' || lookahead === '.' || (lookahead >= '0' && lookahead <= '9')) && previousCommand !== window.SVGPathSeg.PATHSEG_CLOSEPATH) { - if (previousCommand === window.SVGPathSeg.PATHSEG_MOVETO_ABS) { - return window.SVGPathSeg.PATHSEG_LINETO_ABS; - } - if (previousCommand === window.SVGPathSeg.PATHSEG_MOVETO_REL) { - return window.SVGPathSeg.PATHSEG_LINETO_REL; - } - return previousCommand; - } - return window.SVGPathSeg.PATHSEG_UNKNOWN; - }; + Source.prototype._nextCommandHelper = function (lookahead, previousCommand) { + // Check for remaining coordinates in the current command. + if ((lookahead === '+' || lookahead === '-' || lookahead === '.' || (lookahead >= '0' && lookahead <= '9')) && previousCommand !== window.SVGPathSeg.PATHSEG_CLOSEPATH) { + if (previousCommand === window.SVGPathSeg.PATHSEG_MOVETO_ABS) { + return window.SVGPathSeg.PATHSEG_LINETO_ABS; + } + if (previousCommand === window.SVGPathSeg.PATHSEG_MOVETO_REL) { + return window.SVGPathSeg.PATHSEG_LINETO_REL; + } + return previousCommand; + } + return window.SVGPathSeg.PATHSEG_UNKNOWN; + }; - Source.prototype.initialCommandIsMoveTo = function () { - // If the path is empty it is still valid, so return true. - if (!this.hasMoreData()) { - return true; - } - var command = this.peekSegmentType(); - // Path must start with moveTo. - return command === window.SVGPathSeg.PATHSEG_MOVETO_ABS || command === window.SVGPathSeg.PATHSEG_MOVETO_REL; - }; + Source.prototype.initialCommandIsMoveTo = function () { + // If the path is empty it is still valid, so return true. + if (!this.hasMoreData()) { + return true; + } + var command = this.peekSegmentType(); + // Path must start with moveTo. + return command === window.SVGPathSeg.PATHSEG_MOVETO_ABS || command === window.SVGPathSeg.PATHSEG_MOVETO_REL; + }; - // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF - Source.prototype._parseNumber = function () { - var exponent = 0; - var integer = 0; - var frac = 1; - var decimal = 0; - var sign = 1; - var expsign = 1; + // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF + Source.prototype._parseNumber = function () { + var exponent = 0; + var integer = 0; + var frac = 1; + var decimal = 0; + var sign = 1; + var expsign = 1; - var startIndex = this._currentIndex; + var startIndex = this._currentIndex; - this._skipOptionalSpaces(); + this._skipOptionalSpaces(); - // Read the sign. - if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) === '+') { - this._currentIndex++; - } else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) === '-') { - this._currentIndex++; - sign = -1; - } + // Read the sign. + if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) === '+') { + this._currentIndex++; + } else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) === '-') { + this._currentIndex++; + sign = -1; + } - if (this._currentIndex === this._endIndex || ((this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') && this._string.charAt(this._currentIndex) !== '.')) { - // The first character of a number must be one of [0-9+-.]. - return undefined; - } + if (this._currentIndex === this._endIndex || ((this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') && this._string.charAt(this._currentIndex) !== '.')) { + // The first character of a number must be one of [0-9+-.]. + return undefined; + } - // Read the integer part, build right-to-left. - var startIntPartIndex = this._currentIndex; - while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { - this._currentIndex++; // Advance to first non-digit. - } + // Read the integer part, build right-to-left. + var startIntPartIndex = this._currentIndex; + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { + this._currentIndex++; // Advance to first non-digit. + } - if (this._currentIndex !== startIntPartIndex) { - var scanIntPartIndex = this._currentIndex - 1; - var multiplier = 1; - while (scanIntPartIndex >= startIntPartIndex) { - integer += multiplier * (this._string.charAt(scanIntPartIndex--) - '0'); - multiplier *= 10; - } - } + if (this._currentIndex !== startIntPartIndex) { + var scanIntPartIndex = this._currentIndex - 1; + var multiplier = 1; + while (scanIntPartIndex >= startIntPartIndex) { + integer += multiplier * (this._string.charAt(scanIntPartIndex--) - '0'); + multiplier *= 10; + } + } - // Read the decimals. - if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) === '.') { - this._currentIndex++; + // Read the decimals. + if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) === '.') { + this._currentIndex++; - // There must be a least one digit following the . - if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') { - return undefined; - } - while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { - frac *= 10; - decimal += (this._string.charAt(this._currentIndex) - '0') / frac; - this._currentIndex += 1; - } - } + // There must be a least one digit following the . + if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') { + return undefined; + } + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { + frac *= 10; + decimal += (this._string.charAt(this._currentIndex) - '0') / frac; + this._currentIndex += 1; + } + } - // Read the exponent part. - if (this._currentIndex !== startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) === 'e' || this._string.charAt(this._currentIndex) === 'E') && (this._string.charAt(this._currentIndex + 1) !== 'x' && this._string.charAt(this._currentIndex + 1) !== 'm')) { - this._currentIndex++; + // Read the exponent part. + if (this._currentIndex !== startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) === 'e' || this._string.charAt(this._currentIndex) === 'E') && (this._string.charAt(this._currentIndex + 1) !== 'x' && this._string.charAt(this._currentIndex + 1) !== 'm')) { + this._currentIndex++; - // Read the sign of the exponent. - if (this._string.charAt(this._currentIndex) === '+') { - this._currentIndex++; - } else if (this._string.charAt(this._currentIndex) === '-') { - this._currentIndex++; - expsign = -1; - } + // Read the sign of the exponent. + if (this._string.charAt(this._currentIndex) === '+') { + this._currentIndex++; + } else if (this._string.charAt(this._currentIndex) === '-') { + this._currentIndex++; + expsign = -1; + } - // There must be an exponent. - if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') { - return undefined; - } + // There must be an exponent. + if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') { + return undefined; + } - while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { - exponent *= 10; - exponent += (this._string.charAt(this._currentIndex) - '0'); - this._currentIndex++; - } - } + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { + exponent *= 10; + exponent += (this._string.charAt(this._currentIndex) - '0'); + this._currentIndex++; + } + } - var number = integer + decimal; - number *= sign; + var number = integer + decimal; + number *= sign; - if (exponent) { - number *= Math.pow(10, expsign * exponent); - } + if (exponent) { + number *= Math.pow(10, expsign * exponent); + } - if (startIndex === this._currentIndex) { - return undefined; - } + if (startIndex === this._currentIndex) { + return undefined; + } - this._skipOptionalSpacesOrDelimiter(); + this._skipOptionalSpacesOrDelimiter(); - return number; - }; + return number; + }; - Source.prototype._parseArcFlag = function () { - if (this._currentIndex >= this._endIndex) { - return undefined; - } - var flag = false; - var flagChar = this._string.charAt(this._currentIndex++); - if (flagChar === '0') { - flag = false; - } else if (flagChar === '1') { - flag = true; - } else { - return undefined; - } + Source.prototype._parseArcFlag = function () { + if (this._currentIndex >= this._endIndex) { + return undefined; + } + var flag = false; + var flagChar = this._string.charAt(this._currentIndex++); + if (flagChar === '0') { + flag = false; + } else if (flagChar === '1') { + flag = true; + } else { + return undefined; + } - this._skipOptionalSpacesOrDelimiter(); - return flag; - }; + this._skipOptionalSpacesOrDelimiter(); + return flag; + }; - Source.prototype.parseSegment = function () { - var lookahead = this._string[this._currentIndex]; - var command = this._pathSegTypeFromChar(lookahead); - if (command === window.SVGPathSeg.PATHSEG_UNKNOWN) { - // Possibly an implicit command. Not allowed if this is the first command. - if (this._previousCommand === window.SVGPathSeg.PATHSEG_UNKNOWN) { - return null; - } - command = this._nextCommandHelper(lookahead, this._previousCommand); - if (command === window.SVGPathSeg.PATHSEG_UNKNOWN) { - return null; - } - } else { - this._currentIndex++; - } + Source.prototype.parseSegment = function () { + var lookahead = this._string[this._currentIndex]; + var command = this._pathSegTypeFromChar(lookahead); + if (command === window.SVGPathSeg.PATHSEG_UNKNOWN) { + // Possibly an implicit command. Not allowed if this is the first command. + if (this._previousCommand === window.SVGPathSeg.PATHSEG_UNKNOWN) { + return null; + } + command = this._nextCommandHelper(lookahead, this._previousCommand); + if (command === window.SVGPathSeg.PATHSEG_UNKNOWN) { + return null; + } + } else { + this._currentIndex++; + } - this._previousCommand = command; + this._previousCommand = command; - switch (command) { - case window.SVGPathSeg.PATHSEG_MOVETO_REL: - return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); - case window.SVGPathSeg.PATHSEG_MOVETO_ABS: - return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); - case window.SVGPathSeg.PATHSEG_LINETO_REL: - return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); - case window.SVGPathSeg.PATHSEG_LINETO_ABS: - return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); - case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: - return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber()); - case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: - return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber()); - case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: - return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber()); - case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: - return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber()); - case window.SVGPathSeg.PATHSEG_CLOSEPATH: - this._skipOptionalSpaces(); - return new window.SVGPathSegClosePath(owningPathSegList); - case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); - case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); - case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: - var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2); - case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: - var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2); - case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1); - case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1); - case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: - return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber()); - case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: - return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); - case window.SVGPathSeg.PATHSEG_ARC_REL: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}; - return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); - case window.SVGPathSeg.PATHSEG_ARC_ABS: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}; - return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); - default: - throw new Error('Unknown path seg type.'); - } - }; + switch (command) { + case window.SVGPathSeg.PATHSEG_MOVETO_REL: + return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_MOVETO_ABS: + return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_REL: + return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_ABS: + return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: + return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: + return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: + return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: + return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber()); + case window.SVGPathSeg.PATHSEG_CLOSEPATH: + this._skipOptionalSpaces(); + return new window.SVGPathSegClosePath(owningPathSegList); + case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); + case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); + case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2); + case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2); + case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1); + case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; + return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1); + case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_ARC_REL: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}; + return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); + case window.SVGPathSeg.PATHSEG_ARC_ABS: + var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}; + return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); + default: + throw new Error('Unknown path seg type.'); + } + }; - var builder = new Builder(); - var source = new Source(string); + var builder = new Builder(); + var source = new Source(string); - if (!source.initialCommandIsMoveTo()) { - return []; - } - while (source.hasMoreData()) { - var pathSeg = source.parseSegment(); - if (!pathSeg) { - return []; - } - builder.appendSegment(pathSeg); - } + if (!source.initialCommandIsMoveTo()) { + return []; + } + while (source.hasMoreData()) { + var pathSeg = source.parseSegment(); + if (!pathSeg) { + return []; + } + builder.appendSegment(pathSeg); + } - return builder.pathSegList; - }; + return builder.pathSegList; + }; } }()); diff --git a/editor/recalculate.js b/editor/recalculate.js index 0065bdb7..3e37eacc 100644 --- a/editor/recalculate.js +++ b/editor/recalculate.js @@ -24,7 +24,7 @@ var svgedit = svgedit || {}; // eslint-disable-line no-use-before-define (function () { if (!svgedit.recalculate) { - svgedit.recalculate = {}; + svgedit.recalculate = {}; } var NS = svgedit.NS; @@ -32,7 +32,7 @@ var context_; // Function: svgedit.recalculate.init svgedit.recalculate.init = function (editorContext) { - context_ = editorContext; + context_ = editorContext; }; // Function: svgedit.recalculate.updateClipPath @@ -43,15 +43,15 @@ svgedit.recalculate.init = function (editorContext) { // tx - The translation's x value // ty - The translation's y value svgedit.recalculate.updateClipPath = function (attr, tx, ty) { - var path = getRefElem(attr).firstChild; - var cpXform = svgedit.transformlist.getTransformList(path); - var newxlate = context_.getSVGRoot().createSVGTransform(); - newxlate.setTranslate(tx, ty); + var path = getRefElem(attr).firstChild; + var cpXform = svgedit.transformlist.getTransformList(path); + var newxlate = context_.getSVGRoot().createSVGTransform(); + newxlate.setTranslate(tx, ty); - cpXform.appendItem(newxlate); + cpXform.appendItem(newxlate); - // Update clipPath's dimensions - svgedit.recalculate.recalculateDimensions(path); + // Update clipPath's dimensions + svgedit.recalculate.recalculateDimensions(path); }; // Function: svgedit.recalculate.recalculateDimensions @@ -63,748 +63,748 @@ svgedit.recalculate.updateClipPath = function (attr, tx, ty) { // Returns: // Undo command object with the resulting change svgedit.recalculate.recalculateDimensions = function (selected) { - if (selected == null) { return null; } + if (selected == null) { return null; } - // Firefox Issue - 1081 - if (selected.nodeName === 'svg' && navigator.userAgent.indexOf('Firefox/20') >= 0) { - return null; - } + // Firefox Issue - 1081 + if (selected.nodeName === 'svg' && navigator.userAgent.indexOf('Firefox/20') >= 0) { + return null; + } - var svgroot = context_.getSVGRoot(); - var tlist = svgedit.transformlist.getTransformList(selected); - var k; - // remove any unnecessary transforms - if (tlist && tlist.numberOfItems > 0) { - k = tlist.numberOfItems; - var noi = k; - while (k--) { - var xform = tlist.getItem(k); - if (xform.type === 0) { - tlist.removeItem(k); - // remove identity matrices - } else if (xform.type === 1) { - if (svgedit.math.isIdentity(xform.matrix)) { - if (noi === 1) { - // Overcome Chrome bug (though only when noi is 1) with - // `removeItem` preventing `removeAttribute` from - // subsequently working - // See https://bugs.chromium.org/p/chromium/issues/detail?id=843901 - selected.removeAttribute('transform'); - return null; - } - tlist.removeItem(k); - } - // remove zero-degree rotations - } else if (xform.type === 4) { - if (xform.angle === 0) { - tlist.removeItem(k); - } - } - } - // End here if all it has is a rotation - if (tlist.numberOfItems === 1 && - svgedit.utilities.getRotationAngle(selected)) { return null; } - } + var svgroot = context_.getSVGRoot(); + var tlist = svgedit.transformlist.getTransformList(selected); + var k; + // remove any unnecessary transforms + if (tlist && tlist.numberOfItems > 0) { + k = tlist.numberOfItems; + var noi = k; + while (k--) { + var xform = tlist.getItem(k); + if (xform.type === 0) { + tlist.removeItem(k); + // remove identity matrices + } else if (xform.type === 1) { + if (svgedit.math.isIdentity(xform.matrix)) { + if (noi === 1) { + // Overcome Chrome bug (though only when noi is 1) with + // `removeItem` preventing `removeAttribute` from + // subsequently working + // See https://bugs.chromium.org/p/chromium/issues/detail?id=843901 + selected.removeAttribute('transform'); + return null; + } + tlist.removeItem(k); + } + // remove zero-degree rotations + } else if (xform.type === 4) { + if (xform.angle === 0) { + tlist.removeItem(k); + } + } + } + // End here if all it has is a rotation + if (tlist.numberOfItems === 1 && + svgedit.utilities.getRotationAngle(selected)) { return null; } + } - // if this element had no transforms, we are done - if (!tlist || tlist.numberOfItems === 0) { - // Chrome apparently had a bug that requires clearing the attribute first. - selected.setAttribute('transform', ''); - // However, this still next line currently doesn't work at all in Chrome - selected.removeAttribute('transform'); - // selected.transform.baseVal.clear(); // Didn't help for Chrome bug - return null; - } + // if this element had no transforms, we are done + if (!tlist || tlist.numberOfItems === 0) { + // Chrome apparently had a bug that requires clearing the attribute first. + selected.setAttribute('transform', ''); + // However, this still next line currently doesn't work at all in Chrome + selected.removeAttribute('transform'); + // selected.transform.baseVal.clear(); // Didn't help for Chrome bug + return null; + } - // TODO: Make this work for more than 2 - if (tlist) { - k = tlist.numberOfItems; - var mxs = []; - while (k--) { - var xform = tlist.getItem(k); - if (xform.type === 1) { - mxs.push([xform.matrix, k]); - } else if (mxs.length) { - mxs = []; - } - } - if (mxs.length === 2) { - var mNew = svgroot.createSVGTransformFromMatrix(svgedit.math.matrixMultiply(mxs[1][0], mxs[0][0])); - tlist.removeItem(mxs[0][1]); - tlist.removeItem(mxs[1][1]); - tlist.insertItemBefore(mNew, mxs[1][1]); - } + // TODO: Make this work for more than 2 + if (tlist) { + k = tlist.numberOfItems; + var mxs = []; + while (k--) { + var xform = tlist.getItem(k); + if (xform.type === 1) { + mxs.push([xform.matrix, k]); + } else if (mxs.length) { + mxs = []; + } + } + if (mxs.length === 2) { + var mNew = svgroot.createSVGTransformFromMatrix(svgedit.math.matrixMultiply(mxs[1][0], mxs[0][0])); + tlist.removeItem(mxs[0][1]); + tlist.removeItem(mxs[1][1]); + tlist.insertItemBefore(mNew, 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(); + // 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 = svgedit.math.matrixMultiply( - tlist.getItem(k - 2).matrix, - tlist.getItem(k - 1).matrix); - mt.setMatrix(m); - tlist.removeItem(k - 2); - tlist.removeItem(k - 2); - tlist.appendItem(mt); - } - } + var m = svgedit.math.matrixMultiply( + 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] - case 'line': - case 'polyline': - case 'polygon': - case 'path': - break; - default: - if ((tlist.numberOfItems === 1 && tlist.getItem(0).type === 1) || - (tlist.numberOfItems === 2 && tlist.getItem(0).type === 1 && tlist.getItem(0).type === 4)) { - return null; - } - } + // 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] + case 'line': + case 'polyline': + case 'polygon': + case 'path': + break; + default: + if ((tlist.numberOfItems === 1 && tlist.getItem(0).type === 1) || + (tlist.numberOfItems === 2 && tlist.getItem(0).type === 1 && tlist.getItem(0).type === 4)) { + return null; + } + } - // Grouped SVG element - var gsvg = $(selected).data('gsvg'); + // Grouped SVG element + var gsvg = $(selected).data('gsvg'); - // we know we have some transforms, so set up return variable - var batchCmd = new svgedit.history.BatchCommand('Transform'); + // we know we have some transforms, so set up return variable + var batchCmd = new svgedit.history.BatchCommand('Transform'); - // store initial values that will be affected by reducing the transform list - var changes = {}, initial = null, attrs = []; - switch (selected.tagName) { - case 'line': - attrs = ['x1', 'y1', 'x2', 'y2']; - break; - case 'circle': - attrs = ['cx', 'cy', 'r']; - break; - case 'ellipse': - attrs = ['cx', 'cy', 'rx', 'ry']; - break; - case 'foreignObject': - case 'rect': - case 'image': - attrs = ['width', 'height', 'x', 'y']; - break; - case 'use': - case 'text': - case 'tspan': - attrs = ['x', 'y']; - break; - case 'polygon': - case 'polyline': - initial = {}; - initial.points = selected.getAttribute('points'); - var list = selected.points; - var len = list.numberOfItems; - changes.points = new Array(len); - var i; - for (i = 0; i < len; ++i) { - var pt = list.getItem(i); - changes.points[i] = {x: pt.x, y: pt.y}; - } - break; - case 'path': - initial = {}; - initial.d = selected.getAttribute('d'); - changes.d = selected.getAttribute('d'); - break; - } // switch on element type to get initial values + // store initial values that will be affected by reducing the transform list + var changes = {}, initial = null, attrs = []; + switch (selected.tagName) { + case 'line': + attrs = ['x1', 'y1', 'x2', 'y2']; + break; + case 'circle': + attrs = ['cx', 'cy', 'r']; + break; + case 'ellipse': + attrs = ['cx', 'cy', 'rx', 'ry']; + break; + case 'foreignObject': + case 'rect': + case 'image': + attrs = ['width', 'height', 'x', 'y']; + break; + case 'use': + case 'text': + case 'tspan': + attrs = ['x', 'y']; + break; + case 'polygon': + case 'polyline': + initial = {}; + initial.points = selected.getAttribute('points'); + var list = selected.points; + var len = list.numberOfItems; + changes.points = new Array(len); + var i; + for (i = 0; i < len; ++i) { + var pt = list.getItem(i); + changes.points[i] = {x: pt.x, y: pt.y}; + } + break; + case 'path': + initial = {}; + initial.d = selected.getAttribute('d'); + 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) { - changes[attr] = svgedit.units.convertToNum(attr, val); - }); - } else if (gsvg) { - // GSVG exception - changes = { - x: $(gsvg).attr('x') || 0, - y: $(gsvg).attr('y') || 0 - }; - } + if (attrs.length) { + changes = $(selected).attr(attrs); + $.each(changes, function (attr, val) { + changes[attr] = svgedit.units.convertToNum(attr, val); + }); + } else if (gsvg) { + // GSVG exception + changes = { + x: $(gsvg).attr('x') || 0, + y: $(gsvg).attr('y') || 0 + }; + } - // 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); - $.each(initial, function (attr, val) { - initial[attr] = svgedit.units.convertToNum(attr, val); - }); - } - // save the start transform value too - initial.transform = context_.getStartTransform() || ''; + // 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); + $.each(initial, function (attr, val) { + initial[attr] = svgedit.units.convertToNum(attr, val); + }); + } + // save the start transform value too + initial.transform = context_.getStartTransform() || ''; - // 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), - oldcenter = {x: box.x + box.width / 2, y: box.y + box.height / 2}, - newcenter = svgedit.math.transformPoint(box.x + box.width / 2, - box.y + box.height / 2, - svgedit.math.transformListToTransform(tlist).matrix), - m = svgroot.createSVGMatrix(); + // 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), + oldcenter = {x: box.x + box.width / 2, y: box.y + box.height / 2}, + newcenter = svgedit.math.transformPoint(box.x + box.width / 2, + box.y + box.height / 2, + svgedit.math.transformListToTransform(tlist).matrix), + m = svgroot.createSVGMatrix(); - // temporarily strip off the rotate and save the old center - var gangle = svgedit.utilities.getRotationAngle(selected); - if (gangle) { - var a = gangle * Math.PI / 180; - if (Math.abs(a) > (1.0e-10)) { - var s = Math.sin(a) / (1 - Math.cos(a)); - } else { - // FIXME: This blows up if the angle is exactly 0! - var s = 2 / a; - } - var i; - for (i = 0; i < tlist.numberOfItems; ++i) { - var xform = tlist.getItem(i); - if (xform.type === 4) { - // extract old center through mystical arts - var rm = xform.matrix; - oldcenter.y = (s * rm.e + rm.f) / 2; - oldcenter.x = (rm.e - s * rm.f) / 2; - tlist.removeItem(i); - break; - } - } - } - var tx = 0, ty = 0, - operation = 0, - N = tlist.numberOfItems; + // temporarily strip off the rotate and save the old center + var gangle = svgedit.utilities.getRotationAngle(selected); + if (gangle) { + var a = gangle * Math.PI / 180; + if (Math.abs(a) > (1.0e-10)) { + var s = Math.sin(a) / (1 - Math.cos(a)); + } else { + // FIXME: This blows up if the angle is exactly 0! + var s = 2 / a; + } + var i; + for (i = 0; i < tlist.numberOfItems; ++i) { + var xform = tlist.getItem(i); + if (xform.type === 4) { + // extract old center through mystical arts + var rm = xform.matrix; + oldcenter.y = (s * rm.e + rm.f) / 2; + oldcenter.x = (rm.e - s * rm.f) / 2; + tlist.removeItem(i); + break; + } + } + } + var tx = 0, ty = 0, + operation = 0, + N = tlist.numberOfItems; - if (N) { - var firstM = tlist.getItem(0).matrix; - } + if (N) { + var firstM = tlist.getItem(0).matrix; + } - // 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) { - operation = 3; // scale + // 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) { + 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; + // 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--) { - var child = children.item(c); - tx = 0; - ty = 0; - if (child.nodeType === 1) { - var childTlist = svgedit.transformlist.getTransformList(child); + var children = selected.childNodes; + var c = children.length; + while (c--) { + var child = children.item(c); + tx = 0; + ty = 0; + if (child.nodeType === 1) { + var childTlist = svgedit.transformlist.getTransformList(child); - // some children might not have a transform (, , etc) - if (!childTlist) { continue; } + // some children might not have a transform (, , etc) + if (!childTlist) { continue; } - var m = svgedit.math.transformListToTransform(childTlist).matrix; + var m = svgedit.math.transformListToTransform(childTlist).matrix; - // Convert a matrix to a scale if applicable - // if (svgedit.math.hasMatrixTransform(childTlist) && childTlist.numberOfItems == 1) { - // if (m.b==0 && m.c==0 && m.e==0 && m.f==0) { - // childTlist.removeItem(0); - // var translateOrigin = svgroot.createSVGTransform(), - // scale = svgroot.createSVGTransform(), - // translateBack = svgroot.createSVGTransform(); - // translateOrigin.setTranslate(0, 0); - // scale.setScale(m.a, m.d); - // translateBack.setTranslate(0, 0); - // childTlist.appendItem(translateBack); - // childTlist.appendItem(scale); - // childTlist.appendItem(translateOrigin); - // } - // } + // Convert a matrix to a scale if applicable + // if (svgedit.math.hasMatrixTransform(childTlist) && childTlist.numberOfItems == 1) { + // if (m.b==0 && m.c==0 && m.e==0 && m.f==0) { + // childTlist.removeItem(0); + // var translateOrigin = svgroot.createSVGTransform(), + // scale = svgroot.createSVGTransform(), + // translateBack = svgroot.createSVGTransform(); + // translateOrigin.setTranslate(0, 0); + // scale.setScale(m.a, m.d); + // translateBack.setTranslate(0, 0); + // childTlist.appendItem(translateBack); + // childTlist.appendItem(scale); + // childTlist.appendItem(translateOrigin); + // } + // } - var angle = svgedit.utilities.getRotationAngle(child); - var oldStartTransform = context_.getStartTransform(); - var childxforms = []; - context_.setStartTransform(child.getAttribute('transform')); - if (angle || svgedit.math.hasMatrixTransform(childTlist)) { - var e2t = svgroot.createSVGTransform(); - e2t.setMatrix(svgedit.math.matrixMultiply(tm, sm, tmn, m)); - childTlist.clear(); - childTlist.appendItem(e2t); - childxforms.push(e2t); - // if not rotated or skewed, push the [T][S][-T] down to the child - } else { - // update the transform list with translate,scale,translate + var angle = svgedit.utilities.getRotationAngle(child); + var oldStartTransform = context_.getStartTransform(); + var childxforms = []; + context_.setStartTransform(child.getAttribute('transform')); + if (angle || svgedit.math.hasMatrixTransform(childTlist)) { + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(svgedit.math.matrixMultiply(tm, sm, tmn, m)); + childTlist.clear(); + childTlist.appendItem(e2t); + childxforms.push(e2t); + // 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] + // 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] - var t2n = svgedit.math.matrixMultiply(m.inverse(), tmn, m); - // [T2] is always negative translation of [-T2] - var t2 = svgroot.createSVGMatrix(); - t2.e = -t2n.e; - t2.f = -t2n.f; + // (only bringing [-T] to the right of [M]) + // [T][S][-T][M] = [T][S][M][-T2] + // [-T2] = [M_inv][-T][M] + var t2n = svgedit.math.matrixMultiply(m.inverse(), tmn, m); + // [T2] is always negative translation of [-T2] + 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 = svgedit.math.matrixMultiply(t2.inverse(), m.inverse(), tm, sm, tmn, m, t2n.inverse()); + // [T][S][-T][M] = [M][T2][S2][-T2] + // [S2] = [T2_inv][M_inv][T][S][-T][M][-T2_inv] + var s2 = svgedit.math.matrixMultiply(t2.inverse(), m.inverse(), tm, sm, tmn, m, t2n.inverse()); - var translateOrigin = svgroot.createSVGTransform(), - scale = svgroot.createSVGTransform(), - translateBack = svgroot.createSVGTransform(); - translateOrigin.setTranslate(t2n.e, t2n.f); - scale.setScale(s2.a, s2.d); - translateBack.setTranslate(t2.e, t2.f); - childTlist.appendItem(translateBack); - childTlist.appendItem(scale); - childTlist.appendItem(translateOrigin); - childxforms.push(translateBack); - childxforms.push(scale); - childxforms.push(translateOrigin); - // logMatrix(translateBack.matrix); - // logMatrix(scale.matrix); - } // not rotated - batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); - // TODO: If any 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(NS.SVG, 'use'); - // var href = '#' + child.id; - // var u = uses.length; - // while (u--) { - // var useElem = uses.item(u); - // if (href == svgedit.utilities.getHref(useElem)) { - // var usexlate = svgroot.createSVGTransform(); - // usexlate.setTranslate(-tx,-ty); - // svgedit.transformlist.getTransformList(useElem).insertItemBefore(usexlate,0); - // batchCmd.addSubCommand( svgedit.recalculate.recalculateDimensions(useElem) ); - // } - // } - context_.setStartTransform(oldStartTransform); - } // element - } // for each child - // Remove these transforms from group - tlist.removeItem(N - 1); - tlist.removeItem(N - 2); - tlist.removeItem(N - 3); - } else if (N >= 3 && tlist.getItem(N - 1).type === 1) { - operation = 3; // scale - m = svgedit.math.transformListToTransform(tlist).matrix; - var e2t = svgroot.createSVGTransform(); - e2t.setMatrix(m); - tlist.clear(); - tlist.appendItem(e2t); - // 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) { - operation = 2; // translate - var T_M = svgedit.math.transformListToTransform(tlist).matrix; - tlist.removeItem(0); - var mInv = svgedit.math.transformListToTransform(tlist).matrix.inverse(); - var M2 = svgedit.math.matrixMultiply(mInv, T_M); + var translateOrigin = svgroot.createSVGTransform(), + scale = svgroot.createSVGTransform(), + translateBack = svgroot.createSVGTransform(); + translateOrigin.setTranslate(t2n.e, t2n.f); + scale.setScale(s2.a, s2.d); + translateBack.setTranslate(t2.e, t2.f); + childTlist.appendItem(translateBack); + childTlist.appendItem(scale); + childTlist.appendItem(translateOrigin); + childxforms.push(translateBack); + childxforms.push(scale); + childxforms.push(translateOrigin); + // logMatrix(translateBack.matrix); + // logMatrix(scale.matrix); + } // not rotated + batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); + // TODO: If any 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(NS.SVG, 'use'); + // var href = '#' + child.id; + // var u = uses.length; + // while (u--) { + // var useElem = uses.item(u); + // if (href == svgedit.utilities.getHref(useElem)) { + // var usexlate = svgroot.createSVGTransform(); + // usexlate.setTranslate(-tx,-ty); + // svgedit.transformlist.getTransformList(useElem).insertItemBefore(usexlate,0); + // batchCmd.addSubCommand( svgedit.recalculate.recalculateDimensions(useElem) ); + // } + // } + context_.setStartTransform(oldStartTransform); + } // element + } // for each child + // Remove these transforms from group + tlist.removeItem(N - 1); + tlist.removeItem(N - 2); + tlist.removeItem(N - 3); + } else if (N >= 3 && tlist.getItem(N - 1).type === 1) { + operation = 3; // scale + m = svgedit.math.transformListToTransform(tlist).matrix; + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(m); + tlist.clear(); + tlist.appendItem(e2t); + // 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) { + operation = 2; // translate + var T_M = svgedit.math.transformListToTransform(tlist).matrix; + tlist.removeItem(0); + var mInv = svgedit.math.transformListToTransform(tlist).matrix.inverse(); + var M2 = svgedit.math.matrixMultiply(mInv, T_M); - tx = M2.e; - ty = M2.f; + tx = M2.e; + ty = M2.f; - if (tx !== 0 || ty !== 0) { - // we pass the translates down to the individual children - var children = selected.childNodes; - var c = children.length; + if (tx !== 0 || ty !== 0) { + // we pass the translates down to the individual children + var children = selected.childNodes; + var c = children.length; - var clipPathsDone = []; + var clipPathsDone = []; - while (c--) { - var child = children.item(c); - if (child.nodeType === 1) { - // Check if child has clip-path - if (child.getAttribute('clip-path')) { - // tx, ty - var attr = child.getAttribute('clip-path'); - if (clipPathsDone.indexOf(attr) === -1) { - svgedit.recalculate.updateClipPath(attr, tx, ty); - clipPathsDone.push(attr); - } - } + while (c--) { + var child = children.item(c); + if (child.nodeType === 1) { + // Check if child has clip-path + if (child.getAttribute('clip-path')) { + // tx, ty + var attr = child.getAttribute('clip-path'); + if (clipPathsDone.indexOf(attr) === -1) { + svgedit.recalculate.updateClipPath(attr, tx, ty); + clipPathsDone.push(attr); + } + } - var oldStartTransform = context_.getStartTransform(); - context_.setStartTransform(child.getAttribute('transform')); + var oldStartTransform = context_.getStartTransform(); + context_.setStartTransform(child.getAttribute('transform')); - var childTlist = svgedit.transformlist.getTransformList(child); - // some children might not have a transform (, , etc) - if (childTlist) { - var newxlate = svgroot.createSVGTransform(); - newxlate.setTranslate(tx, ty); - if (childTlist.numberOfItems) { - childTlist.insertItemBefore(newxlate, 0); - } else { - childTlist.appendItem(newxlate); - } - batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); - // If any 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(NS.SVG, 'use'); - var href = '#' + child.id; - var u = uses.length; - while (u--) { - var useElem = uses.item(u); - if (href === svgedit.utilities.getHref(useElem)) { - var usexlate = svgroot.createSVGTransform(); - usexlate.setTranslate(-tx, -ty); - svgedit.transformlist.getTransformList(useElem).insertItemBefore(usexlate, 0); - batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(useElem)); - } - } - context_.setStartTransform(oldStartTransform); - } - } - } + var childTlist = svgedit.transformlist.getTransformList(child); + // some children might not have a transform (, , etc) + if (childTlist) { + var newxlate = svgroot.createSVGTransform(); + newxlate.setTranslate(tx, ty); + if (childTlist.numberOfItems) { + childTlist.insertItemBefore(newxlate, 0); + } else { + childTlist.appendItem(newxlate); + } + batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); + // If any 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(NS.SVG, 'use'); + var href = '#' + child.id; + var u = uses.length; + while (u--) { + var useElem = uses.item(u); + if (href === svgedit.utilities.getHref(useElem)) { + var usexlate = svgroot.createSVGTransform(); + usexlate.setTranslate(-tx, -ty); + svgedit.transformlist.getTransformList(useElem).insertItemBefore(usexlate, 0); + batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(useElem)); + } + } + context_.setStartTransform(oldStartTransform); + } + } + } - clipPathsDone = []; - context_.setStartTransform(oldStartTransform); - } - // else, a matrix imposition from a parent group - // keep pushing it down to the children - } else if (N === 1 && tlist.getItem(0).type === 1 && !gangle) { - operation = 1; - var m = tlist.getItem(0).matrix, - children = selected.childNodes, - c = children.length; - while (c--) { - var child = children.item(c); - if (child.nodeType === 1) { - var oldStartTransform = context_.getStartTransform(); - context_.setStartTransform(child.getAttribute('transform')); - var childTlist = svgedit.transformlist.getTransformList(child); + clipPathsDone = []; + context_.setStartTransform(oldStartTransform); + } + // else, a matrix imposition from a parent group + // keep pushing it down to the children + } else if (N === 1 && tlist.getItem(0).type === 1 && !gangle) { + operation = 1; + var m = tlist.getItem(0).matrix, + children = selected.childNodes, + c = children.length; + while (c--) { + var child = children.item(c); + if (child.nodeType === 1) { + var oldStartTransform = context_.getStartTransform(); + context_.setStartTransform(child.getAttribute('transform')); + var childTlist = svgedit.transformlist.getTransformList(child); - if (!childTlist) { continue; } + if (!childTlist) { continue; } - var em = svgedit.math.matrixMultiply(m, svgedit.math.transformListToTransform(childTlist).matrix); - var e2m = svgroot.createSVGTransform(); - e2m.setMatrix(em); - childTlist.clear(); - childTlist.appendItem(e2m, 0); + var em = svgedit.math.matrixMultiply(m, svgedit.math.transformListToTransform(childTlist).matrix); + var e2m = svgroot.createSVGTransform(); + e2m.setMatrix(em); + childTlist.clear(); + childTlist.appendItem(e2m, 0); - batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); - context_.setStartTransform(oldStartTransform); + batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); + context_.setStartTransform(oldStartTransform); - // Convert stroke - // TODO: Find out if this should actually happen somewhere else - var sw = child.getAttribute('stroke-width'); - if (child.getAttribute('stroke') !== 'none' && !isNaN(sw)) { - var avg = (Math.abs(em.a) + Math.abs(em.d)) / 2; - child.setAttribute('stroke-width', sw * avg); - } - } - } - tlist.clear(); - // else it was just a rotate - } else { - if (gangle) { - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(gangle, newcenter.x, newcenter.y); - if (tlist.numberOfItems) { - tlist.insertItemBefore(newRot, 0); - } else { - tlist.appendItem(newRot); - } - } - if (tlist.numberOfItems === 0) { - selected.removeAttribute('transform'); - } - return null; - } + // Convert stroke + // TODO: Find out if this should actually happen somewhere else + var sw = child.getAttribute('stroke-width'); + if (child.getAttribute('stroke') !== 'none' && !isNaN(sw)) { + var avg = (Math.abs(em.a) + Math.abs(em.d)) / 2; + child.setAttribute('stroke-width', sw * avg); + } + } + } + tlist.clear(); + // else it was just a rotate + } else { + if (gangle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(gangle, newcenter.x, newcenter.y); + if (tlist.numberOfItems) { + tlist.insertItemBefore(newRot, 0); + } else { + tlist.appendItem(newRot); + } + } + if (tlist.numberOfItems === 0) { + selected.removeAttribute('transform'); + } + return null; + } - // if it was a translate, put back the rotate at the new center - if (operation === 2) { - if (gangle) { - newcenter = { - x: oldcenter.x + firstM.e, - y: oldcenter.y + firstM.f - }; + // if it was a translate, put back the rotate at the new center + if (operation === 2) { + if (gangle) { + newcenter = { + x: oldcenter.x + firstM.e, + y: oldcenter.y + firstM.f + }; - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(gangle, newcenter.x, newcenter.y); - if (tlist.numberOfItems) { - tlist.insertItemBefore(newRot, 0); - } else { - tlist.appendItem(newRot); - } - } - // if it was a resize - } else if (operation === 3) { - var m = svgedit.math.transformListToTransform(tlist).matrix; - var roldt = svgroot.createSVGTransform(); - roldt.setRotate(gangle, oldcenter.x, oldcenter.y); - var rold = roldt.matrix; - var rnew = svgroot.createSVGTransform(); - rnew.setRotate(gangle, newcenter.x, newcenter.y); - var rnewInv = rnew.matrix.inverse(), - mInv = m.inverse(), - extrat = svgedit.math.matrixMultiply(mInv, rnewInv, rold, m); + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(gangle, newcenter.x, newcenter.y); + if (tlist.numberOfItems) { + tlist.insertItemBefore(newRot, 0); + } else { + tlist.appendItem(newRot); + } + } + // if it was a resize + } else if (operation === 3) { + var m = svgedit.math.transformListToTransform(tlist).matrix; + var roldt = svgroot.createSVGTransform(); + roldt.setRotate(gangle, oldcenter.x, oldcenter.y); + var rold = roldt.matrix; + var rnew = svgroot.createSVGTransform(); + rnew.setRotate(gangle, newcenter.x, newcenter.y); + var rnewInv = rnew.matrix.inverse(), + mInv = m.inverse(), + extrat = svgedit.math.matrixMultiply(mInv, rnewInv, rold, m); - tx = extrat.e; - ty = extrat.f; + tx = extrat.e; + ty = extrat.f; - if (tx !== 0 || ty !== 0) { - // now push this transform down to the children - // we pass the translates down to the individual children - var children = selected.childNodes; - var c = children.length; - while (c--) { - var child = children.item(c); - if (child.nodeType === 1) { - var oldStartTransform = context_.getStartTransform(); - context_.setStartTransform(child.getAttribute('transform')); - var childTlist = svgedit.transformlist.getTransformList(child); - var newxlate = svgroot.createSVGTransform(); - newxlate.setTranslate(tx, ty); - if (childTlist.numberOfItems) { - childTlist.insertItemBefore(newxlate, 0); - } else { - childTlist.appendItem(newxlate); - } + if (tx !== 0 || ty !== 0) { + // now push this transform down to the children + // we pass the translates down to the individual children + var children = selected.childNodes; + var c = children.length; + while (c--) { + var child = children.item(c); + if (child.nodeType === 1) { + var oldStartTransform = context_.getStartTransform(); + context_.setStartTransform(child.getAttribute('transform')); + var childTlist = svgedit.transformlist.getTransformList(child); + var newxlate = svgroot.createSVGTransform(); + newxlate.setTranslate(tx, ty); + if (childTlist.numberOfItems) { + childTlist.insertItemBefore(newxlate, 0); + } else { + childTlist.appendItem(newxlate); + } - batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); - context_.setStartTransform(oldStartTransform); - } - } - } + batchCmd.addSubCommand(svgedit.recalculate.recalculateDimensions(child)); + context_.setStartTransform(oldStartTransform); + } + } + } - if (gangle) { - if (tlist.numberOfItems) { - tlist.insertItemBefore(rnew, 0); - } else { - tlist.appendItem(rnew); - } - } - } - // else, it's a non-group - } else { - // FIXME: box might be null for some elements ( etc), need to handle this - var box = svgedit.utilities.getBBox(selected); + if (gangle) { + if (tlist.numberOfItems) { + tlist.insertItemBefore(rnew, 0); + } else { + tlist.appendItem(rnew); + } + } + } + // else, it's a non-group + } else { + // FIXME: box might be null for some elements ( etc), need to handle this + var box = svgedit.utilities.getBBox(selected); - // Paths (and possbly other shapes) will have no BBox while still in , - // 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 + // Paths (and possbly other shapes) will have no BBox while still in , + // 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; + if (!box && selected.tagName !== 'path') return null; - var m = svgroot.createSVGMatrix(), - // temporarily strip off the rotate and save the old center - angle = svgedit.utilities.getRotationAngle(selected); - if (angle) { - var oldcenter = {x: box.x + box.width / 2, y: box.y + box.height / 2}, - newcenter = svgedit.math.transformPoint(box.x + box.width / 2, box.y + box.height / 2, - svgedit.math.transformListToTransform(tlist).matrix); + var m = svgroot.createSVGMatrix(), + // temporarily strip off the rotate and save the old center + angle = svgedit.utilities.getRotationAngle(selected); + if (angle) { + var oldcenter = {x: box.x + box.width / 2, y: box.y + box.height / 2}, + newcenter = svgedit.math.transformPoint(box.x + box.width / 2, box.y + box.height / 2, + svgedit.math.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)); - } else { - // FIXME: This blows up if the angle is exactly 0! - var s = 2 / a; - } - for (var i = 0; i < tlist.numberOfItems; ++i) { - var xform = tlist.getItem(i); - if (xform.type === 4) { - // extract old center through mystical arts - var rm = xform.matrix; - oldcenter.y = (s * rm.e + rm.f) / 2; - oldcenter.x = (rm.e - s * rm.f) / 2; - tlist.removeItem(i); - break; - } - } - } + var a = angle * Math.PI / 180; + if (Math.abs(a) > (1.0e-10)) { + var s = Math.sin(a) / (1 - Math.cos(a)); + } else { + // FIXME: This blows up if the angle is exactly 0! + var s = 2 / a; + } + for (var i = 0; i < tlist.numberOfItems; ++i) { + var xform = tlist.getItem(i); + if (xform.type === 4) { + // extract old center through mystical arts + var rm = xform.matrix; + oldcenter.y = (s * rm.e + rm.f) / 2; + oldcenter.x = (rm.e - s * rm.f) / 2; + tlist.removeItem(i); + break; + } + } + } - // 2 = translate, 3 = scale, 4 = rotate, 1 = matrix imposition - var operation = 0; - var N = tlist.numberOfItems; + // 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 - if (!svgedit.browser.isWebkit()) { - var fill = selected.getAttribute('fill'); - if (fill && fill.indexOf('url(') === 0) { - var paint = getRefElem(fill); - var type = 'pattern'; - if (paint.tagName !== type) type = 'gradient'; - var attrVal = paint.getAttribute(type + 'Units'); - if (attrVal === 'userSpaceOnUse') { - // Update the userSpaceOnUse element - m = svgedit.math.transformListToTransform(tlist).matrix; - var gtlist = svgedit.transformlist.getTransformList(paint); - var gmatrix = svgedit.math.transformListToTransform(gtlist).matrix; - m = svgedit.math.matrixMultiply(m, gmatrix); - var mStr = 'matrix(' + [m.a, m.b, m.c, m.d, m.e, m.f].join(',') + ')'; - paint.setAttribute(type + 'Transform', mStr); - } - } - } + // 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 + if (!svgedit.browser.isWebkit()) { + var fill = selected.getAttribute('fill'); + if (fill && fill.indexOf('url(') === 0) { + var paint = getRefElem(fill); + var type = 'pattern'; + if (paint.tagName !== type) type = 'gradient'; + var attrVal = paint.getAttribute(type + 'Units'); + if (attrVal === 'userSpaceOnUse') { + // Update the userSpaceOnUse element + m = svgedit.math.transformListToTransform(tlist).matrix; + var gtlist = svgedit.transformlist.getTransformList(paint); + var gmatrix = svgedit.math.transformListToTransform(gtlist).matrix; + m = svgedit.math.matrixMultiply(m, gmatrix); + var mStr = 'matrix(' + [m.a, m.b, m.c, m.d, m.e, m.f].join(',') + ')'; + paint.setAttribute(type + 'Transform', mStr); + } + } + } - // 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 with a given [T][S][T] would convert to a matrix. - // Is that bad? - // && selected.nodeName != 'use' - operation = 3; // scale - m = svgedit.math.transformListToTransform(tlist, N - 3, N - 1).matrix; - tlist.removeItem(N - 1); - tlist.removeItem(N - 2); - tlist.removeItem(N - 3); - // if we had [T][S][-T][M], then this was a skewed element being resized - // Thus, we simply combine it all into one matrix - } else if (N === 4 && tlist.getItem(N - 1).type === 1) { - operation = 3; // scale - m = svgedit.math.transformListToTransform(tlist).matrix; - var e2t = svgroot.createSVGTransform(); - e2t.setMatrix(m); - tlist.clear(); - 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 [T1][M] we want to transform this into [M][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) { - operation = 2; // translate - var oldxlate = tlist.getItem(0).matrix, - meq = svgedit.math.transformListToTransform(tlist, 1).matrix, - meqInv = meq.inverse(); - m = svgedit.math.matrixMultiply(meqInv, oldxlate, meq); - tlist.removeItem(0); - // else if this child now has a matrix imposition (from a parent group) - // we might be able to simplify - } else if (N === 1 && tlist.getItem(0).type === 1 && !angle) { - // Remap all point-based elements - m = svgedit.math.transformListToTransform(tlist).matrix; - switch (selected.tagName) { - case 'line': - changes = $(selected).attr(['x1', 'y1', 'x2', 'y2']); - // Fallthrough - case 'polyline': - case 'polygon': - changes.points = selected.getAttribute('points'); - if (changes.points) { - var list = selected.points; - var len = list.numberOfItems; - changes.points = new Array(len); - for (var i = 0; i < len; ++i) { - var pt = list.getItem(i); - changes.points[i] = {x: pt.x, y: pt.y}; - } - } - // Fallthrough - case 'path': - changes.d = selected.getAttribute('d'); - operation = 1; - tlist.clear(); - break; - default: - break; - } - // if it was a rotation, put the rotate back and return without a command - // (this function has zero work to do for a rotate()) - } else { - operation = 4; // rotation - if (angle) { - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle, newcenter.x, newcenter.y); + // 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 with a given [T][S][T] would convert to a matrix. + // Is that bad? + // && selected.nodeName != 'use' + operation = 3; // scale + m = svgedit.math.transformListToTransform(tlist, N - 3, N - 1).matrix; + tlist.removeItem(N - 1); + tlist.removeItem(N - 2); + tlist.removeItem(N - 3); + // if we had [T][S][-T][M], then this was a skewed element being resized + // Thus, we simply combine it all into one matrix + } else if (N === 4 && tlist.getItem(N - 1).type === 1) { + operation = 3; // scale + m = svgedit.math.transformListToTransform(tlist).matrix; + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(m); + tlist.clear(); + 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 [T1][M] we want to transform this into [M][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) { + operation = 2; // translate + var oldxlate = tlist.getItem(0).matrix, + meq = svgedit.math.transformListToTransform(tlist, 1).matrix, + meqInv = meq.inverse(); + m = svgedit.math.matrixMultiply(meqInv, oldxlate, meq); + tlist.removeItem(0); + // else if this child now has a matrix imposition (from a parent group) + // we might be able to simplify + } else if (N === 1 && tlist.getItem(0).type === 1 && !angle) { + // Remap all point-based elements + m = svgedit.math.transformListToTransform(tlist).matrix; + switch (selected.tagName) { + case 'line': + changes = $(selected).attr(['x1', 'y1', 'x2', 'y2']); + // Fallthrough + case 'polyline': + case 'polygon': + changes.points = selected.getAttribute('points'); + if (changes.points) { + var list = selected.points; + var len = list.numberOfItems; + changes.points = new Array(len); + for (var i = 0; i < len; ++i) { + var pt = list.getItem(i); + changes.points[i] = {x: pt.x, y: pt.y}; + } + } + // Fallthrough + case 'path': + changes.d = selected.getAttribute('d'); + operation = 1; + tlist.clear(); + break; + default: + break; + } + // if it was a rotation, put the rotate back and return without a command + // (this function has zero work to do for a rotate()) + } else { + operation = 4; // rotation + if (angle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle, newcenter.x, newcenter.y); - if (tlist.numberOfItems) { - tlist.insertItemBefore(newRot, 0); - } else { - tlist.appendItem(newRot); - } - } - if (tlist.numberOfItems === 0) { - selected.removeAttribute('transform'); - } - return null; - } + if (tlist.numberOfItems) { + tlist.insertItemBefore(newRot, 0); + } else { + tlist.appendItem(newRot); + } + } + if (tlist.numberOfItems === 0) { + selected.removeAttribute('transform'); + } + 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) { - svgedit.coords.remapElement(selected, changes, m); - } // if we are remapping + // if it was a translate or resize, we need to remap the element and absorb the xform + if (operation === 1 || operation === 2 || operation === 3) { + svgedit.coords.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) { - if (!svgedit.math.hasMatrixTransform(tlist)) { - newcenter = { - x: oldcenter.x + m.e, - y: oldcenter.y + m.f - }; - } - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle, newcenter.x, newcenter.y); - if (tlist.numberOfItems) { - tlist.insertItemBefore(newRot, 0); - } else { - tlist.appendItem(newRot); - } - } - // We have special processing for tspans: Tspans are not transformable - // but they can have x,y coordinates (sigh). Thus, if this was a translate, - // on a text element, also translate any tspan children. - if (selected.tagName === 'text') { - var children = selected.childNodes; - var c = children.length; - while (c--) { - var child = children.item(c); - if (child.tagName === 'tspan') { - var tspanChanges = { - x: $(child).attr('x') || 0, - y: $(child).attr('y') || 0 - }; - svgedit.coords.remapElement(child, tspanChanges, m); - } - } - } - // [Rold][M][T][S][-T] became [Rold][M] - // we want it to be [Rnew][M][Tr] where Tr is the - // translation required to re-center it - // Therefore, [Tr] = [M_inv][Rnew_inv][Rold][M] - } else if (operation === 3 && angle) { - var m = svgedit.math.transformListToTransform(tlist).matrix; - var roldt = svgroot.createSVGTransform(); - roldt.setRotate(angle, oldcenter.x, oldcenter.y); - var rold = roldt.matrix; - var rnew = svgroot.createSVGTransform(); - rnew.setRotate(angle, newcenter.x, newcenter.y); - var rnewInv = rnew.matrix.inverse(); - var mInv = m.inverse(); - var extrat = svgedit.math.matrixMultiply(mInv, rnewInv, rold, m); + // if it was a translate, put back the rotate at the new center + if (operation === 2) { + if (angle) { + if (!svgedit.math.hasMatrixTransform(tlist)) { + newcenter = { + x: oldcenter.x + m.e, + y: oldcenter.y + m.f + }; + } + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle, newcenter.x, newcenter.y); + if (tlist.numberOfItems) { + tlist.insertItemBefore(newRot, 0); + } else { + tlist.appendItem(newRot); + } + } + // We have special processing for tspans: Tspans are not transformable + // but they can have x,y coordinates (sigh). Thus, if this was a translate, + // on a text element, also translate any tspan children. + if (selected.tagName === 'text') { + var children = selected.childNodes; + var c = children.length; + while (c--) { + var child = children.item(c); + if (child.tagName === 'tspan') { + var tspanChanges = { + x: $(child).attr('x') || 0, + y: $(child).attr('y') || 0 + }; + svgedit.coords.remapElement(child, tspanChanges, m); + } + } + } + // [Rold][M][T][S][-T] became [Rold][M] + // we want it to be [Rnew][M][Tr] where Tr is the + // translation required to re-center it + // Therefore, [Tr] = [M_inv][Rnew_inv][Rold][M] + } else if (operation === 3 && angle) { + var m = svgedit.math.transformListToTransform(tlist).matrix; + var roldt = svgroot.createSVGTransform(); + roldt.setRotate(angle, oldcenter.x, oldcenter.y); + var rold = roldt.matrix; + var rnew = svgroot.createSVGTransform(); + rnew.setRotate(angle, newcenter.x, newcenter.y); + var rnewInv = rnew.matrix.inverse(); + var mInv = m.inverse(); + var extrat = svgedit.math.matrixMultiply(mInv, rnewInv, rold, m); - svgedit.coords.remapElement(selected, changes, extrat); - if (angle) { - if (tlist.numberOfItems) { - tlist.insertItemBefore(rnew, 0); - } else { - tlist.appendItem(rnew); - } - } - } - } // a non-group + svgedit.coords.remapElement(selected, changes, extrat); + if (angle) { + if (tlist.numberOfItems) { + tlist.insertItemBefore(rnew, 0); + } else { + tlist.appendItem(rnew); + } + } + } + } // a non-group - // if the transform list has been emptied, remove it - if (tlist.numberOfItems === 0) { - selected.removeAttribute('transform'); - } + // if the transform list has been emptied, remove it + if (tlist.numberOfItems === 0) { + selected.removeAttribute('transform'); + } - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(selected, initial)); + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(selected, initial)); - return batchCmd; + return batchCmd; }; })(); diff --git a/editor/sanitize.js b/editor/sanitize.js index 8c237bc3..ac1c864c 100644 --- a/editor/sanitize.js +++ b/editor/sanitize.js @@ -19,95 +19,95 @@ 'use strict'; if (!svgedit.sanitize) { - svgedit.sanitize = {}; + svgedit.sanitize = {}; } var NS = svgedit.NS, - REVERSE_NS = svgedit.getReverseNS(); + REVERSE_NS = svgedit.getReverseNS(); // this defines which elements and attributes that we support var svgWhiteList_ = { - // SVG Elements - 'a': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'xlink:href', 'xlink:title'], - 'circle': ['class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], - 'clipPath': ['class', 'clipPathUnits', 'id'], - 'defs': [], - 'style': ['type'], - 'desc': [], - 'ellipse': ['class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], - 'feGaussianBlur': ['class', 'color-interpolation-filters', 'id', 'requiredFeatures', 'stdDeviation'], - 'filter': ['class', 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'id', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y'], - 'foreignObject': ['class', 'font-size', 'height', 'id', 'opacity', 'requiredFeatures', 'style', 'transform', 'width', 'x', 'y'], - 'g': ['class', 'clip-path', 'clip-rule', 'id', 'display', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor'], - 'image': ['class', 'clip-path', 'clip-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'style', 'systemLanguage', 'transform', 'width', 'x', 'xlink:href', 'xlink:title', 'y'], - 'line': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'x1', 'x2', 'y1', 'y2'], - 'linearGradient': ['class', 'id', 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2'], - 'marker': ['id', 'class', 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox'], - 'mask': ['class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y'], - 'metadata': ['class', 'id'], - 'path': ['class', 'clip-path', 'clip-rule', 'd', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], - 'pattern': ['class', 'height', 'id', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y'], - 'polygon': ['class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'class', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], - 'polyline': ['class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], - 'radialGradient': ['class', 'cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'id', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href'], - 'rect': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'width', 'x', 'y'], - 'stop': ['class', 'id', 'offset', 'requiredFeatures', 'stop-color', 'stop-opacity', 'style', 'systemLanguage'], - 'svg': ['class', 'clip-path', 'clip-rule', 'filter', 'id', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'y'], - 'switch': ['class', 'id', 'requiredFeatures', 'systemLanguage'], - 'symbol': ['class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox'], - 'text': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'transform', 'x', 'xml:space', 'y'], - 'textPath': ['class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href'], - 'title': [], - 'tspan': ['class', 'clip-path', 'clip-rule', 'dx', 'dy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'textLength', 'transform', 'x', 'xml:space', 'y'], - 'use': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'transform', 'width', 'x', 'xlink:href', 'y'], + // SVG Elements + 'a': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'xlink:href', 'xlink:title'], + 'circle': ['class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'clipPath': ['class', 'clipPathUnits', 'id'], + 'defs': [], + 'style': ['type'], + 'desc': [], + 'ellipse': ['class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'feGaussianBlur': ['class', 'color-interpolation-filters', 'id', 'requiredFeatures', 'stdDeviation'], + 'filter': ['class', 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'id', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y'], + 'foreignObject': ['class', 'font-size', 'height', 'id', 'opacity', 'requiredFeatures', 'style', 'transform', 'width', 'x', 'y'], + 'g': ['class', 'clip-path', 'clip-rule', 'id', 'display', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor'], + 'image': ['class', 'clip-path', 'clip-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'style', 'systemLanguage', 'transform', 'width', 'x', 'xlink:href', 'xlink:title', 'y'], + 'line': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'x1', 'x2', 'y1', 'y2'], + 'linearGradient': ['class', 'id', 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2'], + 'marker': ['id', 'class', 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox'], + 'mask': ['class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y'], + 'metadata': ['class', 'id'], + 'path': ['class', 'clip-path', 'clip-rule', 'd', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'pattern': ['class', 'height', 'id', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y'], + 'polygon': ['class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'class', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'polyline': ['class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform'], + 'radialGradient': ['class', 'cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'id', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href'], + 'rect': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'width', 'x', 'y'], + 'stop': ['class', 'id', 'offset', 'requiredFeatures', 'stop-color', 'stop-opacity', 'style', 'systemLanguage'], + 'svg': ['class', 'clip-path', 'clip-rule', 'filter', 'id', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'y'], + 'switch': ['class', 'id', 'requiredFeatures', 'systemLanguage'], + 'symbol': ['class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox'], + 'text': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'transform', 'x', 'xml:space', 'y'], + 'textPath': ['class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href'], + 'title': [], + 'tspan': ['class', 'clip-path', 'clip-rule', 'dx', 'dy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'textLength', 'transform', 'x', 'xml:space', 'y'], + 'use': ['class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'transform', 'width', 'x', 'xlink:href', 'y'], - // MathML Elements - 'annotation': ['encoding'], - 'annotation-xml': ['encoding'], - 'maction': ['actiontype', 'other', 'selection'], - 'math': ['class', 'id', 'display', 'xmlns'], - 'menclose': ['notation'], - 'merror': [], - 'mfrac': ['linethickness'], - 'mi': ['mathvariant'], - 'mmultiscripts': [], - 'mn': [], - 'mo': ['fence', 'lspace', 'maxsize', 'minsize', 'rspace', 'stretchy'], - 'mover': [], - 'mpadded': ['lspace', 'width', 'height', 'depth', 'voffset'], - 'mphantom': [], - 'mprescripts': [], - 'mroot': [], - 'mrow': ['xlink:href', 'xlink:type', 'xmlns:xlink'], - 'mspace': ['depth', 'height', 'width'], - 'msqrt': [], - 'mstyle': ['displaystyle', 'mathbackground', 'mathcolor', 'mathvariant', 'scriptlevel'], - 'msub': [], - 'msubsup': [], - 'msup': [], - 'mtable': ['align', 'columnalign', 'columnlines', 'columnspacing', 'displaystyle', 'equalcolumns', 'equalrows', 'frame', 'rowalign', 'rowlines', 'rowspacing', 'width'], - 'mtd': ['columnalign', 'columnspan', 'rowalign', 'rowspan'], - 'mtext': [], - 'mtr': ['columnalign', 'rowalign'], - 'munder': [], - 'munderover': [], - 'none': [], - 'semantics': [] + // MathML Elements + 'annotation': ['encoding'], + 'annotation-xml': ['encoding'], + 'maction': ['actiontype', 'other', 'selection'], + 'math': ['class', 'id', 'display', 'xmlns'], + 'menclose': ['notation'], + 'merror': [], + 'mfrac': ['linethickness'], + 'mi': ['mathvariant'], + 'mmultiscripts': [], + 'mn': [], + 'mo': ['fence', 'lspace', 'maxsize', 'minsize', 'rspace', 'stretchy'], + 'mover': [], + 'mpadded': ['lspace', 'width', 'height', 'depth', 'voffset'], + 'mphantom': [], + 'mprescripts': [], + 'mroot': [], + 'mrow': ['xlink:href', 'xlink:type', 'xmlns:xlink'], + 'mspace': ['depth', 'height', 'width'], + 'msqrt': [], + 'mstyle': ['displaystyle', 'mathbackground', 'mathcolor', 'mathvariant', 'scriptlevel'], + 'msub': [], + 'msubsup': [], + 'msup': [], + 'mtable': ['align', 'columnalign', 'columnlines', 'columnspacing', 'displaystyle', 'equalcolumns', 'equalrows', 'frame', 'rowalign', 'rowlines', 'rowspacing', 'width'], + 'mtd': ['columnalign', 'columnspan', 'rowalign', 'rowspan'], + 'mtext': [], + 'mtr': ['columnalign', 'rowalign'], + 'munder': [], + 'munderover': [], + 'none': [], + 'semantics': [] }; // Produce a Namespace-aware version of svgWhitelist var svgWhiteListNS_ = {}; $.each(svgWhiteList_, function (elt, atts) { - var attNS = {}; - $.each(atts, function (i, att) { - if (att.indexOf(':') >= 0) { - var v = att.split(':'); - attNS[v[1]] = NS[(v[0]).toUpperCase()]; - } else { - attNS[att] = att === 'xmlns' ? NS.XMLNS : null; - } - }); - svgWhiteListNS_[elt] = attNS; + var attNS = {}; + $.each(atts, function (i, att) { + if (att.indexOf(':') >= 0) { + var v = att.split(':'); + attNS[v[1]] = NS[(v[0]).toUpperCase()]; + } else { + attNS[att] = att === 'xmlns' ? NS.XMLNS : null; + } + }); + svgWhiteListNS_[elt] = attNS; }); // Function: svgedit.sanitize.sanitizeSvg @@ -117,140 +117,140 @@ $.each(svgWhiteList_, function (elt, atts) { // Parameters: // node - The DOM element to be checked (we'll also check its children) svgedit.sanitize.sanitizeSvg = function (node) { - // Cleanup text nodes - if (node.nodeType === 3) { // 3 == TEXT_NODE - // Trim whitespace - node.nodeValue = node.nodeValue.replace(/^\s+|\s+$/g, ''); - // Remove if empty - if (node.nodeValue.length === 0) { - node.parentNode.removeChild(node); - } - } + // Cleanup text nodes + if (node.nodeType === 3) { // 3 == TEXT_NODE + // Trim whitespace + node.nodeValue = node.nodeValue.replace(/^\s+|\s+$/g, ''); + // Remove if empty + if (node.nodeValue.length === 0) { + node.parentNode.removeChild(node); + } + } - // We only care about element nodes. - // Automatically return for all non-element nodes, such as comments, etc. - if (node.nodeType !== 1) { // 1 == ELEMENT_NODE - return; - } + // We only care about element nodes. + // Automatically return for all non-element nodes, such as comments, etc. + if (node.nodeType !== 1) { // 1 == ELEMENT_NODE + return; + } - var doc = node.ownerDocument; - var parent = node.parentNode; - // can parent ever be null here? I think the root node's parent is the document... - if (!doc || !parent) { - return; - } + var doc = node.ownerDocument; + var parent = node.parentNode; + // can parent ever be null here? I think the root node's parent is the document... + if (!doc || !parent) { + return; + } - var allowedAttrs = svgWhiteList_[node.nodeName]; - var allowedAttrsNS = svgWhiteListNS_[node.nodeName]; - var i; - // if this element is supported, sanitize it - if (typeof allowedAttrs !== 'undefined') { - var seAttrs = []; - i = node.attributes.length; - while (i--) { - // if the attribute is not in our whitelist, then remove it - // could use jQuery's inArray(), but I don't know if that's any better - var attr = node.attributes.item(i); - var attrName = attr.nodeName; - var attrLocalName = attr.localName; - var attrNsURI = attr.namespaceURI; - // Check that an attribute with the correct localName in the correct namespace is on - // our whitelist or is a namespace declaration for one of our allowed namespaces - if (!(allowedAttrsNS.hasOwnProperty(attrLocalName) && attrNsURI === allowedAttrsNS[attrLocalName] && attrNsURI !== NS.XMLNS) && - !(attrNsURI === NS.XMLNS && REVERSE_NS[attr.value])) { - // TODO(codedread): Programmatically add the se: attributes to the NS-aware whitelist. - // Bypassing the whitelist to allow se: prefixes. - // Is there a more appropriate way to do this? - if (attrName.indexOf('se:') === 0 || attrName.indexOf('data-') === 0) { - seAttrs.push([attrName, attr.value]); - } - node.removeAttributeNS(attrNsURI, attrLocalName); - } + var allowedAttrs = svgWhiteList_[node.nodeName]; + var allowedAttrsNS = svgWhiteListNS_[node.nodeName]; + var i; + // if this element is supported, sanitize it + if (typeof allowedAttrs !== 'undefined') { + var seAttrs = []; + i = node.attributes.length; + while (i--) { + // if the attribute is not in our whitelist, then remove it + // could use jQuery's inArray(), but I don't know if that's any better + var attr = node.attributes.item(i); + var attrName = attr.nodeName; + var attrLocalName = attr.localName; + var attrNsURI = attr.namespaceURI; + // Check that an attribute with the correct localName in the correct namespace is on + // our whitelist or is a namespace declaration for one of our allowed namespaces + if (!(allowedAttrsNS.hasOwnProperty(attrLocalName) && attrNsURI === allowedAttrsNS[attrLocalName] && attrNsURI !== NS.XMLNS) && + !(attrNsURI === NS.XMLNS && REVERSE_NS[attr.value])) { + // TODO(codedread): Programmatically add the se: attributes to the NS-aware whitelist. + // Bypassing the whitelist to allow se: prefixes. + // Is there a more appropriate way to do this? + if (attrName.indexOf('se:') === 0 || attrName.indexOf('data-') === 0) { + seAttrs.push([attrName, attr.value]); + } + node.removeAttributeNS(attrNsURI, attrLocalName); + } - // Add spaces before negative signs where necessary - if (svgedit.browser.isGecko()) { - switch (attrName) { - case 'transform': - case 'gradientTransform': - case 'patternTransform': - var val = attr.value.replace(/(\d)-/g, '$1 -'); - node.setAttribute(attrName, val); - break; - } - } + // Add spaces before negative signs where necessary + if (svgedit.browser.isGecko()) { + switch (attrName) { + case 'transform': + case 'gradientTransform': + case 'patternTransform': + var val = attr.value.replace(/(\d)-/g, '$1 -'); + node.setAttribute(attrName, val); + break; + } + } - // For the style attribute, rewrite it in terms of XML presentational attributes - if (attrName === 'style') { - var props = attr.value.split(';'), - p = props.length; - while (p--) { - var nv = props[p].split(':'); - var styleAttrName = $.trim(nv[0]); - var styleAttrVal = $.trim(nv[1]); - // Now check that this attribute is supported - if (allowedAttrs.indexOf(styleAttrName) >= 0) { - node.setAttribute(styleAttrName, styleAttrVal); - } - } - node.removeAttribute('style'); - } - } + // For the style attribute, rewrite it in terms of XML presentational attributes + if (attrName === 'style') { + var props = attr.value.split(';'), + p = props.length; + while (p--) { + var nv = props[p].split(':'); + var styleAttrName = $.trim(nv[0]); + var styleAttrVal = $.trim(nv[1]); + // Now check that this attribute is supported + if (allowedAttrs.indexOf(styleAttrName) >= 0) { + node.setAttribute(styleAttrName, styleAttrVal); + } + } + node.removeAttribute('style'); + } + } - $.each(seAttrs, function (i, attr) { - node.setAttributeNS(NS.SE, attr[0], attr[1]); - }); + $.each(seAttrs, function (i, attr) { + node.setAttributeNS(NS.SE, attr[0], attr[1]); + }); - // for some elements that have a xlink:href, ensure the URI refers to a local element - // (but not for links) - var href = svgedit.utilities.getHref(node); - if (href && - ['filter', 'linearGradient', 'pattern', - 'radialGradient', 'textPath', 'use'].indexOf(node.nodeName) >= 0) { - // TODO: we simply check if the first character is a #, is this bullet-proof? - if (href[0] !== '#') { - // remove the attribute (but keep the element) - svgedit.utilities.setHref(node, ''); - node.removeAttributeNS(NS.XLINK, 'href'); - } - } + // for some elements that have a xlink:href, ensure the URI refers to a local element + // (but not for links) + var href = svgedit.utilities.getHref(node); + if (href && + ['filter', 'linearGradient', 'pattern', + 'radialGradient', 'textPath', 'use'].indexOf(node.nodeName) >= 0) { + // TODO: we simply check if the first character is a #, is this bullet-proof? + if (href[0] !== '#') { + // remove the attribute (but keep the element) + svgedit.utilities.setHref(node, ''); + node.removeAttributeNS(NS.XLINK, 'href'); + } + } - // Safari crashes on a without a xlink:href, so we just remove the node here - if (node.nodeName === 'use' && !svgedit.utilities.getHref(node)) { - parent.removeChild(node); - return; - } - // if the element has attributes pointing to a non-local reference, - // need to remove the attribute - $.each(['clip-path', 'fill', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke'], function (i, attr) { - var val = node.getAttribute(attr); - if (val) { - val = svgedit.utilities.getUrlFromAttr(val); - // simply check for first character being a '#' - if (val && val[0] !== '#') { - node.setAttribute(attr, ''); - node.removeAttribute(attr); - } - } - }); + // Safari crashes on a without a xlink:href, so we just remove the node here + if (node.nodeName === 'use' && !svgedit.utilities.getHref(node)) { + parent.removeChild(node); + return; + } + // if the element has attributes pointing to a non-local reference, + // need to remove the attribute + $.each(['clip-path', 'fill', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke'], function (i, attr) { + var val = node.getAttribute(attr); + if (val) { + val = svgedit.utilities.getUrlFromAttr(val); + // simply check for first character being a '#' + if (val && val[0] !== '#') { + node.setAttribute(attr, ''); + node.removeAttribute(attr); + } + } + }); - // recurse to children - i = node.childNodes.length; - while (i--) { svgedit.sanitize.sanitizeSvg(node.childNodes.item(i)); } - // else (element not supported), remove it - } else { - // remove all children from this node and insert them before this node - // FIXME: in the case of animation elements this will hardly ever be correct - var children = []; - while (node.hasChildNodes()) { - children.push(parent.insertBefore(node.firstChild, node)); - } + // recurse to children + i = node.childNodes.length; + while (i--) { svgedit.sanitize.sanitizeSvg(node.childNodes.item(i)); } + // else (element not supported), remove it + } else { + // remove all children from this node and insert them before this node + // FIXME: in the case of animation elements this will hardly ever be correct + var children = []; + while (node.hasChildNodes()) { + children.push(parent.insertBefore(node.firstChild, node)); + } - // remove this node from the document altogether - parent.removeChild(node); + // remove this node from the document altogether + parent.removeChild(node); - // call sanitizeSvg on each of those children - i = children.length; - while (i--) { svgedit.sanitize.sanitizeSvg(children[i]); } - } + // call sanitizeSvg on each of those children + i = children.length; + while (i--) { svgedit.sanitize.sanitizeSvg(children[i]); } + } }; }()); diff --git a/editor/select.js b/editor/select.js index 661bd863..43ed97e7 100644 --- a/editor/select.js +++ b/editor/select.js @@ -20,7 +20,7 @@ 'use strict'; if (!svgedit.select) { - svgedit.select = {}; + svgedit.select = {}; } var svgFactory_; @@ -36,50 +36,50 @@ var gripRadius = svgedit.browser.isTouch() ? 10 : 4; // elem - DOM element associated with this selector // bbox - Optional bbox to use for initialization (prevents duplicate getBBox call). svgedit.select.Selector = function (id, elem, bbox) { - // this is the selector's unique number - this.id = id; + // this is the selector's unique number + this.id = id; - // this holds a reference to the element for which this selector is being used - this.selectedElement = elem; + // this holds a reference to the element for which this selector is being used + this.selectedElement = elem; - // this is a flag used internally to track whether the selector is being used or not - this.locked = true; + // this is a flag used internally to track whether the selector is being used or not + this.locked = true; - // this holds a reference to the element that holds all visual elements of the selector - this.selectorGroup = svgFactory_.createSVGElement({ - 'element': 'g', - 'attr': {'id': ('selectorGroup' + this.id)} - }); + // this holds a reference to the element that holds all visual elements of the selector + this.selectorGroup = svgFactory_.createSVGElement({ + 'element': 'g', + 'attr': {'id': ('selectorGroup' + this.id)} + }); - // this holds a reference to the path rect - this.selectorRect = this.selectorGroup.appendChild( - svgFactory_.createSVGElement({ - 'element': 'path', - 'attr': { - 'id': ('selectedBox' + this.id), - 'fill': 'none', - 'stroke': '#22C', - 'stroke-width': '1', - 'stroke-dasharray': '5,5', - // need to specify this so that the rect is not selectable - 'style': 'pointer-events:none' - } - }) - ); + // this holds a reference to the path rect + this.selectorRect = this.selectorGroup.appendChild( + svgFactory_.createSVGElement({ + 'element': 'path', + 'attr': { + 'id': ('selectedBox' + this.id), + 'fill': 'none', + 'stroke': '#22C', + 'stroke-width': '1', + 'stroke-dasharray': '5,5', + // need to specify this so that the rect is not selectable + 'style': 'pointer-events:none' + } + }) + ); - // this holds a reference to the grip coordinates for this selector - this.gripCoords = { - 'nw': null, - 'n': null, - 'ne': null, - 'e': null, - 'se': null, - 's': null, - 'sw': null, - 'w': null - }; + // this holds a reference to the grip coordinates for this selector + this.gripCoords = { + 'nw': null, + 'n': null, + 'ne': null, + 'e': null, + 'se': null, + 's': null, + 'sw': null, + 'w': null + }; - this.reset(this.selectedElement, bbox); + this.reset(this.selectedElement, bbox); }; // Function: svgedit.select.Selector.reset @@ -89,10 +89,10 @@ svgedit.select.Selector = function (id, elem, bbox) { // e - DOM element associated with this selector // bbox - Optional bbox to use for reset (prevents duplicate getBBox call). svgedit.select.Selector.prototype.reset = function (e, bbox) { - this.locked = true; - this.selectedElement = e; - this.resize(bbox); - this.selectorGroup.setAttribute('display', 'inline'); + this.locked = true; + this.selectedElement = e; + this.resize(bbox); + this.selectorGroup.setAttribute('display', 'inline'); }; // Function: svgedit.select.Selector.updateGripCursors @@ -101,22 +101,22 @@ svgedit.select.Selector.prototype.reset = function (e, bbox) { // Parameters: // angle - Float indicating current rotation angle in degrees svgedit.select.Selector.prototype.updateGripCursors = function (angle) { - var dir, - dirArr = [], - steps = Math.round(angle / 45); - if (steps < 0) { steps += 8; } - for (dir in selectorManager_.selectorGrips) { - dirArr.push(dir); - } - while (steps > 0) { - dirArr.push(dirArr.shift()); - steps--; - } - var i = 0; - for (dir in selectorManager_.selectorGrips) { - selectorManager_.selectorGrips[dir].setAttribute('style', ('cursor:' + dirArr[i] + '-resize')); - i++; - } + var dir, + dirArr = [], + steps = Math.round(angle / 45); + if (steps < 0) { steps += 8; } + for (dir in selectorManager_.selectorGrips) { + dirArr.push(dir); + } + while (steps > 0) { + dirArr.push(dirArr.shift()); + steps--; + } + var i = 0; + for (dir in selectorManager_.selectorGrips) { + selectorManager_.selectorGrips[dir].setAttribute('style', ('cursor:' + dirArr[i] + '-resize')); + i++; + } }; // Function: svgedit.select.Selector.showGrips @@ -125,292 +125,292 @@ svgedit.select.Selector.prototype.updateGripCursors = function (angle) { // Parameters: // show - boolean indicating whether grips should be shown or not svgedit.select.Selector.prototype.showGrips = function (show) { - var bShow = show ? 'inline' : 'none'; - selectorManager_.selectorGripsGroup.setAttribute('display', bShow); - var elem = this.selectedElement; - this.hasGrips = show; - if (elem && show) { - this.selectorGroup.appendChild(selectorManager_.selectorGripsGroup); - this.updateGripCursors(svgedit.utilities.getRotationAngle(elem)); - } + var bShow = show ? 'inline' : 'none'; + selectorManager_.selectorGripsGroup.setAttribute('display', bShow); + var elem = this.selectedElement; + this.hasGrips = show; + if (elem && show) { + this.selectorGroup.appendChild(selectorManager_.selectorGripsGroup); + this.updateGripCursors(svgedit.utilities.getRotationAngle(elem)); + } }; // Function: svgedit.select.Selector.resize // Updates the selector to match the element's size // bbox - Optional bbox to use for resize (prevents duplicate getBBox call). svgedit.select.Selector.prototype.resize = function (bbox) { - var selectedBox = this.selectorRect, - mgr = selectorManager_, - selectedGrips = mgr.selectorGrips, - selected = this.selectedElement, - sw = selected.getAttribute('stroke-width'), - currentZoom = svgFactory_.currentZoom(); - var offset = 1 / currentZoom; - if (selected.getAttribute('stroke') !== 'none' && !isNaN(sw)) { - offset += (sw / 2); - } + var selectedBox = this.selectorRect, + mgr = selectorManager_, + selectedGrips = mgr.selectorGrips, + selected = this.selectedElement, + sw = selected.getAttribute('stroke-width'), + currentZoom = svgFactory_.currentZoom(); + var offset = 1 / currentZoom; + if (selected.getAttribute('stroke') !== 'none' && !isNaN(sw)) { + offset += (sw / 2); + } - var tagName = selected.tagName; - if (tagName === 'text') { - offset += 2 / currentZoom; - } + var tagName = selected.tagName; + if (tagName === 'text') { + offset += 2 / currentZoom; + } - // loop and transform our bounding box until we reach our first rotation - var tlist = svgedit.transformlist.getTransformList(selected); - var m = svgedit.math.transformListToTransform(tlist).matrix; + // loop and transform our bounding box until we reach our first rotation + var tlist = svgedit.transformlist.getTransformList(selected); + var m = svgedit.math.transformListToTransform(tlist).matrix; - // This should probably be handled somewhere else, but for now - // it keeps the selection box correctly positioned when zoomed - m.e *= currentZoom; - m.f *= currentZoom; + // This should probably be handled somewhere else, but for now + // it keeps the selection box correctly positioned when zoomed + m.e *= currentZoom; + m.f *= currentZoom; - if (!bbox) { - bbox = svgedit.utilities.getBBox(selected); - } - // TODO: svgedit.utilities.getBBox (previous line) already knows to call getStrokedBBox when tagName === 'g'. Remove this? - // TODO: svgedit.utilities.getBBox doesn't exclude 'gsvg' and calls getStrokedBBox for any 'g'. Should getBBox be updated? - if (tagName === 'g' && !$.data(selected, 'gsvg')) { - // The bbox for a group does not include stroke vals, so we - // get the bbox based on its children. - var strokedBbox = svgFactory_.getStrokedBBox(selected.childNodes); - if (strokedBbox) { - bbox = strokedBbox; - } - } + if (!bbox) { + bbox = svgedit.utilities.getBBox(selected); + } + // TODO: svgedit.utilities.getBBox (previous line) already knows to call getStrokedBBox when tagName === 'g'. Remove this? + // TODO: svgedit.utilities.getBBox doesn't exclude 'gsvg' and calls getStrokedBBox for any 'g'. Should getBBox be updated? + if (tagName === 'g' && !$.data(selected, 'gsvg')) { + // The bbox for a group does not include stroke vals, so we + // get the bbox based on its children. + var strokedBbox = svgFactory_.getStrokedBBox(selected.childNodes); + if (strokedBbox) { + bbox = strokedBbox; + } + } - // apply the transforms - var l = bbox.x, t = bbox.y, w = bbox.width, h = bbox.height; - bbox = {x: l, y: t, width: w, height: h}; + // apply the transforms + var l = bbox.x, t = bbox.y, w = bbox.width, h = bbox.height; + bbox = {x: l, y: t, width: w, height: h}; - // we need to handle temporary transforms too - // if skewed, get its transformed box, then find its axis-aligned bbox + // we need to handle temporary transforms too + // if skewed, get its transformed box, then find its axis-aligned bbox - // * - offset *= currentZoom; + // * + offset *= currentZoom; - var nbox = svgedit.math.transformBox(l * currentZoom, t * currentZoom, w * currentZoom, h * currentZoom, m), - aabox = nbox.aabox, - nbax = aabox.x - offset, - nbay = aabox.y - offset, - nbaw = aabox.width + (offset * 2), - nbah = aabox.height + (offset * 2); + var nbox = svgedit.math.transformBox(l * currentZoom, t * currentZoom, w * currentZoom, h * currentZoom, m), + aabox = nbox.aabox, + nbax = aabox.x - offset, + nbay = aabox.y - offset, + nbaw = aabox.width + (offset * 2), + nbah = aabox.height + (offset * 2); - // now if the shape is rotated, un-rotate it - var cx = nbax + nbaw / 2, - cy = nbay + nbah / 2; + // now if the shape is rotated, un-rotate it + var cx = nbax + nbaw / 2, + cy = nbay + nbah / 2; - var angle = svgedit.utilities.getRotationAngle(selected); - if (angle) { - var rot = svgFactory_.svgRoot().createSVGTransform(); - rot.setRotate(-angle, cx, cy); - var rotm = rot.matrix; - nbox.tl = svgedit.math.transformPoint(nbox.tl.x, nbox.tl.y, rotm); - nbox.tr = svgedit.math.transformPoint(nbox.tr.x, nbox.tr.y, rotm); - nbox.bl = svgedit.math.transformPoint(nbox.bl.x, nbox.bl.y, rotm); - nbox.br = svgedit.math.transformPoint(nbox.br.x, nbox.br.y, rotm); + var angle = svgedit.utilities.getRotationAngle(selected); + if (angle) { + var rot = svgFactory_.svgRoot().createSVGTransform(); + rot.setRotate(-angle, cx, cy); + var rotm = rot.matrix; + nbox.tl = svgedit.math.transformPoint(nbox.tl.x, nbox.tl.y, rotm); + nbox.tr = svgedit.math.transformPoint(nbox.tr.x, nbox.tr.y, rotm); + nbox.bl = svgedit.math.transformPoint(nbox.bl.x, nbox.bl.y, rotm); + nbox.br = svgedit.math.transformPoint(nbox.br.x, nbox.br.y, rotm); - // calculate the axis-aligned bbox - var tl = nbox.tl; - var minx = tl.x, - miny = tl.y, - maxx = tl.x, - maxy = tl.y; + // calculate the axis-aligned bbox + var tl = nbox.tl; + var minx = tl.x, + miny = tl.y, + maxx = tl.x, + maxy = tl.y; - var min = Math.min, max = Math.max; + var min = Math.min, max = Math.max; - minx = min(minx, min(nbox.tr.x, min(nbox.bl.x, nbox.br.x))) - offset; - miny = min(miny, min(nbox.tr.y, min(nbox.bl.y, nbox.br.y))) - offset; - maxx = max(maxx, max(nbox.tr.x, max(nbox.bl.x, nbox.br.x))) + offset; - maxy = max(maxy, max(nbox.tr.y, max(nbox.bl.y, nbox.br.y))) + offset; + minx = min(minx, min(nbox.tr.x, min(nbox.bl.x, nbox.br.x))) - offset; + miny = min(miny, min(nbox.tr.y, min(nbox.bl.y, nbox.br.y))) - offset; + maxx = max(maxx, max(nbox.tr.x, max(nbox.bl.x, nbox.br.x))) + offset; + maxy = max(maxy, max(nbox.tr.y, max(nbox.bl.y, nbox.br.y))) + offset; - nbax = minx; - nbay = miny; - nbaw = (maxx - minx); - nbah = (maxy - miny); - } + nbax = minx; + nbay = miny; + nbaw = (maxx - minx); + nbah = (maxy - miny); + } - var dstr = 'M' + nbax + ',' + nbay + - ' L' + (nbax + nbaw) + ',' + nbay + - ' ' + (nbax + nbaw) + ',' + (nbay + nbah) + - ' ' + nbax + ',' + (nbay + nbah) + 'z'; - selectedBox.setAttribute('d', dstr); + var dstr = 'M' + nbax + ',' + nbay + + ' L' + (nbax + nbaw) + ',' + nbay + + ' ' + (nbax + nbaw) + ',' + (nbay + nbah) + + ' ' + nbax + ',' + (nbay + nbah) + 'z'; + selectedBox.setAttribute('d', dstr); - var xform = angle ? 'rotate(' + [angle, cx, cy].join(',') + ')' : ''; - this.selectorGroup.setAttribute('transform', xform); + var xform = angle ? 'rotate(' + [angle, cx, cy].join(',') + ')' : ''; + this.selectorGroup.setAttribute('transform', xform); - // TODO(codedread): Is this if needed? - // if (selected === selectedElements[0]) { - this.gripCoords = { - 'nw': [nbax, nbay], - 'ne': [nbax + nbaw, nbay], - 'sw': [nbax, nbay + nbah], - 'se': [nbax + nbaw, nbay + nbah], - 'n': [nbax + (nbaw) / 2, nbay], - 'w': [nbax, nbay + (nbah) / 2], - 'e': [nbax + nbaw, nbay + (nbah) / 2], - 's': [nbax + (nbaw) / 2, nbay + nbah] - }; - var dir; - for (dir in this.gripCoords) { - var coords = this.gripCoords[dir]; - selectedGrips[dir].setAttribute('cx', coords[0]); - selectedGrips[dir].setAttribute('cy', coords[1]); - } + // TODO(codedread): Is this if needed? + // if (selected === selectedElements[0]) { + this.gripCoords = { + 'nw': [nbax, nbay], + 'ne': [nbax + nbaw, nbay], + 'sw': [nbax, nbay + nbah], + 'se': [nbax + nbaw, nbay + nbah], + 'n': [nbax + (nbaw) / 2, nbay], + 'w': [nbax, nbay + (nbah) / 2], + 'e': [nbax + nbaw, nbay + (nbah) / 2], + 's': [nbax + (nbaw) / 2, nbay + nbah] + }; + var dir; + for (dir in this.gripCoords) { + var coords = this.gripCoords[dir]; + selectedGrips[dir].setAttribute('cx', coords[0]); + selectedGrips[dir].setAttribute('cy', coords[1]); + } - // we want to go 20 pixels in the negative transformed y direction, ignoring scale - mgr.rotateGripConnector.setAttribute('x1', nbax + (nbaw) / 2); - mgr.rotateGripConnector.setAttribute('y1', nbay); - mgr.rotateGripConnector.setAttribute('x2', nbax + (nbaw) / 2); - mgr.rotateGripConnector.setAttribute('y2', nbay - (gripRadius * 5)); + // we want to go 20 pixels in the negative transformed y direction, ignoring scale + mgr.rotateGripConnector.setAttribute('x1', nbax + (nbaw) / 2); + mgr.rotateGripConnector.setAttribute('y1', nbay); + mgr.rotateGripConnector.setAttribute('x2', nbax + (nbaw) / 2); + mgr.rotateGripConnector.setAttribute('y2', nbay - (gripRadius * 5)); - mgr.rotateGrip.setAttribute('cx', nbax + (nbaw) / 2); - mgr.rotateGrip.setAttribute('cy', nbay - (gripRadius * 5)); - // } + mgr.rotateGrip.setAttribute('cx', nbax + (nbaw) / 2); + mgr.rotateGrip.setAttribute('cy', nbay - (gripRadius * 5)); + // } }; // Class: svgedit.select.SelectorManager svgedit.select.SelectorManager = function () { - // this will hold the element that contains all selector rects/grips - this.selectorParentGroup = null; + // this will hold the element that contains all selector rects/grips + this.selectorParentGroup = null; - // this is a special rect that is used for multi-select - this.rubberBandBox = null; + // this is a special rect that is used for multi-select + this.rubberBandBox = null; - // this will hold objects of type svgedit.select.Selector (see above) - this.selectors = []; + // this will hold objects of type svgedit.select.Selector (see above) + this.selectors = []; - // this holds a map of SVG elements to their Selector object - this.selectorMap = {}; + // this holds a map of SVG elements to their Selector object + this.selectorMap = {}; - // this holds a reference to the grip elements - this.selectorGrips = { - 'nw': null, - 'n': null, - 'ne': null, - 'e': null, - 'se': null, - 's': null, - 'sw': null, - 'w': null - }; + // this holds a reference to the grip elements + this.selectorGrips = { + 'nw': null, + 'n': null, + 'ne': null, + 'e': null, + 'se': null, + 's': null, + 'sw': null, + 'w': null + }; - this.selectorGripsGroup = null; - this.rotateGripConnector = null; - this.rotateGrip = null; + this.selectorGripsGroup = null; + this.rotateGripConnector = null; + this.rotateGrip = null; - this.initGroup(); + this.initGroup(); }; // Function: svgedit.select.SelectorManager.initGroup // Resets the parent selector group element svgedit.select.SelectorManager.prototype.initGroup = function () { - // remove old selector parent group if it existed - if (this.selectorParentGroup && this.selectorParentGroup.parentNode) { - this.selectorParentGroup.parentNode.removeChild(this.selectorParentGroup); - } + // remove old selector parent group if it existed + if (this.selectorParentGroup && this.selectorParentGroup.parentNode) { + this.selectorParentGroup.parentNode.removeChild(this.selectorParentGroup); + } - // create parent selector group and add it to svgroot - this.selectorParentGroup = svgFactory_.createSVGElement({ - 'element': 'g', - 'attr': {'id': 'selectorParentGroup'} - }); - this.selectorGripsGroup = svgFactory_.createSVGElement({ - 'element': 'g', - 'attr': {'display': 'none'} - }); - this.selectorParentGroup.appendChild(this.selectorGripsGroup); - svgFactory_.svgRoot().appendChild(this.selectorParentGroup); + // create parent selector group and add it to svgroot + this.selectorParentGroup = svgFactory_.createSVGElement({ + 'element': 'g', + 'attr': {'id': 'selectorParentGroup'} + }); + this.selectorGripsGroup = svgFactory_.createSVGElement({ + 'element': 'g', + 'attr': {'display': 'none'} + }); + this.selectorParentGroup.appendChild(this.selectorGripsGroup); + svgFactory_.svgRoot().appendChild(this.selectorParentGroup); - this.selectorMap = {}; - this.selectors = []; - this.rubberBandBox = null; + this.selectorMap = {}; + this.selectors = []; + this.rubberBandBox = null; - // add the corner grips - var dir; - for (dir in this.selectorGrips) { - var grip = svgFactory_.createSVGElement({ - 'element': 'circle', - 'attr': { - 'id': ('selectorGrip_resize_' + dir), - 'fill': '#22C', - 'r': gripRadius, - 'style': ('cursor:' + dir + '-resize'), - // This expands the mouse-able area of the grips making them - // easier to grab with the mouse. - // This works in Opera and WebKit, but does not work in Firefox - // see https://bugzilla.mozilla.org/show_bug.cgi?id=500174 - 'stroke-width': 2, - 'pointer-events': 'all' - } - }); + // add the corner grips + var dir; + for (dir in this.selectorGrips) { + var grip = svgFactory_.createSVGElement({ + 'element': 'circle', + 'attr': { + 'id': ('selectorGrip_resize_' + dir), + 'fill': '#22C', + 'r': gripRadius, + 'style': ('cursor:' + dir + '-resize'), + // This expands the mouse-able area of the grips making them + // easier to grab with the mouse. + // This works in Opera and WebKit, but does not work in Firefox + // see https://bugzilla.mozilla.org/show_bug.cgi?id=500174 + 'stroke-width': 2, + 'pointer-events': 'all' + } + }); - $.data(grip, 'dir', dir); - $.data(grip, 'type', 'resize'); - this.selectorGrips[dir] = this.selectorGripsGroup.appendChild(grip); - } + $.data(grip, 'dir', dir); + $.data(grip, 'type', 'resize'); + this.selectorGrips[dir] = this.selectorGripsGroup.appendChild(grip); + } - // add rotator elems - this.rotateGripConnector = this.selectorGripsGroup.appendChild( - svgFactory_.createSVGElement({ - 'element': 'line', - 'attr': { - 'id': ('selectorGrip_rotateconnector'), - 'stroke': '#22C', - 'stroke-width': '1' - } - }) - ); + // add rotator elems + this.rotateGripConnector = this.selectorGripsGroup.appendChild( + svgFactory_.createSVGElement({ + 'element': 'line', + 'attr': { + 'id': ('selectorGrip_rotateconnector'), + 'stroke': '#22C', + 'stroke-width': '1' + } + }) + ); - this.rotateGrip = this.selectorGripsGroup.appendChild( - svgFactory_.createSVGElement({ - 'element': 'circle', - 'attr': { - 'id': 'selectorGrip_rotate', - 'fill': 'lime', - 'r': gripRadius, - 'stroke': '#22C', - 'stroke-width': 2, - 'style': 'cursor:url(' + config_.imgPath + 'rotate.png) 12 12, auto;' - } - }) - ); - $.data(this.rotateGrip, 'type', 'rotate'); + this.rotateGrip = this.selectorGripsGroup.appendChild( + svgFactory_.createSVGElement({ + 'element': 'circle', + 'attr': { + 'id': 'selectorGrip_rotate', + 'fill': 'lime', + 'r': gripRadius, + 'stroke': '#22C', + 'stroke-width': 2, + 'style': 'cursor:url(' + config_.imgPath + 'rotate.png) 12 12, auto;' + } + }) + ); + $.data(this.rotateGrip, 'type', 'rotate'); - if ($('#canvasBackground').length) { return; } + if ($('#canvasBackground').length) { return; } - var dims = config_.dimensions; - var canvasbg = svgFactory_.createSVGElement({ - 'element': 'svg', - 'attr': { - 'id': 'canvasBackground', - 'width': dims[0], - 'height': dims[1], - 'x': 0, - 'y': 0, - 'overflow': (svgedit.browser.isWebkit() ? 'none' : 'visible'), // Chrome 7 has a problem with this when zooming out - 'style': 'pointer-events:none' - } - }); + var dims = config_.dimensions; + var canvasbg = svgFactory_.createSVGElement({ + 'element': 'svg', + 'attr': { + 'id': 'canvasBackground', + 'width': dims[0], + 'height': dims[1], + 'x': 0, + 'y': 0, + 'overflow': (svgedit.browser.isWebkit() ? 'none' : 'visible'), // Chrome 7 has a problem with this when zooming out + 'style': 'pointer-events:none' + } + }); - var rect = svgFactory_.createSVGElement({ - 'element': 'rect', - 'attr': { - 'width': '100%', - 'height': '100%', - 'x': 0, - 'y': 0, - 'stroke-width': 1, - 'stroke': '#000', - 'fill': '#FFF', - 'style': 'pointer-events:none' - } - }); + var rect = svgFactory_.createSVGElement({ + 'element': 'rect', + 'attr': { + 'width': '100%', + 'height': '100%', + 'x': 0, + 'y': 0, + 'stroke-width': 1, + 'stroke': '#000', + 'fill': '#FFF', + 'style': 'pointer-events:none' + } + }); - // Both Firefox and WebKit are too slow with this filter region (especially at higher - // zoom levels) and Opera has at least one bug - // if (!svgedit.browser.isOpera()) rect.setAttribute('filter', 'url(#canvashadow)'); - canvasbg.appendChild(rect); - svgFactory_.svgRoot().insertBefore(canvasbg, svgFactory_.svgContent()); + // Both Firefox and WebKit are too slow with this filter region (especially at higher + // zoom levels) and Opera has at least one bug + // if (!svgedit.browser.isOpera()) rect.setAttribute('filter', 'url(#canvashadow)'); + canvasbg.appendChild(rect); + svgFactory_.svgRoot().insertBefore(canvasbg, svgFactory_.svgContent()); }; // Function: svgedit.select.SelectorManager.requestSelector @@ -420,27 +420,27 @@ svgedit.select.SelectorManager.prototype.initGroup = function () { // elem - DOM element to get the selector for // bbox - Optional bbox to use for reset (prevents duplicate getBBox call). svgedit.select.SelectorManager.prototype.requestSelector = function (elem, bbox) { - if (elem == null) { return null; } - var i, - N = this.selectors.length; - // If we've already acquired one for this element, return it. - if (typeof this.selectorMap[elem.id] === 'object') { - this.selectorMap[elem.id].locked = true; - return this.selectorMap[elem.id]; - } - for (i = 0; i < N; ++i) { - if (this.selectors[i] && !this.selectors[i].locked) { - this.selectors[i].locked = true; - this.selectors[i].reset(elem, bbox); - this.selectorMap[elem.id] = this.selectors[i]; - return this.selectors[i]; - } - } - // if we reached here, no available selectors were found, we create one - this.selectors[N] = new svgedit.select.Selector(N, elem, bbox); - this.selectorParentGroup.appendChild(this.selectors[N].selectorGroup); - this.selectorMap[elem.id] = this.selectors[N]; - return this.selectors[N]; + if (elem == null) { return null; } + var i, + N = this.selectors.length; + // If we've already acquired one for this element, return it. + if (typeof this.selectorMap[elem.id] === 'object') { + this.selectorMap[elem.id].locked = true; + return this.selectorMap[elem.id]; + } + for (i = 0; i < N; ++i) { + if (this.selectors[i] && !this.selectors[i].locked) { + this.selectors[i].locked = true; + this.selectors[i].reset(elem, bbox); + this.selectorMap[elem.id] = this.selectors[i]; + return this.selectors[i]; + } + } + // if we reached here, no available selectors were found, we create one + this.selectors[N] = new svgedit.select.Selector(N, elem, bbox); + this.selectorParentGroup.appendChild(this.selectors[N].selectorGroup); + this.selectorMap[elem.id] = this.selectors[N]; + return this.selectors[N]; }; // Function: svgedit.select.SelectorManager.releaseSelector @@ -449,51 +449,51 @@ svgedit.select.SelectorManager.prototype.requestSelector = function (elem, bbox) // Parameters: // elem - DOM element to remove the selector for svgedit.select.SelectorManager.prototype.releaseSelector = function (elem) { - if (elem == null) { return; } - var i, - N = this.selectors.length, - sel = this.selectorMap[elem.id]; - if (!sel.locked) { - // TODO(codedread): Ensure this exists in this module. - console.log('WARNING! selector was released but was already unlocked'); - } - for (i = 0; i < N; ++i) { - if (this.selectors[i] && this.selectors[i] === sel) { - delete this.selectorMap[elem.id]; - sel.locked = false; - sel.selectedElement = null; - sel.showGrips(false); + if (elem == null) { return; } + var i, + N = this.selectors.length, + sel = this.selectorMap[elem.id]; + if (!sel.locked) { + // TODO(codedread): Ensure this exists in this module. + console.log('WARNING! selector was released but was already unlocked'); + } + for (i = 0; i < N; ++i) { + if (this.selectors[i] && this.selectors[i] === sel) { + delete this.selectorMap[elem.id]; + sel.locked = false; + sel.selectedElement = null; + sel.showGrips(false); - // remove from DOM and store reference in JS but only if it exists in the DOM - try { - sel.selectorGroup.setAttribute('display', 'none'); - } catch (e) {} + // remove from DOM and store reference in JS but only if it exists in the DOM + try { + sel.selectorGroup.setAttribute('display', 'none'); + } catch (e) {} - break; - } - } + break; + } + } }; // Function: svgedit.select.SelectorManager.getRubberBandBox // Returns the rubberBandBox DOM element. This is the rectangle drawn by the user for selecting/zooming svgedit.select.SelectorManager.prototype.getRubberBandBox = function () { - if (!this.rubberBandBox) { - this.rubberBandBox = this.selectorParentGroup.appendChild( - svgFactory_.createSVGElement({ - 'element': 'rect', - 'attr': { - 'id': 'selectorRubberBand', - 'fill': '#22C', - 'fill-opacity': 0.15, - 'stroke': '#22C', - 'stroke-width': 0.5, - 'display': 'none', - 'style': 'pointer-events:none' - } - }) - ); - } - return this.rubberBandBox; + if (!this.rubberBandBox) { + this.rubberBandBox = this.selectorParentGroup.appendChild( + svgFactory_.createSVGElement({ + 'element': 'rect', + 'attr': { + 'id': 'selectorRubberBand', + 'fill': '#22C', + 'fill-opacity': 0.15, + 'stroke': '#22C', + 'stroke-width': 0.5, + 'display': 'none', + 'style': 'pointer-events:none' + } + }) + ); + } + return this.rubberBandBox; }; /** @@ -519,9 +519,9 @@ svgedit.select.SelectorManager.prototype.getRubberBandBox = function () { * svgFactory - an object implementing the SVGFactory interface (see above). */ svgedit.select.init = function (config, svgFactory) { - config_ = config; - svgFactory_ = svgFactory; - selectorManager_ = new svgedit.select.SelectorManager(); + config_ = config; + svgFactory_ = svgFactory; + selectorManager_ = new svgedit.select.SelectorManager(); }; /** @@ -531,6 +531,6 @@ svgedit.select.init = function (config, svgFactory) { * The SelectorManager instance. */ svgedit.select.getSelectorManager = function () { - return selectorManager_; + return selectorManager_; }; }()); diff --git a/editor/svg-editor.css b/editor/svg-editor.css index 2b744a8f..47622325 100644 --- a/editor/svg-editor.css +++ b/editor/svg-editor.css @@ -1,450 +1,450 @@ body { - background: #D0D0D0; + background: #D0D0D0; } html, body, div{ - -webkit-user-select: text; - -khtml-user-select: text; - -moz-user-select: text; - -o-user-select: text; - user-select: text; - /* this will work for QtWebKit in future */ - -webkit-user-drag: text; + -webkit-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -o-user-select: text; + user-select: text; + /* this will work for QtWebKit in future */ + -webkit-user-drag: text; } #svg_editor * { - transform-origin: 0 0; - -moz-transform-origin: 0 0; - -o-transform-origin: 0 0; - -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -moz-transform-origin: 0 0; + -o-transform-origin: 0 0; + -webkit-transform-origin: 0 0; } #svg_editor { - font-size: 8pt; - font-family: Verdana, Helvetica, Arial; - color: #000000; + font-size: 8pt; + font-family: Verdana, Helvetica, Arial; + color: #000000; } a { - color: #19c; + color: #19c; } hr { - border: none; - border-bottom: 1px solid #808080; + border: none; + border-bottom: 1px solid #808080; } select { - margin-top: 4px; + margin-top: 4px; } #svgroot { - -moz-user-select: none; - -webkit-user-select: none; - position: absolute; - top: 0; - left: 0; + -moz-user-select: none; + -webkit-user-select: none; + position: absolute; + top: 0; + left: 0; } #svgcanvas { - line-height: normal; - display: inline-block; - background-color: #A0A0A0; - text-align: center; - vertical-align: middle; - width: 640px; - height: 480px; - -apple-dashboard-region:dashboard-region(control rectangle 0px 0px 0px 0px); /* for widget regions that shouldn't react to dragging */ - position: relative; - /* - A subtle gradient effect in the canvas. - Just experimenting - not sure if this is worth it. - */ - background: -moz-radial-gradient(45deg,#bbb,#222); - background: -webkit-gradient(radial, center center, 3, center center, 1000, from(#bbb), to(#222)); + line-height: normal; + display: inline-block; + background-color: #A0A0A0; + text-align: center; + vertical-align: middle; + width: 640px; + height: 480px; + -apple-dashboard-region:dashboard-region(control rectangle 0px 0px 0px 0px); /* for widget regions that shouldn't react to dragging */ + position: relative; + /* + A subtle gradient effect in the canvas. + Just experimenting - not sure if this is worth it. + */ + background: -moz-radial-gradient(45deg,#bbb,#222); + background: -webkit-gradient(radial, center center, 3, center center, 1000, from(#bbb), to(#222)); } /* Rulers ——————————————————————————————————————*/ #rulers > div { - position: absolute; - background: #DDD; - z-index: 1; - overflow: hidden; + position: absolute; + background: #DDD; + z-index: 1; + overflow: hidden; } #ruler_corner { - top: 41px; - left: 41px; - width: 15px; - height: 15px; + top: 41px; + left: 41px; + width: 15px; + height: 15px; } #ruler_x { - height: 15px; - top: 41px; - left: 56px; - right: 30px; - border-bottom: 1px solid; - border-left: 1px solid #777; + height: 15px; + top: 41px; + left: 56px; + right: 30px; + border-bottom: 1px solid; + border-left: 1px solid #777; } #ruler_y { - width: 15px; - top: 55px; - left: 41px; - bottom: 56px; - border-right: 1px solid; - border-top: 1px solid #777; + width: 15px; + top: 55px; + left: 41px; + bottom: 56px; + border-right: 1px solid; + border-top: 1px solid #777; } #ruler_x canvas:first-child { - margin-left: -16px; + margin-left: -16px; } #ruler_x canvas { - float: left; + float: left; } #ruler_y canvas { - margin-top: -16px; + margin-top: -16px; } #ruler_x > div, #ruler_y > div { - overflow: hidden; + overflow: hidden; } #palette_holder { - overflow: hidden; - margin-top: 5px; - padding: 5px; - position: absolute; - right: 15px; - height: 16px; - background: #f0f0f0; - border-radius: 3px; - z-index: 2; + overflow: hidden; + margin-top: 5px; + padding: 5px; + position: absolute; + right: 15px; + height: 16px; + background: #f0f0f0; + border-radius: 3px; + z-index: 2; } #stroke_bg, #fill_bg { - height: 16px; - width: 16px; - margin: 1px; + height: 16px; + width: 16px; + margin: 1px; } #zoomLabel { - cursor: pointer; - margin-right: 5px; - padding-top: 4px + cursor: pointer; + margin-right: 5px; + padding-top: 4px } #linkLabel > svg { - height: 20px; - padding-top: 4px; + height: 20px; + padding-top: 4px; } #palette { - float: left; - width: 632px; - height: 16px; + float: left; + width: 632px; + height: 16px; } #workarea { - display: inline-table-cell; - position:absolute; - top: 40px; - left: 40px; - bottom: 40px; - right: 14px; - background-color: #A0A0A0; - border: 1px solid #808080; - overflow: auto; - text-align: center; + display: inline-table-cell; + position:absolute; + top: 40px; + left: 40px; + bottom: 40px; + right: 14px; + background-color: #A0A0A0; + border: 1px solid #808080; + overflow: auto; + text-align: center; } #sidepanels { - display: inline-block; - position:absolute; - top: 40px; - bottom: 40px; - right: 0; - width: 2px; - padding: 10px; - border-color: #808080; - border-style: solid; - border-width: 1px; - border-left: none; - overflow-x:hidden; - overflow-y:visible; + display: inline-block; + position:absolute; + top: 40px; + bottom: 40px; + right: 0; + width: 2px; + padding: 10px; + border-color: #808080; + border-style: solid; + border-width: 1px; + border-left: none; + overflow-x:hidden; + overflow-y:visible; } #layerpanel { - display: inline-block; - position:relative; - top: 0px; - bottom: 0; - left: 12px; - width: 0; - overflow: hidden; - margin: 0; - -moz-user-select: none; - -webkit-user-select: none; + display: inline-block; + position:relative; + top: 0px; + bottom: 0; + left: 12px; + width: 0; + overflow: hidden; + margin: 0; + -moz-user-select: none; + -webkit-user-select: none; } /* - border-style: solid; - border-color: #666; - border-width: 0px 0px 0px 1px; + border-style: solid; + border-color: #666; + border-width: 0px 0px 0px 1px; */ #sidepanel_handle { - display: inline-block; - position: absolute; - background-color: #D0D0D0; - font-weight: bold; - left: 0; - top: 40%; - width: 1em; - padding: 5px 1px 5px 5px; - margin-left: 3px; - cursor: pointer; - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - -moz-user-select: none; - -webkit-user-select: none; + display: inline-block; + position: absolute; + background-color: #D0D0D0; + font-weight: bold; + left: 0; + top: 40%; + width: 1em; + padding: 5px 1px 5px 5px; + margin-left: 3px; + cursor: pointer; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -moz-user-select: none; + -webkit-user-select: none; } #sidepanel_handle:hover { - font-weight: bold; + font-weight: bold; } #sidepanel_handle * { - cursor: pointer; - -moz-user-select: none; - -webkit-user-select: none; + cursor: pointer; + -moz-user-select: none; + -webkit-user-select: none; } #layerbuttons { - margin: 0; - padding: 0; - padding-left: 2px; - padding-right: 2px; - width: 125px; - height: 20px; - border-right: 1px solid #FFFFFF; - border-bottom: 1px solid #FFFFFF; - border-left: 1px solid #808080; - border-top: 1px solid #808080; - overflow: hidden; + margin: 0; + padding: 0; + padding-left: 2px; + padding-right: 2px; + width: 125px; + height: 20px; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + border-left: 1px solid #808080; + border-top: 1px solid #808080; + overflow: hidden; } .layer_button { - width: 14px; - height: 14px; - padding: 1px; - border-left: 1px solid #FFFFFF; - border-top: 1px solid #FFFFFF; - border-right: 1px solid #808080; - border-bottom: 1px solid #808080; - cursor: pointer; - float: left; - margin-right: 3px; + width: 14px; + height: 14px; + padding: 1px; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; + cursor: pointer; + float: left; + margin-right: 3px; } .layer_button:last-child { - margin-right: 0; + margin-right: 0; } .layer_buttonpressed { - width: 14px; - height: 14px; - padding: 1px; - border-right: 1px solid #FFFFFF; - border-bottom: 1px solid #FFFFFF; - border-left: 1px solid #808080; - border-top: 1px solid #808080; - cursor: pointer; + width: 14px; + height: 14px; + padding: 1px; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + border-left: 1px solid #808080; + border-top: 1px solid #808080; + cursor: pointer; } #layerlist { - margin: 1px; - padding: 0; - width: 127px; - border-collapse: collapse; - border: 1px solid #808080; - background-color: #FFFFFF; + margin: 1px; + padding: 0; + width: 127px; + border-collapse: collapse; + border: 1px solid #808080; + background-color: #FFFFFF; } #layerlist tr.layer { - background-color: #FFFFFF; - margin: 0; - padding: 0; + background-color: #FFFFFF; + margin: 0; + padding: 0; } #layerlist tr.layersel { - border: 1px solid #808080; - background-color: #CCCCCC; + border: 1px solid #808080; + background-color: #CCCCCC; } #layerlist td.layervis { - width: 22px; - cursor: pointer; + width: 22px; + cursor: pointer; } #layerlist td.layerinvis { - background-image: none; - cursor: pointer; + background-image: none; + cursor: pointer; } #layerlist td.layervis * { - display: block; + display: block; } #layerlist td.layerinvis * { - display: none; + display: none; } #layerlist td.layername { - cursor: pointer; + cursor: pointer; } #layerlist td.layername:hover { - color: blue; - font-style: italic; + color: blue; + font-style: italic; } #layerlist tr.layersel td.layername { - font-weight: bold; + font-weight: bold; } #selLayerLabel { - white-space: nowrap; + white-space: nowrap; } #selLayerNames { - display: block; + display: block; } div.palette_item { - height: 15px; - width: 15px; - float: left; + height: 15px; + width: 15px; + float: left; } div.palette_item:first-child { - background: white; + background: white; } /* Main button —————————————————————————————*/ #main_button { - position: absolute; - top: 4px; - left: 5px; - z-index: 5; + position: absolute; + top: 4px; + left: 5px; + z-index: 5; } #main_icon { - position: relative; - top: -2px; - left: -2px; - width: 95px; - line-height: 26px; + position: relative; + top: -2px; + left: -2px; + width: 95px; + line-height: 26px; } #main_icon:hover { - background: #eee !important; + background: #eee !important; } #main_icon.buttondown { - background: #eee !important; - -moz-box-shadow: none !important; - -webkit-box-shadow: none !important; - box-shadow: none !important; - border-radius: 3px 3px 0 0; + background: #eee !important; + -moz-box-shadow: none !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + border-radius: 3px 3px 0 0; } #logo { - margin-top: -2px; + margin-top: -2px; } #logo img { - border: 0; - width: 28px; - height: 28px; + border: 0; + width: 28px; + height: 28px; } #main_icon > div { - float: left; + float: left; } #main_button .dropdown { - position: absolute; - right: 7px; - top: 4px; + position: absolute; + right: 7px; + top: 4px; } #main_icon span { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - display: block; - z-index: 2; - font-weight: bold; - padding-left: 34px; - line-height: 32px; - font-family: sans-serif; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + display: block; + z-index: 2; + font-weight: bold; + padding-left: 34px; + line-height: 32px; + font-family: sans-serif; } #main_menu { - z-index: 12; - background: #eee; - position: relative; - width: 230px; - padding: 5px; - -moz-box-shadow: #555 1px 1px 4px; - -webkit-box-shadow: #555 1px 1px 4px; - box-shadow: #555 1px 1px 4px; - font-size: 1.1em; - display: none; - overflow: hidden; - clear: both; - top: -9px; + z-index: 12; + background: #eee; + position: relative; + width: 230px; + padding: 5px; + -moz-box-shadow: #555 1px 1px 4px; + -webkit-box-shadow: #555 1px 1px 4px; + box-shadow: #555 1px 1px 4px; + font-size: 1.1em; + display: none; + overflow: hidden; + clear: both; + top: -9px; } #main_menu ul, #main_menu li { - list-style: none; - margin: 0; - padding: 0; + list-style: none; + margin: 0; + padding: 0; } #main_menu li { /* height: 35px;*/ - line-height: 22px; - padding-top: 7px; - padding-left: 7px; - margin: -5px; - overflow: auto; - cursor: default; + line-height: 22px; + padding-top: 7px; + padding-left: 7px; + margin: -5px; + overflow: auto; + cursor: default; } #main_menu li:hover { - background: #FFC; + background: #FFC; } #main_menu li > div { - float: left; - padding-right: 5px; + float: left; + padding-right: 5px; } #main_menu p { - margin-top: 5px; + margin-top: 5px; } /*—————————————————————————————*/ @@ -456,232 +456,232 @@ div.palette_item:first-child { .tool_button_current, .push_button_pressed { - background-color: #ffc !important; + background-color: #ffc !important; } .tool_button_current, .push_button_pressed, .buttondown { - background-color: #f4e284 !important; - -webkit-box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important; - -moz-box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important; - box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important; + background-color: #f4e284 !important; + -webkit-box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important; + -moz-box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important; + box-shadow: inset 1px 1px 2px rgba(0,0,0,0.4), 1px 1px 0 white !important; } #tools_top { - position: absolute; - left: 108px; - right: 2px; - top: 2px; - height: 40px; - border-bottom: none; - overflow: auto; + position: absolute; + left: 108px; + right: 2px; + top: 2px; + height: 40px; + border-bottom: none; + overflow: auto; } #tools_top .tool_sep { - margin-top: 5px; + margin-top: 5px; } #tools_left { - position: absolute; - border-right: none; - width: 32px; - top: 40px; - left: 1px; - margin-top: -2px; - padding-left: 2px; - background: #D0D0D0; /* Needed so flyout icons don't appear on the left */ - z-index: 4; + position: absolute; + border-right: none; + width: 32px; + top: 40px; + left: 1px; + margin-top: -2px; + padding-left: 2px; + background: #D0D0D0; /* Needed so flyout icons don't appear on the left */ + z-index: 4; } #workarea.wireframe #svgcontent * { - fill: none; - stroke: #000; - stroke-width: 1px; - stroke-opacity: 1.0; - stroke-dasharray: 0; - opacity: 1; - pointer-events: stroke; - vector-effect: non-scaling-stroke; - filter: none; + fill: none; + stroke: #000; + stroke-width: 1px; + stroke-opacity: 1.0; + stroke-dasharray: 0; + opacity: 1; + pointer-events: stroke; + vector-effect: non-scaling-stroke; + filter: none; } #workarea.wireframe #svgcontent text { - fill: #000; - stroke: none; + fill: #000; + stroke: none; } #workarea.wireframe #canvasBackground > rect { - fill: #FFF !important; + fill: #FFF !important; } #tools_top div[id$="_panel"]:not(#editor_panel):not(#history_panel) { - display: none; + display: none; } #editor_panel, #history_panel { - height: 34px; - float: left; + height: 34px; + float: left; } #multiselected_panel .selected_tool { - vertical-align: 12px; + vertical-align: 12px; } /*TODO: Adjust position of rulers are not visible*/ #cur_context_panel { - position: absolute; - top: 57px; - left: 56px; - line-height: 22px; - overflow: auto; - padding-left: 5px; - font-size: 12px; - background: rgba(0, 0, 0, 0.8); - color: #ccc; - padding: 0 10px; - border-radius: 0 0 3px 0; + position: absolute; + top: 57px; + left: 56px; + line-height: 22px; + overflow: auto; + padding-left: 5px; + font-size: 12px; + background: rgba(0, 0, 0, 0.8); + color: #ccc; + padding: 0 10px; + border-radius: 0 0 3px 0; } #cur_context_panel a { - float: none; - text-decoration: none; + float: none; + text-decoration: none; } #cur_context_panel a:hover { - text-decoration: underline; + text-decoration: underline; } #tools_top > div, #tools_top { - line-height: 26px; + line-height: 26px; } div.toolset, div.toolset > * { - float: left; + float: left; } div.toolset { - height: 34px; + height: 34px; } div.toolset label span { /* outline: 1px solid red;*/ - padding-top: 3px; - display: inline-block; + padding-top: 3px; + display: inline-block; } #tools_top > div > * { - float: left; - margin-right: 2px; + float: left; + margin-right: 2px; } #tools_top label { - margin-top: 0; - margin-left: 5px; + margin-top: 0; + margin-left: 5px; } #tools_top input { - margin-top: 5px; - height: 15px; + margin-top: 5px; + height: 15px; } input[type=text] { - padding: 2px; + padding: 2px; } #tools_left .tool_button, #tools_left .tool_button_current { - position: relative; - z-index: 11; + position: relative; + z-index: 11; } .flyout_arrow_horiz { - position: absolute; - bottom: -1px; - right: 0; - z-index: 10; + position: absolute; + bottom: -1px; + right: 0; + z-index: 10; } span.zoom_tool { - line-height: 26px; - padding: 3px; + line-height: 26px; + padding: 3px; } #zoom_panel { - margin-top: 5px; + margin-top: 5px; } .dropdown { - position: relative; + position: relative; } .dropdown button { - width: 15px; - height: 21px; - margin: 6px 0 0 1px; - padding: 0; - border-left: 1px solid #FFFFFF; - border-top: 1px solid #FFFFFF; - border-right: 1px solid #808080; - border-bottom: 1px solid #808080; - background-color: #E8E8E8; + width: 15px; + height: 21px; + margin: 6px 0 0 1px; + padding: 0; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; + background-color: #E8E8E8; } .dropdown button.down { - border-left: 1px solid #808080; - border-top: 1px solid #808080; - border-right: 1px solid #FFFFFF; - border-bottom: 1px solid #FFFFFF; - background-color: #B0B0B0; + border-left: 1px solid #808080; + border-top: 1px solid #808080; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + background-color: #B0B0B0; } .dropdown ul { - list-style: none; - position: absolute; - margin: 0; - padding: 0; - left: -85px; - top: 26px; - z-index: 4; - display: none; + list-style: none; + position: absolute; + margin: 0; + padding: 0; + left: -85px; + top: 26px; + z-index: 4; + display: none; } .dropup ul { - top: auto; - bottom: 24px; + top: auto; + bottom: 24px; } .dropdown li { - display: block; - width: 120px; - padding: 4px; - background: #E8E8E8; - border: 1px solid #B0B0B0; - margin: 0 0 -1px 0; - line-height: 16px; + display: block; + width: 120px; + padding: 4px; + background: #E8E8E8; + border: 1px solid #B0B0B0; + margin: 0 0 -1px 0; + line-height: 16px; } .dropdown li:hover { - background-color: #FFC; + background-color: #FFC; } .dropdown li.special { - padding: 10px 4px; + padding: 10px 4px; } .dropdown li.special:hover { - background: #FFC; + background: #FFC; } #font_family_dropdown-list li { - font-size: 1.4em; + font-size: 1.4em; } #font_family { - margin-left: 5px; - margin-right: 0; + margin-left: 5px; + margin-right: 0; } .tool_button, @@ -689,179 +689,179 @@ span.zoom_tool { .tool_button_current, .push_button_pressed { - height: 24px; - width: 24px; - margin: 2px 2px 4px 2px; - padding: 3px; - -webkit-box-shadow: inset 1px 1px 2px white, 1px 1px 1px rgba(0,0,0,0.3); - moz-box-shadow: inset 1px 1px 2px white, 1px 1px 1px rgba(0,0,0,0.3); - box-shadow: inset 1px 1px 2px white, 1px 1px 1px rgba(0,0,0,0.3); - background-color: #E8E8E8; - cursor: pointer; - border-radius: 3px; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; + height: 24px; + width: 24px; + margin: 2px 2px 4px 2px; + padding: 3px; + -webkit-box-shadow: inset 1px 1px 2px white, 1px 1px 1px rgba(0,0,0,0.3); + moz-box-shadow: inset 1px 1px 2px white, 1px 1px 1px rgba(0,0,0,0.3); + box-shadow: inset 1px 1px 2px white, 1px 1px 1px rgba(0,0,0,0.3); + background-color: #E8E8E8; + cursor: pointer; + border-radius: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; } #main_menu li#tool_open, #main_menu li#tool_import { - position: relative; - overflow: hidden; + position: relative; + overflow: hidden; } #tool_image { - overflow: hidden; + overflow: hidden; } #tool_open input, #tool_import input, #tool_image input { - position: absolute; - opacity: 0; - font-size: 10em; - top: -5px; - right: -5px; - margin: 0; - cursor: pointer; /* Sadly doesn't appear to have an effect */ + position: absolute; + opacity: 0; + font-size: 10em; + top: -5px; + right: -5px; + margin: 0; + cursor: pointer; /* Sadly doesn't appear to have an effect */ } .disabled { - opacity: 0.5; - cursor: default; + opacity: 0.5; + cursor: default; } .tool_sep { - width: 1px; - background: #888; - border-left: 1px outset #EEE; - margin: 2px 3px; - padding: 0; - height: 24px; + width: 1px; + background: #888; + border-left: 1px outset #EEE; + margin: 2px 3px; + padding: 0; + height: 24px; } .icon_label { - float: left; - padding-top: 3px; - padding-right: 3px; - box-sizing: border-box; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - height: 0; + float: left; + padding-top: 3px; + padding-right: 3px; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + height: 0; } .width_label { - padding-right: 5px; + padding-right: 5px; } #tool_bold, #tool_italic { - font: bold 2.1em/1.1em serif; - text-align: center; - padding-left: 2px; - position: relative; + font: bold 2.1em/1.1em serif; + text-align: center; + padding-left: 2px; + position: relative; } #text { - position: absolute; - left: -9999px; + position: absolute; + left: -9999px; } #tool_bold span, #tool_italic span { - position: absolute; - width: 100%; - height: 100%; - top: 0; left: 0; - background: #000; - opacity: 0; + position: absolute; + width: 100%; + height: 100%; + top: 0; left: 0; + background: #000; + opacity: 0; } #tool_italic { - font-weight: normal; - font-style: italic; + font-weight: normal; + font-style: italic; } #url_notice { - padding-top: 4px; - display: none; + padding-top: 4px; + display: none; } #color_picker { - position: absolute; - display: none; - background: #E8E8E8; - height: 350px; - z-index: 5; + position: absolute; + display: none; + background: #E8E8E8; + height: 350px; + z-index: 5; } .tools_flyout { - position: absolute; - display: none; - cursor: pointer; - width: 400px; - z-index: 1; + position: absolute; + display: none; + cursor: pointer; + width: 400px; + z-index: 1; } .tools_flyout_v { - position: absolute; - display: none; - cursor: pointer; - width: 30px; + position: absolute; + display: none; + cursor: pointer; + width: 30px; } .tools_flyout .tool_button { - float: left; - background-color: #E8E8E8; - border-left: 1px solid #FFFFFF; - border-top: 1px solid #FFFFFF; - border-right: 1px solid #808080; - border-bottom: 1px solid #808080; - height: 28px; - width: 28px; + float: left; + background-color: #E8E8E8; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; + height: 28px; + width: 28px; } #tools_bottom { - position: absolute; - left: 40px; - right: 0; - bottom: 0; - height: 40px; - overflow: visible; + position: absolute; + left: 40px; + right: 0; + bottom: 0; + height: 40px; + overflow: visible; } #tools_bottom_1 { - width: 115px; - float: left; + width: 115px; + float: left; } #tools_bottom input[type=text] { - width: 2.2em; + width: 2.2em; } /* Color tools: fill, stroke, opacity –––––––––––––––––––––––––––––––––––––*/ #tools_bottom_2 { - float: left; - width: 300px; - position: relative; - margin-top: 5px; - -webkit-transition: width 150ms ease; + float: left; + width: 300px; + position: relative; + margin-top: 5px; + -webkit-transition: width 150ms ease; } .expanded #tools_bottom_2 { - width: 450px; + width: 450px; } #tools_bottom #tools_bottom_2 .dropdown button { - margin-top: 2px; + margin-top: 2px; } .dropdown li.tool_button { - width: 24px; + width: 24px; } #tools_bottom_2 .icon_label { - display: block; - margin: 3px 5px; - padding: 0; + display: block; + margin: 3px 5px; + padding: 0; } #tool_opacity { right: 0;} @@ -869,477 +869,477 @@ span.zoom_tool { #tool_stroke { left: 60px;} #fill_color, #stroke_color { - height: 16px; - width: 16px; - border: 1px solid #808080; - cursor: pointer; - overflow: hidden; + height: 16px; + width: 16px; + border: 1px solid #808080; + cursor: pointer; + overflow: hidden; } #stroke_expand { - width: 0; - overflow: hidden; + width: 0; + overflow: hidden; } #toggle_stroke_tools { - position: absolute; - right: 0; - top: 0; - bottom: 0; - width: 25px; - text-align: center; - border-radius: 0 3px 3px 0; - margin: 0; + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: 25px; + text-align: center; + border-radius: 0 3px 3px 0; + margin: 0; } #toggle_stroke_tools:before { - content: '>>'; - letter-spacing: -3px; - font-weight: bold; - color: #666; + content: '>>'; + letter-spacing: -3px; + font-weight: bold; + color: #666; } .expanded #tool_stroke.color_tool { - width: 280px; + width: 280px; } .expanded #toggle_stroke_tools:before { - content: '<<'; + content: '<<'; } #toggle_stroke_tools:hover { - background: white; + background: white; } .color_tool { - position: absolute; - overflow: hidden; - background: #f0f0f0; - height: 26px; - line-height: 26px; - border-radius: 3px; - min-width: 52px; + position: absolute; + overflow: hidden; + background: #f0f0f0; + height: 26px; + line-height: 26px; + border-radius: 3px; + min-width: 52px; } #tool_stroke.color_tool { - width: 130px; - z-index: 2; - -webkit-transition: width 150ms ease; - -moz-transition: width 150ms ease; - -o-transition: width 150ms ease; - -ms-transition: width 150ms ease; - transition: width 150ms ease; + width: 130px; + z-index: 2; + -webkit-transition: width 150ms ease; + -moz-transition: width 150ms ease; + -o-transition: width 150ms ease; + -ms-transition: width 150ms ease; + transition: width 150ms ease; } .color_block { - position: absolute; - top: 0; - left: 0; + position: absolute; + top: 0; + left: 0; } .color_block svg { - display: block; + display: block; } .color_tool > * { - float: left; - margin-right: 5px; + float: left; + margin-right: 5px; } .color_tool .dropdown > * { - float: left; + float: left; } .color_tool .stroke_label { - margin-left: 25px; - float: left; + margin-left: 25px; + float: left; } .color_tool > .color_block { - top: 3px; - left: 29px; + top: 3px; + left: 29px; } .color_tool input { - margin: 0; + margin: 0; } #tool_opacity { - overflow: visible; + overflow: visible; } @media screen and (max-width:1250px) { - .expanded #palette_holder { - left: 560px; - overflow-x: scroll; - padding: 0 5px; - margin-top: 2px; - height: 30px; - } - #tools_top { - height: 71px; - } - #workarea, #sidepanels { - top: 70px; - } - #rulers #ruler_corner, - #rulers #ruler_x, #tools_left { - top: 71px; - } + .expanded #palette_holder { + left: 560px; + overflow-x: scroll; + padding: 0 5px; + margin-top: 2px; + height: 30px; + } + #tools_top { + height: 71px; + } + #workarea, #sidepanels { + top: 70px; + } + #rulers #ruler_corner, + #rulers #ruler_x, #tools_left { + top: 71px; + } - #rulers #ruler_y { - top: 86px; - } + #rulers #ruler_y { + top: 86px; + } - #cur_context_panel { - top: 87px; - } + #cur_context_panel { + top: 87px; + } - #selected_panel { - clear: right; - } + #selected_panel { + clear: right; + } } @media screen and (max-width:1100px) { - #tools_bottom:not(.expanded) #palette_holder { - left: 410px; - overflow-x: scroll; - padding: 0 5px; - margin-top: 2px; - height: 30px; - } + #tools_bottom:not(.expanded) #palette_holder { + left: 410px; + overflow-x: scroll; + padding: 0 5px; + margin-top: 2px; + height: 30px; + } } /*–––––––––––––––––––––––––––––––––––––*/ #option_lists ul { - display: none; - position: absolute; - height: auto; - z-index: 3; - margin: -10px; - list-style: none; - padding-left: 0; + display: none; + position: absolute; + height: auto; + z-index: 3; + margin: -10px; + list-style: none; + padding-left: 0; } #option_lists .optcols2 { - width: 70px; - margin-left: -15px; + width: 70px; + margin-left: -15px; } #option_lists .optcols3 { - width: 90px; - margin-left: -31px; + width: 90px; + margin-left: -31px; } #option_lists .optcols4 { - width: 130px; - margin-left: -44px; + width: 130px; + margin-left: -44px; } #option_lists ul[class^=optcols] li { - float: left; + float: left; } ul li.current { - background-color: #F4E284; + background-color: #F4E284; } #option_lists ul li { - margin: 0; - border-radius: 0; - -moz-border-radius: 0; - -webkit-border-radius: 0; + margin: 0; + border-radius: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; } #tools_bottom .dropdown button { - margin-top: 2px; + margin-top: 2px; } #opacity_dropdown li { - width: 140px; + width: 140px; } #copyright { - text-align: right; - padding-right: .3em; + text-align: right; + padding-right: .3em; } #svg_source_editor { - display: none; + display: none; } .overlay { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - background-color: black; - opacity: 0.6; - z-index: 5; + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + background-color: black; + opacity: 0.6; + z-index: 5; } #svg_source_editor #svg_source_container { - position: absolute; - top: 30px; - left: 30px; - right: 30px; - bottom: 30px; - background-color: #B0B0B0; - opacity: 1.0; - text-align: center; - border: 1px outset #777; - z-index: 6; + position: absolute; + top: 30px; + left: 30px; + right: 30px; + bottom: 30px; + background-color: #B0B0B0; + opacity: 1.0; + text-align: center; + border: 1px outset #777; + z-index: 6; } #save_output_btns { - display: none; - text-align: left; + display: none; + text-align: left; } #save_output_btns p { - margin: .5em 1.5em; - display: inline-block; + margin: .5em 1.5em; + display: inline-block; } #bg_blocks { - overflow: auto; - margin-left: 30px; + overflow: auto; + margin-left: 30px; } #bg_blocks .color_block { - position: static; + position: static; } #svg_docprops #svg_docprops_container, #svg_prefs #svg_prefs_container { - position: absolute; - top: 50px; - padding: 10px; - background-color: #B0B0B0; - border: 1px outset #777; - opacity: 1.0; + position: absolute; + top: 50px; + padding: 10px; + background-color: #B0B0B0; + border: 1px outset #777; + opacity: 1.0; /* width: 450px;*/ - font-family: Verdana, Helvetica, sans-serif; - font-size: .8em; - z-index: 20001; + font-family: Verdana, Helvetica, sans-serif; + font-size: .8em; + z-index: 20001; } #svg_docprops .error { - border: 1px solid red; - padding: 3px; + border: 1px solid red; + padding: 3px; } #svg_docprops #resolution { - max-width: 14em; + max-width: 14em; } #tool_docprops_back, #tool_prefs_back { - margin-left: 1em; - overflow: auto; + margin-left: 1em; + overflow: auto; } #svg_docprops_container #svg_docprops_docprops, #svg_prefs #svg_docprops_prefs { - float: left; - width: 221px; - margin: 5px .7em; - overflow: hidden; + float: left; + width: 221px; + margin: 5px .7em; + overflow: hidden; } #svg_prefs_container fieldset + fieldset { - float: right; + float: right; } #svg_docprops legend, #svg_prefs legend { - max-width: 195px; + max-width: 195px; } #svg_docprops_docprops > legend, #svg_prefs_container > fieldset > legend { - font-weight: bold; - font-size: 1.1em; + font-weight: bold; + font-size: 1.1em; } #svg_docprops_container fieldset, #svg_prefs fieldset { - padding: 5px; - margin: 5px; - border: 1px solid #DDD; + padding: 5px; + margin: 5px; + border: 1px solid #DDD; } #svg_docprops_container label, #svg_prefs_container label { - display: block; - margin: .5em; + display: block; + margin: .5em; } #svginfo_bg_note { - font-size: .9em; - font-style: italic; - color: #444; + font-size: .9em; + font-style: italic; + color: #444; } #canvas_title, #canvas_bg_url { - display: block; - width: 96%; + display: block; + width: 96%; } #svg_source_editor form { - position: absolute; - top: 40px; - bottom: 30px; - width: 100%; + position: absolute; + top: 40px; + bottom: 30px; + width: 100%; } #svg_source_editor #svg_source_textarea { - position: relative; - width: 95%; - height: 95%; - padding: 5px; - font-size: 12px; + position: relative; + width: 95%; + height: 95%; + padding: 5px; + font-size: 12px; } #svg_source_editor #tool_source_back { - text-align: left; - padding-left: 20px; + text-align: left; + padding-left: 20px; } #svg_prefs_container div.color_block { - float: left; - margin: 2px; - padding: 20px; + float: left; + margin: 2px; + padding: 20px; } #change_background div.cur_background { - border: 2px solid blue; - padding: 18px; + border: 2px solid blue; + padding: 18px; } #background_img { - position: absolute; - top: 0; - left: 0; - text-align: left; + position: absolute; + top: 0; + left: 0; + text-align: left; } #svg_docprops button, #svg_prefs button { - margin-top: 0; - margin-bottom: 5px; + margin-top: 0; + margin-bottom: 5px; } #svg_docprops, #svg_prefs { - display: none; + display: none; } #image_save_opts label { - font-size: .9em; + font-size: .9em; } #image_save_opts input { - margin-left: 0; + margin-left: 0; } #tool_prefs_option { - float: right; + float: right; } .toolbar_button button { - border:1px solid #dedede; - line-height:130%; - float: left; - background: #E8E8E8 none; - padding:5px 10px 5px 7px; /* Firefox */ - line-height:17px; /* Safari */ - margin: 5px 20px 0 0; - border: 1px #808080 solid; - border-top-color: #FFF; - border-left-color: #FFF; - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; + border:1px solid #dedede; + line-height:130%; + float: left; + background: #E8E8E8 none; + padding:5px 10px 5px 7px; /* Firefox */ + line-height:17px; /* Safari */ + margin: 5px 20px 0 0; + border: 1px #808080 solid; + border-top-color: #FFF; + border-left-color: #FFF; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; } .toolbar_button button:hover { - border: 1px #e0a874 solid; - border-top-color: #fcd9ba; - border-left-color: #fcd9ba; - background-color: #FFC; + border: 1px #e0a874 solid; + border-top-color: #fcd9ba; + border-left-color: #fcd9ba; + background-color: #FFC; } .toolbar_button button:active { - background-color: #F4E284; - border-left: 1px solid #663300; - border-top: 1px solid #663300; + background-color: #F4E284; + border-left: 1px solid #663300; + border-top: 1px solid #663300; } .toolbar_button button .svg_icon { - margin: 0 3px -3px 0 !important; - padding: 0; - border: none; - width: 16px; - height: 16px; + margin: 0 3px -3px 0 !important; + padding: 0; + border: none; + width: 16px; + height: 16px; } #dialog_box { - display: none; + display: none; } #dialog_content { - height: 95px; - margin: 10px 10px 5px 10px; - background: #DDD; - overflow: auto; - text-align: left; - border: 1px solid #B0B0B0; + height: 95px; + margin: 10px 10px 5px 10px; + background: #DDD; + overflow: auto; + text-align: left; + border: 1px solid #B0B0B0; } #dialog_content.prompt { - height: 75px; + height: 75px; } #dialog_content p { - margin: 10px; - line-height: 1.3em; + margin: 10px; + line-height: 1.3em; } #dialog_container { - position: absolute; - font-family: Verdana; - text-align: center; - left: 50%; - top: 50%; - width: 300px; - margin-left: -150px; - height: 150px; - margin-top: -80px; - position: fixed; - z-index: 50001; - background: #CCC; - border: 1px outset #777; - font-family:Verdana,Helvetica,sans-serif; - font-size:0.8em; + position: absolute; + font-family: Verdana; + text-align: center; + left: 50%; + top: 50%; + width: 300px; + margin-left: -150px; + height: 150px; + margin-top: -80px; + position: fixed; + z-index: 50001; + background: #CCC; + border: 1px outset #777; + font-family:Verdana,Helvetica,sans-serif; + font-size:0.8em; } #dialog_container, #dialog_content { - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; } #dialog_buttons input[type=text] { - width: 90%; - display: block; - margin: 0 0 5px 11px; + width: 90%; + display: block; + margin: 0 0 5px 11px; } #dialog_buttons input[type=button] { - margin: 0 1em; + margin: 0 1em; } /* Slider @@ -1361,93 +1361,93 @@ ul li.current { .ui-slider-vertical .ui-slider-range-max { top: 0; } .ui-slider { - border: 1px solid #B0B0B0; + border: 1px solid #B0B0B0; } .ui-slider-handle { - background: #B0B0B0; - border: 1px solid #000; + background: #B0B0B0; + border: 1px solid #000; } /* Necessary to keep the flyouts sized properly */ .tools_flyout .tool_button, .tools_flyout .tool_flyout { - padding: 2px; - width: 24px; - height: 24px; - margin: 0; - border-radius: 0px; - -moz-border-radius: 0px; - -webkit-border-radius: 0px; + padding: 2px; + width: 24px; + height: 24px; + margin: 0; + border-radius: 0px; + -moz-border-radius: 0px; + -webkit-border-radius: 0px; } /* Generic context menu styles */ .contextMenu { - position: absolute; - z-index: 99999; - border: solid 1px rgba(0,0,0,.33); - background: rgba(255,255,255,.95); - padding: 5px 0; - margin: 0px; - display: none; - font: 12px/15px Lucida Sans, Helvetica, Verdana, sans-serif; - border-radius: 5px; - -moz-border-radius: 5px; - -moz-box-shadow: 2px 5px 10px rgba(0,0,0,.3); - -webkit-box-shadow: 2px 5px 10px rgba(0,0,0,.3); - box-shadow: 2px 5px 10px rgba(0,0,0,.3); + position: absolute; + z-index: 99999; + border: solid 1px rgba(0,0,0,.33); + background: rgba(255,255,255,.95); + padding: 5px 0; + margin: 0px; + display: none; + font: 12px/15px Lucida Sans, Helvetica, Verdana, sans-serif; + border-radius: 5px; + -moz-border-radius: 5px; + -moz-box-shadow: 2px 5px 10px rgba(0,0,0,.3); + -webkit-box-shadow: 2px 5px 10px rgba(0,0,0,.3); + box-shadow: 2px 5px 10px rgba(0,0,0,.3); } .contextMenu LI { - list-style: none; - padding: 0px; - margin: 0px; + list-style: none; + padding: 0px; + margin: 0px; } .contextMenu .shortcut { - width: 115px; - text-align:right; - float:right; + width: 115px; + text-align:right; + float:right; } .contextMenu A { - -moz-user-select: none; - -webkit-user-select: none; - color: #222; - text-decoration: none; - display: block; - line-height: 20px; - height: 20px; - background-position: 6px center; - background-repeat: no-repeat; - outline: none; - padding: 0px 15px 1px 20px; + -moz-user-select: none; + -webkit-user-select: none; + color: #222; + text-decoration: none; + display: block; + line-height: 20px; + height: 20px; + background-position: 6px center; + background-repeat: no-repeat; + outline: none; + padding: 0px 15px 1px 20px; } .contextMenu LI.hover A { - background-color: #2e5dea; - color: white; - cursor: default; + background-color: #2e5dea; + color: white; + cursor: default; } .contextMenu LI.disabled A { - color: #999; + color: #999; } .contextMenu LI.hover.disabled A { - background-color: transparent; + background-color: transparent; } .contextMenu LI.separator { - border-top: solid 1px #E3E3E3; - padding-top: 5px; - margin-top: 5px; + border-top: solid 1px #E3E3E3; + padding-top: 5px; + margin-top: 5px; } /* - Adding Icons - You can add icons to the context menu by adding - classes to the respective LI element(s) + Adding Icons + You can add icons to the context menu by adding + classes to the respective LI element(s) */ /* diff --git a/editor/svg-editor.html b/editor/svg-editor.html index 8c93d9e6..0439b938 100644 --- a/editor/svg-editor.html +++ b/editor/svg-editor.html @@ -71,17 +71,17 @@
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    @@ -92,514 +92,514 @@
    -
    -

    Layers

    -
    -
    -
    -
    -
    -
    -
    -
    +
    +

    Layers

    +
    +
    +
    +
    +
    +
    +
    +
    - - - - - -
    Layer 1
    - Move elements to: - -
    -
    L a y e r s
    + + + + + +
    Layer 1
    + Move elements to: + +
    +
    L a y e r s
    -
    - SVG-Edit - - -
    +
    + SVG-Edit + + +
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - -
    - - - -
    - - -
    - - - -
    - - -
    -
    - - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - -
    - -
    - -
    -
    - - -
    - -
    - -
    -
    - - -
    -
    - - -
    +
    +
    +
    +
    -
    -
    - - -
    -
    - -
    -
    + +
    +
    +
    +
    +
    -
    -
    - - -
    -
    - - -
    -
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + +
    -
    -
    - - -
    -
    - - -
    -
    + -
    -
    -
    B
    -
    i
    -
    +
    + + +
    -
    - - -
    + - +
    + + +
    +
    - - -
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    - -
    -
    +
    - +
    +
    + + +
    + +
    - -
    +
    +
    + + +
    +
    + + +
    +
    -
    - -
    +
    +
    + + +
    +
    + +
    +
    -
    -
    -
    +
    +
    + + +
    +
    + + +
    +
    - -
    - -
    +
    +
    + + +
    +
    + + +
    +
    -
    -
    - -
    - - +
    +
    +
    B
    +
    i
    +
    - -
    -
    -
    -
    -
    +
    + + +
    + + + + + +
    + + +
    +
    + + + + +
    + +
    + +
    + +
    +
    +
    + + +
    + +
    + +
    +
    + +
    + + + + +
    +
    +
    +
    +
    -
    +
    -
    +
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +
    - -
    - - -
    -
    + +
    + + +
    +
    -
    -
    +
    +
    -
    - -
    -
    -
    -
    -
    +
    + +
    +
    +
    +
    +
    -
    - -
    -
    -
    -
    +
    + +
    +
    +
    +
    - + -
    +
    - + - + - + -
    +
    -
    - - -
    -
    +
    + + +
    +
    -
    +
    -
    -
    -
    - +
    +
    +
    +
    @@ -609,194 +609,194 @@
    -
    -
    -
    - - -
    -
    -

    Copy the contents of this box into a text editor, then save the file with a .svg extension.

    - -
    -
    - -
    -
    +
    +
    +
    + + +
    +
    +

    Copy the contents of this box into a text editor, then save the file with a .svg extension.

    + +
    +
    + +
    +
    -
    -
    -
    - - -
    +
    +
    +
    + + +
    -
    - Image Properties - +
    + Image Properties + -
    - Canvas Dimensions +
    + Canvas Dimensions - + - + - -
    + +
    -
    - Included Images - - -
    -
    +
    + Included Images + + +
    +
    -
    +
    -
    -
    -
    - - -
    +
    +
    +
    + + +
    -
    - Editor Preferences +
    + Editor Preferences - + - + -
    - Editor Background -
    - -

    Note: Background will not be saved with image.

    -
    +
    + Editor Background +
    + +

    Note: Background will not be saved with image.

    +
    -
    - Grid - - - -
    +
    + Grid + + + +
    -
    - Units & Rulers - - - +
    + Units & Rulers + + + -
    +
    -
    +
    -
    +
    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    diff --git a/editor/svg-editor.js b/editor/svg-editor.js index 2cb022f3..cd247e42 100644 --- a/editor/svg-editor.js +++ b/editor/svg-editor.js @@ -24,5154 +24,5154 @@ TODOS */ (function () { if (window.svgEditor) { - return; + return; } window.svgEditor = (function ($) { - var editor = {}; - // EDITOR PROPERTIES: (defined below) - // curPrefs, curConfig, canvas, storage, uiStrings - // - // STATE MAINTENANCE PROPERTIES - editor.tool_scale = 1; // Dependent on icon size, so any use to making configurable instead? Used by JQuerySpinBtn.js - editor.exportWindowCt = 0; - editor.langChanged = false; - editor.showSaveWarning = false; - editor.storagePromptClosed = false; // For use with ext-storage.js - - var svgCanvas, urldata, - Utils = svgedit.utilities, - isReady = false, - customExportImage = false, - customExportPDF = false, - callbacks = [], - /** - * PREFS AND CONFIG - */ - // The iteration algorithm for defaultPrefs does not currently support array/objects - defaultPrefs = { - // EDITOR OPTIONS (DIALOG) - lang: '', // Default to "en" if locale.js detection does not detect another language - iconsize: '', // Will default to 's' if the window height is smaller than the minimum height and 'm' otherwise - bkgd_color: '#FFF', - bkgd_url: '', - // DOCUMENT PROPERTIES (DIALOG) - img_save: 'embed', - // ALERT NOTICES - // Only shows in UI as far as alert notices, but useful to remember, so keeping as pref - save_notice_done: false, - export_notice_done: false - }, - curPrefs = {}, - // Note: The difference between Prefs and Config is that Prefs - // can be changed in the UI and are stored in the browser, - // while config cannot - curConfig = { - // We do not put on defaultConfig to simplify object copying - // procedures (we obtain instead from defaultExtensions) - extensions: [], - /** - * Can use window.location.origin to indicate the current - * origin. Can contain a '*' to allow all domains or 'null' (as - * a string) to support all file:// URLs. Cannot be set by - * URL for security reasons (not safe, at least for - * privacy or data integrity of SVG content). - * Might have been fairly safe to allow - * `new URL(window.location.href).origin` by default but - * avoiding it ensures some more security that even third - * party apps on the same domain also cannot communicate - * with this app by default. - * For use with ext-xdomain-messaging.js - * @todo We might instead make as a user-facing preference. - */ - allowedOrigins: [] - }, - defaultExtensions = [ - 'ext-overview_window.js', - 'ext-markers.js', - 'ext-connector.js', - 'ext-eyedropper.js', - 'ext-shapes.js', - 'ext-imagelib.js', - 'ext-grid.js', - 'ext-polygon.js', - 'ext-star.js', - 'ext-panning.js', - 'ext-storage.js' - ], - defaultConfig = { - // Todo: svgcanvas.js also sets and checks: show_outside_canvas, selectNew; add here? - // Change the following to preferences and add pref controls to the UI (e.g., initTool, wireframe, showlayers)? - canvasName: 'default', - canvas_expansion: 3, - initFill: { - color: 'FF0000', // solid red - opacity: 1 - }, - initStroke: { - width: 5, - color: '000000', // solid black - opacity: 1 - }, - text: { - stroke_width: 0, - font_size: 24, - font_family: 'serif' - }, - initOpacity: 1, - colorPickerCSS: null, // Defaults to 'left' with a position equal to that of the fill_color or stroke_color element minus 140, and a 'bottom' equal to 40 - initTool: 'select', - exportWindowType: 'new', // 'same' (todo: also support 'download') - wireframe: false, - showlayers: false, - no_save_warning: false, - // PATH CONFIGURATION - // The following path configuration items are disallowed in the URL (as should any future path configurations) - imgPath: 'images/', - langPath: 'locale/', - extPath: 'extensions/', - jGraduatePath: 'jgraduate/images/', - // DOCUMENT PROPERTIES - // Change the following to a preference (already in the Document Properties dialog)? - dimensions: [640, 480], - // EDITOR OPTIONS - // Change the following to preferences (already in the Editor Options dialog)? - gridSnapping: false, - gridColor: '#000', - baseUnit: 'px', - snappingStep: 10, - showRulers: true, - // URL BEHAVIOR CONFIGURATION - preventAllURLConfig: false, - preventURLContentLoading: false, - // EXTENSION CONFIGURATION (see also preventAllURLConfig) - lockExtensions: false, // Disallowed in URL setting - noDefaultExtensions: false, // noDefaultExtensions can only be meaningfully used in config.js or in the URL - // EXTENSION-RELATED (GRID) - showGrid: false, // Set by ext-grid.js - // EXTENSION-RELATED (STORAGE) - noStorageOnLoad: false, // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage - forceStorage: false, // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not - emptyStorageOnDecline: false // Used by ext-storage.js; empty any prior storage if the user declines to store - }, - /** - * LOCALE - * @todo Can we remove now that we are always loading even English? (unless locale is set to null) - */ - uiStrings = editor.uiStrings = { - common: { - ok: 'OK', - cancel: 'Cancel', - key_up: 'Up', - key_down: 'Down', - key_backspace: 'Backspace', - key_del: 'Del' - }, - // This is needed if the locale is English, since the locale strings are not read in that instance. - layers: { - layer: 'Layer' - }, - notification: { - invalidAttrValGiven: 'Invalid value given', - noContentToFitTo: 'No content to fit to', - dupeLayerName: 'There is already a layer named that!', - enterUniqueLayerName: 'Please enter a unique layer name', - enterNewLayerName: 'Please enter the new layer name', - layerHasThatName: 'Layer already has that name', - QmoveElemsToLayer: 'Move selected elements to layer \'%s\'?', - QwantToClear: 'Do you want to clear the drawing?\nThis will also erase your undo history!', - QwantToOpen: 'Do you want to open a new file?\nThis will also erase your undo history!', - QerrorsRevertToSource: 'There were parsing errors in your SVG source.\nRevert back to original SVG source?', - QignoreSourceChanges: 'Ignore changes made to SVG source?', - featNotSupported: 'Feature not supported', - enterNewImgURL: 'Enter the new image URL', - defsFailOnSave: 'NOTE: Due to a bug in your browser, this image may appear wrong (missing gradients or elements). It will however appear correct once actually saved.', - loadingImage: 'Loading image, please wait...', - saveFromBrowser: 'Select \'Save As...\' in your browser to save this image as a %s file.', - noteTheseIssues: 'Also note the following issues: ', - unsavedChanges: 'There are unsaved changes.', - enterNewLinkURL: 'Enter the new hyperlink URL', - errorLoadingSVG: 'Error: Unable to load SVG data', - URLloadFail: 'Unable to load from URL', - retrieving: 'Retrieving \'%s\' ...', - popupWindowBlocked: 'Popup window may be blocked by browser' - } - }; - - function loadSvgString (str, callback) { - var success = svgCanvas.setSvgString(str) !== false; - callback = callback || $.noop; - if (success) { - callback(true); - } else { - $.alert(uiStrings.notification.errorLoadingSVG, function () { - callback(false); - }); - } - } - - /** - * EXPORTS - */ - - /** - * Store and retrieve preferences - * @param {string} key The preference name to be retrieved or set - * @param {string} [val] The value. If the value supplied is missing or falsey, no change to the preference will be made. - * @returns {string} If val is missing or falsey, the value of the previously stored preference will be returned. - * @todo Can we change setting on the jQuery namespace (onto editor) to avoid conflicts? - * @todo Review whether any remaining existing direct references to - * getting curPrefs can be changed to use $.pref() getting to ensure - * defaultPrefs fallback (also for sake of allowInitialUserOverride); specifically, bkgd_color could be changed so that - * the pref dialog has a button to auto-calculate background, but otherwise uses $.pref() to be able to get default prefs - * or overridable settings - */ - $.pref = function (key, val) { - if (val) { - curPrefs[key] = val; - editor.curPrefs = curPrefs; // Update exported value - return; - } - return (key in curPrefs) ? curPrefs[key] : defaultPrefs[key]; - }; - - /** - * EDITOR PUBLIC METHODS - * locale.js also adds "putLang" and "readLang" as editor methods - * @todo Sort these methods per invocation order, ideally with init at the end - * @todo Prevent execution until init executes if dependent on it? - */ - - /** - * Where permitted, sets canvas and/or defaultPrefs based on previous - * storage. This will override URL settings (for security reasons) but - * not config.js configuration (unless initial user overriding is explicitly - * permitted there via allowInitialUserOverride). - * @todo Split allowInitialUserOverride into allowOverrideByURL and - * allowOverrideByUserStorage so config.js can disallow some - * individual items for URL setting but allow for user storage AND/OR - * change URL setting so that it always uses a different namespace, - * so it won't affect pre-existing user storage (but then if users saves - * that, it will then be subject to tampering - */ - editor.loadContentAndPrefs = function () { - if (!curConfig.forceStorage && (curConfig.noStorageOnLoad || !document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/))) { - return; - } - - // LOAD CONTENT - if (editor.storage && // Cookies do not have enough available memory to hold large documents - (curConfig.forceStorage || (!curConfig.noStorageOnLoad && document.cookie.match(/(?:^|;\s*)store=prefsAndContent/))) - ) { - var name = 'svgedit-' + curConfig.canvasName; - var cached = editor.storage.getItem(name); - if (cached) { - editor.loadFromString(cached); - } - } - - // LOAD PREFS - var key; - for (key in defaultPrefs) { - if (defaultPrefs.hasOwnProperty(key)) { // It's our own config, so we don't need to iterate up the prototype chain - var storeKey = 'svg-edit-' + key; - if (editor.storage) { - var val = editor.storage.getItem(storeKey); - if (val) { - defaultPrefs[key] = String(val); // Convert to string for FF (.value fails in Webkit) - } - } else if (window.widget) { - defaultPrefs[key] = widget.preferenceForKey(storeKey); - } else { - var result = document.cookie.match(new RegExp('(?:^|;\\s*)' + Utils.preg_quote(encodeURIComponent(storeKey)) + '=([^;]+)')); - defaultPrefs[key] = result ? decodeURIComponent(result[1]) : ''; - } - } - } - }; - - /** - * Allows setting of preferences or configuration (including extensions). - * @param {Object} opts The preferences or configuration (including extensions) - * @param {Object} [cfgCfg] Describes configuration which applies to the particular batch of supplied options - * @param {boolean} [cfgCfg.allowInitialUserOverride=false] Set to true if you wish - * to allow initial overriding of settings by the user via the URL - * (if permitted) or previously stored preferences (if permitted); - * note that it will be too late if you make such calls in extension - * code because the URL or preference storage settings will - * have already taken place. - * @param {boolean} [cfgCfg.overwrite=true] Set to false if you wish to - * prevent the overwriting of prior-set preferences or configuration - * (URL settings will always follow this requirement for security - * reasons, so config.js settings cannot be overridden unless it - * explicitly permits via "allowInitialUserOverride" but extension config - * can be overridden as they will run after URL settings). Should - * not be needed in config.js. - */ - editor.setConfig = function (opts, cfgCfg) { - cfgCfg = cfgCfg || {}; - function extendOrAdd (cfgObj, key, val) { - if (cfgObj[key] && typeof cfgObj[key] === 'object') { - $.extend(true, cfgObj[key], val); - } else { - cfgObj[key] = val; - } - } - $.each(opts, function (key, val) { - if (opts.hasOwnProperty(key)) { - // Only allow prefs defined in defaultPrefs - if (defaultPrefs.hasOwnProperty(key)) { - if (cfgCfg.overwrite === false && ( - curConfig.preventAllURLConfig || - curPrefs.hasOwnProperty(key) - )) { - return; - } - if (cfgCfg.allowInitialUserOverride === true) { - defaultPrefs[key] = val; - } else { - $.pref(key, val); - } - } else if (['extensions', 'allowedOrigins'].indexOf(key) > -1) { - if (cfgCfg.overwrite === false && - ( - curConfig.preventAllURLConfig || - key === 'allowedOrigins' || - (key === 'extensions' && curConfig.lockExtensions) - ) - ) { - return; - } - curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later - // Only allow other curConfig if defined in defaultConfig - } else if (defaultConfig.hasOwnProperty(key)) { - if (cfgCfg.overwrite === false && ( - curConfig.preventAllURLConfig || - curConfig.hasOwnProperty(key) - )) { - return; - } - // Potentially overwriting of previously set config - if (curConfig.hasOwnProperty(key)) { - if (cfgCfg.overwrite === false) { - return; - } - extendOrAdd(curConfig, key, val); - } else { - if (cfgCfg.allowInitialUserOverride === true) { - extendOrAdd(defaultConfig, key, val); - } else { - if (defaultConfig[key] && typeof defaultConfig[key] === 'object') { - curConfig[key] = {}; - $.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects - } else { - curConfig[key] = val; - } - } - } - } - } - }); - editor.curConfig = curConfig; // Update exported value - }; - - /** - * @param {Object} opts Extension mechanisms may call setCustomHandlers with three functions: opts.open, opts.save, and opts.exportImage - * opts.open's responsibilities are: - * - invoke a file chooser dialog in 'open' mode - * - let user pick a SVG file - * - calls svgCanvas.setSvgString() with the string contents of that file - * opts.save's responsibilities are: - * - accept the string contents of the current document - * - invoke a file chooser dialog in 'save' mode - * - save the file to location chosen by the user - * opts.exportImage's responsibilities (with regard to the object it is supplied in its 2nd argument) are: - * - inform user of any issues supplied via the "issues" property - * - convert the "svg" property SVG string into an image for export; - * utilize the properties "type" (currently 'PNG', 'JPEG', 'BMP', - * 'WEBP', 'PDF'), "mimeType", and "quality" (for 'JPEG' and 'WEBP' - * types) to determine the proper output. - */ - editor.setCustomHandlers = function (opts) { - editor.ready(function () { - if (opts.open) { - $('#tool_open > input[type="file"]').remove(); - $('#tool_open').show(); - svgCanvas.open = opts.open; - } - if (opts.save) { - editor.showSaveWarning = false; - svgCanvas.bind('saved', opts.save); - } - if (opts.exportImage) { - customExportImage = opts.exportImage; - svgCanvas.bind('exported', customExportImage); // canvg and our RGBColor will be available to the method - } - if (opts.exportPDF) { - customExportPDF = opts.exportPDF; - svgCanvas.bind('exportedPDF', customExportPDF); // jsPDF and our RGBColor will be available to the method - } - }); - }; - - editor.randomizeIds = function () { - svgCanvas.randomizeIds(arguments); - }; - - editor.init = function () { - // var host = location.hostname, - // onWeb = host && host.indexOf('.') >= 0; - // Some FF versions throw security errors here when directly accessing - try { - if ('localStorage' in window) { // && onWeb removed so Webkit works locally - editor.storage = localStorage; - } - } catch (err) {} - - // Todo: Avoid var-defined functions and group functions together, etc. where possible - var goodLangs = []; - $('#lang_select option').each(function () { - goodLangs.push(this.value); - }); - - function setupCurPrefs () { - curPrefs = $.extend(true, {}, defaultPrefs, curPrefs); // Now safe to merge with priority for curPrefs in the event any are already set - // Export updated prefs - editor.curPrefs = curPrefs; - } - function setupCurConfig () { - curConfig = $.extend(true, {}, defaultConfig, curConfig); // Now safe to merge with priority for curConfig in the event any are already set - - // Now deal with extensions and other array config - if (!curConfig.noDefaultExtensions) { - curConfig.extensions = curConfig.extensions.concat(defaultExtensions); - } - // ...and remove any dupes - $.each(['extensions', 'allowedOrigins'], function (i, cfg) { - curConfig[cfg] = $.grep(curConfig[cfg], function (n, i) { - return i === curConfig[cfg].indexOf(n); - }); - }); - // Export updated config - editor.curConfig = curConfig; - } - (function () { - // Load config/data from URL if given - var src, qstr; - urldata = $.deparam.querystring(true); - if (!$.isEmptyObject(urldata)) { - if (urldata.dimensions) { - urldata.dimensions = urldata.dimensions.split(','); - } - - if (urldata.bkgd_color) { - urldata.bkgd_color = '#' + urldata.bkgd_color; - } - - if (urldata.extensions) { - // For security reasons, disallow cross-domain or cross-folder extensions via URL - urldata.extensions = urldata.extensions.match(/[:/\\]/) ? '' : urldata.extensions.split(','); - } - - // Disallowing extension paths via URL for - // security reasons, even for same-domain - // ones given potential to interact in undesirable - // ways with other script resources - $.each( - [ - 'extPath', 'imgPath', - 'langPath', 'jGraduatePath' - ], - function (pathConfig) { - if (urldata[pathConfig]) { - delete urldata[pathConfig]; - } - } - ); - - editor.setConfig(urldata, {overwrite: false}); // Note: source and url (as with storagePrompt later) are not set on config but are used below - - setupCurConfig(); - - if (!curConfig.preventURLContentLoading) { - src = urldata.source; - qstr = $.param.querystring(); - if (!src) { // urldata.source may have been null if it ended with '=' - if (qstr.indexOf('source=data:') >= 0) { - src = qstr.match(/source=(data:[^&]*)/)[1]; - } - } - if (src) { - if (src.indexOf('data:') === 0) { - editor.loadFromDataURI(src); - } else { - editor.loadFromString(src); - } - return; - } - if (urldata.url) { - editor.loadFromURL(urldata.url); - return; - } - } - if (!urldata.noStorageOnLoad || curConfig.forceStorage) { - editor.loadContentAndPrefs(); - } - setupCurPrefs(); - } else { - setupCurConfig(); - editor.loadContentAndPrefs(); - setupCurPrefs(); - } - }()); - - var setIcon = editor.setIcon = function (elem, iconId, forcedSize) { - var icon = (typeof iconId === 'string') ? $.getSvgIcon(iconId, true) : iconId.clone(); - if (!icon) { - console.log('NOTE: Icon image missing: ' + iconId); - return; - } - $(elem).empty().append(icon); - }; - - var extFunc = function () { - $.each(curConfig.extensions, function () { - var extname = this; - if (!extname.match(/^ext-.*\.js/)) { // Ensure URL cannot specify some other unintended file in the extPath - return; - } - $.getScript(curConfig.extPath + extname, function (d) { - // Fails locally in Chrome 5 - if (!d) { - var s = document.createElement('script'); - s.src = curConfig.extPath + extname; - document.querySelector('head').appendChild(s); - } - }).fail(function (jqxhr, settings, exception) { - console.log(exception); - }); - }); - - // var lang = ('lang' in curPrefs) ? curPrefs.lang : null; - editor.putLocale(null, goodLangs); - }; - - // Load extensions - // Bit of a hack to run extensions in local Opera/IE9 - if (document.location.protocol === 'file:') { - setTimeout(extFunc, 100); - } else { - extFunc(); - } - $.svgIcons(curConfig.imgPath + 'svg_edit_icons.svg', { - w: 24, h: 24, - id_match: false, - no_img: !svgedit.browser.isWebkit(), // Opera & Firefox 4 gives odd behavior w/images - fallback_path: curConfig.imgPath, - fallback: { - 'new_image': 'clear.png', - 'save': 'save.png', - 'open': 'open.png', - 'source': 'source.png', - 'docprops': 'document-properties.png', - 'wireframe': 'wireframe.png', - - 'undo': 'undo.png', - 'redo': 'redo.png', - - 'select': 'select.png', - 'select_node': 'select_node.png', - 'pencil': 'fhpath.png', - 'pen': 'line.png', - 'square': 'square.png', - 'rect': 'rect.png', - 'fh_rect': 'freehand-square.png', - 'circle': 'circle.png', - 'ellipse': 'ellipse.png', - 'fh_ellipse': 'freehand-circle.png', - 'path': 'path.png', - 'text': 'text.png', - 'image': 'image.png', - 'zoom': 'zoom.png', - - 'clone': 'clone.png', - 'node_clone': 'node_clone.png', - 'delete': 'delete.png', - 'node_delete': 'node_delete.png', - 'group': 'shape_group_elements.png', - 'ungroup': 'shape_ungroup.png', - 'move_top': 'move_top.png', - 'move_bottom': 'move_bottom.png', - 'to_path': 'to_path.png', - 'link_controls': 'link_controls.png', - 'reorient': 'reorient.png', - - 'align_left': 'align-left.png', - 'align_center': 'align-center.png', - 'align_right': 'align-right.png', - 'align_top': 'align-top.png', - 'align_middle': 'align-middle.png', - 'align_bottom': 'align-bottom.png', - - 'go_up': 'go-up.png', - 'go_down': 'go-down.png', - - 'ok': 'save.png', - 'cancel': 'cancel.png', - - 'arrow_right': 'flyouth.png', - 'arrow_down': 'dropdown.gif' - }, - placement: { - '#logo': 'logo', - - '#tool_clear div,#layer_new': 'new_image', - '#tool_save div': 'save', - '#tool_export div': 'export', - '#tool_open div div': 'open', - '#tool_import div div': 'import', - '#tool_source': 'source', - '#tool_docprops > div': 'docprops', - '#tool_wireframe': 'wireframe', - - '#tool_undo': 'undo', - '#tool_redo': 'redo', - - '#tool_select': 'select', - '#tool_fhpath': 'pencil', - '#tool_line': 'pen', - '#tool_rect,#tools_rect_show': 'rect', - '#tool_square': 'square', - '#tool_fhrect': 'fh_rect', - '#tool_ellipse,#tools_ellipse_show': 'ellipse', - '#tool_circle': 'circle', - '#tool_fhellipse': 'fh_ellipse', - '#tool_path': 'path', - '#tool_text,#layer_rename': 'text', - '#tool_image': 'image', - '#tool_zoom': 'zoom', - - '#tool_clone,#tool_clone_multi': 'clone', - '#tool_node_clone': 'node_clone', - '#layer_delete,#tool_delete,#tool_delete_multi': 'delete', - '#tool_node_delete': 'node_delete', - '#tool_add_subpath': 'add_subpath', - '#tool_openclose_path': 'open_path', - '#tool_move_top': 'move_top', - '#tool_move_bottom': 'move_bottom', - '#tool_topath': 'to_path', - '#tool_node_link': 'link_controls', - '#tool_reorient': 'reorient', - '#tool_group_elements': 'group_elements', - '#tool_ungroup': 'ungroup', - '#tool_unlink_use': 'unlink_use', - - '#tool_alignleft, #tool_posleft': 'align_left', - '#tool_aligncenter, #tool_poscenter': 'align_center', - '#tool_alignright, #tool_posright': 'align_right', - '#tool_aligntop, #tool_postop': 'align_top', - '#tool_alignmiddle, #tool_posmiddle': 'align_middle', - '#tool_alignbottom, #tool_posbottom': 'align_bottom', - '#cur_position': 'align', - - '#linecap_butt,#cur_linecap': 'linecap_butt', - '#linecap_round': 'linecap_round', - '#linecap_square': 'linecap_square', - - '#linejoin_miter,#cur_linejoin': 'linejoin_miter', - '#linejoin_round': 'linejoin_round', - '#linejoin_bevel': 'linejoin_bevel', - - '#url_notice': 'warning', - - '#layer_up': 'go_up', - '#layer_down': 'go_down', - '#layer_moreopts': 'context_menu', - '#layerlist td.layervis': 'eye', - - '#tool_source_save,#tool_docprops_save,#tool_prefs_save': 'ok', - '#tool_source_cancel,#tool_docprops_cancel,#tool_prefs_cancel': 'cancel', - - '#rwidthLabel, #iwidthLabel': 'width', - '#rheightLabel, #iheightLabel': 'height', - '#cornerRadiusLabel span': 'c_radius', - '#angleLabel': 'angle', - '#linkLabel,#tool_make_link,#tool_make_link_multi': 'globe_link', - '#zoomLabel': 'zoom', - '#tool_fill label': 'fill', - '#tool_stroke .icon_label': 'stroke', - '#group_opacityLabel': 'opacity', - '#blurLabel': 'blur', - '#font_sizeLabel': 'fontsize', - - '.flyout_arrow_horiz': 'arrow_right', - '.dropdown button, #main_button .dropdown': 'arrow_down', - '#palette .palette_item:first, #fill_bg, #stroke_bg': 'no_color' - }, - resize: { - '#logo .svg_icon': 28, - '.flyout_arrow_horiz .svg_icon': 5, - '.layer_button .svg_icon, #layerlist td.layervis .svg_icon': 14, - '.dropdown button .svg_icon': 7, - '#main_button .dropdown .svg_icon': 9, - '.palette_item:first .svg_icon': 15, - '#fill_bg .svg_icon, #stroke_bg .svg_icon': 16, - '.toolbar_button button .svg_icon': 16, - '.stroke_tool div div .svg_icon': 20, - '#tools_bottom label .svg_icon': 18 - }, - callback: function (icons) { - $('.toolbar_button button > svg, .toolbar_button button > img').each(function () { - $(this).parent().prepend(this); - }); - - var minHeight, - tleft = $('#tools_left'); - if (tleft.length !== 0) { - minHeight = tleft.offset().top + tleft.outerHeight(); - } - - var size = $.pref('iconsize'); - editor.setIconSize(size || ($(window).height() < minHeight ? 's' : 'm')); - - // Look for any missing flyout icons from plugins - $('.tools_flyout').each(function () { - var shower = $('#' + this.id + '_show'); - var sel = shower.attr('data-curopt'); - // Check if there's an icon here - if (!shower.children('svg, img').length) { - var clone = $(sel).children().clone(); - if (clone.length) { - clone[0].removeAttribute('style'); // Needed for Opera - shower.append(clone); - } - } - }); - - editor.runCallbacks(); - - setTimeout(function () { - $('.flyout_arrow_horiz:empty').each(function () { - $(this).append($.getSvgIcon('arrow_right').width(5).height(5)); - }); - }, 1); - } - }); - - editor.canvas = svgCanvas = new $.SvgCanvas(document.getElementById('svgcanvas'), curConfig); - var supportsNonSS, resizeTimer, changeZoom, Actions, curScrollPos, - palette = [ // Todo: Make into configuration item? - '#000000', '#3f3f3f', '#7f7f7f', '#bfbfbf', '#ffffff', - '#ff0000', '#ff7f00', '#ffff00', '#7fff00', - '#00ff00', '#00ff7f', '#00ffff', '#007fff', - '#0000ff', '#7f00ff', '#ff00ff', '#ff007f', - '#7f0000', '#7f3f00', '#7f7f00', '#3f7f00', - '#007f00', '#007f3f', '#007f7f', '#003f7f', - '#00007f', '#3f007f', '#7f007f', '#7f003f', - '#ffaaaa', '#ffd4aa', '#ffffaa', '#d4ffaa', - '#aaffaa', '#aaffd4', '#aaffff', '#aad4ff', - '#aaaaff', '#d4aaff', '#ffaaff', '#ffaad4' - ], - modKey = (svgedit.browser.isMac() ? 'meta+' : 'ctrl+'), // ⌘ - path = svgCanvas.pathActions, - undoMgr = svgCanvas.undoMgr, - defaultImageURL = curConfig.imgPath + 'logo.png', - workarea = $('#workarea'), - canvMenu = $('#cmenu_canvas'), - // layerMenu = $('#cmenu_layers'), // Unused - exportWindow = null, - zoomInIcon = 'crosshair', - zoomOutIcon = 'crosshair', - uiContext = 'toolbars', - origSource = '', - paintBox = {fill: null, stroke: null}; - - // For external openers - (function () { - // let the opener know SVG Edit is ready (now that config is set up) - var svgEditorReadyEvent, - w = window.opener || window.parent; - if (w) { - try { - svgEditorReadyEvent = w.document.createEvent('Event'); - svgEditorReadyEvent.initEvent('svgEditorReady', true, true); - w.document.documentElement.dispatchEvent(svgEditorReadyEvent); - } catch (e) {} - } - }()); - - // This sets up alternative dialog boxes. They mostly work the same way as - // their UI counterparts, expect instead of returning the result, a callback - // needs to be included that returns the result as its first parameter. - // In the future we may want to add additional types of dialog boxes, since - // they should be easy to handle this way. - (function () { - $('#dialog_container').draggable({cancel: '#dialog_content, #dialog_buttons *', containment: 'window'}); - var box = $('#dialog_box'), - btnHolder = $('#dialog_buttons'), - dialogContent = $('#dialog_content'), - dbox = function (type, msg, callback, defaultVal, opts, changeCb, checkbox) { - var ok, ctrl, chkbx; - dialogContent.html('

    ' + msg.replace(/\n/g, '

    ') + '

    ') - .toggleClass('prompt', (type === 'prompt')); - btnHolder.empty(); - - ok = $('').appendTo(btnHolder); - - if (type !== 'alert') { - $('') - .appendTo(btnHolder) - .click(function () { box.hide(); if (callback) { callback(false); } }); - } - - if (type === 'prompt') { - ctrl = $('').prependTo(btnHolder); - ctrl.val(defaultVal || ''); - ctrl.bind('keydown', 'return', function () { ok.click(); }); - } else if (type === 'select') { - var div = $('
    '); - ctrl = $('').appendTo(label); - chkbx.val(checkbox.value); - if (checkbox.tooltip) { - label.attr('title', checkbox.tooltip); - } - chkbx.prop('checked', !!checkbox.checked); - div.append($('
    ').append(label)); - } - $.each(opts || [], function (opt, val) { - if (typeof val === 'object') { - ctrl.append($(''); - } - if (icon !== undefined) { - var copy = icon.clone(); - $('td.layervis', layerlist).append(copy); - $.resizeSvgIcons({'td.layervis .svg_icon': 14}); - } - // handle selection of layer - $('#layerlist td.layername') - .mouseup(function (evt) { - $('#layerlist tr.layer').removeClass('layersel'); - $(this.parentNode).addClass('layersel'); - svgCanvas.setCurrentLayer(this.textContent); - evt.preventDefault(); - }) - .mouseover(function () { - toggleHighlightLayer(this.textContent); - }) - .mouseout(function () { - toggleHighlightLayer(); - }); - $('#layerlist td.layervis').click(function () { - var row = $(this.parentNode).prevAll().length; - var name = $('#layerlist tr.layer:eq(' + row + ') td.layername').text(); - var vis = $(this).hasClass('layerinvis'); - svgCanvas.setLayerVisibility(name, vis); - $(this).toggleClass('layerinvis'); - }); - - // if there were too few rows, let's add a few to make it not so lonely - var num = 5 - $('#layerlist tr.layer').size(); - while (num-- > 0) { - // FIXME: there must a better way to do this - layerlist.append('_'); - } - }; - - var showSourceEditor = function (e, forSaving) { - if (editingsource) { return; } - - editingsource = true; - origSource = svgCanvas.getSvgString(); - $('#save_output_btns').toggle(!!forSaving); - $('#tool_source_back').toggle(!forSaving); - $('#svg_source_textarea').val(origSource); - $('#svg_source_editor').fadeIn(); - $('#svg_source_textarea').focus(); - }; - - var togglePathEditMode = function (editmode, elems) { - $('#path_node_panel').toggle(editmode); - $('#tools_bottom_2,#tools_bottom_3').toggle(!editmode); - if (editmode) { - // Change select icon - $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); - $('#tool_select').addClass('tool_button_current').removeClass('tool_button'); - setIcon('#tool_select', 'select_node'); - multiselected = false; - if (elems.length) { - selectedElement = elems[0]; - } - } else { - setTimeout(function () { - setIcon('#tool_select', 'select'); - }, 1000); - } - }; - - var saveHandler = function (wind, svg) { - editor.showSaveWarning = false; - - // by default, we add the XML prolog back, systems integrating SVG-edit (wikis, CMSs) - // can just provide their own custom save handler and might not want the XML prolog - svg = '\n' + svg; - - // IE9 doesn't allow standalone Data URLs - // https://connect.microsoft.com/IE/feedback/details/542600/data-uri-images-fail-when-loaded-by-themselves - if (svgedit.browser.isIE()) { - showSourceEditor(0, true); - return; - } - - // Since saving SVGs by opening a new window was removed in Chrome use artificial link-click - // https://stackoverflow.com/questions/45603201/window-is-not-allowed-to-navigate-top-frame-navigations-to-data-urls - var a = document.createElement('a'); - a.href = 'data:image/svg+xml;base64,' + Utils.encode64(svg); - a.download = 'icon.svg'; - - a.click(); - - // Alert will only appear the first time saved OR the - // first time the bug is encountered - var done = $.pref('save_notice_done'); - if (done !== 'all') { - var note = uiStrings.notification.saveFromBrowser.replace('%s', 'SVG'); - - // Check if FF and has - if (svgedit.browser.isGecko()) { - if (svg.indexOf('').hide().appendTo('body').remove(); - }; - - function setStrokeOpt (opt, changeElem) { - var id = opt.id; - var bits = id.split('_'); - var pre = bits[0]; - var val = bits[1]; - - if (changeElem) { - svgCanvas.setStrokeAttr('stroke-' + pre, val); - } - operaRepaint(); - setIcon('#cur_' + pre, id, 20); - $(opt).addClass('current').siblings().removeClass('current'); - } - - // This is a common function used when a tool has been clicked (chosen) - // It does several common things: - // - removes the tool_button_current class from whatever tool currently has it - // - hides any flyouts - // - adds the tool_button_current class to the button passed in - var toolButtonClick = editor.toolButtonClick = function (button, noHiding) { - if ($(button).hasClass('disabled')) { return false; } - if ($(button).parent().hasClass('tools_flyout')) { return true; } - var fadeFlyouts = 'normal'; - if (!noHiding) { - $('.tools_flyout').fadeOut(fadeFlyouts); - } - $('#styleoverrides').text(''); - workarea.css('cursor', 'auto'); - $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); - $(button).addClass('tool_button_current').removeClass('tool_button'); - return true; - }; - - var clickSelect = editor.clickSelect = function () { - if (toolButtonClick('#tool_select')) { - svgCanvas.setMode('select'); - $('#styleoverrides').text('#svgcanvas svg *{cursor:move;pointer-events:all}, #svgcanvas svg{cursor:default}'); - } - }; - - var setImageURL = editor.setImageURL = function (url) { - if (!url) { - url = defaultImageURL; - } - svgCanvas.setImageURL(url); - $('#image_url').val(url); - - if (url.indexOf('data:') === 0) { - // data URI found - $('#image_url').hide(); - $('#change_image_url').show(); - } else { - // regular URL - svgCanvas.embedImage(url, function (dataURI) { - // Couldn't embed, so show warning - $('#url_notice').toggle(!dataURI); - defaultImageURL = url; - }); - $('#image_url').show(); - $('#change_image_url').hide(); - } - }; - - function setBackground (color, url) { - // if (color == $.pref('bkgd_color') && url == $.pref('bkgd_url')) { return; } - $.pref('bkgd_color', color); - $.pref('bkgd_url', url); - - // This should be done in svgcanvas.js for the borderRect fill - svgCanvas.setBackground(color, url); - } - - function promptImgURL () { - var curhref = svgCanvas.getHref(selectedElement); - curhref = curhref.indexOf('data:') === 0 ? '' : curhref; - $.prompt(uiStrings.notification.enterNewImgURL, curhref, function (url) { - if (url) { setImageURL(url); } - }); - } - - var setInputWidth = function (elem) { - var w = Math.min(Math.max(12 + elem.value.length * 6, 50), 300); - $(elem).width(w); - }; - - function updateRulers (scanvas, zoom) { - if (!zoom) { zoom = svgCanvas.getZoom(); } - if (!scanvas) { scanvas = $('#svgcanvas'); } - - var d, i; - var limit = 30000; - var contentElem = svgCanvas.getContentElem(); - var units = svgedit.units.getTypeMap(); - var unit = units[curConfig.baseUnit]; // 1 = 1px - - // draw x ruler then y ruler - for (d = 0; d < 2; d++) { - var isX = (d === 0); - var dim = isX ? 'x' : 'y'; - var lentype = isX ? 'width' : 'height'; - var contentDim = Number(contentElem.getAttribute(dim)); - - var $hcanvOrig = $('#ruler_' + dim + ' canvas:first'); - - // Bit of a hack to fully clear the canvas in Safari & IE9 - var $hcanv = $hcanvOrig.clone(); - $hcanvOrig.replaceWith($hcanv); - - var hcanv = $hcanv[0]; - - // Set the canvas size to the width of the container - var rulerLen = scanvas[lentype](); - var totalLen = rulerLen; - hcanv.parentNode.style[lentype] = totalLen + 'px'; - var ctxNum = 0; - var ctx = hcanv.getContext('2d'); - var ctxArr, num, ctxArrNum; - - ctx.fillStyle = 'rgb(200,0,0)'; - ctx.fillRect(0, 0, hcanv.width, hcanv.height); - - // Remove any existing canvasses - $hcanv.siblings().remove(); - - // Create multiple canvases when necessary (due to browser limits) - if (rulerLen >= limit) { - ctxArrNum = parseInt(rulerLen / limit, 10) + 1; - ctxArr = []; - ctxArr[0] = ctx; - var copy; - for (i = 1; i < ctxArrNum; i++) { - hcanv[lentype] = limit; - copy = hcanv.cloneNode(true); - hcanv.parentNode.appendChild(copy); - ctxArr[i] = copy.getContext('2d'); - } - - copy[lentype] = rulerLen % limit; - - // set copy width to last - rulerLen = limit; - } - - hcanv[lentype] = rulerLen; - - var uMulti = unit * zoom; - - // Calculate the main number interval - var rawM = 50 / uMulti; - var multi = 1; - for (i = 0; i < rIntervals.length; i++) { - num = rIntervals[i]; - multi = num; - if (rawM <= num) { - break; - } - } - - var bigInt = multi * uMulti; - - ctx.font = '9px sans-serif'; - - var rulerD = ((contentDim / uMulti) % multi) * uMulti; - var labelPos = rulerD - bigInt; - // draw big intervals - while (rulerD < totalLen) { - labelPos += bigInt; - // var real_d = rulerD - contentDim; // Currently unused - - var curD = Math.round(rulerD) + 0.5; - if (isX) { - ctx.moveTo(curD, 15); - ctx.lineTo(curD, 0); - } else { - ctx.moveTo(15, curD); - ctx.lineTo(0, curD); - } - - num = (labelPos - contentDim) / uMulti; - var label; - if (multi >= 1) { - label = Math.round(num); - } else { - var decs = String(multi).split('.')[1].length; - label = num.toFixed(decs); - } - - // Change 1000s to Ks - if (label !== 0 && label !== 1000 && label % 1000 === 0) { - label = (label / 1000) + 'K'; - } - - if (isX) { - ctx.fillText(label, rulerD + 2, 8); - } else { - // draw label vertically - var str = String(label).split(''); - for (i = 0; i < str.length; i++) { - ctx.fillText(str[i], 1, (rulerD + 9) + i * 9); - } - } - - var part = bigInt / 10; - // draw the small intervals - for (i = 1; i < 10; i++) { - var subD = Math.round(rulerD + part * i) + 0.5; - if (ctxArr && subD > rulerLen) { - ctxNum++; - ctx.stroke(); - if (ctxNum >= ctxArrNum) { - i = 10; - rulerD = totalLen; - continue; - } - ctx = ctxArr[ctxNum]; - rulerD -= limit; - subD = Math.round(rulerD + part * i) + 0.5; - } - - // odd lines are slighly longer - var lineNum = (i % 2) ? 12 : 10; - if (isX) { - ctx.moveTo(subD, 15); - ctx.lineTo(subD, lineNum); - } else { - ctx.moveTo(15, subD); - ctx.lineTo(lineNum, subD); - } - } - rulerD += bigInt; - } - ctx.strokeStyle = '#000'; - ctx.stroke(); - } - } - - var updateCanvas = editor.updateCanvas = function (center, newCtr) { - var w = workarea.width(), h = workarea.height(); - var wOrig = w, hOrig = h; - var zoom = svgCanvas.getZoom(); - var wArea = workarea; - var cnvs = $('#svgcanvas'); - var oldCtr = { - x: wArea[0].scrollLeft + wOrig / 2, - y: wArea[0].scrollTop + hOrig / 2 - }; - var multi = curConfig.canvas_expansion; - w = Math.max(wOrig, svgCanvas.contentW * zoom * multi); - h = Math.max(hOrig, svgCanvas.contentH * zoom * multi); - - if (w === wOrig && h === hOrig) { - workarea.css('overflow', 'hidden'); - } else { - workarea.css('overflow', 'scroll'); - } - - var oldCanY = cnvs.height() / 2; - var oldCanX = cnvs.width() / 2; - cnvs.width(w).height(h); - var newCanY = h / 2; - var newCanX = w / 2; - var offset = svgCanvas.updateCanvas(w, h); - - var ratio = newCanX / oldCanX; - - var scrollX = w / 2 - wOrig / 2; - var scrollY = h / 2 - hOrig / 2; - - if (!newCtr) { - var oldDistX = oldCtr.x - oldCanX; - var newX = newCanX + oldDistX * ratio; - - var oldDistY = oldCtr.y - oldCanY; - var newY = newCanY + oldDistY * ratio; - - newCtr = { - x: newX, - y: newY - }; - } else { - newCtr.x += offset.x; - newCtr.y += offset.y; - } - - if (center) { - // Go to top-left for larger documents - if (svgCanvas.contentW > wArea.width()) { - // Top-left - workarea[0].scrollLeft = offset.x - 10; - workarea[0].scrollTop = offset.y - 10; - } else { - // Center - wArea[0].scrollLeft = scrollX; - wArea[0].scrollTop = scrollY; - } - } else { - wArea[0].scrollLeft = newCtr.x - wOrig / 2; - wArea[0].scrollTop = newCtr.y - hOrig / 2; - } - if (curConfig.showRulers) { - updateRulers(cnvs, zoom); - workarea.scroll(); - } - if (urldata.storagePrompt !== true && !editor.storagePromptClosed) { - $('#dialog_box').hide(); - } - }; - - var updateToolButtonState = function () { - var index, button; - var bNoFill = (svgCanvas.getColor('fill') === 'none'); - var bNoStroke = (svgCanvas.getColor('stroke') === 'none'); - var buttonsNeedingStroke = ['#tool_fhpath', '#tool_line']; - var buttonsNeedingFillAndStroke = ['#tools_rect .tool_button', '#tools_ellipse .tool_button', '#tool_text', '#tool_path']; - if (bNoStroke) { - for (index in buttonsNeedingStroke) { - button = buttonsNeedingStroke[index]; - if ($(button).hasClass('tool_button_current')) { - clickSelect(); - } - $(button).addClass('disabled'); - } - } else { - for (index in buttonsNeedingStroke) { - button = buttonsNeedingStroke[index]; - $(button).removeClass('disabled'); - } - } - - if (bNoStroke && bNoFill) { - for (index in buttonsNeedingFillAndStroke) { - button = buttonsNeedingFillAndStroke[index]; - if ($(button).hasClass('tool_button_current')) { - clickSelect(); - } - $(button).addClass('disabled'); - } - } else { - for (index in buttonsNeedingFillAndStroke) { - button = buttonsNeedingFillAndStroke[index]; - $(button).removeClass('disabled'); - } - } - - svgCanvas.runExtensions('toolButtonStateUpdate', { - nofill: bNoFill, - nostroke: bNoStroke - }); - - // Disable flyouts if all inside are disabled - $('.tools_flyout').each(function () { - var shower = $('#' + this.id + '_show'); - var hasEnabled = false; - $(this).children().each(function () { - if (!$(this).hasClass('disabled')) { - hasEnabled = true; - } - }); - shower.toggleClass('disabled', !hasEnabled); - }); - - operaRepaint(); - }; - - // Updates the toolbar (colors, opacity, etc) based on the selected element - // This function also updates the opacity and id elements that are in the context panel - var updateToolbar = function () { - var i, len; - if (selectedElement != null) { - switch (selectedElement.tagName) { - case 'use': - case 'image': - case 'foreignObject': - break; - case 'g': - case 'a': - // Look for common styles - var gWidth = null; - var childs = selectedElement.getElementsByTagName('*'); - for (i = 0, len = childs.length; i < len; i++) { - var swidth = childs[i].getAttribute('stroke-width'); - - if (i === 0) { - gWidth = swidth; - } else if (gWidth !== swidth) { - gWidth = null; - } - } - - $('#stroke_width').val(gWidth === null ? '' : gWidth); - - paintBox.fill.update(true); - paintBox.stroke.update(true); - - break; - default: - paintBox.fill.update(true); - paintBox.stroke.update(true); - - $('#stroke_width').val(selectedElement.getAttribute('stroke-width') || 1); - $('#stroke_style').val(selectedElement.getAttribute('stroke-dasharray') || 'none'); - - var attr = selectedElement.getAttribute('stroke-linejoin') || 'miter'; - - if ($('#linejoin_' + attr).length !== 0) { - setStrokeOpt($('#linejoin_' + attr)[0]); - } - - attr = selectedElement.getAttribute('stroke-linecap') || 'butt'; - - if ($('#linecap_' + attr).length !== 0) { - setStrokeOpt($('#linecap_' + attr)[0]); - } - } - } - - // All elements including image and group have opacity - if (selectedElement != null) { - var opacPerc = (selectedElement.getAttribute('opacity') || 1.0) * 100; - $('#group_opacity').val(opacPerc); - $('#opac_slider').slider('option', 'value', opacPerc); - $('#elem_id').val(selectedElement.id); - $('#elem_class').val(selectedElement.getAttribute('class')); - } - - updateToolButtonState(); - }; - - // updates the context panel tools based on the selected element - var updateContextPanel = function () { - var elem = selectedElement; - // If element has just been deleted, consider it null - if (elem != null && !elem.parentNode) { elem = null; } - var currentLayerName = svgCanvas.getCurrentDrawing().getCurrentLayerName(); - var currentMode = svgCanvas.getMode(); - var unit = curConfig.baseUnit !== 'px' ? curConfig.baseUnit : null; - - var isNode = currentMode === 'pathedit'; // elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false; - var menuItems = $('#cmenu_canvas li'); - $('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,' + - '#ellipse_panel, #line_panel, #text_panel, #image_panel, #container_panel,' + - ' #use_panel, #a_panel').hide(); - if (elem != null) { - var elname = elem.nodeName; - // If this is a link with no transform and one child, pretend - // its child is selected - // if (elname === 'a') { // && !$(elem).attr('transform')) { - // elem = elem.firstChild; - // } - - var angle = svgCanvas.getRotationAngle(elem); - $('#angle').val(angle); - - var blurval = svgCanvas.getBlur(elem); - $('#blur').val(blurval); - $('#blur_slider').slider('option', 'value', blurval); - - if (svgCanvas.addedNew) { - if (elname === 'image' && svgCanvas.getMode() === 'image') { - // Prompt for URL if not a data URL - if (svgCanvas.getHref(elem).indexOf('data:') !== 0) { - promptImgURL(); - } - } - /* else if (elname == 'text') { - // TODO: Do something here for new text - } */ - } - - if (!isNode && currentMode !== 'pathedit') { - $('#selected_panel').show(); - // Elements in this array already have coord fields - if (['line', 'circle', 'ellipse'].indexOf(elname) >= 0) { - $('#xy_panel').hide(); - } else { - var x, y; - - // Get BBox vals for g, polyline and path - if (['g', 'polyline', 'path'].indexOf(elname) >= 0) { - var bb = svgCanvas.getStrokedBBox([elem]); - if (bb) { - x = bb.x; - y = bb.y; - } - } else { - x = elem.getAttribute('x'); - y = elem.getAttribute('y'); - } - - if (unit) { - x = svgedit.units.convertUnit(x); - y = svgedit.units.convertUnit(y); - } - - $('#selected_x').val(x || 0); - $('#selected_y').val(y || 0); - $('#xy_panel').show(); - } - - // Elements in this array cannot be converted to a path - var noPath = ['image', 'text', 'path', 'g', 'use'].indexOf(elname) === -1; - $('#tool_topath').toggle(noPath); - $('#tool_reorient').toggle(elname === 'path'); - $('#tool_reorient').toggleClass('disabled', angle === 0); - } else { - var point = path.getNodePoint(); - $('#tool_add_subpath').removeClass('push_button_pressed').addClass('tool_button'); - $('#tool_node_delete').toggleClass('disabled', !path.canDeleteNodes); - - // Show open/close button based on selected point - setIcon('#tool_openclose_path', path.closed_subpath ? 'open_path' : 'close_path'); - - if (point) { - var segType = $('#seg_type'); - if (unit) { - point.x = svgedit.units.convertUnit(point.x); - point.y = svgedit.units.convertUnit(point.y); - } - $('#path_node_x').val(point.x); - $('#path_node_y').val(point.y); - if (point.type) { - segType.val(point.type).removeAttr('disabled'); - } else { - segType.val(4).attr('disabled', 'disabled'); - } - } - return; - } - - // update contextual tools here - var panels = { - g: [], - a: [], - rect: ['rx', 'width', 'height'], - image: ['width', 'height'], - circle: ['cx', 'cy', 'r'], - ellipse: ['cx', 'cy', 'rx', 'ry'], - line: ['x1', 'y1', 'x2', 'y2'], - text: [], - use: [] - }; - - var tagName = elem.tagName; - - // if ($(elem).data('gsvg')) { - // $('#g_panel').show(); - // } - - var linkHref = null; - if (tagName === 'a') { - linkHref = svgCanvas.getHref(elem); - $('#g_panel').show(); - } - - if (elem.parentNode.tagName === 'a') { - if (!$(elem).siblings().length) { - $('#a_panel').show(); - linkHref = svgCanvas.getHref(elem.parentNode); - } - } - - // Hide/show the make_link buttons - $('#tool_make_link, #tool_make_link').toggle(!linkHref); - - if (linkHref) { - $('#link_url').val(linkHref); - } - - if (panels[tagName]) { - var curPanel = panels[tagName]; - - $('#' + tagName + '_panel').show(); - - $.each(curPanel, function (i, item) { - var attrVal = elem.getAttribute(item); - if (curConfig.baseUnit !== 'px' && elem[item]) { - var bv = elem[item].baseVal.value; - attrVal = svgedit.units.convertUnit(bv); - } - $('#' + tagName + '_' + item).val(attrVal || 0); - }); - - if (tagName === 'text') { - $('#text_panel').css('display', 'inline'); - $('#tool_font_size').css('display', 'inline'); - if (svgCanvas.getItalic()) { - $('#tool_italic').addClass('push_button_pressed').removeClass('tool_button'); - } else { - $('#tool_italic').removeClass('push_button_pressed').addClass('tool_button'); - } - if (svgCanvas.getBold()) { - $('#tool_bold').addClass('push_button_pressed').removeClass('tool_button'); - } else { - $('#tool_bold').removeClass('push_button_pressed').addClass('tool_button'); - } - $('#font_family').val(elem.getAttribute('font-family')); - $('#font_size').val(elem.getAttribute('font-size')); - $('#text').val(elem.textContent); - if (svgCanvas.addedNew) { - // Timeout needed for IE9 - setTimeout(function () { - $('#text').focus().select(); - }, 100); - } - // text - } else if (tagName === 'image' && svgCanvas.getMode() === 'image') { - setImageURL(svgCanvas.getHref(elem)); - // image - } else if (tagName === 'g' || tagName === 'use') { - $('#container_panel').show(); - var title = svgCanvas.getTitle(); - var label = $('#g_title')[0]; - label.value = title; - setInputWidth(label); - $('#g_title').prop('disabled', tagName === 'use'); - } - } - menuItems[(tagName === 'g' ? 'en' : 'dis') + 'ableContextMenuItems']('#ungroup'); - menuItems[((tagName === 'g' || !multiselected) ? 'dis' : 'en') + 'ableContextMenuItems']('#group'); - // if (elem != null) - } else if (multiselected) { - $('#multiselected_panel').show(); - menuItems - .enableContextMenuItems('#group') - .disableContextMenuItems('#ungroup'); - } else { - menuItems.disableContextMenuItems('#delete,#cut,#copy,#group,#ungroup,#move_front,#move_up,#move_down,#move_back'); - } - - // update history buttons - $('#tool_undo').toggleClass('disabled', undoMgr.getUndoStackSize() === 0); - $('#tool_redo').toggleClass('disabled', undoMgr.getRedoStackSize() === 0); - - svgCanvas.addedNew = false; - - if ((elem && !isNode) || multiselected) { - // update the selected elements' layer - $('#selLayerNames').removeAttr('disabled').val(currentLayerName); - - // Enable regular menu options - canvMenu.enableContextMenuItems('#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back'); - } else { - $('#selLayerNames').attr('disabled', 'disabled'); - } - }; - - var updateWireFrame = function () { - // Test support - if (supportsNonSS) { return; } - - var rule = '#workarea.wireframe #svgcontent * { stroke-width: ' + 1 / svgCanvas.getZoom() + 'px; }'; - $('#wireframe_rules').text(workarea.hasClass('wireframe') ? rule : ''); - }; - - var updateTitle = function (title) { - title = title || svgCanvas.getDocumentTitle(); - var newTitle = origTitle + (title ? ': ' + title : ''); - - // Remove title update with current context info, isn't really necessary - // if (curContext) { - // new_title = new_title + curContext; - // } - $('title:first').text(newTitle); - }; - - // called when we've selected a different element - var selectedChanged = function (win, elems) { - var mode = svgCanvas.getMode(); - if (mode === 'select') { - setSelectMode(); - } - var isNode = mode === 'pathedit'; - // if elems[1] is present, then we have more than one element - selectedElement = (elems.length === 1 || elems[1] == null ? elems[0] : null); - multiselected = (elems.length >= 2 && elems[1] != null); - if (selectedElement != null) { - // unless we're already in always set the mode of the editor to select because - // upon creation of a text element the editor is switched into - // select mode and this event fires - we need our UI to be in sync - - if (!isNode) { - updateToolbar(); - } - } // if (elem != null) - - // Deal with pathedit mode - togglePathEditMode(isNode, elems); - updateContextPanel(); - svgCanvas.runExtensions('selectedChanged', { - elems: elems, - selectedElement: selectedElement, - multiselected: multiselected - }); - }; - - // Call when part of element is in process of changing, generally - // on mousemove actions like rotate, move, etc. - var elementTransition = function (win, elems) { - var mode = svgCanvas.getMode(); - var elem = elems[0]; - - if (!elem) { - return; - } - - multiselected = (elems.length >= 2 && elems[1] != null); - // Only updating fields for single elements for now - if (!multiselected) { - switch (mode) { - case 'rotate': - var ang = svgCanvas.getRotationAngle(elem); - $('#angle').val(ang); - $('#tool_reorient').toggleClass('disabled', ang === 0); - break; - - // TODO: Update values that change on move/resize, etc - // case "select": - // case "resize": - // break; - } - } - svgCanvas.runExtensions('elementTransition', { - elems: elems - }); - }; - - /** - * Test whether an element is a layer or not. - * @param {SVGGElement} elem - The SVGGElement to test. - * @returns {boolean} True if the element is a layer - */ - function isLayer (elem) { - return elem && elem.tagName === 'g' && svgedit.draw.Layer.CLASS_REGEX.test(elem.getAttribute('class')); - } - - // called when any element has changed - var elementChanged = function (win, elems) { - var i, - mode = svgCanvas.getMode(); - if (mode === 'select') { - setSelectMode(); - } - - for (i = 0; i < elems.length; ++i) { - var elem = elems[i]; - - var isSvgElem = (elem && elem.tagName === 'svg'); - if (isSvgElem || isLayer(elem)) { - populateLayers(); - // if the element changed was the svg, then it could be a resolution change - if (isSvgElem) { - updateCanvas(); - } - // Update selectedElement if element is no longer part of the image. - // This occurs for the text elements in Firefox - } else if (elem && selectedElement && selectedElement.parentNode == null) { - // || elem && elem.tagName == "path" && !multiselected) { // This was added in r1430, but not sure why - selectedElement = elem; - } - } - - editor.showSaveWarning = true; - - // we update the contextual panel with potentially new - // positional/sizing information (we DON'T want to update the - // toolbar here as that creates an infinite loop) - // also this updates the history buttons - - // we tell it to skip focusing the text control if the - // text element was previously in focus - updateContextPanel(); - - // In the event a gradient was flipped: - if (selectedElement && mode === 'select') { - paintBox.fill.update(); - paintBox.stroke.update(); - } - - svgCanvas.runExtensions('elementChanged', { - elems: elems - }); - }; - - var zoomDone = function () { - updateWireFrame(); - // updateCanvas(); // necessary? - }; - - var zoomChanged = svgCanvas.zoomChanged = function (win, bbox, autoCenter) { - var scrbar = 15, - // res = svgCanvas.getResolution(), // Currently unused - wArea = workarea; - // var canvas_pos = $('#svgcanvas').position(); // Currently unused - var zInfo = svgCanvas.setBBoxZoom(bbox, wArea.width() - scrbar, wArea.height() - scrbar); - if (!zInfo) { return; } - var zoomlevel = zInfo.zoom, - bb = zInfo.bbox; - - if (zoomlevel < 0.001) { - changeZoom({value: 0.1}); - return; - } - - $('#zoom').val((zoomlevel * 100).toFixed(1)); - - if (autoCenter) { - updateCanvas(); - } else { - updateCanvas(false, {x: bb.x * zoomlevel + (bb.width * zoomlevel) / 2, y: bb.y * zoomlevel + (bb.height * zoomlevel) / 2}); - } - - if (svgCanvas.getMode() === 'zoom' && bb.width) { - // Go to select if a zoom box was drawn - setSelectMode(); - } - - zoomDone(); - }; - - changeZoom = function (ctl) { - var zoomlevel = ctl.value / 100; - if (zoomlevel < 0.001) { - ctl.value = 0.1; - return; - } - var zoom = svgCanvas.getZoom(); - var wArea = workarea; - - zoomChanged(window, { - width: 0, - height: 0, - // center pt of scroll position - x: (wArea[0].scrollLeft + wArea.width() / 2) / zoom, - y: (wArea[0].scrollTop + wArea.height() / 2) / zoom, - zoom: zoomlevel - }, true); - }; - - $('#cur_context_panel').delegate('a', 'click', function () { - var link = $(this); - if (link.attr('data-root')) { - svgCanvas.leaveContext(); - } else { - svgCanvas.setContext(link.text()); - } - svgCanvas.clearSelection(); - return false; - }); - - var contextChanged = function (win, context) { - var linkStr = ''; - if (context) { - var str = ''; - linkStr = '' + svgCanvas.getCurrentDrawing().getCurrentLayerName() + ''; - - $(context).parentsUntil('#svgcontent > g').andSelf().each(function () { - if (this.id) { - str += ' > ' + this.id; - if (this !== context) { - linkStr += ' > ' + this.id + ''; - } else { - linkStr += ' > ' + this.id; - } - } - }); - - curContext = str; - } else { - curContext = null; - } - $('#cur_context_panel').toggle(!!context).html(linkStr); - - updateTitle(); - }; - - // Makes sure the current selected paint is available to work with - var prepPaints = function () { - paintBox.fill.prep(); - paintBox.stroke.prep(); - }; - - var flyoutFuncs = {}; - - var setFlyoutTitles = function () { - $('.tools_flyout').each(function () { - var shower = $('#' + this.id + '_show'); - if (shower.data('isLibrary')) { - return; - } - - var tooltips = []; - $(this).children().each(function () { - tooltips.push(this.title); - }); - shower[0].title = tooltips.join(' / '); - }); - }; - - var setFlyoutPositions = function () { - $('.tools_flyout').each(function () { - var shower = $('#' + this.id + '_show'); - var pos = shower.offset(); - var w = shower.outerWidth(); - $(this).css({left: (pos.left + w) * editor.tool_scale, top: pos.top}); - }); - }; - - var setupFlyouts = function (holders) { - $.each(holders, function (holdSel, btnOpts) { - var buttons = $(holdSel).children(); - var showSel = holdSel + '_show'; - var shower = $(showSel); - var def = false; - buttons.addClass('tool_button') - .unbind('click mousedown mouseup') // may not be necessary - .each(function (i) { - // Get this buttons options - var opts = btnOpts[i]; - - // Remember the function that goes with this ID - flyoutFuncs[opts.sel] = opts.fn; - - if (opts.isDefault) { def = i; } - - // Clicking the icon in flyout should set this set's icon - var func = function (event) { - var options = opts; - // Find the currently selected tool if comes from keystroke - if (event.type === 'keydown') { - var flyoutIsSelected = $(options.parent + '_show').hasClass('tool_button_current'); - var currentOperation = $(options.parent + '_show').attr('data-curopt'); - $.each(holders[opts.parent], function (i, tool) { - if (tool.sel === currentOperation) { - if (!event.shiftKey || !flyoutIsSelected) { - options = tool; - } else { - options = holders[opts.parent][i + 1] || holders[opts.parent][0]; - } - } - }); - } - if ($(this).hasClass('disabled')) { return false; } - if (toolButtonClick(showSel)) { - options.fn(); - } - var icon; - if (options.icon) { - icon = $.getSvgIcon(options.icon, true); - } else { - icon = $(options.sel).children().eq(0).clone(); - } - - icon[0].setAttribute('width', shower.width()); - icon[0].setAttribute('height', shower.height()); - shower.children(':not(.flyout_arrow_horiz)').remove(); - shower.append(icon).attr('data-curopt', options.sel); // This sets the current mode - }; - - $(this).mouseup(func); - - if (opts.key) { - $(document).bind('keydown', opts.key[0] + ' shift+' + opts.key[0], func); - } - }); - - if (def) { - shower.attr('data-curopt', btnOpts[def].sel); - } else if (!shower.attr('data-curopt')) { - // Set first as default - shower.attr('data-curopt', btnOpts[0].sel); - } - - var timer; - var pos = $(showSel).position(); - - // Clicking the "show" icon should set the current mode - shower.mousedown(function (evt) { - if (shower.hasClass('disabled')) { - return false; - } - var holder = $(holdSel); - var l = pos.left + 34; - var w = holder.width() * -1; - var time = holder.data('shown_popop') ? 200 : 0; - timer = setTimeout(function () { - // Show corresponding menu - if (!shower.data('isLibrary')) { - holder.css('left', w).show().animate({ - left: l - }, 150); - } else { - holder.css('left', l).show(); - } - holder.data('shown_popop', true); - }, time); - evt.preventDefault(); - }).mouseup(function (evt) { - clearTimeout(timer); - var opt = $(this).attr('data-curopt'); - // Is library and popped up, so do nothing - if (shower.data('isLibrary') && $(showSel.replace('_show', '')).is(':visible')) { - toolButtonClick(showSel, true); - return; - } - if (toolButtonClick(showSel) && flyoutFuncs[opt]) { - flyoutFuncs[opt](); - } - }); - // $('#tools_rect').mouseleave(function () { $('#tools_rect').fadeOut(); }); - }); - setFlyoutTitles(); - setFlyoutPositions(); - }; - - var makeFlyoutHolder = function (id, child) { - var div = $('
    ', { - 'class': 'tools_flyout', - id: id - }).appendTo('#svg_editor').append(child); - - return div; - }; - - var uaPrefix = (function () { - var prop; - var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/; - var someScript = document.getElementsByTagName('script')[0]; - for (prop in someScript.style) { - if (regex.test(prop)) { - // test is faster than match, so it's better to perform - // that on the lot and match only when necessary - return prop.match(regex)[0]; - } - } - // Nothing found so far? - if ('WebkitOpacity' in someScript.style) { return 'Webkit'; } - if ('KhtmlOpacity' in someScript.style) { return 'Khtml'; } - - return ''; - }()); - - var scaleElements = function (elems, scale) { - // var prefix = '-' + uaPrefix.toLowerCase() + '-'; // Currently unused - var sides = ['top', 'left', 'bottom', 'right']; - - elems.each(function () { - // Handled in CSS - // this.style[uaPrefix + 'Transform'] = 'scale(' + scale + ')'; - var i; - var el = $(this); - var w = el.outerWidth() * (scale - 1); - var h = el.outerHeight() * (scale - 1); - // var margins = {}; // Currently unused - - for (i = 0; i < 4; i++) { - var s = sides[i]; - var cur = el.data('orig_margin-' + s); - if (cur == null) { - cur = parseInt(el.css('margin-' + s), 10); - // Cache the original margin - el.data('orig_margin-' + s, cur); - } - var val = cur * scale; - if (s === 'right') { - val += w; - } else if (s === 'bottom') { - val += h; - } - - el.css('margin-' + s, val); - // el.css('outline', '1px solid red'); - } - }); - }; - - var setIconSize = editor.setIconSize = function (size) { - // var elems = $('.tool_button, .push_button, .tool_button_current, .disabled, .icon_label, #url_notice, #tool_open'); - var selToscale = '#tools_top .toolset, #editor_panel > *, #history_panel > *,' + + var editor = {}; + // EDITOR PROPERTIES: (defined below) + // curPrefs, curConfig, canvas, storage, uiStrings + // + // STATE MAINTENANCE PROPERTIES + editor.tool_scale = 1; // Dependent on icon size, so any use to making configurable instead? Used by JQuerySpinBtn.js + editor.exportWindowCt = 0; + editor.langChanged = false; + editor.showSaveWarning = false; + editor.storagePromptClosed = false; // For use with ext-storage.js + + var svgCanvas, urldata, + Utils = svgedit.utilities, + isReady = false, + customExportImage = false, + customExportPDF = false, + callbacks = [], + /** + * PREFS AND CONFIG + */ + // The iteration algorithm for defaultPrefs does not currently support array/objects + defaultPrefs = { + // EDITOR OPTIONS (DIALOG) + lang: '', // Default to "en" if locale.js detection does not detect another language + iconsize: '', // Will default to 's' if the window height is smaller than the minimum height and 'm' otherwise + bkgd_color: '#FFF', + bkgd_url: '', + // DOCUMENT PROPERTIES (DIALOG) + img_save: 'embed', + // ALERT NOTICES + // Only shows in UI as far as alert notices, but useful to remember, so keeping as pref + save_notice_done: false, + export_notice_done: false + }, + curPrefs = {}, + // Note: The difference between Prefs and Config is that Prefs + // can be changed in the UI and are stored in the browser, + // while config cannot + curConfig = { + // We do not put on defaultConfig to simplify object copying + // procedures (we obtain instead from defaultExtensions) + extensions: [], + /** + * Can use window.location.origin to indicate the current + * origin. Can contain a '*' to allow all domains or 'null' (as + * a string) to support all file:// URLs. Cannot be set by + * URL for security reasons (not safe, at least for + * privacy or data integrity of SVG content). + * Might have been fairly safe to allow + * `new URL(window.location.href).origin` by default but + * avoiding it ensures some more security that even third + * party apps on the same domain also cannot communicate + * with this app by default. + * For use with ext-xdomain-messaging.js + * @todo We might instead make as a user-facing preference. + */ + allowedOrigins: [] + }, + defaultExtensions = [ + 'ext-overview_window.js', + 'ext-markers.js', + 'ext-connector.js', + 'ext-eyedropper.js', + 'ext-shapes.js', + 'ext-imagelib.js', + 'ext-grid.js', + 'ext-polygon.js', + 'ext-star.js', + 'ext-panning.js', + 'ext-storage.js' + ], + defaultConfig = { + // Todo: svgcanvas.js also sets and checks: show_outside_canvas, selectNew; add here? + // Change the following to preferences and add pref controls to the UI (e.g., initTool, wireframe, showlayers)? + canvasName: 'default', + canvas_expansion: 3, + initFill: { + color: 'FF0000', // solid red + opacity: 1 + }, + initStroke: { + width: 5, + color: '000000', // solid black + opacity: 1 + }, + text: { + stroke_width: 0, + font_size: 24, + font_family: 'serif' + }, + initOpacity: 1, + colorPickerCSS: null, // Defaults to 'left' with a position equal to that of the fill_color or stroke_color element minus 140, and a 'bottom' equal to 40 + initTool: 'select', + exportWindowType: 'new', // 'same' (todo: also support 'download') + wireframe: false, + showlayers: false, + no_save_warning: false, + // PATH CONFIGURATION + // The following path configuration items are disallowed in the URL (as should any future path configurations) + imgPath: 'images/', + langPath: 'locale/', + extPath: 'extensions/', + jGraduatePath: 'jgraduate/images/', + // DOCUMENT PROPERTIES + // Change the following to a preference (already in the Document Properties dialog)? + dimensions: [640, 480], + // EDITOR OPTIONS + // Change the following to preferences (already in the Editor Options dialog)? + gridSnapping: false, + gridColor: '#000', + baseUnit: 'px', + snappingStep: 10, + showRulers: true, + // URL BEHAVIOR CONFIGURATION + preventAllURLConfig: false, + preventURLContentLoading: false, + // EXTENSION CONFIGURATION (see also preventAllURLConfig) + lockExtensions: false, // Disallowed in URL setting + noDefaultExtensions: false, // noDefaultExtensions can only be meaningfully used in config.js or in the URL + // EXTENSION-RELATED (GRID) + showGrid: false, // Set by ext-grid.js + // EXTENSION-RELATED (STORAGE) + noStorageOnLoad: false, // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage + forceStorage: false, // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not + emptyStorageOnDecline: false // Used by ext-storage.js; empty any prior storage if the user declines to store + }, + /** + * LOCALE + * @todo Can we remove now that we are always loading even English? (unless locale is set to null) + */ + uiStrings = editor.uiStrings = { + common: { + ok: 'OK', + cancel: 'Cancel', + key_up: 'Up', + key_down: 'Down', + key_backspace: 'Backspace', + key_del: 'Del' + }, + // This is needed if the locale is English, since the locale strings are not read in that instance. + layers: { + layer: 'Layer' + }, + notification: { + invalidAttrValGiven: 'Invalid value given', + noContentToFitTo: 'No content to fit to', + dupeLayerName: 'There is already a layer named that!', + enterUniqueLayerName: 'Please enter a unique layer name', + enterNewLayerName: 'Please enter the new layer name', + layerHasThatName: 'Layer already has that name', + QmoveElemsToLayer: 'Move selected elements to layer \'%s\'?', + QwantToClear: 'Do you want to clear the drawing?\nThis will also erase your undo history!', + QwantToOpen: 'Do you want to open a new file?\nThis will also erase your undo history!', + QerrorsRevertToSource: 'There were parsing errors in your SVG source.\nRevert back to original SVG source?', + QignoreSourceChanges: 'Ignore changes made to SVG source?', + featNotSupported: 'Feature not supported', + enterNewImgURL: 'Enter the new image URL', + defsFailOnSave: 'NOTE: Due to a bug in your browser, this image may appear wrong (missing gradients or elements). It will however appear correct once actually saved.', + loadingImage: 'Loading image, please wait...', + saveFromBrowser: 'Select \'Save As...\' in your browser to save this image as a %s file.', + noteTheseIssues: 'Also note the following issues: ', + unsavedChanges: 'There are unsaved changes.', + enterNewLinkURL: 'Enter the new hyperlink URL', + errorLoadingSVG: 'Error: Unable to load SVG data', + URLloadFail: 'Unable to load from URL', + retrieving: 'Retrieving \'%s\' ...', + popupWindowBlocked: 'Popup window may be blocked by browser' + } + }; + + function loadSvgString (str, callback) { + var success = svgCanvas.setSvgString(str) !== false; + callback = callback || $.noop; + if (success) { + callback(true); + } else { + $.alert(uiStrings.notification.errorLoadingSVG, function () { + callback(false); + }); + } + } + + /** + * EXPORTS + */ + + /** + * Store and retrieve preferences + * @param {string} key The preference name to be retrieved or set + * @param {string} [val] The value. If the value supplied is missing or falsey, no change to the preference will be made. + * @returns {string} If val is missing or falsey, the value of the previously stored preference will be returned. + * @todo Can we change setting on the jQuery namespace (onto editor) to avoid conflicts? + * @todo Review whether any remaining existing direct references to + * getting curPrefs can be changed to use $.pref() getting to ensure + * defaultPrefs fallback (also for sake of allowInitialUserOverride); specifically, bkgd_color could be changed so that + * the pref dialog has a button to auto-calculate background, but otherwise uses $.pref() to be able to get default prefs + * or overridable settings + */ + $.pref = function (key, val) { + if (val) { + curPrefs[key] = val; + editor.curPrefs = curPrefs; // Update exported value + return; + } + return (key in curPrefs) ? curPrefs[key] : defaultPrefs[key]; + }; + + /** + * EDITOR PUBLIC METHODS + * locale.js also adds "putLang" and "readLang" as editor methods + * @todo Sort these methods per invocation order, ideally with init at the end + * @todo Prevent execution until init executes if dependent on it? + */ + + /** + * Where permitted, sets canvas and/or defaultPrefs based on previous + * storage. This will override URL settings (for security reasons) but + * not config.js configuration (unless initial user overriding is explicitly + * permitted there via allowInitialUserOverride). + * @todo Split allowInitialUserOverride into allowOverrideByURL and + * allowOverrideByUserStorage so config.js can disallow some + * individual items for URL setting but allow for user storage AND/OR + * change URL setting so that it always uses a different namespace, + * so it won't affect pre-existing user storage (but then if users saves + * that, it will then be subject to tampering + */ + editor.loadContentAndPrefs = function () { + if (!curConfig.forceStorage && (curConfig.noStorageOnLoad || !document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/))) { + return; + } + + // LOAD CONTENT + if (editor.storage && // Cookies do not have enough available memory to hold large documents + (curConfig.forceStorage || (!curConfig.noStorageOnLoad && document.cookie.match(/(?:^|;\s*)store=prefsAndContent/))) + ) { + var name = 'svgedit-' + curConfig.canvasName; + var cached = editor.storage.getItem(name); + if (cached) { + editor.loadFromString(cached); + } + } + + // LOAD PREFS + var key; + for (key in defaultPrefs) { + if (defaultPrefs.hasOwnProperty(key)) { // It's our own config, so we don't need to iterate up the prototype chain + var storeKey = 'svg-edit-' + key; + if (editor.storage) { + var val = editor.storage.getItem(storeKey); + if (val) { + defaultPrefs[key] = String(val); // Convert to string for FF (.value fails in Webkit) + } + } else if (window.widget) { + defaultPrefs[key] = widget.preferenceForKey(storeKey); + } else { + var result = document.cookie.match(new RegExp('(?:^|;\\s*)' + Utils.preg_quote(encodeURIComponent(storeKey)) + '=([^;]+)')); + defaultPrefs[key] = result ? decodeURIComponent(result[1]) : ''; + } + } + } + }; + + /** + * Allows setting of preferences or configuration (including extensions). + * @param {Object} opts The preferences or configuration (including extensions) + * @param {Object} [cfgCfg] Describes configuration which applies to the particular batch of supplied options + * @param {boolean} [cfgCfg.allowInitialUserOverride=false] Set to true if you wish + * to allow initial overriding of settings by the user via the URL + * (if permitted) or previously stored preferences (if permitted); + * note that it will be too late if you make such calls in extension + * code because the URL or preference storage settings will + * have already taken place. + * @param {boolean} [cfgCfg.overwrite=true] Set to false if you wish to + * prevent the overwriting of prior-set preferences or configuration + * (URL settings will always follow this requirement for security + * reasons, so config.js settings cannot be overridden unless it + * explicitly permits via "allowInitialUserOverride" but extension config + * can be overridden as they will run after URL settings). Should + * not be needed in config.js. + */ + editor.setConfig = function (opts, cfgCfg) { + cfgCfg = cfgCfg || {}; + function extendOrAdd (cfgObj, key, val) { + if (cfgObj[key] && typeof cfgObj[key] === 'object') { + $.extend(true, cfgObj[key], val); + } else { + cfgObj[key] = val; + } + } + $.each(opts, function (key, val) { + if (opts.hasOwnProperty(key)) { + // Only allow prefs defined in defaultPrefs + if (defaultPrefs.hasOwnProperty(key)) { + if (cfgCfg.overwrite === false && ( + curConfig.preventAllURLConfig || + curPrefs.hasOwnProperty(key) + )) { + return; + } + if (cfgCfg.allowInitialUserOverride === true) { + defaultPrefs[key] = val; + } else { + $.pref(key, val); + } + } else if (['extensions', 'allowedOrigins'].indexOf(key) > -1) { + if (cfgCfg.overwrite === false && + ( + curConfig.preventAllURLConfig || + key === 'allowedOrigins' || + (key === 'extensions' && curConfig.lockExtensions) + ) + ) { + return; + } + curConfig[key] = curConfig[key].concat(val); // We will handle any dupes later + // Only allow other curConfig if defined in defaultConfig + } else if (defaultConfig.hasOwnProperty(key)) { + if (cfgCfg.overwrite === false && ( + curConfig.preventAllURLConfig || + curConfig.hasOwnProperty(key) + )) { + return; + } + // Potentially overwriting of previously set config + if (curConfig.hasOwnProperty(key)) { + if (cfgCfg.overwrite === false) { + return; + } + extendOrAdd(curConfig, key, val); + } else { + if (cfgCfg.allowInitialUserOverride === true) { + extendOrAdd(defaultConfig, key, val); + } else { + if (defaultConfig[key] && typeof defaultConfig[key] === 'object') { + curConfig[key] = {}; + $.extend(true, curConfig[key], val); // Merge properties recursively, e.g., on initFill, initStroke objects + } else { + curConfig[key] = val; + } + } + } + } + } + }); + editor.curConfig = curConfig; // Update exported value + }; + + /** + * @param {Object} opts Extension mechanisms may call setCustomHandlers with three functions: opts.open, opts.save, and opts.exportImage + * opts.open's responsibilities are: + * - invoke a file chooser dialog in 'open' mode + * - let user pick a SVG file + * - calls svgCanvas.setSvgString() with the string contents of that file + * opts.save's responsibilities are: + * - accept the string contents of the current document + * - invoke a file chooser dialog in 'save' mode + * - save the file to location chosen by the user + * opts.exportImage's responsibilities (with regard to the object it is supplied in its 2nd argument) are: + * - inform user of any issues supplied via the "issues" property + * - convert the "svg" property SVG string into an image for export; + * utilize the properties "type" (currently 'PNG', 'JPEG', 'BMP', + * 'WEBP', 'PDF'), "mimeType", and "quality" (for 'JPEG' and 'WEBP' + * types) to determine the proper output. + */ + editor.setCustomHandlers = function (opts) { + editor.ready(function () { + if (opts.open) { + $('#tool_open > input[type="file"]').remove(); + $('#tool_open').show(); + svgCanvas.open = opts.open; + } + if (opts.save) { + editor.showSaveWarning = false; + svgCanvas.bind('saved', opts.save); + } + if (opts.exportImage) { + customExportImage = opts.exportImage; + svgCanvas.bind('exported', customExportImage); // canvg and our RGBColor will be available to the method + } + if (opts.exportPDF) { + customExportPDF = opts.exportPDF; + svgCanvas.bind('exportedPDF', customExportPDF); // jsPDF and our RGBColor will be available to the method + } + }); + }; + + editor.randomizeIds = function () { + svgCanvas.randomizeIds(arguments); + }; + + editor.init = function () { + // var host = location.hostname, + // onWeb = host && host.indexOf('.') >= 0; + // Some FF versions throw security errors here when directly accessing + try { + if ('localStorage' in window) { // && onWeb removed so Webkit works locally + editor.storage = localStorage; + } + } catch (err) {} + + // Todo: Avoid var-defined functions and group functions together, etc. where possible + var goodLangs = []; + $('#lang_select option').each(function () { + goodLangs.push(this.value); + }); + + function setupCurPrefs () { + curPrefs = $.extend(true, {}, defaultPrefs, curPrefs); // Now safe to merge with priority for curPrefs in the event any are already set + // Export updated prefs + editor.curPrefs = curPrefs; + } + function setupCurConfig () { + curConfig = $.extend(true, {}, defaultConfig, curConfig); // Now safe to merge with priority for curConfig in the event any are already set + + // Now deal with extensions and other array config + if (!curConfig.noDefaultExtensions) { + curConfig.extensions = curConfig.extensions.concat(defaultExtensions); + } + // ...and remove any dupes + $.each(['extensions', 'allowedOrigins'], function (i, cfg) { + curConfig[cfg] = $.grep(curConfig[cfg], function (n, i) { + return i === curConfig[cfg].indexOf(n); + }); + }); + // Export updated config + editor.curConfig = curConfig; + } + (function () { + // Load config/data from URL if given + var src, qstr; + urldata = $.deparam.querystring(true); + if (!$.isEmptyObject(urldata)) { + if (urldata.dimensions) { + urldata.dimensions = urldata.dimensions.split(','); + } + + if (urldata.bkgd_color) { + urldata.bkgd_color = '#' + urldata.bkgd_color; + } + + if (urldata.extensions) { + // For security reasons, disallow cross-domain or cross-folder extensions via URL + urldata.extensions = urldata.extensions.match(/[:/\\]/) ? '' : urldata.extensions.split(','); + } + + // Disallowing extension paths via URL for + // security reasons, even for same-domain + // ones given potential to interact in undesirable + // ways with other script resources + $.each( + [ + 'extPath', 'imgPath', + 'langPath', 'jGraduatePath' + ], + function (pathConfig) { + if (urldata[pathConfig]) { + delete urldata[pathConfig]; + } + } + ); + + editor.setConfig(urldata, {overwrite: false}); // Note: source and url (as with storagePrompt later) are not set on config but are used below + + setupCurConfig(); + + if (!curConfig.preventURLContentLoading) { + src = urldata.source; + qstr = $.param.querystring(); + if (!src) { // urldata.source may have been null if it ended with '=' + if (qstr.indexOf('source=data:') >= 0) { + src = qstr.match(/source=(data:[^&]*)/)[1]; + } + } + if (src) { + if (src.indexOf('data:') === 0) { + editor.loadFromDataURI(src); + } else { + editor.loadFromString(src); + } + return; + } + if (urldata.url) { + editor.loadFromURL(urldata.url); + return; + } + } + if (!urldata.noStorageOnLoad || curConfig.forceStorage) { + editor.loadContentAndPrefs(); + } + setupCurPrefs(); + } else { + setupCurConfig(); + editor.loadContentAndPrefs(); + setupCurPrefs(); + } + }()); + + var setIcon = editor.setIcon = function (elem, iconId, forcedSize) { + var icon = (typeof iconId === 'string') ? $.getSvgIcon(iconId, true) : iconId.clone(); + if (!icon) { + console.log('NOTE: Icon image missing: ' + iconId); + return; + } + $(elem).empty().append(icon); + }; + + var extFunc = function () { + $.each(curConfig.extensions, function () { + var extname = this; + if (!extname.match(/^ext-.*\.js/)) { // Ensure URL cannot specify some other unintended file in the extPath + return; + } + $.getScript(curConfig.extPath + extname, function (d) { + // Fails locally in Chrome 5 + if (!d) { + var s = document.createElement('script'); + s.src = curConfig.extPath + extname; + document.querySelector('head').appendChild(s); + } + }).fail(function (jqxhr, settings, exception) { + console.log(exception); + }); + }); + + // var lang = ('lang' in curPrefs) ? curPrefs.lang : null; + editor.putLocale(null, goodLangs); + }; + + // Load extensions + // Bit of a hack to run extensions in local Opera/IE9 + if (document.location.protocol === 'file:') { + setTimeout(extFunc, 100); + } else { + extFunc(); + } + $.svgIcons(curConfig.imgPath + 'svg_edit_icons.svg', { + w: 24, h: 24, + id_match: false, + no_img: !svgedit.browser.isWebkit(), // Opera & Firefox 4 gives odd behavior w/images + fallback_path: curConfig.imgPath, + fallback: { + 'new_image': 'clear.png', + 'save': 'save.png', + 'open': 'open.png', + 'source': 'source.png', + 'docprops': 'document-properties.png', + 'wireframe': 'wireframe.png', + + 'undo': 'undo.png', + 'redo': 'redo.png', + + 'select': 'select.png', + 'select_node': 'select_node.png', + 'pencil': 'fhpath.png', + 'pen': 'line.png', + 'square': 'square.png', + 'rect': 'rect.png', + 'fh_rect': 'freehand-square.png', + 'circle': 'circle.png', + 'ellipse': 'ellipse.png', + 'fh_ellipse': 'freehand-circle.png', + 'path': 'path.png', + 'text': 'text.png', + 'image': 'image.png', + 'zoom': 'zoom.png', + + 'clone': 'clone.png', + 'node_clone': 'node_clone.png', + 'delete': 'delete.png', + 'node_delete': 'node_delete.png', + 'group': 'shape_group_elements.png', + 'ungroup': 'shape_ungroup.png', + 'move_top': 'move_top.png', + 'move_bottom': 'move_bottom.png', + 'to_path': 'to_path.png', + 'link_controls': 'link_controls.png', + 'reorient': 'reorient.png', + + 'align_left': 'align-left.png', + 'align_center': 'align-center.png', + 'align_right': 'align-right.png', + 'align_top': 'align-top.png', + 'align_middle': 'align-middle.png', + 'align_bottom': 'align-bottom.png', + + 'go_up': 'go-up.png', + 'go_down': 'go-down.png', + + 'ok': 'save.png', + 'cancel': 'cancel.png', + + 'arrow_right': 'flyouth.png', + 'arrow_down': 'dropdown.gif' + }, + placement: { + '#logo': 'logo', + + '#tool_clear div,#layer_new': 'new_image', + '#tool_save div': 'save', + '#tool_export div': 'export', + '#tool_open div div': 'open', + '#tool_import div div': 'import', + '#tool_source': 'source', + '#tool_docprops > div': 'docprops', + '#tool_wireframe': 'wireframe', + + '#tool_undo': 'undo', + '#tool_redo': 'redo', + + '#tool_select': 'select', + '#tool_fhpath': 'pencil', + '#tool_line': 'pen', + '#tool_rect,#tools_rect_show': 'rect', + '#tool_square': 'square', + '#tool_fhrect': 'fh_rect', + '#tool_ellipse,#tools_ellipse_show': 'ellipse', + '#tool_circle': 'circle', + '#tool_fhellipse': 'fh_ellipse', + '#tool_path': 'path', + '#tool_text,#layer_rename': 'text', + '#tool_image': 'image', + '#tool_zoom': 'zoom', + + '#tool_clone,#tool_clone_multi': 'clone', + '#tool_node_clone': 'node_clone', + '#layer_delete,#tool_delete,#tool_delete_multi': 'delete', + '#tool_node_delete': 'node_delete', + '#tool_add_subpath': 'add_subpath', + '#tool_openclose_path': 'open_path', + '#tool_move_top': 'move_top', + '#tool_move_bottom': 'move_bottom', + '#tool_topath': 'to_path', + '#tool_node_link': 'link_controls', + '#tool_reorient': 'reorient', + '#tool_group_elements': 'group_elements', + '#tool_ungroup': 'ungroup', + '#tool_unlink_use': 'unlink_use', + + '#tool_alignleft, #tool_posleft': 'align_left', + '#tool_aligncenter, #tool_poscenter': 'align_center', + '#tool_alignright, #tool_posright': 'align_right', + '#tool_aligntop, #tool_postop': 'align_top', + '#tool_alignmiddle, #tool_posmiddle': 'align_middle', + '#tool_alignbottom, #tool_posbottom': 'align_bottom', + '#cur_position': 'align', + + '#linecap_butt,#cur_linecap': 'linecap_butt', + '#linecap_round': 'linecap_round', + '#linecap_square': 'linecap_square', + + '#linejoin_miter,#cur_linejoin': 'linejoin_miter', + '#linejoin_round': 'linejoin_round', + '#linejoin_bevel': 'linejoin_bevel', + + '#url_notice': 'warning', + + '#layer_up': 'go_up', + '#layer_down': 'go_down', + '#layer_moreopts': 'context_menu', + '#layerlist td.layervis': 'eye', + + '#tool_source_save,#tool_docprops_save,#tool_prefs_save': 'ok', + '#tool_source_cancel,#tool_docprops_cancel,#tool_prefs_cancel': 'cancel', + + '#rwidthLabel, #iwidthLabel': 'width', + '#rheightLabel, #iheightLabel': 'height', + '#cornerRadiusLabel span': 'c_radius', + '#angleLabel': 'angle', + '#linkLabel,#tool_make_link,#tool_make_link_multi': 'globe_link', + '#zoomLabel': 'zoom', + '#tool_fill label': 'fill', + '#tool_stroke .icon_label': 'stroke', + '#group_opacityLabel': 'opacity', + '#blurLabel': 'blur', + '#font_sizeLabel': 'fontsize', + + '.flyout_arrow_horiz': 'arrow_right', + '.dropdown button, #main_button .dropdown': 'arrow_down', + '#palette .palette_item:first, #fill_bg, #stroke_bg': 'no_color' + }, + resize: { + '#logo .svg_icon': 28, + '.flyout_arrow_horiz .svg_icon': 5, + '.layer_button .svg_icon, #layerlist td.layervis .svg_icon': 14, + '.dropdown button .svg_icon': 7, + '#main_button .dropdown .svg_icon': 9, + '.palette_item:first .svg_icon': 15, + '#fill_bg .svg_icon, #stroke_bg .svg_icon': 16, + '.toolbar_button button .svg_icon': 16, + '.stroke_tool div div .svg_icon': 20, + '#tools_bottom label .svg_icon': 18 + }, + callback: function (icons) { + $('.toolbar_button button > svg, .toolbar_button button > img').each(function () { + $(this).parent().prepend(this); + }); + + var minHeight, + tleft = $('#tools_left'); + if (tleft.length !== 0) { + minHeight = tleft.offset().top + tleft.outerHeight(); + } + + var size = $.pref('iconsize'); + editor.setIconSize(size || ($(window).height() < minHeight ? 's' : 'm')); + + // Look for any missing flyout icons from plugins + $('.tools_flyout').each(function () { + var shower = $('#' + this.id + '_show'); + var sel = shower.attr('data-curopt'); + // Check if there's an icon here + if (!shower.children('svg, img').length) { + var clone = $(sel).children().clone(); + if (clone.length) { + clone[0].removeAttribute('style'); // Needed for Opera + shower.append(clone); + } + } + }); + + editor.runCallbacks(); + + setTimeout(function () { + $('.flyout_arrow_horiz:empty').each(function () { + $(this).append($.getSvgIcon('arrow_right').width(5).height(5)); + }); + }, 1); + } + }); + + editor.canvas = svgCanvas = new $.SvgCanvas(document.getElementById('svgcanvas'), curConfig); + var supportsNonSS, resizeTimer, changeZoom, Actions, curScrollPos, + palette = [ // Todo: Make into configuration item? + '#000000', '#3f3f3f', '#7f7f7f', '#bfbfbf', '#ffffff', + '#ff0000', '#ff7f00', '#ffff00', '#7fff00', + '#00ff00', '#00ff7f', '#00ffff', '#007fff', + '#0000ff', '#7f00ff', '#ff00ff', '#ff007f', + '#7f0000', '#7f3f00', '#7f7f00', '#3f7f00', + '#007f00', '#007f3f', '#007f7f', '#003f7f', + '#00007f', '#3f007f', '#7f007f', '#7f003f', + '#ffaaaa', '#ffd4aa', '#ffffaa', '#d4ffaa', + '#aaffaa', '#aaffd4', '#aaffff', '#aad4ff', + '#aaaaff', '#d4aaff', '#ffaaff', '#ffaad4' + ], + modKey = (svgedit.browser.isMac() ? 'meta+' : 'ctrl+'), // ⌘ + path = svgCanvas.pathActions, + undoMgr = svgCanvas.undoMgr, + defaultImageURL = curConfig.imgPath + 'logo.png', + workarea = $('#workarea'), + canvMenu = $('#cmenu_canvas'), + // layerMenu = $('#cmenu_layers'), // Unused + exportWindow = null, + zoomInIcon = 'crosshair', + zoomOutIcon = 'crosshair', + uiContext = 'toolbars', + origSource = '', + paintBox = {fill: null, stroke: null}; + + // For external openers + (function () { + // let the opener know SVG Edit is ready (now that config is set up) + var svgEditorReadyEvent, + w = window.opener || window.parent; + if (w) { + try { + svgEditorReadyEvent = w.document.createEvent('Event'); + svgEditorReadyEvent.initEvent('svgEditorReady', true, true); + w.document.documentElement.dispatchEvent(svgEditorReadyEvent); + } catch (e) {} + } + }()); + + // This sets up alternative dialog boxes. They mostly work the same way as + // their UI counterparts, expect instead of returning the result, a callback + // needs to be included that returns the result as its first parameter. + // In the future we may want to add additional types of dialog boxes, since + // they should be easy to handle this way. + (function () { + $('#dialog_container').draggable({cancel: '#dialog_content, #dialog_buttons *', containment: 'window'}); + var box = $('#dialog_box'), + btnHolder = $('#dialog_buttons'), + dialogContent = $('#dialog_content'), + dbox = function (type, msg, callback, defaultVal, opts, changeCb, checkbox) { + var ok, ctrl, chkbx; + dialogContent.html('

    ' + msg.replace(/\n/g, '

    ') + '

    ') + .toggleClass('prompt', (type === 'prompt')); + btnHolder.empty(); + + ok = $('').appendTo(btnHolder); + + if (type !== 'alert') { + $('') + .appendTo(btnHolder) + .click(function () { box.hide(); if (callback) { callback(false); } }); + } + + if (type === 'prompt') { + ctrl = $('').prependTo(btnHolder); + ctrl.val(defaultVal || ''); + ctrl.bind('keydown', 'return', function () { ok.click(); }); + } else if (type === 'select') { + var div = $('
    '); + ctrl = $('').appendTo(label); + chkbx.val(checkbox.value); + if (checkbox.tooltip) { + label.attr('title', checkbox.tooltip); + } + chkbx.prop('checked', !!checkbox.checked); + div.append($('
    ').append(label)); + } + $.each(opts || [], function (opt, val) { + if (typeof val === 'object') { + ctrl.append($(''); + } + if (icon !== undefined) { + var copy = icon.clone(); + $('td.layervis', layerlist).append(copy); + $.resizeSvgIcons({'td.layervis .svg_icon': 14}); + } + // handle selection of layer + $('#layerlist td.layername') + .mouseup(function (evt) { + $('#layerlist tr.layer').removeClass('layersel'); + $(this.parentNode).addClass('layersel'); + svgCanvas.setCurrentLayer(this.textContent); + evt.preventDefault(); + }) + .mouseover(function () { + toggleHighlightLayer(this.textContent); + }) + .mouseout(function () { + toggleHighlightLayer(); + }); + $('#layerlist td.layervis').click(function () { + var row = $(this.parentNode).prevAll().length; + var name = $('#layerlist tr.layer:eq(' + row + ') td.layername').text(); + var vis = $(this).hasClass('layerinvis'); + svgCanvas.setLayerVisibility(name, vis); + $(this).toggleClass('layerinvis'); + }); + + // if there were too few rows, let's add a few to make it not so lonely + var num = 5 - $('#layerlist tr.layer').size(); + while (num-- > 0) { + // FIXME: there must a better way to do this + layerlist.append('_'); + } + }; + + var showSourceEditor = function (e, forSaving) { + if (editingsource) { return; } + + editingsource = true; + origSource = svgCanvas.getSvgString(); + $('#save_output_btns').toggle(!!forSaving); + $('#tool_source_back').toggle(!forSaving); + $('#svg_source_textarea').val(origSource); + $('#svg_source_editor').fadeIn(); + $('#svg_source_textarea').focus(); + }; + + var togglePathEditMode = function (editmode, elems) { + $('#path_node_panel').toggle(editmode); + $('#tools_bottom_2,#tools_bottom_3').toggle(!editmode); + if (editmode) { + // Change select icon + $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); + $('#tool_select').addClass('tool_button_current').removeClass('tool_button'); + setIcon('#tool_select', 'select_node'); + multiselected = false; + if (elems.length) { + selectedElement = elems[0]; + } + } else { + setTimeout(function () { + setIcon('#tool_select', 'select'); + }, 1000); + } + }; + + var saveHandler = function (wind, svg) { + editor.showSaveWarning = false; + + // by default, we add the XML prolog back, systems integrating SVG-edit (wikis, CMSs) + // can just provide their own custom save handler and might not want the XML prolog + svg = '\n' + svg; + + // IE9 doesn't allow standalone Data URLs + // https://connect.microsoft.com/IE/feedback/details/542600/data-uri-images-fail-when-loaded-by-themselves + if (svgedit.browser.isIE()) { + showSourceEditor(0, true); + return; + } + + // Since saving SVGs by opening a new window was removed in Chrome use artificial link-click + // https://stackoverflow.com/questions/45603201/window-is-not-allowed-to-navigate-top-frame-navigations-to-data-urls + var a = document.createElement('a'); + a.href = 'data:image/svg+xml;base64,' + Utils.encode64(svg); + a.download = 'icon.svg'; + + a.click(); + + // Alert will only appear the first time saved OR the + // first time the bug is encountered + var done = $.pref('save_notice_done'); + if (done !== 'all') { + var note = uiStrings.notification.saveFromBrowser.replace('%s', 'SVG'); + + // Check if FF and has + if (svgedit.browser.isGecko()) { + if (svg.indexOf('').hide().appendTo('body').remove(); + }; + + function setStrokeOpt (opt, changeElem) { + var id = opt.id; + var bits = id.split('_'); + var pre = bits[0]; + var val = bits[1]; + + if (changeElem) { + svgCanvas.setStrokeAttr('stroke-' + pre, val); + } + operaRepaint(); + setIcon('#cur_' + pre, id, 20); + $(opt).addClass('current').siblings().removeClass('current'); + } + + // This is a common function used when a tool has been clicked (chosen) + // It does several common things: + // - removes the tool_button_current class from whatever tool currently has it + // - hides any flyouts + // - adds the tool_button_current class to the button passed in + var toolButtonClick = editor.toolButtonClick = function (button, noHiding) { + if ($(button).hasClass('disabled')) { return false; } + if ($(button).parent().hasClass('tools_flyout')) { return true; } + var fadeFlyouts = 'normal'; + if (!noHiding) { + $('.tools_flyout').fadeOut(fadeFlyouts); + } + $('#styleoverrides').text(''); + workarea.css('cursor', 'auto'); + $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); + $(button).addClass('tool_button_current').removeClass('tool_button'); + return true; + }; + + var clickSelect = editor.clickSelect = function () { + if (toolButtonClick('#tool_select')) { + svgCanvas.setMode('select'); + $('#styleoverrides').text('#svgcanvas svg *{cursor:move;pointer-events:all}, #svgcanvas svg{cursor:default}'); + } + }; + + var setImageURL = editor.setImageURL = function (url) { + if (!url) { + url = defaultImageURL; + } + svgCanvas.setImageURL(url); + $('#image_url').val(url); + + if (url.indexOf('data:') === 0) { + // data URI found + $('#image_url').hide(); + $('#change_image_url').show(); + } else { + // regular URL + svgCanvas.embedImage(url, function (dataURI) { + // Couldn't embed, so show warning + $('#url_notice').toggle(!dataURI); + defaultImageURL = url; + }); + $('#image_url').show(); + $('#change_image_url').hide(); + } + }; + + function setBackground (color, url) { + // if (color == $.pref('bkgd_color') && url == $.pref('bkgd_url')) { return; } + $.pref('bkgd_color', color); + $.pref('bkgd_url', url); + + // This should be done in svgcanvas.js for the borderRect fill + svgCanvas.setBackground(color, url); + } + + function promptImgURL () { + var curhref = svgCanvas.getHref(selectedElement); + curhref = curhref.indexOf('data:') === 0 ? '' : curhref; + $.prompt(uiStrings.notification.enterNewImgURL, curhref, function (url) { + if (url) { setImageURL(url); } + }); + } + + var setInputWidth = function (elem) { + var w = Math.min(Math.max(12 + elem.value.length * 6, 50), 300); + $(elem).width(w); + }; + + function updateRulers (scanvas, zoom) { + if (!zoom) { zoom = svgCanvas.getZoom(); } + if (!scanvas) { scanvas = $('#svgcanvas'); } + + var d, i; + var limit = 30000; + var contentElem = svgCanvas.getContentElem(); + var units = svgedit.units.getTypeMap(); + var unit = units[curConfig.baseUnit]; // 1 = 1px + + // draw x ruler then y ruler + for (d = 0; d < 2; d++) { + var isX = (d === 0); + var dim = isX ? 'x' : 'y'; + var lentype = isX ? 'width' : 'height'; + var contentDim = Number(contentElem.getAttribute(dim)); + + var $hcanvOrig = $('#ruler_' + dim + ' canvas:first'); + + // Bit of a hack to fully clear the canvas in Safari & IE9 + var $hcanv = $hcanvOrig.clone(); + $hcanvOrig.replaceWith($hcanv); + + var hcanv = $hcanv[0]; + + // Set the canvas size to the width of the container + var rulerLen = scanvas[lentype](); + var totalLen = rulerLen; + hcanv.parentNode.style[lentype] = totalLen + 'px'; + var ctxNum = 0; + var ctx = hcanv.getContext('2d'); + var ctxArr, num, ctxArrNum; + + ctx.fillStyle = 'rgb(200,0,0)'; + ctx.fillRect(0, 0, hcanv.width, hcanv.height); + + // Remove any existing canvasses + $hcanv.siblings().remove(); + + // Create multiple canvases when necessary (due to browser limits) + if (rulerLen >= limit) { + ctxArrNum = parseInt(rulerLen / limit, 10) + 1; + ctxArr = []; + ctxArr[0] = ctx; + var copy; + for (i = 1; i < ctxArrNum; i++) { + hcanv[lentype] = limit; + copy = hcanv.cloneNode(true); + hcanv.parentNode.appendChild(copy); + ctxArr[i] = copy.getContext('2d'); + } + + copy[lentype] = rulerLen % limit; + + // set copy width to last + rulerLen = limit; + } + + hcanv[lentype] = rulerLen; + + var uMulti = unit * zoom; + + // Calculate the main number interval + var rawM = 50 / uMulti; + var multi = 1; + for (i = 0; i < rIntervals.length; i++) { + num = rIntervals[i]; + multi = num; + if (rawM <= num) { + break; + } + } + + var bigInt = multi * uMulti; + + ctx.font = '9px sans-serif'; + + var rulerD = ((contentDim / uMulti) % multi) * uMulti; + var labelPos = rulerD - bigInt; + // draw big intervals + while (rulerD < totalLen) { + labelPos += bigInt; + // var real_d = rulerD - contentDim; // Currently unused + + var curD = Math.round(rulerD) + 0.5; + if (isX) { + ctx.moveTo(curD, 15); + ctx.lineTo(curD, 0); + } else { + ctx.moveTo(15, curD); + ctx.lineTo(0, curD); + } + + num = (labelPos - contentDim) / uMulti; + var label; + if (multi >= 1) { + label = Math.round(num); + } else { + var decs = String(multi).split('.')[1].length; + label = num.toFixed(decs); + } + + // Change 1000s to Ks + if (label !== 0 && label !== 1000 && label % 1000 === 0) { + label = (label / 1000) + 'K'; + } + + if (isX) { + ctx.fillText(label, rulerD + 2, 8); + } else { + // draw label vertically + var str = String(label).split(''); + for (i = 0; i < str.length; i++) { + ctx.fillText(str[i], 1, (rulerD + 9) + i * 9); + } + } + + var part = bigInt / 10; + // draw the small intervals + for (i = 1; i < 10; i++) { + var subD = Math.round(rulerD + part * i) + 0.5; + if (ctxArr && subD > rulerLen) { + ctxNum++; + ctx.stroke(); + if (ctxNum >= ctxArrNum) { + i = 10; + rulerD = totalLen; + continue; + } + ctx = ctxArr[ctxNum]; + rulerD -= limit; + subD = Math.round(rulerD + part * i) + 0.5; + } + + // odd lines are slighly longer + var lineNum = (i % 2) ? 12 : 10; + if (isX) { + ctx.moveTo(subD, 15); + ctx.lineTo(subD, lineNum); + } else { + ctx.moveTo(15, subD); + ctx.lineTo(lineNum, subD); + } + } + rulerD += bigInt; + } + ctx.strokeStyle = '#000'; + ctx.stroke(); + } + } + + var updateCanvas = editor.updateCanvas = function (center, newCtr) { + var w = workarea.width(), h = workarea.height(); + var wOrig = w, hOrig = h; + var zoom = svgCanvas.getZoom(); + var wArea = workarea; + var cnvs = $('#svgcanvas'); + var oldCtr = { + x: wArea[0].scrollLeft + wOrig / 2, + y: wArea[0].scrollTop + hOrig / 2 + }; + var multi = curConfig.canvas_expansion; + w = Math.max(wOrig, svgCanvas.contentW * zoom * multi); + h = Math.max(hOrig, svgCanvas.contentH * zoom * multi); + + if (w === wOrig && h === hOrig) { + workarea.css('overflow', 'hidden'); + } else { + workarea.css('overflow', 'scroll'); + } + + var oldCanY = cnvs.height() / 2; + var oldCanX = cnvs.width() / 2; + cnvs.width(w).height(h); + var newCanY = h / 2; + var newCanX = w / 2; + var offset = svgCanvas.updateCanvas(w, h); + + var ratio = newCanX / oldCanX; + + var scrollX = w / 2 - wOrig / 2; + var scrollY = h / 2 - hOrig / 2; + + if (!newCtr) { + var oldDistX = oldCtr.x - oldCanX; + var newX = newCanX + oldDistX * ratio; + + var oldDistY = oldCtr.y - oldCanY; + var newY = newCanY + oldDistY * ratio; + + newCtr = { + x: newX, + y: newY + }; + } else { + newCtr.x += offset.x; + newCtr.y += offset.y; + } + + if (center) { + // Go to top-left for larger documents + if (svgCanvas.contentW > wArea.width()) { + // Top-left + workarea[0].scrollLeft = offset.x - 10; + workarea[0].scrollTop = offset.y - 10; + } else { + // Center + wArea[0].scrollLeft = scrollX; + wArea[0].scrollTop = scrollY; + } + } else { + wArea[0].scrollLeft = newCtr.x - wOrig / 2; + wArea[0].scrollTop = newCtr.y - hOrig / 2; + } + if (curConfig.showRulers) { + updateRulers(cnvs, zoom); + workarea.scroll(); + } + if (urldata.storagePrompt !== true && !editor.storagePromptClosed) { + $('#dialog_box').hide(); + } + }; + + var updateToolButtonState = function () { + var index, button; + var bNoFill = (svgCanvas.getColor('fill') === 'none'); + var bNoStroke = (svgCanvas.getColor('stroke') === 'none'); + var buttonsNeedingStroke = ['#tool_fhpath', '#tool_line']; + var buttonsNeedingFillAndStroke = ['#tools_rect .tool_button', '#tools_ellipse .tool_button', '#tool_text', '#tool_path']; + if (bNoStroke) { + for (index in buttonsNeedingStroke) { + button = buttonsNeedingStroke[index]; + if ($(button).hasClass('tool_button_current')) { + clickSelect(); + } + $(button).addClass('disabled'); + } + } else { + for (index in buttonsNeedingStroke) { + button = buttonsNeedingStroke[index]; + $(button).removeClass('disabled'); + } + } + + if (bNoStroke && bNoFill) { + for (index in buttonsNeedingFillAndStroke) { + button = buttonsNeedingFillAndStroke[index]; + if ($(button).hasClass('tool_button_current')) { + clickSelect(); + } + $(button).addClass('disabled'); + } + } else { + for (index in buttonsNeedingFillAndStroke) { + button = buttonsNeedingFillAndStroke[index]; + $(button).removeClass('disabled'); + } + } + + svgCanvas.runExtensions('toolButtonStateUpdate', { + nofill: bNoFill, + nostroke: bNoStroke + }); + + // Disable flyouts if all inside are disabled + $('.tools_flyout').each(function () { + var shower = $('#' + this.id + '_show'); + var hasEnabled = false; + $(this).children().each(function () { + if (!$(this).hasClass('disabled')) { + hasEnabled = true; + } + }); + shower.toggleClass('disabled', !hasEnabled); + }); + + operaRepaint(); + }; + + // Updates the toolbar (colors, opacity, etc) based on the selected element + // This function also updates the opacity and id elements that are in the context panel + var updateToolbar = function () { + var i, len; + if (selectedElement != null) { + switch (selectedElement.tagName) { + case 'use': + case 'image': + case 'foreignObject': + break; + case 'g': + case 'a': + // Look for common styles + var gWidth = null; + var childs = selectedElement.getElementsByTagName('*'); + for (i = 0, len = childs.length; i < len; i++) { + var swidth = childs[i].getAttribute('stroke-width'); + + if (i === 0) { + gWidth = swidth; + } else if (gWidth !== swidth) { + gWidth = null; + } + } + + $('#stroke_width').val(gWidth === null ? '' : gWidth); + + paintBox.fill.update(true); + paintBox.stroke.update(true); + + break; + default: + paintBox.fill.update(true); + paintBox.stroke.update(true); + + $('#stroke_width').val(selectedElement.getAttribute('stroke-width') || 1); + $('#stroke_style').val(selectedElement.getAttribute('stroke-dasharray') || 'none'); + + var attr = selectedElement.getAttribute('stroke-linejoin') || 'miter'; + + if ($('#linejoin_' + attr).length !== 0) { + setStrokeOpt($('#linejoin_' + attr)[0]); + } + + attr = selectedElement.getAttribute('stroke-linecap') || 'butt'; + + if ($('#linecap_' + attr).length !== 0) { + setStrokeOpt($('#linecap_' + attr)[0]); + } + } + } + + // All elements including image and group have opacity + if (selectedElement != null) { + var opacPerc = (selectedElement.getAttribute('opacity') || 1.0) * 100; + $('#group_opacity').val(opacPerc); + $('#opac_slider').slider('option', 'value', opacPerc); + $('#elem_id').val(selectedElement.id); + $('#elem_class').val(selectedElement.getAttribute('class')); + } + + updateToolButtonState(); + }; + + // updates the context panel tools based on the selected element + var updateContextPanel = function () { + var elem = selectedElement; + // If element has just been deleted, consider it null + if (elem != null && !elem.parentNode) { elem = null; } + var currentLayerName = svgCanvas.getCurrentDrawing().getCurrentLayerName(); + var currentMode = svgCanvas.getMode(); + var unit = curConfig.baseUnit !== 'px' ? curConfig.baseUnit : null; + + var isNode = currentMode === 'pathedit'; // elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false; + var menuItems = $('#cmenu_canvas li'); + $('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,' + + '#ellipse_panel, #line_panel, #text_panel, #image_panel, #container_panel,' + + ' #use_panel, #a_panel').hide(); + if (elem != null) { + var elname = elem.nodeName; + // If this is a link with no transform and one child, pretend + // its child is selected + // if (elname === 'a') { // && !$(elem).attr('transform')) { + // elem = elem.firstChild; + // } + + var angle = svgCanvas.getRotationAngle(elem); + $('#angle').val(angle); + + var blurval = svgCanvas.getBlur(elem); + $('#blur').val(blurval); + $('#blur_slider').slider('option', 'value', blurval); + + if (svgCanvas.addedNew) { + if (elname === 'image' && svgCanvas.getMode() === 'image') { + // Prompt for URL if not a data URL + if (svgCanvas.getHref(elem).indexOf('data:') !== 0) { + promptImgURL(); + } + } + /* else if (elname == 'text') { + // TODO: Do something here for new text + } */ + } + + if (!isNode && currentMode !== 'pathedit') { + $('#selected_panel').show(); + // Elements in this array already have coord fields + if (['line', 'circle', 'ellipse'].indexOf(elname) >= 0) { + $('#xy_panel').hide(); + } else { + var x, y; + + // Get BBox vals for g, polyline and path + if (['g', 'polyline', 'path'].indexOf(elname) >= 0) { + var bb = svgCanvas.getStrokedBBox([elem]); + if (bb) { + x = bb.x; + y = bb.y; + } + } else { + x = elem.getAttribute('x'); + y = elem.getAttribute('y'); + } + + if (unit) { + x = svgedit.units.convertUnit(x); + y = svgedit.units.convertUnit(y); + } + + $('#selected_x').val(x || 0); + $('#selected_y').val(y || 0); + $('#xy_panel').show(); + } + + // Elements in this array cannot be converted to a path + var noPath = ['image', 'text', 'path', 'g', 'use'].indexOf(elname) === -1; + $('#tool_topath').toggle(noPath); + $('#tool_reorient').toggle(elname === 'path'); + $('#tool_reorient').toggleClass('disabled', angle === 0); + } else { + var point = path.getNodePoint(); + $('#tool_add_subpath').removeClass('push_button_pressed').addClass('tool_button'); + $('#tool_node_delete').toggleClass('disabled', !path.canDeleteNodes); + + // Show open/close button based on selected point + setIcon('#tool_openclose_path', path.closed_subpath ? 'open_path' : 'close_path'); + + if (point) { + var segType = $('#seg_type'); + if (unit) { + point.x = svgedit.units.convertUnit(point.x); + point.y = svgedit.units.convertUnit(point.y); + } + $('#path_node_x').val(point.x); + $('#path_node_y').val(point.y); + if (point.type) { + segType.val(point.type).removeAttr('disabled'); + } else { + segType.val(4).attr('disabled', 'disabled'); + } + } + return; + } + + // update contextual tools here + var panels = { + g: [], + a: [], + rect: ['rx', 'width', 'height'], + image: ['width', 'height'], + circle: ['cx', 'cy', 'r'], + ellipse: ['cx', 'cy', 'rx', 'ry'], + line: ['x1', 'y1', 'x2', 'y2'], + text: [], + use: [] + }; + + var tagName = elem.tagName; + + // if ($(elem).data('gsvg')) { + // $('#g_panel').show(); + // } + + var linkHref = null; + if (tagName === 'a') { + linkHref = svgCanvas.getHref(elem); + $('#g_panel').show(); + } + + if (elem.parentNode.tagName === 'a') { + if (!$(elem).siblings().length) { + $('#a_panel').show(); + linkHref = svgCanvas.getHref(elem.parentNode); + } + } + + // Hide/show the make_link buttons + $('#tool_make_link, #tool_make_link').toggle(!linkHref); + + if (linkHref) { + $('#link_url').val(linkHref); + } + + if (panels[tagName]) { + var curPanel = panels[tagName]; + + $('#' + tagName + '_panel').show(); + + $.each(curPanel, function (i, item) { + var attrVal = elem.getAttribute(item); + if (curConfig.baseUnit !== 'px' && elem[item]) { + var bv = elem[item].baseVal.value; + attrVal = svgedit.units.convertUnit(bv); + } + $('#' + tagName + '_' + item).val(attrVal || 0); + }); + + if (tagName === 'text') { + $('#text_panel').css('display', 'inline'); + $('#tool_font_size').css('display', 'inline'); + if (svgCanvas.getItalic()) { + $('#tool_italic').addClass('push_button_pressed').removeClass('tool_button'); + } else { + $('#tool_italic').removeClass('push_button_pressed').addClass('tool_button'); + } + if (svgCanvas.getBold()) { + $('#tool_bold').addClass('push_button_pressed').removeClass('tool_button'); + } else { + $('#tool_bold').removeClass('push_button_pressed').addClass('tool_button'); + } + $('#font_family').val(elem.getAttribute('font-family')); + $('#font_size').val(elem.getAttribute('font-size')); + $('#text').val(elem.textContent); + if (svgCanvas.addedNew) { + // Timeout needed for IE9 + setTimeout(function () { + $('#text').focus().select(); + }, 100); + } + // text + } else if (tagName === 'image' && svgCanvas.getMode() === 'image') { + setImageURL(svgCanvas.getHref(elem)); + // image + } else if (tagName === 'g' || tagName === 'use') { + $('#container_panel').show(); + var title = svgCanvas.getTitle(); + var label = $('#g_title')[0]; + label.value = title; + setInputWidth(label); + $('#g_title').prop('disabled', tagName === 'use'); + } + } + menuItems[(tagName === 'g' ? 'en' : 'dis') + 'ableContextMenuItems']('#ungroup'); + menuItems[((tagName === 'g' || !multiselected) ? 'dis' : 'en') + 'ableContextMenuItems']('#group'); + // if (elem != null) + } else if (multiselected) { + $('#multiselected_panel').show(); + menuItems + .enableContextMenuItems('#group') + .disableContextMenuItems('#ungroup'); + } else { + menuItems.disableContextMenuItems('#delete,#cut,#copy,#group,#ungroup,#move_front,#move_up,#move_down,#move_back'); + } + + // update history buttons + $('#tool_undo').toggleClass('disabled', undoMgr.getUndoStackSize() === 0); + $('#tool_redo').toggleClass('disabled', undoMgr.getRedoStackSize() === 0); + + svgCanvas.addedNew = false; + + if ((elem && !isNode) || multiselected) { + // update the selected elements' layer + $('#selLayerNames').removeAttr('disabled').val(currentLayerName); + + // Enable regular menu options + canvMenu.enableContextMenuItems('#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back'); + } else { + $('#selLayerNames').attr('disabled', 'disabled'); + } + }; + + var updateWireFrame = function () { + // Test support + if (supportsNonSS) { return; } + + var rule = '#workarea.wireframe #svgcontent * { stroke-width: ' + 1 / svgCanvas.getZoom() + 'px; }'; + $('#wireframe_rules').text(workarea.hasClass('wireframe') ? rule : ''); + }; + + var updateTitle = function (title) { + title = title || svgCanvas.getDocumentTitle(); + var newTitle = origTitle + (title ? ': ' + title : ''); + + // Remove title update with current context info, isn't really necessary + // if (curContext) { + // new_title = new_title + curContext; + // } + $('title:first').text(newTitle); + }; + + // called when we've selected a different element + var selectedChanged = function (win, elems) { + var mode = svgCanvas.getMode(); + if (mode === 'select') { + setSelectMode(); + } + var isNode = mode === 'pathedit'; + // if elems[1] is present, then we have more than one element + selectedElement = (elems.length === 1 || elems[1] == null ? elems[0] : null); + multiselected = (elems.length >= 2 && elems[1] != null); + if (selectedElement != null) { + // unless we're already in always set the mode of the editor to select because + // upon creation of a text element the editor is switched into + // select mode and this event fires - we need our UI to be in sync + + if (!isNode) { + updateToolbar(); + } + } // if (elem != null) + + // Deal with pathedit mode + togglePathEditMode(isNode, elems); + updateContextPanel(); + svgCanvas.runExtensions('selectedChanged', { + elems: elems, + selectedElement: selectedElement, + multiselected: multiselected + }); + }; + + // Call when part of element is in process of changing, generally + // on mousemove actions like rotate, move, etc. + var elementTransition = function (win, elems) { + var mode = svgCanvas.getMode(); + var elem = elems[0]; + + if (!elem) { + return; + } + + multiselected = (elems.length >= 2 && elems[1] != null); + // Only updating fields for single elements for now + if (!multiselected) { + switch (mode) { + case 'rotate': + var ang = svgCanvas.getRotationAngle(elem); + $('#angle').val(ang); + $('#tool_reorient').toggleClass('disabled', ang === 0); + break; + + // TODO: Update values that change on move/resize, etc + // case "select": + // case "resize": + // break; + } + } + svgCanvas.runExtensions('elementTransition', { + elems: elems + }); + }; + + /** + * Test whether an element is a layer or not. + * @param {SVGGElement} elem - The SVGGElement to test. + * @returns {boolean} True if the element is a layer + */ + function isLayer (elem) { + return elem && elem.tagName === 'g' && svgedit.draw.Layer.CLASS_REGEX.test(elem.getAttribute('class')); + } + + // called when any element has changed + var elementChanged = function (win, elems) { + var i, + mode = svgCanvas.getMode(); + if (mode === 'select') { + setSelectMode(); + } + + for (i = 0; i < elems.length; ++i) { + var elem = elems[i]; + + var isSvgElem = (elem && elem.tagName === 'svg'); + if (isSvgElem || isLayer(elem)) { + populateLayers(); + // if the element changed was the svg, then it could be a resolution change + if (isSvgElem) { + updateCanvas(); + } + // Update selectedElement if element is no longer part of the image. + // This occurs for the text elements in Firefox + } else if (elem && selectedElement && selectedElement.parentNode == null) { + // || elem && elem.tagName == "path" && !multiselected) { // This was added in r1430, but not sure why + selectedElement = elem; + } + } + + editor.showSaveWarning = true; + + // we update the contextual panel with potentially new + // positional/sizing information (we DON'T want to update the + // toolbar here as that creates an infinite loop) + // also this updates the history buttons + + // we tell it to skip focusing the text control if the + // text element was previously in focus + updateContextPanel(); + + // In the event a gradient was flipped: + if (selectedElement && mode === 'select') { + paintBox.fill.update(); + paintBox.stroke.update(); + } + + svgCanvas.runExtensions('elementChanged', { + elems: elems + }); + }; + + var zoomDone = function () { + updateWireFrame(); + // updateCanvas(); // necessary? + }; + + var zoomChanged = svgCanvas.zoomChanged = function (win, bbox, autoCenter) { + var scrbar = 15, + // res = svgCanvas.getResolution(), // Currently unused + wArea = workarea; + // var canvas_pos = $('#svgcanvas').position(); // Currently unused + var zInfo = svgCanvas.setBBoxZoom(bbox, wArea.width() - scrbar, wArea.height() - scrbar); + if (!zInfo) { return; } + var zoomlevel = zInfo.zoom, + bb = zInfo.bbox; + + if (zoomlevel < 0.001) { + changeZoom({value: 0.1}); + return; + } + + $('#zoom').val((zoomlevel * 100).toFixed(1)); + + if (autoCenter) { + updateCanvas(); + } else { + updateCanvas(false, {x: bb.x * zoomlevel + (bb.width * zoomlevel) / 2, y: bb.y * zoomlevel + (bb.height * zoomlevel) / 2}); + } + + if (svgCanvas.getMode() === 'zoom' && bb.width) { + // Go to select if a zoom box was drawn + setSelectMode(); + } + + zoomDone(); + }; + + changeZoom = function (ctl) { + var zoomlevel = ctl.value / 100; + if (zoomlevel < 0.001) { + ctl.value = 0.1; + return; + } + var zoom = svgCanvas.getZoom(); + var wArea = workarea; + + zoomChanged(window, { + width: 0, + height: 0, + // center pt of scroll position + x: (wArea[0].scrollLeft + wArea.width() / 2) / zoom, + y: (wArea[0].scrollTop + wArea.height() / 2) / zoom, + zoom: zoomlevel + }, true); + }; + + $('#cur_context_panel').delegate('a', 'click', function () { + var link = $(this); + if (link.attr('data-root')) { + svgCanvas.leaveContext(); + } else { + svgCanvas.setContext(link.text()); + } + svgCanvas.clearSelection(); + return false; + }); + + var contextChanged = function (win, context) { + var linkStr = ''; + if (context) { + var str = ''; + linkStr = '' + svgCanvas.getCurrentDrawing().getCurrentLayerName() + ''; + + $(context).parentsUntil('#svgcontent > g').andSelf().each(function () { + if (this.id) { + str += ' > ' + this.id; + if (this !== context) { + linkStr += ' > ' + this.id + ''; + } else { + linkStr += ' > ' + this.id; + } + } + }); + + curContext = str; + } else { + curContext = null; + } + $('#cur_context_panel').toggle(!!context).html(linkStr); + + updateTitle(); + }; + + // Makes sure the current selected paint is available to work with + var prepPaints = function () { + paintBox.fill.prep(); + paintBox.stroke.prep(); + }; + + var flyoutFuncs = {}; + + var setFlyoutTitles = function () { + $('.tools_flyout').each(function () { + var shower = $('#' + this.id + '_show'); + if (shower.data('isLibrary')) { + return; + } + + var tooltips = []; + $(this).children().each(function () { + tooltips.push(this.title); + }); + shower[0].title = tooltips.join(' / '); + }); + }; + + var setFlyoutPositions = function () { + $('.tools_flyout').each(function () { + var shower = $('#' + this.id + '_show'); + var pos = shower.offset(); + var w = shower.outerWidth(); + $(this).css({left: (pos.left + w) * editor.tool_scale, top: pos.top}); + }); + }; + + var setupFlyouts = function (holders) { + $.each(holders, function (holdSel, btnOpts) { + var buttons = $(holdSel).children(); + var showSel = holdSel + '_show'; + var shower = $(showSel); + var def = false; + buttons.addClass('tool_button') + .unbind('click mousedown mouseup') // may not be necessary + .each(function (i) { + // Get this buttons options + var opts = btnOpts[i]; + + // Remember the function that goes with this ID + flyoutFuncs[opts.sel] = opts.fn; + + if (opts.isDefault) { def = i; } + + // Clicking the icon in flyout should set this set's icon + var func = function (event) { + var options = opts; + // Find the currently selected tool if comes from keystroke + if (event.type === 'keydown') { + var flyoutIsSelected = $(options.parent + '_show').hasClass('tool_button_current'); + var currentOperation = $(options.parent + '_show').attr('data-curopt'); + $.each(holders[opts.parent], function (i, tool) { + if (tool.sel === currentOperation) { + if (!event.shiftKey || !flyoutIsSelected) { + options = tool; + } else { + options = holders[opts.parent][i + 1] || holders[opts.parent][0]; + } + } + }); + } + if ($(this).hasClass('disabled')) { return false; } + if (toolButtonClick(showSel)) { + options.fn(); + } + var icon; + if (options.icon) { + icon = $.getSvgIcon(options.icon, true); + } else { + icon = $(options.sel).children().eq(0).clone(); + } + + icon[0].setAttribute('width', shower.width()); + icon[0].setAttribute('height', shower.height()); + shower.children(':not(.flyout_arrow_horiz)').remove(); + shower.append(icon).attr('data-curopt', options.sel); // This sets the current mode + }; + + $(this).mouseup(func); + + if (opts.key) { + $(document).bind('keydown', opts.key[0] + ' shift+' + opts.key[0], func); + } + }); + + if (def) { + shower.attr('data-curopt', btnOpts[def].sel); + } else if (!shower.attr('data-curopt')) { + // Set first as default + shower.attr('data-curopt', btnOpts[0].sel); + } + + var timer; + var pos = $(showSel).position(); + + // Clicking the "show" icon should set the current mode + shower.mousedown(function (evt) { + if (shower.hasClass('disabled')) { + return false; + } + var holder = $(holdSel); + var l = pos.left + 34; + var w = holder.width() * -1; + var time = holder.data('shown_popop') ? 200 : 0; + timer = setTimeout(function () { + // Show corresponding menu + if (!shower.data('isLibrary')) { + holder.css('left', w).show().animate({ + left: l + }, 150); + } else { + holder.css('left', l).show(); + } + holder.data('shown_popop', true); + }, time); + evt.preventDefault(); + }).mouseup(function (evt) { + clearTimeout(timer); + var opt = $(this).attr('data-curopt'); + // Is library and popped up, so do nothing + if (shower.data('isLibrary') && $(showSel.replace('_show', '')).is(':visible')) { + toolButtonClick(showSel, true); + return; + } + if (toolButtonClick(showSel) && flyoutFuncs[opt]) { + flyoutFuncs[opt](); + } + }); + // $('#tools_rect').mouseleave(function () { $('#tools_rect').fadeOut(); }); + }); + setFlyoutTitles(); + setFlyoutPositions(); + }; + + var makeFlyoutHolder = function (id, child) { + var div = $('
    ', { + 'class': 'tools_flyout', + id: id + }).appendTo('#svg_editor').append(child); + + return div; + }; + + var uaPrefix = (function () { + var prop; + var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/; + var someScript = document.getElementsByTagName('script')[0]; + for (prop in someScript.style) { + if (regex.test(prop)) { + // test is faster than match, so it's better to perform + // that on the lot and match only when necessary + return prop.match(regex)[0]; + } + } + // Nothing found so far? + if ('WebkitOpacity' in someScript.style) { return 'Webkit'; } + if ('KhtmlOpacity' in someScript.style) { return 'Khtml'; } + + return ''; + }()); + + var scaleElements = function (elems, scale) { + // var prefix = '-' + uaPrefix.toLowerCase() + '-'; // Currently unused + var sides = ['top', 'left', 'bottom', 'right']; + + elems.each(function () { + // Handled in CSS + // this.style[uaPrefix + 'Transform'] = 'scale(' + scale + ')'; + var i; + var el = $(this); + var w = el.outerWidth() * (scale - 1); + var h = el.outerHeight() * (scale - 1); + // var margins = {}; // Currently unused + + for (i = 0; i < 4; i++) { + var s = sides[i]; + var cur = el.data('orig_margin-' + s); + if (cur == null) { + cur = parseInt(el.css('margin-' + s), 10); + // Cache the original margin + el.data('orig_margin-' + s, cur); + } + var val = cur * scale; + if (s === 'right') { + val += w; + } else if (s === 'bottom') { + val += h; + } + + el.css('margin-' + s, val); + // el.css('outline', '1px solid red'); + } + }); + }; + + var setIconSize = editor.setIconSize = function (size) { + // var elems = $('.tool_button, .push_button, .tool_button_current, .disabled, .icon_label, #url_notice, #tool_open'); + var selToscale = '#tools_top .toolset, #editor_panel > *, #history_panel > *,' + ' #main_button, #tools_left > *, #path_node_panel > *, #multiselected_panel > *,' + ' #g_panel > *, #tool_font_size > *, .tools_flyout'; - var elems = $(selToscale); - var scale = 1; - - if (typeof size === 'number') { - scale = size; - } else { - var iconSizes = {s: 0.75, m: 1, l: 1.25, xl: 1.5}; - scale = iconSizes[size]; - } - - editor.tool_scale = scale; - - setFlyoutPositions(); - // $('.tools_flyout').each(function () { - // var pos = $(this).position(); - // console.log($(this), pos.left+(34 * scale)); - // $(this).css({'left': pos.left+(34 * scale), 'top': pos.top+(77 * scale)}); - // console.log('l', $(this).css('left')); - // }); - // - // var scale = .75; - - var hiddenPs = elems.parents(':hidden'); - hiddenPs.css('visibility', 'hidden').show(); - scaleElements(elems, scale); - hiddenPs.css('visibility', 'visible').hide(); - // return; - - $.pref('iconsize', size); - $('#iconsize').val(size); - - // Change icon size - // $('.tool_button, .push_button, .tool_button_current, .disabled, .icon_label, #url_notice, #tool_open') - // .find('> svg, > img').each(function () { - // this.setAttribute('width',size_num); - // this.setAttribute('height',size_num); - // }); - // - // $.resizeSvgIcons({ - // '.flyout_arrow_horiz > svg, .flyout_arrow_horiz > img': size_num / 5, - // '#logo > svg, #logo > img': size_num * 1.3, - // '#tools_bottom .icon_label > *': (size_num === 16 ? 18 : size_num * .75) - // }); - // if (size != 's') { - // $.resizeSvgIcons({'#layerbuttons svg, #layerbuttons img': size_num * .6}); - // } - - // Note that all rules will be prefixed with '#svg_editor' when parsed - var cssResizeRules = { - // '.tool_button,\ - // .push_button,\ - // .tool_button_current,\ - // .push_button_pressed,\ - // .disabled,\ - // .icon_label,\ - // .tools_flyout .tool_button': { - // 'width': {s: '16px', l: '32px', xl: '48px'}, - // 'height': {s: '16px', l: '32px', xl: '48px'}, - // 'padding': {s: '1px', l: '2px', xl: '3px'} - // }, - // '.tool_sep': { - // 'height': {s: '16px', l: '32px', xl: '48px'}, - // 'margin': {s: '2px 2px', l: '2px 5px', xl: '2px 8px'} - // }, - // '#main_icon': { - // 'width': {s: '31px', l: '53px', xl: '75px'}, - // 'height': {s: '22px', l: '42px', xl: '64px'} - // }, - '#tools_top': { - 'left': 50 + $('#main_button').width(), - 'height': 72 - }, - '#tools_left': { - 'width': 31, - 'top': 74 - }, - 'div#workarea': { - 'left': 38, - 'top': 74 - } - // '#tools_bottom': { - // 'left': {s: '27px', l: '46px', xl: '65px'}, - // 'height': {s: '58px', l: '98px', xl: '145px'} - // }, - // '#color_tools': { - // 'border-spacing': {s: '0 1px'}, - // 'margin-top': {s: '-1px'} - // }, - // '#color_tools .icon_label': { - // 'width': {l:'43px', xl: '60px'} - // }, - // '.color_tool': { - // 'height': {s: '20px'} - // }, - // '#tool_opacity': { - // 'top': {s: '1px'}, - // 'height': {s: 'auto', l:'auto', xl:'auto'} - // }, - // '#tools_top input, #tools_bottom input': { - // 'margin-top': {s: '2px', l: '4px', xl: '5px'}, - // 'height': {s: 'auto', l: 'auto', xl: 'auto'}, - // 'border': {s: '1px solid #555', l: 'auto', xl: 'auto'}, - // 'font-size': {s: '.9em', l: '1.2em', xl: '1.4em'} - // }, - // '#zoom_panel': { - // 'margin-top': {s: '3px', l: '4px', xl: '5px'} - // }, - // '#copyright, #tools_bottom .label': { - // 'font-size': {l: '1.5em', xl: '2em'}, - // 'line-height': {s: '15px'} - // }, - // '#tools_bottom_2': { - // 'width': {l: '295px', xl: '355px'}, - // 'top': {s: '4px'} - // }, - // '#tools_top > div, #tools_top': { - // 'line-height': {s: '17px', l: '34px', xl: '50px'} - // }, - // '.dropdown button': { - // 'height': {s: '18px', l: '34px', xl: '40px'}, - // 'line-height': {s: '18px', l: '34px', xl: '40px'}, - // 'margin-top': {s: '3px'} - // }, - // '#tools_top label, #tools_bottom label': { - // 'font-size': {s: '1em', l: '1.5em', xl: '2em'}, - // 'height': {s: '25px', l: '42px', xl: '64px'} - // }, - // 'div.toolset': { - // 'height': {s: '25px', l: '42px', xl: '64px'} - // }, - // '#tool_bold, #tool_italic': { - // 'font-size': {s: '1.5em', l: '3em', xl: '4.5em'} - // }, - // '#sidepanels': { - // 'top': {s: '50px', l: '88px', xl: '125px'}, - // 'bottom': {s: '51px', l: '68px', xl: '65px'} - // }, - // '#layerbuttons': { - // 'width': {l: '130px', xl: '175px'}, - // 'height': {l: '24px', xl: '30px'} - // }, - // '#layerlist': { - // 'width': {l: '128px', xl: '150px'} - // }, - // '.layer_button': { - // 'width': {l: '19px', xl: '28px'}, - // 'height': {l: '19px', xl: '28px'} - // }, - // 'input.spin-button': { - // 'background-image': {l: 'url('images/spinbtn_updn_big.png')', xl: 'url('images/spinbtn_updn_big.png')'}, - // 'background-position': {l: '100% -5px', xl: '100% -2px'}, - // 'padding-right': {l: '24px', xl: '24px' } - // }, - // 'input.spin-button.up': { - // 'background-position': {l: '100% -45px', xl: '100% -42px'} - // }, - // 'input.spin-button.down': { - // 'background-position': {l: '100% -85px', xl: '100% -82px'} - // }, - // '#position_opts': { - // 'width': {all: (size_num*4) +'px'} - // } - }; - - var ruleElem = $('#tool_size_rules'); - if (!ruleElem.length) { - ruleElem = $('').appendTo('head'); - } else { - ruleElem.empty(); - } - - if (size !== 'm') { - var styleStr = ''; - $.each(cssResizeRules, function (selector, rules) { - selector = '#svg_editor ' + selector.replace(/,/g, ', #svg_editor'); - styleStr += selector + '{'; - $.each(rules, function (prop, values) { - var val; - if (typeof values === 'number') { - val = (values * scale) + 'px'; - } else if (values[size] || values.all) { - val = (values[size] || values.all); - } - styleStr += (prop + ':' + val + ';'); - }); - styleStr += '}'; - }); - // this.style[uaPrefix + 'Transform'] = 'scale(' + scale + ')'; - var prefix = '-' + uaPrefix.toLowerCase() + '-'; - styleStr += (selToscale + '{' + prefix + 'transform: scale(' + scale + ');}' + - ' #svg_editor div.toolset .toolset {' + prefix + 'transform: scale(1); margin: 1px !important;}' + // Hack for markers - ' #svg_editor .ui-slider {' + prefix + 'transform: scale(' + (1 / scale) + ');}' // Hack for sliders - ); - ruleElem.text(styleStr); - } - - setFlyoutPositions(); - }; - - // TODO: Combine this with addDropDown or find other way to optimize - var addAltDropDown = function (elem, list, callback, opts) { - var button = $(elem); - list = $(list); - var onButton = false; - var dropUp = opts.dropUp; - if (dropUp) { - $(elem).addClass('dropup'); - } - list.find('li').bind('mouseup', function () { - if (opts.seticon) { - setIcon('#cur_' + button[0].id, $(this).children()); - $(this).addClass('current').siblings().removeClass('current'); - } - callback.apply(this, arguments); - }); - - $(window).mouseup(function (evt) { - if (!onButton) { - button.removeClass('down'); - list.hide(); - list.css({top: 0, left: 0}); - } - onButton = false; - }); - - // var height = list.height(); // Currently unused - button.bind('mousedown', function () { - var off = button.offset(); - if (dropUp) { - off.top -= list.height(); - off.left += 8; - } else { - off.top += button.height(); - } - list.offset(off); - - if (!button.hasClass('down')) { - list.show(); - onButton = true; - } else { - // CSS position must be reset for Webkit - list.hide(); - list.css({top: 0, left: 0}); - } - button.toggleClass('down'); - }).hover(function () { - onButton = true; - }).mouseout(function () { - onButton = false; - }); - - if (opts.multiclick) { - list.mousedown(function () { - onButton = true; - }); - } - }; - - var extsPreLang = []; - var extAdded = function (win, ext) { - if (!ext) { - return; - } - var cbCalled = false; - var resizeDone = false; - var cbReady = true; // Set to false to delay callback (e.g. wait for $.svgIcons) - - if (ext.langReady) { - if (editor.langChanged) { // We check for this since the "lang" pref could have been set by storage - var lang = $.pref('lang'); - ext.langReady({lang: lang, uiStrings: uiStrings}); - } else { - extsPreLang.push(ext); - } - } - - function prepResize () { - if (resizeTimer) { - clearTimeout(resizeTimer); - resizeTimer = null; - } - if (!resizeDone) { - resizeTimer = setTimeout(function () { - resizeDone = true; - setIconSize($.pref('iconsize')); - }, 50); - } - } - - var runCallback = function () { - if (ext.callback && !cbCalled && cbReady) { - cbCalled = true; - ext.callback(); - } - }; - - var btnSelects = []; - - if (ext.context_tools) { - $.each(ext.context_tools, function (i, tool) { - // Add select tool - var html; - var contId = tool.container_id ? (' id="' + tool.container_id + '"') : ''; - var panel = $('#' + tool.panel); - - // create the panel if it doesn't exist - if (!panel.length) { - panel = $('
    ', {id: tool.panel}).appendTo('#tools_top'); - } - - // TODO: Allow support for other types, or adding to existing tool - switch (tool.type) { - case 'tool_button': - html = '
    ' + tool.id + '
    '; - var div = $(html).appendTo(panel); - if (tool.events) { - $.each(tool.events, function (evt, func) { - $(div).bind(evt, func); - }); - } - break; - case 'select': - html = '' + - ''; - // Creates the tool, hides & adds it, returns the select element - var sel = $(html).appendTo(panel).find('select'); - - $.each(tool.events, function (evt, func) { - $(sel).bind(evt, func); - }); - break; - case 'button-select': - html = ''; - - var list = $('
      ').appendTo('#option_lists'); - - if (tool.colnum) { - list.addClass('optcols' + tool.colnum); - } - - // Creates the tool, hides & adds it, returns the select element - /* var dropdown = */ $(html).appendTo(panel).children(); - - btnSelects.push({ - elem: ('#' + tool.id), - list: ('#' + tool.id + '_opts'), - title: tool.title, - callback: tool.events.change, - cur: ('#cur_' + tool.id) - }); - - break; - case 'input': - html = '' + - '' + - tool.label + ':' + - ''; - - // Creates the tool, hides & adds it, returns the select element - - // Add to given tool.panel - var inp = $(html).appendTo(panel).find('input'); - - if (tool.spindata) { - inp.SpinButton(tool.spindata); - } - - if (tool.events) { - $.each(tool.events, function (evt, func) { - inp.bind(evt, func); - }); - } - break; - - default: - break; - } - }); - } - - if (ext.buttons) { - var fallbackObj = {}, - placementObj = {}, - svgicons = ext.svgicons, - holders = {}; - - // Add buttons given by extension - $.each(ext.buttons, function (i, btn) { - var icon, svgicon, tlsId; - var id = btn.id; - var num = i; - - // Give button a unique ID - while ($('#' + id).length) { - id = btn.id + '_' + (++num); - } - - if (!svgicons) { - icon = $(''); - } else { - fallbackObj[id] = btn.icon; - svgicon = btn.svgicon || btn.id; - if (btn.type === 'app_menu') { - placementObj['#' + id + ' > div'] = svgicon; - } else { - placementObj['#' + id] = svgicon; - } - } - - var cls, parent; - - // Set button up according to its type - switch (btn.type) { - case 'mode_flyout': - case 'mode': - cls = 'tool_button'; - parent = '#tools_left'; - break; - case 'context': - cls = 'tool_button'; - parent = '#' + btn.panel; - // create the panel if it doesn't exist - if (!$(parent).length) { - $('
      ', {id: btn.panel}).appendTo('#tools_top'); - } - break; - case 'app_menu': - cls = ''; - parent = '#main_menu ul'; - break; - } - var flyoutHolder, curH, showBtn, refData, refBtn; - var button = $((btn.list || btn.type === 'app_menu') ? '
    • ' : '
      ') - .attr('id', id) - .attr('title', btn.title) - .addClass(cls); - if (!btn.includeWith && !btn.list) { - if ('position' in btn) { - if ($(parent).children().eq(btn.position).length) { - $(parent).children().eq(btn.position).before(button); - } else { - $(parent).children().last().before(button); - } - } else { - button.appendTo(parent); - } - - if (btn.type === 'mode_flyout') { - // Add to flyout menu / make flyout menu - // var opts = btn.includeWith; - // // opts.button, default, position - refBtn = $(button); - - flyoutHolder = refBtn.parent(); - // Create a flyout menu if there isn't one already - if (!refBtn.parent().hasClass('tools_flyout')) { - // Create flyout placeholder - tlsId = refBtn[0].id.replace('tool_', 'tools_'); - showBtn = refBtn.clone() - .attr('id', tlsId + '_show') - .append($('
      ', {'class': 'flyout_arrow_horiz'})); - - refBtn.before(showBtn); - - // Create a flyout div - flyoutHolder = makeFlyoutHolder(tlsId, refBtn); - flyoutHolder.data('isLibrary', true); - showBtn.data('isLibrary', true); - } - // refData = Actions.getButtonData(opts.button); - - placementObj['#' + tlsId + '_show'] = btn.id; - // TODO: Find way to set the current icon using the iconloader if this is not default - - // Include data for extension button as well as ref button - curH = holders['#' + flyoutHolder[0].id] = [{ - sel: '#' + id, - fn: btn.events.click, - icon: btn.id, - // key: btn.key, - isDefault: true - }, refData]; - // - // // {sel:'#tool_rect', fn: clickRect, evt: 'mouseup', key: 4, parent: '#tools_rect', icon: 'rect'} - // - // var pos = ('position' in opts)?opts.position:'last'; - // var len = flyoutHolder.children().length; - // - // // Add at given position or end - // if (!isNaN(pos) && pos >= 0 && pos < len) { - // flyoutHolder.children().eq(pos).before(button); - // } else { - // flyoutHolder.append(button); - // curH.reverse(); - // } - } else if (btn.type === 'app_menu') { - button.append('
      ').append(btn.title); - } - } else if (btn.list) { - // Add button to list - button.addClass('push_button'); - $('#' + btn.list + '_opts').append(button); - if (btn.isDefault) { - $('#cur_' + btn.list).append(button.children().clone()); - svgicon = btn.svgicon || btn.id; - placementObj['#cur_' + btn.list] = svgicon; - } - } else if (btn.includeWith) { - // Add to flyout menu / make flyout menu - var opts = btn.includeWith; - // opts.button, default, position - refBtn = $(opts.button); - - flyoutHolder = refBtn.parent(); - // Create a flyout menu if there isn't one already - if (!refBtn.parent().hasClass('tools_flyout')) { - // Create flyout placeholder - tlsId = refBtn[0].id.replace('tool_', 'tools_'); - showBtn = refBtn.clone() - .attr('id', tlsId + '_show') - .append($('
      ', {'class': 'flyout_arrow_horiz'})); - - refBtn.before(showBtn); - - // Create a flyout div - flyoutHolder = makeFlyoutHolder(tlsId, refBtn); - } - - refData = Actions.getButtonData(opts.button); - - if (opts.isDefault) { - placementObj['#' + tlsId + '_show'] = btn.id; - } - // TODO: Find way to set the current icon using the iconloader if this is not default - - // Include data for extension button as well as ref button - curH = holders['#' + flyoutHolder[0].id] = [{ - sel: '#' + id, - fn: btn.events.click, - icon: btn.id, - key: btn.key, - isDefault: btn.includeWith ? btn.includeWith.isDefault : 0 - }, refData]; - - // {sel:'#tool_rect', fn: clickRect, evt: 'mouseup', key: 4, parent: '#tools_rect', icon: 'rect'} - - var pos = ('position' in opts) ? opts.position : 'last'; - var len = flyoutHolder.children().length; - - // Add at given position or end - if (!isNaN(pos) && pos >= 0 && pos < len) { - flyoutHolder.children().eq(pos).before(button); - } else { - flyoutHolder.append(button); - curH.reverse(); - } - } - - if (!svgicons) { - button.append(icon); - } - - if (!btn.list) { - // Add given events to button - $.each(btn.events, function (name, func) { - if (name === 'click' && btn.type === 'mode') { - if (btn.includeWith) { - button.bind(name, func); - } else { - button.bind(name, function () { - if (toolButtonClick(button)) { - func(); - } - }); - } - if (btn.key) { - $(document).bind('keydown', btn.key, func); - if (btn.title) { - button.attr('title', btn.title + ' [' + btn.key + ']'); - } - } - } else { - button.bind(name, func); - } - }); - } - - setupFlyouts(holders); - }); - - $.each(btnSelects, function () { - addAltDropDown(this.elem, this.list, this.callback, {seticon: true}); - }); - - if (svgicons) { - cbReady = false; // Delay callback - } - - $.svgIcons(svgicons, { - w: 24, h: 24, - id_match: false, - no_img: (!svgedit.browser.isWebkit()), - fallback: fallbackObj, - placement: placementObj, - callback: function (icons) { - // Non-ideal hack to make the icon match the current size - // if (curPrefs.iconsize && curPrefs.iconsize !== 'm') { - if ($.pref('iconsize') !== 'm') { - prepResize(); - } - cbReady = true; // Ready for callback - runCallback(); - } - }); - } - - runCallback(); - }; - - var getPaint = function (color, opac, type) { - // update the editor's fill paint - var opts = { alpha: opac }; - if (color.indexOf('url(#') === 0) { - var refElem = svgCanvas.getRefElem(color); - if (refElem) { - refElem = refElem.cloneNode(true); - } else { - refElem = $('#' + type + '_color defs *')[0]; - } - opts[refElem.tagName] = refElem; - } else if (color.indexOf('#') === 0) { - opts.solidColor = color.substr(1); - } else { - opts.solidColor = 'none'; - } - return new $.jGraduate.Paint(opts); - }; - - // $('#text').focus(function () { textBeingEntered = true; }); - // $('#text').blur(function () { textBeingEntered = false; }); - - // bind the selected event to our function that handles updates to the UI - svgCanvas.bind('selected', selectedChanged); - svgCanvas.bind('transition', elementTransition); - svgCanvas.bind('changed', elementChanged); - svgCanvas.bind('saved', saveHandler); - svgCanvas.bind('exported', exportHandler); - svgCanvas.bind('exportedPDF', function (win, data) { - var exportWindowName = data.exportWindowName; - if (exportWindowName) { - exportWindow = window.open('', exportWindowName); // A hack to get the window via JSON-able name without opening a new one - } - exportWindow.location.href = data.dataurlstring; - }); - svgCanvas.bind('zoomed', zoomChanged); - svgCanvas.bind('contextset', contextChanged); - svgCanvas.bind('extension_added', extAdded); - svgCanvas.textActions.setInputElem($('#text')[0]); - - var str = '
      '; - $.each(palette, function (i, item) { - str += '
      '; - }); - $('#palette').append(str); - - // Set up editor background functionality - // TODO add checkerboard as "pattern" - var colorBlocks = ['#FFF', '#888', '#000']; // ,'url(data:image/gif;base64,R0lGODlhEAAQAIAAAP%2F%2F%2F9bW1iH5BAAAAAAALAAAAAAQABAAAAIfjG%2Bgq4jM3IFLJgpswNly%2FXkcBpIiVaInlLJr9FZWAQA7)']; - str = ''; - $.each(colorBlocks, function () { - str += '
      '; - }); - $('#bg_blocks').append(str); - var blocks = $('#bg_blocks div'); - var curBg = 'cur_background'; - blocks.each(function () { - var blk = $(this); - blk.click(function () { - blocks.removeClass(curBg); - $(this).addClass(curBg); - }); - }); - - setBackground($.pref('bkgd_color'), $.pref('bkgd_url')); - - $('#image_save_opts input').val([$.pref('img_save')]); - - var changeRectRadius = function (ctl) { - svgCanvas.setRectRadius(ctl.value); - }; - - var changeFontSize = function (ctl) { - svgCanvas.setFontSize(ctl.value); - }; - - var changeStrokeWidth = function (ctl) { - var val = ctl.value; - if (val === 0 && selectedElement && ['line', 'polyline'].indexOf(selectedElement.nodeName) >= 0) { - val = ctl.value = 1; - } - svgCanvas.setStrokeWidth(val); - }; - - var changeRotationAngle = function (ctl) { - svgCanvas.setRotationAngle(ctl.value); - $('#tool_reorient').toggleClass('disabled', parseInt(ctl.value, 10) === 0); - }; - - var changeOpacity = function (ctl, val) { - if (val == null) { val = ctl.value; } - $('#group_opacity').val(val); - if (!ctl || !ctl.handle) { - $('#opac_slider').slider('option', 'value', val); - } - svgCanvas.setOpacity(val / 100); - }; - - var changeBlur = function (ctl, val, noUndo) { - if (val == null) { val = ctl.value; } - $('#blur').val(val); - var complete = false; - if (!ctl || !ctl.handle) { - $('#blur_slider').slider('option', 'value', val); - complete = true; - } - if (noUndo) { - svgCanvas.setBlurNoUndo(val); - } else { - svgCanvas.setBlur(val, complete); - } - }; - - $('#stroke_style').change(function () { - svgCanvas.setStrokeAttr('stroke-dasharray', $(this).val()); - operaRepaint(); - }); - - $('#stroke_linejoin').change(function () { - svgCanvas.setStrokeAttr('stroke-linejoin', $(this).val()); - operaRepaint(); - }); - - // Lose focus for select elements when changed (Allows keyboard shortcuts to work better) - $('select').change(function () { $(this).blur(); }); - - // fired when user wants to move elements to another layer - var promptMoveLayerOnce = false; - $('#selLayerNames').change(function () { - var destLayer = this.options[this.selectedIndex].value; - var confirmStr = uiStrings.notification.QmoveElemsToLayer.replace('%s', destLayer); - var moveToLayer = function (ok) { - if (!ok) { return; } - promptMoveLayerOnce = true; - svgCanvas.moveSelectedToLayer(destLayer); - svgCanvas.clearSelection(); - populateLayers(); - }; - if (destLayer) { - if (promptMoveLayerOnce) { - moveToLayer(true); - } else { - $.confirm(confirmStr, moveToLayer); - } - } - }); - - $('#font_family').change(function () { - svgCanvas.setFontFamily(this.value); - }); - - $('#seg_type').change(function () { - svgCanvas.setSegType($(this).val()); - }); - - $('#text').bind('keyup input', function () { - svgCanvas.setTextContent(this.value); - }); - - $('#image_url').change(function () { - setImageURL(this.value); - }); - - $('#link_url').change(function () { - if (this.value.length) { - svgCanvas.setLinkURL(this.value); - } else { - svgCanvas.removeHyperlink(); - } - }); - - $('#g_title').change(function () { - svgCanvas.setGroupTitle(this.value); - }); - - $('.attr_changer').change(function () { - var attr = this.getAttribute('data-attr'); - var val = this.value; - var valid = svgedit.units.isValidUnit(attr, val, selectedElement); - - if (!valid) { - $.alert(uiStrings.notification.invalidAttrValGiven); - this.value = selectedElement.getAttribute(attr); - return false; - } - - if (attr !== 'id' && attr !== 'class') { - if (isNaN(val)) { - val = svgCanvas.convertToNum(attr, val); - } else if (curConfig.baseUnit !== 'px') { - // Convert unitless value to one with given unit - - var unitData = svgedit.units.getTypeMap(); - - if (selectedElement[attr] || svgCanvas.getMode() === 'pathedit' || attr === 'x' || attr === 'y') { - val *= unitData[curConfig.baseUnit]; - } - } - } - - // if the user is changing the id, then de-select the element first - // change the ID, then re-select it with the new ID - if (attr === 'id') { - var elem = selectedElement; - svgCanvas.clearSelection(); - elem.id = val; - svgCanvas.addToSelection([elem], true); - } else { - svgCanvas.changeSelectedAttribute(attr, val); - } - this.blur(); - }); - - // Prevent selection of elements when shift-clicking - $('#palette').mouseover(function () { - var inp = $(''); - $(this).append(inp); - inp.focus().remove(); - }); - - $('.palette_item').mousedown(function (evt) { - // shift key or right click for stroke - var picker = evt.shiftKey || evt.button === 2 ? 'stroke' : 'fill'; - var color = $(this).data('rgb'); - var paint; - - // Webkit-based browsers returned 'initial' here for no stroke - if (color === 'none' || color === 'transparent' || color === 'initial') { - color = 'none'; - paint = new $.jGraduate.Paint(); - } else { - paint = new $.jGraduate.Paint({alpha: 100, solidColor: color.substr(1)}); - } - - paintBox[picker].setPaint(paint); - svgCanvas.setColor(picker, color); - - if (color !== 'none' && svgCanvas.getPaintOpacity(picker) !== 1) { - svgCanvas.setPaintOpacity(picker, 1.0); - } - updateToolButtonState(); - }).bind('contextmenu', function (e) { e.preventDefault(); }); - - $('#toggle_stroke_tools').on('click', function () { - $('#tools_bottom').toggleClass('expanded'); - }); - - (function () { - var lastX = null, lastY = null, wArea = workarea[0], - panning = false, keypan = false; - - $('#svgcanvas').bind('mousemove mouseup', function (evt) { - if (panning === false) { return; } - - wArea.scrollLeft -= (evt.clientX - lastX); - wArea.scrollTop -= (evt.clientY - lastY); - - lastX = evt.clientX; - lastY = evt.clientY; - - if (evt.type === 'mouseup') { panning = false; } - return false; - }).mousedown(function (evt) { - if (evt.button === 1 || keypan === true) { - panning = true; - lastX = evt.clientX; - lastY = evt.clientY; - return false; - } - }); - - $(window).mouseup(function () { - panning = false; - }); - - $(document).bind('keydown', 'space', function (evt) { - svgCanvas.spaceKey = keypan = true; - evt.preventDefault(); - }).bind('keyup', 'space', function (evt) { - evt.preventDefault(); - svgCanvas.spaceKey = keypan = false; - }).bind('keydown', 'shift', function (evt) { - if (svgCanvas.getMode() === 'zoom') { - workarea.css('cursor', zoomOutIcon); - } - }).bind('keyup', 'shift', function (evt) { - if (svgCanvas.getMode() === 'zoom') { - workarea.css('cursor', zoomInIcon); - } - }); - - editor.setPanning = function (active) { - svgCanvas.spaceKey = keypan = active; - }; - }()); - - (function () { - var button = $('#main_icon'); - var overlay = $('#main_icon span'); - var list = $('#main_menu'); - var onButton = false; - var height = 0; - var jsHover = true; - var setClick = false; - - /* - // Currently unused - var hideMenu = function () { - list.fadeOut(200); - }; - */ - - $(window).mouseup(function (evt) { - if (!onButton) { - button.removeClass('buttondown'); - // do not hide if it was the file input as that input needs to be visible - // for its change event to fire - if (evt.target.tagName !== 'INPUT') { - list.fadeOut(200); - } else if (!setClick) { - setClick = true; - $(evt.target).click(function () { - list.css('margin-left', '-9999px').show(); - }); - } - } - onButton = false; - }).mousedown(function (evt) { - // $('.contextMenu').hide(); - var islib = $(evt.target).closest('div.tools_flyout, .contextMenu').length; - if (!islib) { - $('.tools_flyout:visible,.contextMenu').fadeOut(250); - } - }); - - overlay.bind('mousedown', function () { - if (!button.hasClass('buttondown')) { - // Margin must be reset in case it was changed before; - list.css('margin-left', 0).show(); - if (!height) { - height = list.height(); - } - // Using custom animation as slideDown has annoying 'bounce effect' - list.css('height', 0).animate({ - 'height': height - }, 200); - onButton = true; - } else { - list.fadeOut(200); - } - button.toggleClass('buttondown buttonup'); - }).hover(function () { - onButton = true; - }).mouseout(function () { - onButton = false; - }); - - var listItems = $('#main_menu li'); - - // Check if JS method of hovering needs to be used (Webkit bug) - listItems.mouseover(function () { - jsHover = ($(this).css('background-color') === 'rgba(0, 0, 0, 0)'); - - listItems.unbind('mouseover'); - if (jsHover) { - listItems.mouseover(function () { - this.style.backgroundColor = '#FFC'; - }).mouseout(function () { - this.style.backgroundColor = 'transparent'; - return true; - }); - } - }); - }()); - // Made public for UI customization. - // TODO: Group UI functions into a public editor.ui interface. - editor.addDropDown = function (elem, callback, dropUp) { - if ($(elem).length === 0) { return; } // Quit if called on non-existant element - var button = $(elem).find('button'); - var list = $(elem).find('ul').attr('id', $(elem)[0].id + '-list'); - var onButton = false; - if (dropUp) { - $(elem).addClass('dropup'); - } else { - // Move list to place where it can overflow container - $('#option_lists').append(list); - } - list.find('li').bind('mouseup', callback); - - $(window).mouseup(function (evt) { - if (!onButton) { - button.removeClass('down'); - list.hide(); - } - onButton = false; - }); - - button.bind('mousedown', function () { - if (!button.hasClass('down')) { - if (!dropUp) { - var pos = $(elem).position(); - list.css({ - top: pos.top + 24, - left: pos.left - 10 - }); - } - list.show(); - onButton = true; - } else { - list.hide(); - } - button.toggleClass('down'); - }).hover(function () { - onButton = true; - }).mouseout(function () { - onButton = false; - }); - }; - - editor.addDropDown('#font_family_dropdown', function () { - $('#font_family').val($(this).text()).change(); - }); - - editor.addDropDown('#opacity_dropdown', function () { - if ($(this).find('div').length) { return; } - var perc = parseInt($(this).text().split('%')[0], 10); - changeOpacity(false, perc); - }, true); - - // For slider usage, see: http://jqueryui.com/demos/slider/ - $('#opac_slider').slider({ - start: function () { - $('#opacity_dropdown li:not(.special)').hide(); - }, - stop: function () { - $('#opacity_dropdown li').show(); - $(window).mouseup(); - }, - slide: function (evt, ui) { - changeOpacity(ui); - } - }); - - editor.addDropDown('#blur_dropdown', $.noop); - - var slideStart = false; - - $('#blur_slider').slider({ - max: 10, - step: 0.1, - stop: function (evt, ui) { - slideStart = false; - changeBlur(ui); - $('#blur_dropdown li').show(); - $(window).mouseup(); - }, - start: function () { - slideStart = true; - }, - slide: function (evt, ui) { - changeBlur(ui, null, slideStart); - } - }); - - editor.addDropDown('#zoom_dropdown', function () { - var item = $(this); - var val = item.data('val'); - if (val) { - zoomChanged(window, val); - } else { - changeZoom({value: parseFloat(item.text())}); - } - }, true); - - addAltDropDown('#stroke_linecap', '#linecap_opts', function () { - setStrokeOpt(this, true); - }, {dropUp: true}); - - addAltDropDown('#stroke_linejoin', '#linejoin_opts', function () { - setStrokeOpt(this, true); - }, {dropUp: true}); - - addAltDropDown('#tool_position', '#position_opts', function () { - var letter = this.id.replace('tool_pos', '').charAt(0); - svgCanvas.alignSelectedElements(letter, 'page'); - }, {multiclick: true}); - - /* - - When a flyout icon is selected - (if flyout) { - - Change the icon - - Make pressing the button run its stuff - } - - Run its stuff - - When its shortcut key is pressed - - If not current in list, do as above - , else: - - Just run its stuff - - */ - - // Unfocus text input when workarea is mousedowned. - (function () { - var inp; - var unfocus = function () { - $(inp).blur(); - }; - - $('#svg_editor').find('button, select, input:not(#text)').focus(function () { - inp = this; - uiContext = 'toolbars'; - workarea.mousedown(unfocus); - }).blur(function () { - uiContext = 'canvas'; - workarea.unbind('mousedown', unfocus); - // Go back to selecting text if in textedit mode - if (svgCanvas.getMode() === 'textedit') { - $('#text').focus(); - } - }); - }()); - - var clickFHPath = function () { - if (toolButtonClick('#tool_fhpath')) { - svgCanvas.setMode('fhpath'); - } - }; - - var clickLine = function () { - if (toolButtonClick('#tool_line')) { - svgCanvas.setMode('line'); - } - }; - - var clickSquare = function () { - if (toolButtonClick('#tool_square')) { - svgCanvas.setMode('square'); - } - }; - - var clickRect = function () { - if (toolButtonClick('#tool_rect')) { - svgCanvas.setMode('rect'); - } - }; - - var clickFHRect = function () { - if (toolButtonClick('#tool_fhrect')) { - svgCanvas.setMode('fhrect'); - } - }; - - var clickCircle = function () { - if (toolButtonClick('#tool_circle')) { - svgCanvas.setMode('circle'); - } - }; - - var clickEllipse = function () { - if (toolButtonClick('#tool_ellipse')) { - svgCanvas.setMode('ellipse'); - } - }; - - var clickFHEllipse = function () { - if (toolButtonClick('#tool_fhellipse')) { - svgCanvas.setMode('fhellipse'); - } - }; - - var clickImage = function () { - if (toolButtonClick('#tool_image')) { - svgCanvas.setMode('image'); - } - }; - - var clickZoom = function () { - if (toolButtonClick('#tool_zoom')) { - svgCanvas.setMode('zoom'); - workarea.css('cursor', zoomInIcon); - } - }; - - var zoomImage = function (multiplier) { - var res = svgCanvas.getResolution(); - multiplier = multiplier ? res.zoom * multiplier : 1; - // setResolution(res.w * multiplier, res.h * multiplier, true); - $('#zoom').val(multiplier * 100); - svgCanvas.setZoom(multiplier); - zoomDone(); - updateCanvas(true); - }; - - var dblclickZoom = function () { - if (toolButtonClick('#tool_zoom')) { - zoomImage(); - setSelectMode(); - } - }; - - var clickText = function () { - if (toolButtonClick('#tool_text')) { - svgCanvas.setMode('text'); - } - }; - - var clickPath = function () { - if (toolButtonClick('#tool_path')) { - svgCanvas.setMode('path'); - } - }; - - // Delete is a contextual tool that only appears in the ribbon if - // an element has been selected - var deleteSelected = function () { - if (selectedElement != null || multiselected) { - svgCanvas.deleteSelectedElements(); - } - }; - - var cutSelected = function () { - if (selectedElement != null || multiselected) { - svgCanvas.cutSelectedElements(); - } - }; - - var copySelected = function () { - if (selectedElement != null || multiselected) { - svgCanvas.copySelectedElements(); - } - }; - - var pasteInCenter = function () { - var zoom = svgCanvas.getZoom(); - var x = (workarea[0].scrollLeft + workarea.width() / 2) / zoom - svgCanvas.contentW; - var y = (workarea[0].scrollTop + workarea.height() / 2) / zoom - svgCanvas.contentH; - svgCanvas.pasteElements('point', x, y); - }; - - var moveToTopSelected = function () { - if (selectedElement != null) { - svgCanvas.moveToTopSelectedElement(); - } - }; - - var moveToBottomSelected = function () { - if (selectedElement != null) { - svgCanvas.moveToBottomSelectedElement(); - } - }; - - var moveUpDownSelected = function (dir) { - if (selectedElement != null) { - svgCanvas.moveUpDownSelected(dir); - } - }; - - var convertToPath = function () { - if (selectedElement != null) { - svgCanvas.convertToPath(); - } - }; - - var reorientPath = function () { - if (selectedElement != null) { - path.reorient(); - } - }; - - var makeHyperlink = function () { - if (selectedElement != null || multiselected) { - $.prompt(uiStrings.notification.enterNewLinkURL, 'http://', function (url) { - if (url) { svgCanvas.makeHyperlink(url); } - }); - } - }; - - var moveSelected = function (dx, dy) { - if (selectedElement != null || multiselected) { - if (curConfig.gridSnapping) { - // Use grid snap value regardless of zoom level - var multi = svgCanvas.getZoom() * curConfig.snappingStep; - dx *= multi; - dy *= multi; - } - svgCanvas.moveSelectedElements(dx, dy); - } - }; - - var linkControlPoints = function () { - $('#tool_node_link').toggleClass('push_button_pressed tool_button'); - var linked = $('#tool_node_link').hasClass('push_button_pressed'); - path.linkControlPoints(linked); - }; - - var clonePathNode = function () { - if (path.getNodePoint()) { - path.clonePathNode(); - } - }; - - var deletePathNode = function () { - if (path.getNodePoint()) { - path.deletePathNode(); - } - }; - - var addSubPath = function () { - var button = $('#tool_add_subpath'); - var sp = !button.hasClass('push_button_pressed'); - button.toggleClass('push_button_pressed tool_button'); - path.addSubPath(sp); - }; - - var opencloseSubPath = function () { - path.opencloseSubPath(); - }; - - var selectNext = function () { - svgCanvas.cycleElement(1); - }; - - var selectPrev = function () { - svgCanvas.cycleElement(0); - }; - - var rotateSelected = function (cw, step) { - if (selectedElement == null || multiselected) { return; } - if (!cw) { step *= -1; } - var angle = parseFloat($('#angle').val()) + step; - svgCanvas.setRotationAngle(angle); - updateContextPanel(); - }; - - var clickClear = function () { - var dims = curConfig.dimensions; - $.confirm(uiStrings.notification.QwantToClear, function (ok) { - if (!ok) { return; } - setSelectMode(); - svgCanvas.clear(); - svgCanvas.setResolution(dims[0], dims[1]); - updateCanvas(true); - zoomImage(); - populateLayers(); - updateContextPanel(); - prepPaints(); - svgCanvas.runExtensions('onNewDocument'); - }); - }; - - var clickBold = function () { - svgCanvas.setBold(!svgCanvas.getBold()); - updateContextPanel(); - return false; - }; - - var clickItalic = function () { - svgCanvas.setItalic(!svgCanvas.getItalic()); - updateContextPanel(); - return false; - }; - - var clickSave = function () { - // In the future, more options can be provided here - var saveOpts = { - 'images': $.pref('img_save'), - 'round_digits': 6 - }; - svgCanvas.save(saveOpts); - }; - - var loadingURL; - var clickExport = function () { - $.select('Select an image type for export: ', [ - // See http://kangax.github.io/jstests/toDataUrl_mime_type_test/ for a useful list of MIME types and browser support - // 'ICO', // Todo: Find a way to preserve transparency in SVG-Edit if not working presently and do full packaging for x-icon; then switch back to position after 'PNG' - 'PNG', - 'JPEG', 'BMP', 'WEBP', 'PDF' - ], function (imgType) { // todo: replace hard-coded msg with uiStrings.notification. - if (!imgType) { - return; - } - // Open placeholder window (prevents popup) - var exportWindowName; - function openExportWindow () { - var str = uiStrings.notification.loadingImage; - if (curConfig.exportWindowType === 'new') { - editor.exportWindowCt++; - } - exportWindowName = curConfig.canvasName + editor.exportWindowCt; - var popHTML, popURL; - if (loadingURL) { - popURL = loadingURL; - } else { - popHTML = '' + str + '

      ' + str + '

      '; - if (typeof URL && URL.createObjectURL) { - var blob = new Blob([popHTML], {type: 'text/html'}); - popURL = URL.createObjectURL(blob); - } else { - popURL = 'data:text/html;base64;charset=utf-8,' + svgedit.utilities.encode64(popHTML); - } - loadingURL = popURL; - } - exportWindow = window.open(popURL, exportWindowName); - } - if (imgType === 'PDF') { - if (!customExportPDF) { - openExportWindow(); - } - svgCanvas.exportPDF(exportWindowName); - } else { - if (!customExportImage) { - openExportWindow(); - } - var quality = parseInt($('#image-slider').val(), 10) / 100; - svgCanvas.rasterExport(imgType, quality, exportWindowName); - } - }, function () { - var sel = $(this); - if (sel.val() === 'JPEG' || sel.val() === 'WEBP') { - if (!$('#image-slider').length) { - $('
      ').appendTo(sel.parent()); // Todo: i18n-ize label - } - } else { - $('#image-slider').parent().remove(); - } - }); - }; - - // by default, svgCanvas.open() is a no-op. - // it is up to an extension mechanism (opera widget, etc) - // to call setCustomHandlers() which will make it do something - var clickOpen = function () { - svgCanvas.open(); - }; - - var clickImport = function () { - }; - - var clickUndo = function () { - if (undoMgr.getUndoStackSize() > 0) { - undoMgr.undo(); - populateLayers(); - } - }; - - var clickRedo = function () { - if (undoMgr.getRedoStackSize() > 0) { - undoMgr.redo(); - populateLayers(); - } - }; - - var clickGroup = function () { - // group - if (multiselected) { - svgCanvas.groupSelectedElements(); - // ungroup - } else if (selectedElement) { - svgCanvas.ungroupSelectedElement(); - } - }; - - var clickClone = function () { - svgCanvas.cloneSelectedElements(20, 20); - }; - - var clickAlign = function () { - var letter = this.id.replace('tool_align', '').charAt(0); - svgCanvas.alignSelectedElements(letter, $('#align_relative_to').val()); - }; - - var clickWireframe = function () { - $('#tool_wireframe').toggleClass('push_button_pressed tool_button'); - workarea.toggleClass('wireframe'); - - if (supportsNonSS) { return; } - var wfRules = $('#wireframe_rules'); - if (!wfRules.length) { - wfRules = $('').appendTo('head'); - } else { - wfRules.empty(); - } - - updateWireFrame(); - }; - - $('#svg_docprops_container, #svg_prefs_container').draggable({cancel: 'button,fieldset', containment: 'window'}); - - var showDocProperties = function () { - if (docprops) { return; } - docprops = true; - - // This selects the correct radio button by using the array notation - $('#image_save_opts input').val([$.pref('img_save')]); - - // update resolution option with actual resolution - var res = svgCanvas.getResolution(); - if (curConfig.baseUnit !== 'px') { - res.w = svgedit.units.convertUnit(res.w) + curConfig.baseUnit; - res.h = svgedit.units.convertUnit(res.h) + curConfig.baseUnit; - } - - $('#canvas_width').val(res.w); - $('#canvas_height').val(res.h); - $('#canvas_title').val(svgCanvas.getDocumentTitle()); - - $('#svg_docprops').show(); - }; - - var showPreferences = function () { - if (preferences) { return; } - preferences = true; - $('#main_menu').hide(); - - // Update background color with current one - var blocks = $('#bg_blocks div'); - var curBg = 'cur_background'; - var canvasBg = curPrefs.bkgd_color; - var url = $.pref('bkgd_url'); - blocks.each(function () { - var blk = $(this); - var isBg = blk.css('background-color') === canvasBg; - blk.toggleClass(curBg, isBg); - if (isBg) { $('#canvas_bg_url').removeClass(curBg); } - }); - if (!canvasBg) { blocks.eq(0).addClass(curBg); } - if (url) { - $('#canvas_bg_url').val(url); - } - $('#grid_snapping_on').prop('checked', curConfig.gridSnapping); - $('#grid_snapping_step').attr('value', curConfig.snappingStep); - $('#grid_color').attr('value', curConfig.gridColor); - - $('#svg_prefs').show(); - }; - - var hideSourceEditor = function () { - $('#svg_source_editor').hide(); - editingsource = false; - $('#svg_source_textarea').blur(); - }; - - var saveSourceEditor = function () { - if (!editingsource) { return; } - - var saveChanges = function () { - svgCanvas.clearSelection(); - hideSourceEditor(); - zoomImage(); - populateLayers(); - updateTitle(); - prepPaints(); - }; - - if (!svgCanvas.setSvgString($('#svg_source_textarea').val())) { - $.confirm(uiStrings.notification.QerrorsRevertToSource, function (ok) { - if (!ok) { return false; } - saveChanges(); - }); - } else { - saveChanges(); - } - setSelectMode(); - }; - - var hideDocProperties = function () { - $('#svg_docprops').hide(); - $('#canvas_width,#canvas_height').removeAttr('disabled'); - $('#resolution')[0].selectedIndex = 0; - $('#image_save_opts input').val([$.pref('img_save')]); - docprops = false; - }; - - var hidePreferences = function () { - $('#svg_prefs').hide(); - preferences = false; - }; - - var saveDocProperties = function () { - // set title - var newTitle = $('#canvas_title').val(); - updateTitle(newTitle); - svgCanvas.setDocumentTitle(newTitle); - - // update resolution - var width = $('#canvas_width'), w = width.val(); - var height = $('#canvas_height'), h = height.val(); - - if (w !== 'fit' && !svgedit.units.isValidUnit('width', w)) { - $.alert(uiStrings.notification.invalidAttrValGiven); - width.parent().addClass('error'); - return false; - } - - width.parent().removeClass('error'); - - if (h !== 'fit' && !svgedit.units.isValidUnit('height', h)) { - $.alert(uiStrings.notification.invalidAttrValGiven); - height.parent().addClass('error'); - return false; - } - - height.parent().removeClass('error'); - - if (!svgCanvas.setResolution(w, h)) { - $.alert(uiStrings.notification.noContentToFitTo); - return false; - } - - // Set image save option - $.pref('img_save', $('#image_save_opts :checked').val()); - updateCanvas(); - hideDocProperties(); - }; - - var savePreferences = editor.savePreferences = function () { - // Set background - var color = $('#bg_blocks div.cur_background').css('background-color') || '#FFF'; - setBackground(color, $('#canvas_bg_url').val()); - - // set language - var lang = $('#lang_select').val(); - if (lang !== $.pref('lang')) { - editor.putLocale(lang, goodLangs); - } - - // set icon size - setIconSize($('#iconsize').val()); - - // set grid setting - curConfig.gridSnapping = $('#grid_snapping_on')[0].checked; - curConfig.snappingStep = $('#grid_snapping_step').val(); - curConfig.gridColor = $('#grid_color').val(); - curConfig.showRulers = $('#show_rulers')[0].checked; - - $('#rulers').toggle(curConfig.showRulers); - if (curConfig.showRulers) { updateRulers(); } - curConfig.baseUnit = $('#base_unit').val(); - - svgCanvas.setConfig(curConfig); - - updateCanvas(); - hidePreferences(); - }; - - var resetScrollPos = $.noop; - - var cancelOverlays = function () { - $('#dialog_box').hide(); - if (!editingsource && !docprops && !preferences) { - if (curContext) { - svgCanvas.leaveContext(); - } - return; - } - - if (editingsource) { - if (origSource !== $('#svg_source_textarea').val()) { - $.confirm(uiStrings.notification.QignoreSourceChanges, function (ok) { - if (ok) { hideSourceEditor(); } - }); - } else { - hideSourceEditor(); - } - } else if (docprops) { - hideDocProperties(); - } else if (preferences) { - hidePreferences(); - } - resetScrollPos(); - }; - - var winWh = {width: $(window).width(), height: $(window).height()}; - - // Fix for Issue 781: Drawing area jumps to top-left corner on window resize (IE9) - if (svgedit.browser.isIE()) { - (function () { - resetScrollPos = function () { - if (workarea[0].scrollLeft === 0 && workarea[0].scrollTop === 0) { - workarea[0].scrollLeft = curScrollPos.left; - workarea[0].scrollTop = curScrollPos.top; - } - }; - - curScrollPos = { - left: workarea[0].scrollLeft, - top: workarea[0].scrollTop - }; - - $(window).resize(resetScrollPos); - editor.ready(function () { - // TODO: Find better way to detect when to do this to minimize - // flickering effect - setTimeout(function () { - resetScrollPos(); - }, 500); - }); - - workarea.scroll(function () { - curScrollPos = { - left: workarea[0].scrollLeft, - top: workarea[0].scrollTop - }; - }); - }()); - } - - $(window).resize(function (evt) { - $.each(winWh, function (type, val) { - var curval = $(window)[type](); - workarea[0]['scroll' + (type === 'width' ? 'Left' : 'Top')] -= (curval - val) / 2; - winWh[type] = curval; - }); - setFlyoutPositions(); - }); - - (function () { - workarea.scroll(function () { - // TODO: jQuery's scrollLeft/Top() wouldn't require a null check - if ($('#ruler_x').length !== 0) { - $('#ruler_x')[0].scrollLeft = workarea[0].scrollLeft; - } - if ($('#ruler_y').length !== 0) { - $('#ruler_y')[0].scrollTop = workarea[0].scrollTop; - } - }); - }()); - - $('#url_notice').click(function () { - $.alert(this.title); - }); - - $('#change_image_url').click(promptImgURL); - - // added these event handlers for all the push buttons so they - // behave more like buttons being pressed-in and not images - (function () { - var toolnames = ['clear', 'open', 'save', 'source', 'delete', 'delete_multi', 'paste', 'clone', 'clone_multi', 'move_top', 'move_bottom']; - var allTools = ''; - var curClass = 'tool_button_current'; - - $.each(toolnames, function (i, item) { - allTools += (i ? ',' : '') + '#tool_' + item; - }); - - $(allTools).mousedown(function () { - $(this).addClass(curClass); - }).bind('mousedown mouseout', function () { - $(this).removeClass(curClass); - }); - - $('#tool_undo, #tool_redo').mousedown(function () { - if (!$(this).hasClass('disabled')) { $(this).addClass(curClass); } - }).bind('mousedown mouseout', function () { - $(this).removeClass(curClass); - }); - }()); - - // switch modifier key in tooltips if mac - // NOTE: This code is not used yet until I can figure out how to successfully bind ctrl/meta - // in Opera and Chrome - if (svgedit.browser.isMac() && !window.opera) { - var shortcutButtons = ['tool_clear', 'tool_save', 'tool_source', 'tool_undo', 'tool_redo', 'tool_clone']; - i = shortcutButtons.length; - while (i--) { - var button = document.getElementById(shortcutButtons[i]); - if (button) { - var title = button.title; - var index = title.indexOf('Ctrl+'); - button.title = [title.substr(0, index), 'Cmd+', title.substr(index + 5)].join(''); - } - } - } - - // TODO: go back to the color boxes having white background-color and then setting - // background-image to none.png (otherwise partially transparent gradients look weird) - var colorPicker = function (elem) { - var picker = elem.attr('id') === 'stroke_color' ? 'stroke' : 'fill'; - // var opacity = (picker == 'stroke' ? $('#stroke_opacity') : $('#fill_opacity')); - var paint = paintBox[picker].paint; - var title = (picker === 'stroke' ? 'Pick a Stroke Paint and Opacity' : 'Pick a Fill Paint and Opacity'); - // var was_none = false; // Currently unused - var pos = elem.offset(); - $('#color_picker') - .draggable({cancel: '.jGraduate_tabs, .jGraduate_colPick, .jGraduate_gradPick, .jPicker', containment: 'window'}) - .css(curConfig.colorPickerCSS || {'left': pos.left - 140, 'bottom': 40}) - .jGraduate( - { - paint: paint, - window: { pickerTitle: title }, - images: { clientPath: curConfig.jGraduatePath }, - newstop: 'inverse' - }, - function (p) { - paint = new $.jGraduate.Paint(p); - paintBox[picker].setPaint(paint); - svgCanvas.setPaint(picker, paint); - $('#color_picker').hide(); - }, - function () { - $('#color_picker').hide(); - } - ); - }; - - var PaintBox = function (container, type) { - var paintColor, paintOpacity, - cur = curConfig[type === 'fill' ? 'initFill' : 'initStroke']; - // set up gradients to be used for the buttons - var svgdocbox = new DOMParser().parseFromString( - ' svg, > img').each(function () { + // this.setAttribute('width',size_num); + // this.setAttribute('height',size_num); + // }); + // + // $.resizeSvgIcons({ + // '.flyout_arrow_horiz > svg, .flyout_arrow_horiz > img': size_num / 5, + // '#logo > svg, #logo > img': size_num * 1.3, + // '#tools_bottom .icon_label > *': (size_num === 16 ? 18 : size_num * .75) + // }); + // if (size != 's') { + // $.resizeSvgIcons({'#layerbuttons svg, #layerbuttons img': size_num * .6}); + // } + + // Note that all rules will be prefixed with '#svg_editor' when parsed + var cssResizeRules = { + // '.tool_button,\ + // .push_button,\ + // .tool_button_current,\ + // .push_button_pressed,\ + // .disabled,\ + // .icon_label,\ + // .tools_flyout .tool_button': { + // 'width': {s: '16px', l: '32px', xl: '48px'}, + // 'height': {s: '16px', l: '32px', xl: '48px'}, + // 'padding': {s: '1px', l: '2px', xl: '3px'} + // }, + // '.tool_sep': { + // 'height': {s: '16px', l: '32px', xl: '48px'}, + // 'margin': {s: '2px 2px', l: '2px 5px', xl: '2px 8px'} + // }, + // '#main_icon': { + // 'width': {s: '31px', l: '53px', xl: '75px'}, + // 'height': {s: '22px', l: '42px', xl: '64px'} + // }, + '#tools_top': { + 'left': 50 + $('#main_button').width(), + 'height': 72 + }, + '#tools_left': { + 'width': 31, + 'top': 74 + }, + 'div#workarea': { + 'left': 38, + 'top': 74 + } + // '#tools_bottom': { + // 'left': {s: '27px', l: '46px', xl: '65px'}, + // 'height': {s: '58px', l: '98px', xl: '145px'} + // }, + // '#color_tools': { + // 'border-spacing': {s: '0 1px'}, + // 'margin-top': {s: '-1px'} + // }, + // '#color_tools .icon_label': { + // 'width': {l:'43px', xl: '60px'} + // }, + // '.color_tool': { + // 'height': {s: '20px'} + // }, + // '#tool_opacity': { + // 'top': {s: '1px'}, + // 'height': {s: 'auto', l:'auto', xl:'auto'} + // }, + // '#tools_top input, #tools_bottom input': { + // 'margin-top': {s: '2px', l: '4px', xl: '5px'}, + // 'height': {s: 'auto', l: 'auto', xl: 'auto'}, + // 'border': {s: '1px solid #555', l: 'auto', xl: 'auto'}, + // 'font-size': {s: '.9em', l: '1.2em', xl: '1.4em'} + // }, + // '#zoom_panel': { + // 'margin-top': {s: '3px', l: '4px', xl: '5px'} + // }, + // '#copyright, #tools_bottom .label': { + // 'font-size': {l: '1.5em', xl: '2em'}, + // 'line-height': {s: '15px'} + // }, + // '#tools_bottom_2': { + // 'width': {l: '295px', xl: '355px'}, + // 'top': {s: '4px'} + // }, + // '#tools_top > div, #tools_top': { + // 'line-height': {s: '17px', l: '34px', xl: '50px'} + // }, + // '.dropdown button': { + // 'height': {s: '18px', l: '34px', xl: '40px'}, + // 'line-height': {s: '18px', l: '34px', xl: '40px'}, + // 'margin-top': {s: '3px'} + // }, + // '#tools_top label, #tools_bottom label': { + // 'font-size': {s: '1em', l: '1.5em', xl: '2em'}, + // 'height': {s: '25px', l: '42px', xl: '64px'} + // }, + // 'div.toolset': { + // 'height': {s: '25px', l: '42px', xl: '64px'} + // }, + // '#tool_bold, #tool_italic': { + // 'font-size': {s: '1.5em', l: '3em', xl: '4.5em'} + // }, + // '#sidepanels': { + // 'top': {s: '50px', l: '88px', xl: '125px'}, + // 'bottom': {s: '51px', l: '68px', xl: '65px'} + // }, + // '#layerbuttons': { + // 'width': {l: '130px', xl: '175px'}, + // 'height': {l: '24px', xl: '30px'} + // }, + // '#layerlist': { + // 'width': {l: '128px', xl: '150px'} + // }, + // '.layer_button': { + // 'width': {l: '19px', xl: '28px'}, + // 'height': {l: '19px', xl: '28px'} + // }, + // 'input.spin-button': { + // 'background-image': {l: 'url('images/spinbtn_updn_big.png')', xl: 'url('images/spinbtn_updn_big.png')'}, + // 'background-position': {l: '100% -5px', xl: '100% -2px'}, + // 'padding-right': {l: '24px', xl: '24px' } + // }, + // 'input.spin-button.up': { + // 'background-position': {l: '100% -45px', xl: '100% -42px'} + // }, + // 'input.spin-button.down': { + // 'background-position': {l: '100% -85px', xl: '100% -82px'} + // }, + // '#position_opts': { + // 'width': {all: (size_num*4) +'px'} + // } + }; + + var ruleElem = $('#tool_size_rules'); + if (!ruleElem.length) { + ruleElem = $('').appendTo('head'); + } else { + ruleElem.empty(); + } + + if (size !== 'm') { + var styleStr = ''; + $.each(cssResizeRules, function (selector, rules) { + selector = '#svg_editor ' + selector.replace(/,/g, ', #svg_editor'); + styleStr += selector + '{'; + $.each(rules, function (prop, values) { + var val; + if (typeof values === 'number') { + val = (values * scale) + 'px'; + } else if (values[size] || values.all) { + val = (values[size] || values.all); + } + styleStr += (prop + ':' + val + ';'); + }); + styleStr += '}'; + }); + // this.style[uaPrefix + 'Transform'] = 'scale(' + scale + ')'; + var prefix = '-' + uaPrefix.toLowerCase() + '-'; + styleStr += (selToscale + '{' + prefix + 'transform: scale(' + scale + ');}' + + ' #svg_editor div.toolset .toolset {' + prefix + 'transform: scale(1); margin: 1px !important;}' + // Hack for markers + ' #svg_editor .ui-slider {' + prefix + 'transform: scale(' + (1 / scale) + ');}' // Hack for sliders + ); + ruleElem.text(styleStr); + } + + setFlyoutPositions(); + }; + + // TODO: Combine this with addDropDown or find other way to optimize + var addAltDropDown = function (elem, list, callback, opts) { + var button = $(elem); + list = $(list); + var onButton = false; + var dropUp = opts.dropUp; + if (dropUp) { + $(elem).addClass('dropup'); + } + list.find('li').bind('mouseup', function () { + if (opts.seticon) { + setIcon('#cur_' + button[0].id, $(this).children()); + $(this).addClass('current').siblings().removeClass('current'); + } + callback.apply(this, arguments); + }); + + $(window).mouseup(function (evt) { + if (!onButton) { + button.removeClass('down'); + list.hide(); + list.css({top: 0, left: 0}); + } + onButton = false; + }); + + // var height = list.height(); // Currently unused + button.bind('mousedown', function () { + var off = button.offset(); + if (dropUp) { + off.top -= list.height(); + off.left += 8; + } else { + off.top += button.height(); + } + list.offset(off); + + if (!button.hasClass('down')) { + list.show(); + onButton = true; + } else { + // CSS position must be reset for Webkit + list.hide(); + list.css({top: 0, left: 0}); + } + button.toggleClass('down'); + }).hover(function () { + onButton = true; + }).mouseout(function () { + onButton = false; + }); + + if (opts.multiclick) { + list.mousedown(function () { + onButton = true; + }); + } + }; + + var extsPreLang = []; + var extAdded = function (win, ext) { + if (!ext) { + return; + } + var cbCalled = false; + var resizeDone = false; + var cbReady = true; // Set to false to delay callback (e.g. wait for $.svgIcons) + + if (ext.langReady) { + if (editor.langChanged) { // We check for this since the "lang" pref could have been set by storage + var lang = $.pref('lang'); + ext.langReady({lang: lang, uiStrings: uiStrings}); + } else { + extsPreLang.push(ext); + } + } + + function prepResize () { + if (resizeTimer) { + clearTimeout(resizeTimer); + resizeTimer = null; + } + if (!resizeDone) { + resizeTimer = setTimeout(function () { + resizeDone = true; + setIconSize($.pref('iconsize')); + }, 50); + } + } + + var runCallback = function () { + if (ext.callback && !cbCalled && cbReady) { + cbCalled = true; + ext.callback(); + } + }; + + var btnSelects = []; + + if (ext.context_tools) { + $.each(ext.context_tools, function (i, tool) { + // Add select tool + var html; + var contId = tool.container_id ? (' id="' + tool.container_id + '"') : ''; + var panel = $('#' + tool.panel); + + // create the panel if it doesn't exist + if (!panel.length) { + panel = $('
      ', {id: tool.panel}).appendTo('#tools_top'); + } + + // TODO: Allow support for other types, or adding to existing tool + switch (tool.type) { + case 'tool_button': + html = '
      ' + tool.id + '
      '; + var div = $(html).appendTo(panel); + if (tool.events) { + $.each(tool.events, function (evt, func) { + $(div).bind(evt, func); + }); + } + break; + case 'select': + html = '' + + ''; + // Creates the tool, hides & adds it, returns the select element + var sel = $(html).appendTo(panel).find('select'); + + $.each(tool.events, function (evt, func) { + $(sel).bind(evt, func); + }); + break; + case 'button-select': + html = ''; + + var list = $('
        ').appendTo('#option_lists'); + + if (tool.colnum) { + list.addClass('optcols' + tool.colnum); + } + + // Creates the tool, hides & adds it, returns the select element + /* var dropdown = */ $(html).appendTo(panel).children(); + + btnSelects.push({ + elem: ('#' + tool.id), + list: ('#' + tool.id + '_opts'), + title: tool.title, + callback: tool.events.change, + cur: ('#cur_' + tool.id) + }); + + break; + case 'input': + html = '' + + '' + + tool.label + ':' + + ''; + + // Creates the tool, hides & adds it, returns the select element + + // Add to given tool.panel + var inp = $(html).appendTo(panel).find('input'); + + if (tool.spindata) { + inp.SpinButton(tool.spindata); + } + + if (tool.events) { + $.each(tool.events, function (evt, func) { + inp.bind(evt, func); + }); + } + break; + + default: + break; + } + }); + } + + if (ext.buttons) { + var fallbackObj = {}, + placementObj = {}, + svgicons = ext.svgicons, + holders = {}; + + // Add buttons given by extension + $.each(ext.buttons, function (i, btn) { + var icon, svgicon, tlsId; + var id = btn.id; + var num = i; + + // Give button a unique ID + while ($('#' + id).length) { + id = btn.id + '_' + (++num); + } + + if (!svgicons) { + icon = $(''); + } else { + fallbackObj[id] = btn.icon; + svgicon = btn.svgicon || btn.id; + if (btn.type === 'app_menu') { + placementObj['#' + id + ' > div'] = svgicon; + } else { + placementObj['#' + id] = svgicon; + } + } + + var cls, parent; + + // Set button up according to its type + switch (btn.type) { + case 'mode_flyout': + case 'mode': + cls = 'tool_button'; + parent = '#tools_left'; + break; + case 'context': + cls = 'tool_button'; + parent = '#' + btn.panel; + // create the panel if it doesn't exist + if (!$(parent).length) { + $('
        ', {id: btn.panel}).appendTo('#tools_top'); + } + break; + case 'app_menu': + cls = ''; + parent = '#main_menu ul'; + break; + } + var flyoutHolder, curH, showBtn, refData, refBtn; + var button = $((btn.list || btn.type === 'app_menu') ? '
      • ' : '
        ') + .attr('id', id) + .attr('title', btn.title) + .addClass(cls); + if (!btn.includeWith && !btn.list) { + if ('position' in btn) { + if ($(parent).children().eq(btn.position).length) { + $(parent).children().eq(btn.position).before(button); + } else { + $(parent).children().last().before(button); + } + } else { + button.appendTo(parent); + } + + if (btn.type === 'mode_flyout') { + // Add to flyout menu / make flyout menu + // var opts = btn.includeWith; + // // opts.button, default, position + refBtn = $(button); + + flyoutHolder = refBtn.parent(); + // Create a flyout menu if there isn't one already + if (!refBtn.parent().hasClass('tools_flyout')) { + // Create flyout placeholder + tlsId = refBtn[0].id.replace('tool_', 'tools_'); + showBtn = refBtn.clone() + .attr('id', tlsId + '_show') + .append($('
        ', {'class': 'flyout_arrow_horiz'})); + + refBtn.before(showBtn); + + // Create a flyout div + flyoutHolder = makeFlyoutHolder(tlsId, refBtn); + flyoutHolder.data('isLibrary', true); + showBtn.data('isLibrary', true); + } + // refData = Actions.getButtonData(opts.button); + + placementObj['#' + tlsId + '_show'] = btn.id; + // TODO: Find way to set the current icon using the iconloader if this is not default + + // Include data for extension button as well as ref button + curH = holders['#' + flyoutHolder[0].id] = [{ + sel: '#' + id, + fn: btn.events.click, + icon: btn.id, + // key: btn.key, + isDefault: true + }, refData]; + // + // // {sel:'#tool_rect', fn: clickRect, evt: 'mouseup', key: 4, parent: '#tools_rect', icon: 'rect'} + // + // var pos = ('position' in opts)?opts.position:'last'; + // var len = flyoutHolder.children().length; + // + // // Add at given position or end + // if (!isNaN(pos) && pos >= 0 && pos < len) { + // flyoutHolder.children().eq(pos).before(button); + // } else { + // flyoutHolder.append(button); + // curH.reverse(); + // } + } else if (btn.type === 'app_menu') { + button.append('
        ').append(btn.title); + } + } else if (btn.list) { + // Add button to list + button.addClass('push_button'); + $('#' + btn.list + '_opts').append(button); + if (btn.isDefault) { + $('#cur_' + btn.list).append(button.children().clone()); + svgicon = btn.svgicon || btn.id; + placementObj['#cur_' + btn.list] = svgicon; + } + } else if (btn.includeWith) { + // Add to flyout menu / make flyout menu + var opts = btn.includeWith; + // opts.button, default, position + refBtn = $(opts.button); + + flyoutHolder = refBtn.parent(); + // Create a flyout menu if there isn't one already + if (!refBtn.parent().hasClass('tools_flyout')) { + // Create flyout placeholder + tlsId = refBtn[0].id.replace('tool_', 'tools_'); + showBtn = refBtn.clone() + .attr('id', tlsId + '_show') + .append($('
        ', {'class': 'flyout_arrow_horiz'})); + + refBtn.before(showBtn); + + // Create a flyout div + flyoutHolder = makeFlyoutHolder(tlsId, refBtn); + } + + refData = Actions.getButtonData(opts.button); + + if (opts.isDefault) { + placementObj['#' + tlsId + '_show'] = btn.id; + } + // TODO: Find way to set the current icon using the iconloader if this is not default + + // Include data for extension button as well as ref button + curH = holders['#' + flyoutHolder[0].id] = [{ + sel: '#' + id, + fn: btn.events.click, + icon: btn.id, + key: btn.key, + isDefault: btn.includeWith ? btn.includeWith.isDefault : 0 + }, refData]; + + // {sel:'#tool_rect', fn: clickRect, evt: 'mouseup', key: 4, parent: '#tools_rect', icon: 'rect'} + + var pos = ('position' in opts) ? opts.position : 'last'; + var len = flyoutHolder.children().length; + + // Add at given position or end + if (!isNaN(pos) && pos >= 0 && pos < len) { + flyoutHolder.children().eq(pos).before(button); + } else { + flyoutHolder.append(button); + curH.reverse(); + } + } + + if (!svgicons) { + button.append(icon); + } + + if (!btn.list) { + // Add given events to button + $.each(btn.events, function (name, func) { + if (name === 'click' && btn.type === 'mode') { + if (btn.includeWith) { + button.bind(name, func); + } else { + button.bind(name, function () { + if (toolButtonClick(button)) { + func(); + } + }); + } + if (btn.key) { + $(document).bind('keydown', btn.key, func); + if (btn.title) { + button.attr('title', btn.title + ' [' + btn.key + ']'); + } + } + } else { + button.bind(name, func); + } + }); + } + + setupFlyouts(holders); + }); + + $.each(btnSelects, function () { + addAltDropDown(this.elem, this.list, this.callback, {seticon: true}); + }); + + if (svgicons) { + cbReady = false; // Delay callback + } + + $.svgIcons(svgicons, { + w: 24, h: 24, + id_match: false, + no_img: (!svgedit.browser.isWebkit()), + fallback: fallbackObj, + placement: placementObj, + callback: function (icons) { + // Non-ideal hack to make the icon match the current size + // if (curPrefs.iconsize && curPrefs.iconsize !== 'm') { + if ($.pref('iconsize') !== 'm') { + prepResize(); + } + cbReady = true; // Ready for callback + runCallback(); + } + }); + } + + runCallback(); + }; + + var getPaint = function (color, opac, type) { + // update the editor's fill paint + var opts = { alpha: opac }; + if (color.indexOf('url(#') === 0) { + var refElem = svgCanvas.getRefElem(color); + if (refElem) { + refElem = refElem.cloneNode(true); + } else { + refElem = $('#' + type + '_color defs *')[0]; + } + opts[refElem.tagName] = refElem; + } else if (color.indexOf('#') === 0) { + opts.solidColor = color.substr(1); + } else { + opts.solidColor = 'none'; + } + return new $.jGraduate.Paint(opts); + }; + + // $('#text').focus(function () { textBeingEntered = true; }); + // $('#text').blur(function () { textBeingEntered = false; }); + + // bind the selected event to our function that handles updates to the UI + svgCanvas.bind('selected', selectedChanged); + svgCanvas.bind('transition', elementTransition); + svgCanvas.bind('changed', elementChanged); + svgCanvas.bind('saved', saveHandler); + svgCanvas.bind('exported', exportHandler); + svgCanvas.bind('exportedPDF', function (win, data) { + var exportWindowName = data.exportWindowName; + if (exportWindowName) { + exportWindow = window.open('', exportWindowName); // A hack to get the window via JSON-able name without opening a new one + } + exportWindow.location.href = data.dataurlstring; + }); + svgCanvas.bind('zoomed', zoomChanged); + svgCanvas.bind('contextset', contextChanged); + svgCanvas.bind('extension_added', extAdded); + svgCanvas.textActions.setInputElem($('#text')[0]); + + var str = '
        '; + $.each(palette, function (i, item) { + str += '
        '; + }); + $('#palette').append(str); + + // Set up editor background functionality + // TODO add checkerboard as "pattern" + var colorBlocks = ['#FFF', '#888', '#000']; // ,'url(data:image/gif;base64,R0lGODlhEAAQAIAAAP%2F%2F%2F9bW1iH5BAAAAAAALAAAAAAQABAAAAIfjG%2Bgq4jM3IFLJgpswNly%2FXkcBpIiVaInlLJr9FZWAQA7)']; + str = ''; + $.each(colorBlocks, function () { + str += '
        '; + }); + $('#bg_blocks').append(str); + var blocks = $('#bg_blocks div'); + var curBg = 'cur_background'; + blocks.each(function () { + var blk = $(this); + blk.click(function () { + blocks.removeClass(curBg); + $(this).addClass(curBg); + }); + }); + + setBackground($.pref('bkgd_color'), $.pref('bkgd_url')); + + $('#image_save_opts input').val([$.pref('img_save')]); + + var changeRectRadius = function (ctl) { + svgCanvas.setRectRadius(ctl.value); + }; + + var changeFontSize = function (ctl) { + svgCanvas.setFontSize(ctl.value); + }; + + var changeStrokeWidth = function (ctl) { + var val = ctl.value; + if (val === 0 && selectedElement && ['line', 'polyline'].indexOf(selectedElement.nodeName) >= 0) { + val = ctl.value = 1; + } + svgCanvas.setStrokeWidth(val); + }; + + var changeRotationAngle = function (ctl) { + svgCanvas.setRotationAngle(ctl.value); + $('#tool_reorient').toggleClass('disabled', parseInt(ctl.value, 10) === 0); + }; + + var changeOpacity = function (ctl, val) { + if (val == null) { val = ctl.value; } + $('#group_opacity').val(val); + if (!ctl || !ctl.handle) { + $('#opac_slider').slider('option', 'value', val); + } + svgCanvas.setOpacity(val / 100); + }; + + var changeBlur = function (ctl, val, noUndo) { + if (val == null) { val = ctl.value; } + $('#blur').val(val); + var complete = false; + if (!ctl || !ctl.handle) { + $('#blur_slider').slider('option', 'value', val); + complete = true; + } + if (noUndo) { + svgCanvas.setBlurNoUndo(val); + } else { + svgCanvas.setBlur(val, complete); + } + }; + + $('#stroke_style').change(function () { + svgCanvas.setStrokeAttr('stroke-dasharray', $(this).val()); + operaRepaint(); + }); + + $('#stroke_linejoin').change(function () { + svgCanvas.setStrokeAttr('stroke-linejoin', $(this).val()); + operaRepaint(); + }); + + // Lose focus for select elements when changed (Allows keyboard shortcuts to work better) + $('select').change(function () { $(this).blur(); }); + + // fired when user wants to move elements to another layer + var promptMoveLayerOnce = false; + $('#selLayerNames').change(function () { + var destLayer = this.options[this.selectedIndex].value; + var confirmStr = uiStrings.notification.QmoveElemsToLayer.replace('%s', destLayer); + var moveToLayer = function (ok) { + if (!ok) { return; } + promptMoveLayerOnce = true; + svgCanvas.moveSelectedToLayer(destLayer); + svgCanvas.clearSelection(); + populateLayers(); + }; + if (destLayer) { + if (promptMoveLayerOnce) { + moveToLayer(true); + } else { + $.confirm(confirmStr, moveToLayer); + } + } + }); + + $('#font_family').change(function () { + svgCanvas.setFontFamily(this.value); + }); + + $('#seg_type').change(function () { + svgCanvas.setSegType($(this).val()); + }); + + $('#text').bind('keyup input', function () { + svgCanvas.setTextContent(this.value); + }); + + $('#image_url').change(function () { + setImageURL(this.value); + }); + + $('#link_url').change(function () { + if (this.value.length) { + svgCanvas.setLinkURL(this.value); + } else { + svgCanvas.removeHyperlink(); + } + }); + + $('#g_title').change(function () { + svgCanvas.setGroupTitle(this.value); + }); + + $('.attr_changer').change(function () { + var attr = this.getAttribute('data-attr'); + var val = this.value; + var valid = svgedit.units.isValidUnit(attr, val, selectedElement); + + if (!valid) { + $.alert(uiStrings.notification.invalidAttrValGiven); + this.value = selectedElement.getAttribute(attr); + return false; + } + + if (attr !== 'id' && attr !== 'class') { + if (isNaN(val)) { + val = svgCanvas.convertToNum(attr, val); + } else if (curConfig.baseUnit !== 'px') { + // Convert unitless value to one with given unit + + var unitData = svgedit.units.getTypeMap(); + + if (selectedElement[attr] || svgCanvas.getMode() === 'pathedit' || attr === 'x' || attr === 'y') { + val *= unitData[curConfig.baseUnit]; + } + } + } + + // if the user is changing the id, then de-select the element first + // change the ID, then re-select it with the new ID + if (attr === 'id') { + var elem = selectedElement; + svgCanvas.clearSelection(); + elem.id = val; + svgCanvas.addToSelection([elem], true); + } else { + svgCanvas.changeSelectedAttribute(attr, val); + } + this.blur(); + }); + + // Prevent selection of elements when shift-clicking + $('#palette').mouseover(function () { + var inp = $(''); + $(this).append(inp); + inp.focus().remove(); + }); + + $('.palette_item').mousedown(function (evt) { + // shift key or right click for stroke + var picker = evt.shiftKey || evt.button === 2 ? 'stroke' : 'fill'; + var color = $(this).data('rgb'); + var paint; + + // Webkit-based browsers returned 'initial' here for no stroke + if (color === 'none' || color === 'transparent' || color === 'initial') { + color = 'none'; + paint = new $.jGraduate.Paint(); + } else { + paint = new $.jGraduate.Paint({alpha: 100, solidColor: color.substr(1)}); + } + + paintBox[picker].setPaint(paint); + svgCanvas.setColor(picker, color); + + if (color !== 'none' && svgCanvas.getPaintOpacity(picker) !== 1) { + svgCanvas.setPaintOpacity(picker, 1.0); + } + updateToolButtonState(); + }).bind('contextmenu', function (e) { e.preventDefault(); }); + + $('#toggle_stroke_tools').on('click', function () { + $('#tools_bottom').toggleClass('expanded'); + }); + + (function () { + var lastX = null, lastY = null, wArea = workarea[0], + panning = false, keypan = false; + + $('#svgcanvas').bind('mousemove mouseup', function (evt) { + if (panning === false) { return; } + + wArea.scrollLeft -= (evt.clientX - lastX); + wArea.scrollTop -= (evt.clientY - lastY); + + lastX = evt.clientX; + lastY = evt.clientY; + + if (evt.type === 'mouseup') { panning = false; } + return false; + }).mousedown(function (evt) { + if (evt.button === 1 || keypan === true) { + panning = true; + lastX = evt.clientX; + lastY = evt.clientY; + return false; + } + }); + + $(window).mouseup(function () { + panning = false; + }); + + $(document).bind('keydown', 'space', function (evt) { + svgCanvas.spaceKey = keypan = true; + evt.preventDefault(); + }).bind('keyup', 'space', function (evt) { + evt.preventDefault(); + svgCanvas.spaceKey = keypan = false; + }).bind('keydown', 'shift', function (evt) { + if (svgCanvas.getMode() === 'zoom') { + workarea.css('cursor', zoomOutIcon); + } + }).bind('keyup', 'shift', function (evt) { + if (svgCanvas.getMode() === 'zoom') { + workarea.css('cursor', zoomInIcon); + } + }); + + editor.setPanning = function (active) { + svgCanvas.spaceKey = keypan = active; + }; + }()); + + (function () { + var button = $('#main_icon'); + var overlay = $('#main_icon span'); + var list = $('#main_menu'); + var onButton = false; + var height = 0; + var jsHover = true; + var setClick = false; + + /* + // Currently unused + var hideMenu = function () { + list.fadeOut(200); + }; + */ + + $(window).mouseup(function (evt) { + if (!onButton) { + button.removeClass('buttondown'); + // do not hide if it was the file input as that input needs to be visible + // for its change event to fire + if (evt.target.tagName !== 'INPUT') { + list.fadeOut(200); + } else if (!setClick) { + setClick = true; + $(evt.target).click(function () { + list.css('margin-left', '-9999px').show(); + }); + } + } + onButton = false; + }).mousedown(function (evt) { + // $('.contextMenu').hide(); + var islib = $(evt.target).closest('div.tools_flyout, .contextMenu').length; + if (!islib) { + $('.tools_flyout:visible,.contextMenu').fadeOut(250); + } + }); + + overlay.bind('mousedown', function () { + if (!button.hasClass('buttondown')) { + // Margin must be reset in case it was changed before; + list.css('margin-left', 0).show(); + if (!height) { + height = list.height(); + } + // Using custom animation as slideDown has annoying 'bounce effect' + list.css('height', 0).animate({ + 'height': height + }, 200); + onButton = true; + } else { + list.fadeOut(200); + } + button.toggleClass('buttondown buttonup'); + }).hover(function () { + onButton = true; + }).mouseout(function () { + onButton = false; + }); + + var listItems = $('#main_menu li'); + + // Check if JS method of hovering needs to be used (Webkit bug) + listItems.mouseover(function () { + jsHover = ($(this).css('background-color') === 'rgba(0, 0, 0, 0)'); + + listItems.unbind('mouseover'); + if (jsHover) { + listItems.mouseover(function () { + this.style.backgroundColor = '#FFC'; + }).mouseout(function () { + this.style.backgroundColor = 'transparent'; + return true; + }); + } + }); + }()); + // Made public for UI customization. + // TODO: Group UI functions into a public editor.ui interface. + editor.addDropDown = function (elem, callback, dropUp) { + if ($(elem).length === 0) { return; } // Quit if called on non-existant element + var button = $(elem).find('button'); + var list = $(elem).find('ul').attr('id', $(elem)[0].id + '-list'); + var onButton = false; + if (dropUp) { + $(elem).addClass('dropup'); + } else { + // Move list to place where it can overflow container + $('#option_lists').append(list); + } + list.find('li').bind('mouseup', callback); + + $(window).mouseup(function (evt) { + if (!onButton) { + button.removeClass('down'); + list.hide(); + } + onButton = false; + }); + + button.bind('mousedown', function () { + if (!button.hasClass('down')) { + if (!dropUp) { + var pos = $(elem).position(); + list.css({ + top: pos.top + 24, + left: pos.left - 10 + }); + } + list.show(); + onButton = true; + } else { + list.hide(); + } + button.toggleClass('down'); + }).hover(function () { + onButton = true; + }).mouseout(function () { + onButton = false; + }); + }; + + editor.addDropDown('#font_family_dropdown', function () { + $('#font_family').val($(this).text()).change(); + }); + + editor.addDropDown('#opacity_dropdown', function () { + if ($(this).find('div').length) { return; } + var perc = parseInt($(this).text().split('%')[0], 10); + changeOpacity(false, perc); + }, true); + + // For slider usage, see: http://jqueryui.com/demos/slider/ + $('#opac_slider').slider({ + start: function () { + $('#opacity_dropdown li:not(.special)').hide(); + }, + stop: function () { + $('#opacity_dropdown li').show(); + $(window).mouseup(); + }, + slide: function (evt, ui) { + changeOpacity(ui); + } + }); + + editor.addDropDown('#blur_dropdown', $.noop); + + var slideStart = false; + + $('#blur_slider').slider({ + max: 10, + step: 0.1, + stop: function (evt, ui) { + slideStart = false; + changeBlur(ui); + $('#blur_dropdown li').show(); + $(window).mouseup(); + }, + start: function () { + slideStart = true; + }, + slide: function (evt, ui) { + changeBlur(ui, null, slideStart); + } + }); + + editor.addDropDown('#zoom_dropdown', function () { + var item = $(this); + var val = item.data('val'); + if (val) { + zoomChanged(window, val); + } else { + changeZoom({value: parseFloat(item.text())}); + } + }, true); + + addAltDropDown('#stroke_linecap', '#linecap_opts', function () { + setStrokeOpt(this, true); + }, {dropUp: true}); + + addAltDropDown('#stroke_linejoin', '#linejoin_opts', function () { + setStrokeOpt(this, true); + }, {dropUp: true}); + + addAltDropDown('#tool_position', '#position_opts', function () { + var letter = this.id.replace('tool_pos', '').charAt(0); + svgCanvas.alignSelectedElements(letter, 'page'); + }, {multiclick: true}); + + /* + + When a flyout icon is selected + (if flyout) { + - Change the icon + - Make pressing the button run its stuff + } + - Run its stuff + + When its shortcut key is pressed + - If not current in list, do as above + , else: + - Just run its stuff + + */ + + // Unfocus text input when workarea is mousedowned. + (function () { + var inp; + var unfocus = function () { + $(inp).blur(); + }; + + $('#svg_editor').find('button, select, input:not(#text)').focus(function () { + inp = this; + uiContext = 'toolbars'; + workarea.mousedown(unfocus); + }).blur(function () { + uiContext = 'canvas'; + workarea.unbind('mousedown', unfocus); + // Go back to selecting text if in textedit mode + if (svgCanvas.getMode() === 'textedit') { + $('#text').focus(); + } + }); + }()); + + var clickFHPath = function () { + if (toolButtonClick('#tool_fhpath')) { + svgCanvas.setMode('fhpath'); + } + }; + + var clickLine = function () { + if (toolButtonClick('#tool_line')) { + svgCanvas.setMode('line'); + } + }; + + var clickSquare = function () { + if (toolButtonClick('#tool_square')) { + svgCanvas.setMode('square'); + } + }; + + var clickRect = function () { + if (toolButtonClick('#tool_rect')) { + svgCanvas.setMode('rect'); + } + }; + + var clickFHRect = function () { + if (toolButtonClick('#tool_fhrect')) { + svgCanvas.setMode('fhrect'); + } + }; + + var clickCircle = function () { + if (toolButtonClick('#tool_circle')) { + svgCanvas.setMode('circle'); + } + }; + + var clickEllipse = function () { + if (toolButtonClick('#tool_ellipse')) { + svgCanvas.setMode('ellipse'); + } + }; + + var clickFHEllipse = function () { + if (toolButtonClick('#tool_fhellipse')) { + svgCanvas.setMode('fhellipse'); + } + }; + + var clickImage = function () { + if (toolButtonClick('#tool_image')) { + svgCanvas.setMode('image'); + } + }; + + var clickZoom = function () { + if (toolButtonClick('#tool_zoom')) { + svgCanvas.setMode('zoom'); + workarea.css('cursor', zoomInIcon); + } + }; + + var zoomImage = function (multiplier) { + var res = svgCanvas.getResolution(); + multiplier = multiplier ? res.zoom * multiplier : 1; + // setResolution(res.w * multiplier, res.h * multiplier, true); + $('#zoom').val(multiplier * 100); + svgCanvas.setZoom(multiplier); + zoomDone(); + updateCanvas(true); + }; + + var dblclickZoom = function () { + if (toolButtonClick('#tool_zoom')) { + zoomImage(); + setSelectMode(); + } + }; + + var clickText = function () { + if (toolButtonClick('#tool_text')) { + svgCanvas.setMode('text'); + } + }; + + var clickPath = function () { + if (toolButtonClick('#tool_path')) { + svgCanvas.setMode('path'); + } + }; + + // Delete is a contextual tool that only appears in the ribbon if + // an element has been selected + var deleteSelected = function () { + if (selectedElement != null || multiselected) { + svgCanvas.deleteSelectedElements(); + } + }; + + var cutSelected = function () { + if (selectedElement != null || multiselected) { + svgCanvas.cutSelectedElements(); + } + }; + + var copySelected = function () { + if (selectedElement != null || multiselected) { + svgCanvas.copySelectedElements(); + } + }; + + var pasteInCenter = function () { + var zoom = svgCanvas.getZoom(); + var x = (workarea[0].scrollLeft + workarea.width() / 2) / zoom - svgCanvas.contentW; + var y = (workarea[0].scrollTop + workarea.height() / 2) / zoom - svgCanvas.contentH; + svgCanvas.pasteElements('point', x, y); + }; + + var moveToTopSelected = function () { + if (selectedElement != null) { + svgCanvas.moveToTopSelectedElement(); + } + }; + + var moveToBottomSelected = function () { + if (selectedElement != null) { + svgCanvas.moveToBottomSelectedElement(); + } + }; + + var moveUpDownSelected = function (dir) { + if (selectedElement != null) { + svgCanvas.moveUpDownSelected(dir); + } + }; + + var convertToPath = function () { + if (selectedElement != null) { + svgCanvas.convertToPath(); + } + }; + + var reorientPath = function () { + if (selectedElement != null) { + path.reorient(); + } + }; + + var makeHyperlink = function () { + if (selectedElement != null || multiselected) { + $.prompt(uiStrings.notification.enterNewLinkURL, 'http://', function (url) { + if (url) { svgCanvas.makeHyperlink(url); } + }); + } + }; + + var moveSelected = function (dx, dy) { + if (selectedElement != null || multiselected) { + if (curConfig.gridSnapping) { + // Use grid snap value regardless of zoom level + var multi = svgCanvas.getZoom() * curConfig.snappingStep; + dx *= multi; + dy *= multi; + } + svgCanvas.moveSelectedElements(dx, dy); + } + }; + + var linkControlPoints = function () { + $('#tool_node_link').toggleClass('push_button_pressed tool_button'); + var linked = $('#tool_node_link').hasClass('push_button_pressed'); + path.linkControlPoints(linked); + }; + + var clonePathNode = function () { + if (path.getNodePoint()) { + path.clonePathNode(); + } + }; + + var deletePathNode = function () { + if (path.getNodePoint()) { + path.deletePathNode(); + } + }; + + var addSubPath = function () { + var button = $('#tool_add_subpath'); + var sp = !button.hasClass('push_button_pressed'); + button.toggleClass('push_button_pressed tool_button'); + path.addSubPath(sp); + }; + + var opencloseSubPath = function () { + path.opencloseSubPath(); + }; + + var selectNext = function () { + svgCanvas.cycleElement(1); + }; + + var selectPrev = function () { + svgCanvas.cycleElement(0); + }; + + var rotateSelected = function (cw, step) { + if (selectedElement == null || multiselected) { return; } + if (!cw) { step *= -1; } + var angle = parseFloat($('#angle').val()) + step; + svgCanvas.setRotationAngle(angle); + updateContextPanel(); + }; + + var clickClear = function () { + var dims = curConfig.dimensions; + $.confirm(uiStrings.notification.QwantToClear, function (ok) { + if (!ok) { return; } + setSelectMode(); + svgCanvas.clear(); + svgCanvas.setResolution(dims[0], dims[1]); + updateCanvas(true); + zoomImage(); + populateLayers(); + updateContextPanel(); + prepPaints(); + svgCanvas.runExtensions('onNewDocument'); + }); + }; + + var clickBold = function () { + svgCanvas.setBold(!svgCanvas.getBold()); + updateContextPanel(); + return false; + }; + + var clickItalic = function () { + svgCanvas.setItalic(!svgCanvas.getItalic()); + updateContextPanel(); + return false; + }; + + var clickSave = function () { + // In the future, more options can be provided here + var saveOpts = { + 'images': $.pref('img_save'), + 'round_digits': 6 + }; + svgCanvas.save(saveOpts); + }; + + var loadingURL; + var clickExport = function () { + $.select('Select an image type for export: ', [ + // See http://kangax.github.io/jstests/toDataUrl_mime_type_test/ for a useful list of MIME types and browser support + // 'ICO', // Todo: Find a way to preserve transparency in SVG-Edit if not working presently and do full packaging for x-icon; then switch back to position after 'PNG' + 'PNG', + 'JPEG', 'BMP', 'WEBP', 'PDF' + ], function (imgType) { // todo: replace hard-coded msg with uiStrings.notification. + if (!imgType) { + return; + } + // Open placeholder window (prevents popup) + var exportWindowName; + function openExportWindow () { + var str = uiStrings.notification.loadingImage; + if (curConfig.exportWindowType === 'new') { + editor.exportWindowCt++; + } + exportWindowName = curConfig.canvasName + editor.exportWindowCt; + var popHTML, popURL; + if (loadingURL) { + popURL = loadingURL; + } else { + popHTML = '' + str + '

        ' + str + '

        '; + if (typeof URL && URL.createObjectURL) { + var blob = new Blob([popHTML], {type: 'text/html'}); + popURL = URL.createObjectURL(blob); + } else { + popURL = 'data:text/html;base64;charset=utf-8,' + svgedit.utilities.encode64(popHTML); + } + loadingURL = popURL; + } + exportWindow = window.open(popURL, exportWindowName); + } + if (imgType === 'PDF') { + if (!customExportPDF) { + openExportWindow(); + } + svgCanvas.exportPDF(exportWindowName); + } else { + if (!customExportImage) { + openExportWindow(); + } + var quality = parseInt($('#image-slider').val(), 10) / 100; + svgCanvas.rasterExport(imgType, quality, exportWindowName); + } + }, function () { + var sel = $(this); + if (sel.val() === 'JPEG' || sel.val() === 'WEBP') { + if (!$('#image-slider').length) { + $('
        ').appendTo(sel.parent()); // Todo: i18n-ize label + } + } else { + $('#image-slider').parent().remove(); + } + }); + }; + + // by default, svgCanvas.open() is a no-op. + // it is up to an extension mechanism (opera widget, etc) + // to call setCustomHandlers() which will make it do something + var clickOpen = function () { + svgCanvas.open(); + }; + + var clickImport = function () { + }; + + var clickUndo = function () { + if (undoMgr.getUndoStackSize() > 0) { + undoMgr.undo(); + populateLayers(); + } + }; + + var clickRedo = function () { + if (undoMgr.getRedoStackSize() > 0) { + undoMgr.redo(); + populateLayers(); + } + }; + + var clickGroup = function () { + // group + if (multiselected) { + svgCanvas.groupSelectedElements(); + // ungroup + } else if (selectedElement) { + svgCanvas.ungroupSelectedElement(); + } + }; + + var clickClone = function () { + svgCanvas.cloneSelectedElements(20, 20); + }; + + var clickAlign = function () { + var letter = this.id.replace('tool_align', '').charAt(0); + svgCanvas.alignSelectedElements(letter, $('#align_relative_to').val()); + }; + + var clickWireframe = function () { + $('#tool_wireframe').toggleClass('push_button_pressed tool_button'); + workarea.toggleClass('wireframe'); + + if (supportsNonSS) { return; } + var wfRules = $('#wireframe_rules'); + if (!wfRules.length) { + wfRules = $('').appendTo('head'); + } else { + wfRules.empty(); + } + + updateWireFrame(); + }; + + $('#svg_docprops_container, #svg_prefs_container').draggable({cancel: 'button,fieldset', containment: 'window'}); + + var showDocProperties = function () { + if (docprops) { return; } + docprops = true; + + // This selects the correct radio button by using the array notation + $('#image_save_opts input').val([$.pref('img_save')]); + + // update resolution option with actual resolution + var res = svgCanvas.getResolution(); + if (curConfig.baseUnit !== 'px') { + res.w = svgedit.units.convertUnit(res.w) + curConfig.baseUnit; + res.h = svgedit.units.convertUnit(res.h) + curConfig.baseUnit; + } + + $('#canvas_width').val(res.w); + $('#canvas_height').val(res.h); + $('#canvas_title').val(svgCanvas.getDocumentTitle()); + + $('#svg_docprops').show(); + }; + + var showPreferences = function () { + if (preferences) { return; } + preferences = true; + $('#main_menu').hide(); + + // Update background color with current one + var blocks = $('#bg_blocks div'); + var curBg = 'cur_background'; + var canvasBg = curPrefs.bkgd_color; + var url = $.pref('bkgd_url'); + blocks.each(function () { + var blk = $(this); + var isBg = blk.css('background-color') === canvasBg; + blk.toggleClass(curBg, isBg); + if (isBg) { $('#canvas_bg_url').removeClass(curBg); } + }); + if (!canvasBg) { blocks.eq(0).addClass(curBg); } + if (url) { + $('#canvas_bg_url').val(url); + } + $('#grid_snapping_on').prop('checked', curConfig.gridSnapping); + $('#grid_snapping_step').attr('value', curConfig.snappingStep); + $('#grid_color').attr('value', curConfig.gridColor); + + $('#svg_prefs').show(); + }; + + var hideSourceEditor = function () { + $('#svg_source_editor').hide(); + editingsource = false; + $('#svg_source_textarea').blur(); + }; + + var saveSourceEditor = function () { + if (!editingsource) { return; } + + var saveChanges = function () { + svgCanvas.clearSelection(); + hideSourceEditor(); + zoomImage(); + populateLayers(); + updateTitle(); + prepPaints(); + }; + + if (!svgCanvas.setSvgString($('#svg_source_textarea').val())) { + $.confirm(uiStrings.notification.QerrorsRevertToSource, function (ok) { + if (!ok) { return false; } + saveChanges(); + }); + } else { + saveChanges(); + } + setSelectMode(); + }; + + var hideDocProperties = function () { + $('#svg_docprops').hide(); + $('#canvas_width,#canvas_height').removeAttr('disabled'); + $('#resolution')[0].selectedIndex = 0; + $('#image_save_opts input').val([$.pref('img_save')]); + docprops = false; + }; + + var hidePreferences = function () { + $('#svg_prefs').hide(); + preferences = false; + }; + + var saveDocProperties = function () { + // set title + var newTitle = $('#canvas_title').val(); + updateTitle(newTitle); + svgCanvas.setDocumentTitle(newTitle); + + // update resolution + var width = $('#canvas_width'), w = width.val(); + var height = $('#canvas_height'), h = height.val(); + + if (w !== 'fit' && !svgedit.units.isValidUnit('width', w)) { + $.alert(uiStrings.notification.invalidAttrValGiven); + width.parent().addClass('error'); + return false; + } + + width.parent().removeClass('error'); + + if (h !== 'fit' && !svgedit.units.isValidUnit('height', h)) { + $.alert(uiStrings.notification.invalidAttrValGiven); + height.parent().addClass('error'); + return false; + } + + height.parent().removeClass('error'); + + if (!svgCanvas.setResolution(w, h)) { + $.alert(uiStrings.notification.noContentToFitTo); + return false; + } + + // Set image save option + $.pref('img_save', $('#image_save_opts :checked').val()); + updateCanvas(); + hideDocProperties(); + }; + + var savePreferences = editor.savePreferences = function () { + // Set background + var color = $('#bg_blocks div.cur_background').css('background-color') || '#FFF'; + setBackground(color, $('#canvas_bg_url').val()); + + // set language + var lang = $('#lang_select').val(); + if (lang !== $.pref('lang')) { + editor.putLocale(lang, goodLangs); + } + + // set icon size + setIconSize($('#iconsize').val()); + + // set grid setting + curConfig.gridSnapping = $('#grid_snapping_on')[0].checked; + curConfig.snappingStep = $('#grid_snapping_step').val(); + curConfig.gridColor = $('#grid_color').val(); + curConfig.showRulers = $('#show_rulers')[0].checked; + + $('#rulers').toggle(curConfig.showRulers); + if (curConfig.showRulers) { updateRulers(); } + curConfig.baseUnit = $('#base_unit').val(); + + svgCanvas.setConfig(curConfig); + + updateCanvas(); + hidePreferences(); + }; + + var resetScrollPos = $.noop; + + var cancelOverlays = function () { + $('#dialog_box').hide(); + if (!editingsource && !docprops && !preferences) { + if (curContext) { + svgCanvas.leaveContext(); + } + return; + } + + if (editingsource) { + if (origSource !== $('#svg_source_textarea').val()) { + $.confirm(uiStrings.notification.QignoreSourceChanges, function (ok) { + if (ok) { hideSourceEditor(); } + }); + } else { + hideSourceEditor(); + } + } else if (docprops) { + hideDocProperties(); + } else if (preferences) { + hidePreferences(); + } + resetScrollPos(); + }; + + var winWh = {width: $(window).width(), height: $(window).height()}; + + // Fix for Issue 781: Drawing area jumps to top-left corner on window resize (IE9) + if (svgedit.browser.isIE()) { + (function () { + resetScrollPos = function () { + if (workarea[0].scrollLeft === 0 && workarea[0].scrollTop === 0) { + workarea[0].scrollLeft = curScrollPos.left; + workarea[0].scrollTop = curScrollPos.top; + } + }; + + curScrollPos = { + left: workarea[0].scrollLeft, + top: workarea[0].scrollTop + }; + + $(window).resize(resetScrollPos); + editor.ready(function () { + // TODO: Find better way to detect when to do this to minimize + // flickering effect + setTimeout(function () { + resetScrollPos(); + }, 500); + }); + + workarea.scroll(function () { + curScrollPos = { + left: workarea[0].scrollLeft, + top: workarea[0].scrollTop + }; + }); + }()); + } + + $(window).resize(function (evt) { + $.each(winWh, function (type, val) { + var curval = $(window)[type](); + workarea[0]['scroll' + (type === 'width' ? 'Left' : 'Top')] -= (curval - val) / 2; + winWh[type] = curval; + }); + setFlyoutPositions(); + }); + + (function () { + workarea.scroll(function () { + // TODO: jQuery's scrollLeft/Top() wouldn't require a null check + if ($('#ruler_x').length !== 0) { + $('#ruler_x')[0].scrollLeft = workarea[0].scrollLeft; + } + if ($('#ruler_y').length !== 0) { + $('#ruler_y')[0].scrollTop = workarea[0].scrollTop; + } + }); + }()); + + $('#url_notice').click(function () { + $.alert(this.title); + }); + + $('#change_image_url').click(promptImgURL); + + // added these event handlers for all the push buttons so they + // behave more like buttons being pressed-in and not images + (function () { + var toolnames = ['clear', 'open', 'save', 'source', 'delete', 'delete_multi', 'paste', 'clone', 'clone_multi', 'move_top', 'move_bottom']; + var allTools = ''; + var curClass = 'tool_button_current'; + + $.each(toolnames, function (i, item) { + allTools += (i ? ',' : '') + '#tool_' + item; + }); + + $(allTools).mousedown(function () { + $(this).addClass(curClass); + }).bind('mousedown mouseout', function () { + $(this).removeClass(curClass); + }); + + $('#tool_undo, #tool_redo').mousedown(function () { + if (!$(this).hasClass('disabled')) { $(this).addClass(curClass); } + }).bind('mousedown mouseout', function () { + $(this).removeClass(curClass); + }); + }()); + + // switch modifier key in tooltips if mac + // NOTE: This code is not used yet until I can figure out how to successfully bind ctrl/meta + // in Opera and Chrome + if (svgedit.browser.isMac() && !window.opera) { + var shortcutButtons = ['tool_clear', 'tool_save', 'tool_source', 'tool_undo', 'tool_redo', 'tool_clone']; + i = shortcutButtons.length; + while (i--) { + var button = document.getElementById(shortcutButtons[i]); + if (button) { + var title = button.title; + var index = title.indexOf('Ctrl+'); + button.title = [title.substr(0, index), 'Cmd+', title.substr(index + 5)].join(''); + } + } + } + + // TODO: go back to the color boxes having white background-color and then setting + // background-image to none.png (otherwise partially transparent gradients look weird) + var colorPicker = function (elem) { + var picker = elem.attr('id') === 'stroke_color' ? 'stroke' : 'fill'; + // var opacity = (picker == 'stroke' ? $('#stroke_opacity') : $('#fill_opacity')); + var paint = paintBox[picker].paint; + var title = (picker === 'stroke' ? 'Pick a Stroke Paint and Opacity' : 'Pick a Fill Paint and Opacity'); + // var was_none = false; // Currently unused + var pos = elem.offset(); + $('#color_picker') + .draggable({cancel: '.jGraduate_tabs, .jGraduate_colPick, .jGraduate_gradPick, .jPicker', containment: 'window'}) + .css(curConfig.colorPickerCSS || {'left': pos.left - 140, 'bottom': 40}) + .jGraduate( + { + paint: paint, + window: { pickerTitle: title }, + images: { clientPath: curConfig.jGraduatePath }, + newstop: 'inverse' + }, + function (p) { + paint = new $.jGraduate.Paint(p); + paintBox[picker].setPaint(paint); + svgCanvas.setPaint(picker, paint); + $('#color_picker').hide(); + }, + function () { + $('#color_picker').hide(); + } + ); + }; + + var PaintBox = function (container, type) { + var paintColor, paintOpacity, + cur = curConfig[type === 'fill' ? 'initFill' : 'initStroke']; + // set up gradients to be used for the buttons + var svgdocbox = new DOMParser().parseFromString( + '' + ' ', 'text/xml'); - var docElem = svgdocbox.documentElement; - - docElem = $(container)[0].appendChild(document.importNode(docElem, true)); - docElem.setAttribute('width', 16.5); - - this.rect = docElem.firstChild; - this.defs = docElem.getElementsByTagName('defs')[0]; - this.grad = this.defs.firstChild; - this.paint = new $.jGraduate.Paint({solidColor: cur.color}); - this.type = type; - - this.setPaint = function (paint, apply) { - this.paint = paint; - - var fillAttr = 'none'; - var ptype = paint.type; - var opac = paint.alpha / 100; - - switch (ptype) { - case 'solidColor': - fillAttr = (paint[ptype] !== 'none') ? '#' + paint[ptype] : paint[ptype]; - break; - case 'linearGradient': - case 'radialGradient': - this.defs.removeChild(this.grad); - this.grad = this.defs.appendChild(paint[ptype]); - var id = this.grad.id = 'gradbox_' + this.type; - fillAttr = 'url(#' + id + ')'; - break; - } - - this.rect.setAttribute('fill', fillAttr); - this.rect.setAttribute('opacity', opac); - - if (apply) { - svgCanvas.setColor(this.type, paintColor, true); - svgCanvas.setPaintOpacity(this.type, paintOpacity, true); - } - }; - - this.update = function (apply) { - if (!selectedElement) { return; } - var i, len; - var type = this.type; - switch (selectedElement.tagName) { - case 'use': - case 'image': - case 'foreignObject': - // These elements don't have fill or stroke, so don't change - // the current value - return; - case 'g': - case 'a': - var gPaint = null; - - var childs = selectedElement.getElementsByTagName('*'); - for (i = 0, len = childs.length; i < len; i++) { - var elem = childs[i]; - var p = elem.getAttribute(type); - if (i === 0) { - gPaint = p; - } else if (gPaint !== p) { - gPaint = null; - break; - } - } - - if (gPaint === null) { - // No common color, don't update anything - paintColor = null; - return; - } - paintColor = gPaint; - paintOpacity = 1; - break; - default: - paintOpacity = parseFloat(selectedElement.getAttribute(type + '-opacity')); - if (isNaN(paintOpacity)) { - paintOpacity = 1.0; - } - - var defColor = type === 'fill' ? 'black' : 'none'; - paintColor = selectedElement.getAttribute(type) || defColor; - } - - if (apply) { - svgCanvas.setColor(type, paintColor, true); - svgCanvas.setPaintOpacity(type, paintOpacity, true); - } - - paintOpacity *= 100; - - var paint = getPaint(paintColor, paintOpacity, type); - // update the rect inside #fill_color/#stroke_color - this.setPaint(paint); - }; - - this.prep = function () { - var ptype = this.paint.type; - - switch (ptype) { - case 'linearGradient': - case 'radialGradient': - var paint = new $.jGraduate.Paint({copy: this.paint}); - svgCanvas.setPaint(type, paint); - break; - } - }; - }; - - paintBox.fill = new PaintBox('#fill_color', 'fill'); - paintBox.stroke = new PaintBox('#stroke_color', 'stroke'); - - $('#stroke_width').val(curConfig.initStroke.width); - $('#group_opacity').val(curConfig.initOpacity * 100); - - // Use this SVG elem to test vectorEffect support - var testEl = paintBox.fill.rect.cloneNode(false); - testEl.setAttribute('style', 'vector-effect:non-scaling-stroke'); - supportsNonSS = (testEl.style.vectorEffect === 'non-scaling-stroke'); - testEl.removeAttribute('style'); - var svgdocbox = paintBox.fill.rect.ownerDocument; - // Use this to test support for blur element. Seems to work to test support in Webkit - var blurTest = svgdocbox.createElementNS(svgedit.NS.SVG, 'feGaussianBlur'); - if (blurTest.stdDeviationX === undefined) { - $('#tool_blur').hide(); - } - $(blurTest).remove(); - - // Test for zoom icon support - (function () { - var pre = '-' + uaPrefix.toLowerCase() + '-zoom-'; - var zoom = pre + 'in'; - workarea.css('cursor', zoom); - if (workarea.css('cursor') === zoom) { - zoomInIcon = zoom; - zoomOutIcon = pre + 'out'; - } - workarea.css('cursor', 'auto'); - }()); - - // Test for embedImage support (use timeout to not interfere with page load) - setTimeout(function () { - svgCanvas.embedImage('images/logo.png', function (datauri) { - if (!datauri) { - // Disable option - $('#image_save_opts [value=embed]').attr('disabled', 'disabled'); - $('#image_save_opts input').val(['ref']); - $.pref('img_save', 'ref'); - $('#image_opt_embed').css('color', '#666').attr('title', uiStrings.notification.featNotSupported); - } - }); - }, 1000); - - $('#fill_color, #tool_fill .icon_label').click(function () { - colorPicker($('#fill_color')); - updateToolButtonState(); - }); - - $('#stroke_color, #tool_stroke .icon_label').click(function () { - colorPicker($('#stroke_color')); - updateToolButtonState(); - }); - - $('#group_opacityLabel').click(function () { - $('#opacity_dropdown button').mousedown(); - $(window).mouseup(); - }); - - $('#zoomLabel').click(function () { - $('#zoom_dropdown button').mousedown(); - $(window).mouseup(); - }); - - $('#tool_move_top').mousedown(function (evt) { - $('#tools_stacking').show(); - evt.preventDefault(); - }); - - $('.layer_button').mousedown(function () { - $(this).addClass('layer_buttonpressed'); - }).mouseout(function () { - $(this).removeClass('layer_buttonpressed'); - }).mouseup(function () { - $(this).removeClass('layer_buttonpressed'); - }); - - $('.push_button').mousedown(function () { - if (!$(this).hasClass('disabled')) { - $(this).addClass('push_button_pressed').removeClass('push_button'); - } - }).mouseout(function () { - $(this).removeClass('push_button_pressed').addClass('push_button'); - }).mouseup(function () { - $(this).removeClass('push_button_pressed').addClass('push_button'); - }); - - // ask for a layer name - $('#layer_new').click(function () { - var uniqName, - i = svgCanvas.getCurrentDrawing().getNumLayers(); - do { - uniqName = uiStrings.layers.layer + ' ' + (++i); - } while (svgCanvas.getCurrentDrawing().hasLayer(uniqName)); - - $.prompt(uiStrings.notification.enterUniqueLayerName, uniqName, function (newName) { - if (!newName) { return; } - if (svgCanvas.getCurrentDrawing().hasLayer(newName)) { - $.alert(uiStrings.notification.dupeLayerName); - return; - } - svgCanvas.createLayer(newName); - updateContextPanel(); - populateLayers(); - }); - }); - - function deleteLayer () { - if (svgCanvas.deleteCurrentLayer()) { - updateContextPanel(); - populateLayers(); - // This matches what SvgCanvas does - // TODO: make this behavior less brittle (svg-editor should get which - // layer is selected from the canvas and then select that one in the UI) - $('#layerlist tr.layer').removeClass('layersel'); - $('#layerlist tr.layer:first').addClass('layersel'); - } - } - - function cloneLayer () { - var name = svgCanvas.getCurrentDrawing().getCurrentLayerName() + ' copy'; - - $.prompt(uiStrings.notification.enterUniqueLayerName, name, function (newName) { - if (!newName) { return; } - if (svgCanvas.getCurrentDrawing().hasLayer(newName)) { - $.alert(uiStrings.notification.dupeLayerName); - return; - } - svgCanvas.cloneLayer(newName); - updateContextPanel(); - populateLayers(); - }); - } - - function mergeLayer () { - if ($('#layerlist tr.layersel').index() === svgCanvas.getCurrentDrawing().getNumLayers() - 1) { - return; - } - svgCanvas.mergeLayer(); - updateContextPanel(); - populateLayers(); - } - - function moveLayer (pos) { - var curIndex = $('#layerlist tr.layersel').index(); - var total = svgCanvas.getCurrentDrawing().getNumLayers(); - if (curIndex > 0 || curIndex < total - 1) { - curIndex += pos; - svgCanvas.setCurrentLayerPosition(total - curIndex - 1); - populateLayers(); - } - } - - $('#layer_delete').click(deleteLayer); - - $('#layer_up').click(function () { - moveLayer(-1); - }); - - $('#layer_down').click(function () { - moveLayer(1); - }); - - $('#layer_rename').click(function () { - // var curIndex = $('#layerlist tr.layersel').prevAll().length; // Currently unused - var oldName = $('#layerlist tr.layersel td.layername').text(); - $.prompt(uiStrings.notification.enterNewLayerName, '', function (newName) { - if (!newName) { return; } - if (oldName === newName || svgCanvas.getCurrentDrawing().hasLayer(newName)) { - $.alert(uiStrings.notification.layerHasThatName); - return; - } - - svgCanvas.renameCurrentLayer(newName); - populateLayers(); - }); - }); - - var SIDEPANEL_MAXWIDTH = 300; - var SIDEPANEL_OPENWIDTH = 150; - var sidedrag = -1, sidedragging = false, allowmove = false; - - var changeSidePanelWidth = function (delta) { - var rulerX = $('#ruler_x'); - $('#sidepanels').width('+=' + delta); - $('#layerpanel').width('+=' + delta); - rulerX.css('right', parseInt(rulerX.css('right'), 10) + delta); - workarea.css('right', parseInt(workarea.css('right'), 10) + delta); - svgCanvas.runExtensions('workareaResized'); - }; - - var resizeSidePanel = function (evt) { - if (!allowmove) { return; } - if (sidedrag === -1) { return; } - sidedragging = true; - var deltaX = sidedrag - evt.pageX; - var sideWidth = $('#sidepanels').width(); - if (sideWidth + deltaX > SIDEPANEL_MAXWIDTH) { - deltaX = SIDEPANEL_MAXWIDTH - sideWidth; - sideWidth = SIDEPANEL_MAXWIDTH; - } else if (sideWidth + deltaX < 2) { - deltaX = 2 - sideWidth; - sideWidth = 2; - } - if (deltaX === 0) { return; } - sidedrag -= deltaX; - changeSidePanelWidth(deltaX); - }; - - // if width is non-zero, then fully close it, otherwise fully open it - // the optional close argument forces the side panel closed - var toggleSidePanel = function (close) { - var w = $('#sidepanels').width(); - var deltaX = (w > 2 || close ? 2 : SIDEPANEL_OPENWIDTH) - w; - changeSidePanelWidth(deltaX); - }; - - $('#sidepanel_handle') - .mousedown(function (evt) { - sidedrag = evt.pageX; - $(window).mousemove(resizeSidePanel); - allowmove = false; - // Silly hack for Chrome, which always runs mousemove right after mousedown - setTimeout(function () { - allowmove = true; - }, 20); - }) - .mouseup(function (evt) { - if (!sidedragging) { toggleSidePanel(); } - sidedrag = -1; - sidedragging = false; - }); - - $(window).mouseup(function () { - sidedrag = -1; - sidedragging = false; - $('#svg_editor').unbind('mousemove', resizeSidePanel); - }); - - populateLayers(); - - // function changeResolution (x,y) { - // var zoom = svgCanvas.getResolution().zoom; - // setResolution(x * zoom, y * zoom); - // } - - var centerCanvas = function () { - // this centers the canvas vertically in the workarea (horizontal handled in CSS) - workarea.css('line-height', workarea.height() + 'px'); - }; - - $(window).bind('load resize', centerCanvas); - - function stepFontSize (elem, step) { - var origVal = Number(elem.value); - var sugVal = origVal + step; - var increasing = sugVal >= origVal; - if (step === 0) { return origVal; } - - if (origVal >= 24) { - if (increasing) { - return Math.round(origVal * 1.1); - } - return Math.round(origVal / 1.1); - } - if (origVal <= 1) { - if (increasing) { - return origVal * 2; - } - return origVal / 2; - } - return sugVal; - } - - function stepZoom (elem, step) { - var origVal = Number(elem.value); - if (origVal === 0) { return 100; } - var sugVal = origVal + step; - if (step === 0) { return origVal; } - - if (origVal >= 100) { - return sugVal; - } - if (sugVal >= origVal) { - return origVal * 2; - } - return origVal / 2; - } - - // function setResolution (w, h, center) { - // updateCanvas(); - // // w -= 0; h -= 0; - // // $('#svgcanvas').css({'width': w, 'height': h}); - // // $('#canvas_width').val(w); - // // $('#canvas_height').val(h); - // // - // // if (center) { - // // var wArea = workarea; - // // var scrollY = h/2 - wArea.height()/2; - // // var scrollX = w/2 - wArea.width()/2; - // // wArea[0].scrollTop = scrollY; - // // wArea[0].scrollLeft = scrollX; - // // } - // } - - $('#resolution').change(function () { - var wh = $('#canvas_width,#canvas_height'); - if (!this.selectedIndex) { - if ($('#canvas_width').val() === 'fit') { - wh.removeAttr('disabled').val(100); - } - } else if (this.value === 'content') { - wh.val('fit').attr('disabled', 'disabled'); - } else { - var dims = this.value.split('x'); - $('#canvas_width').val(dims[0]); - $('#canvas_height').val(dims[1]); - wh.removeAttr('disabled'); - } - }); - - // Prevent browser from erroneously repopulating fields - $('input,select').attr('autocomplete', 'off'); - - // Associate all button actions as well as non-button keyboard shortcuts - Actions = (function () { - // sel:'selector', fn:function, evt:'event', key:[key, preventDefault, NoDisableInInput] - var toolButtons = [ - {sel: '#tool_select', fn: clickSelect, evt: 'click', key: ['V', true]}, - {sel: '#tool_fhpath', fn: clickFHPath, evt: 'click', key: ['Q', true]}, - {sel: '#tool_line', fn: clickLine, evt: 'click', key: ['L', true]}, - {sel: '#tool_rect', fn: clickRect, evt: 'mouseup', key: ['R', true], parent: '#tools_rect', icon: 'rect'}, - {sel: '#tool_square', fn: clickSquare, evt: 'mouseup', parent: '#tools_rect', icon: 'square'}, - {sel: '#tool_fhrect', fn: clickFHRect, evt: 'mouseup', parent: '#tools_rect', icon: 'fh_rect'}, - {sel: '#tool_ellipse', fn: clickEllipse, evt: 'mouseup', key: ['E', true], parent: '#tools_ellipse', icon: 'ellipse'}, - {sel: '#tool_circle', fn: clickCircle, evt: 'mouseup', parent: '#tools_ellipse', icon: 'circle'}, - {sel: '#tool_fhellipse', fn: clickFHEllipse, evt: 'mouseup', parent: '#tools_ellipse', icon: 'fh_ellipse'}, - {sel: '#tool_path', fn: clickPath, evt: 'click', key: ['P', true]}, - {sel: '#tool_text', fn: clickText, evt: 'click', key: ['T', true]}, - {sel: '#tool_image', fn: clickImage, evt: 'mouseup'}, - {sel: '#tool_zoom', fn: clickZoom, evt: 'mouseup', key: ['Z', true]}, - {sel: '#tool_clear', fn: clickClear, evt: 'mouseup', key: ['N', true]}, - {sel: '#tool_save', fn: function () { - if (editingsource) { - saveSourceEditor(); - } else { - clickSave(); - } - }, evt: 'mouseup', key: ['S', true]}, - {sel: '#tool_export', fn: clickExport, evt: 'mouseup'}, - {sel: '#tool_open', fn: clickOpen, evt: 'mouseup', key: ['O', true]}, - {sel: '#tool_import', fn: clickImport, evt: 'mouseup'}, - {sel: '#tool_source', fn: showSourceEditor, evt: 'click', key: ['U', true]}, - {sel: '#tool_wireframe', fn: clickWireframe, evt: 'click', key: ['F', true]}, - {sel: '#tool_source_cancel,.overlay,#tool_docprops_cancel,#tool_prefs_cancel', fn: cancelOverlays, evt: 'click', key: ['esc', false, false], hidekey: true}, - {sel: '#tool_source_save', fn: saveSourceEditor, evt: 'click'}, - {sel: '#tool_docprops_save', fn: saveDocProperties, evt: 'click'}, - {sel: '#tool_docprops', fn: showDocProperties, evt: 'mouseup'}, - {sel: '#tool_prefs_save', fn: savePreferences, evt: 'click'}, - {sel: '#tool_prefs_option', fn: function () { showPreferences(); return false; }, evt: 'mouseup'}, - {sel: '#tool_delete,#tool_delete_multi', fn: deleteSelected, evt: 'click', key: ['del/backspace', true]}, - {sel: '#tool_reorient', fn: reorientPath, evt: 'click'}, - {sel: '#tool_node_link', fn: linkControlPoints, evt: 'click'}, - {sel: '#tool_node_clone', fn: clonePathNode, evt: 'click'}, - {sel: '#tool_node_delete', fn: deletePathNode, evt: 'click'}, - {sel: '#tool_openclose_path', fn: opencloseSubPath, evt: 'click'}, - {sel: '#tool_add_subpath', fn: addSubPath, evt: 'click'}, - {sel: '#tool_move_top', fn: moveToTopSelected, evt: 'click', key: 'ctrl+shift+]'}, - {sel: '#tool_move_bottom', fn: moveToBottomSelected, evt: 'click', key: 'ctrl+shift+['}, - {sel: '#tool_topath', fn: convertToPath, evt: 'click'}, - {sel: '#tool_make_link,#tool_make_link_multi', fn: makeHyperlink, evt: 'click'}, - {sel: '#tool_undo', fn: clickUndo, evt: 'click'}, - {sel: '#tool_redo', fn: clickRedo, evt: 'click'}, - {sel: '#tool_clone,#tool_clone_multi', fn: clickClone, evt: 'click', key: ['D', true]}, - {sel: '#tool_group_elements', fn: clickGroup, evt: 'click', key: ['G', true]}, - {sel: '#tool_ungroup', fn: clickGroup, evt: 'click'}, - {sel: '#tool_unlink_use', fn: clickGroup, evt: 'click'}, - {sel: '[id^=tool_align]', fn: clickAlign, evt: 'click'}, - // these two lines are required to make Opera work properly with the flyout mechanism - // {sel: '#tools_rect_show', fn: clickRect, evt: 'click'}, - // {sel: '#tools_ellipse_show', fn: clickEllipse, evt: 'click'}, - {sel: '#tool_bold', fn: clickBold, evt: 'mousedown'}, - {sel: '#tool_italic', fn: clickItalic, evt: 'mousedown'}, - {sel: '#sidepanel_handle', fn: toggleSidePanel, key: ['X']}, - {sel: '#copy_save_done', fn: cancelOverlays, evt: 'click'}, - - // Shortcuts not associated with buttons - - {key: 'ctrl+left', fn: function () { rotateSelected(0, 1); }}, - {key: 'ctrl+right', fn: function () { rotateSelected(1, 1); }}, - {key: 'ctrl+shift+left', fn: function () { rotateSelected(0, 5); }}, - {key: 'ctrl+shift+right', fn: function () { rotateSelected(1, 5); }}, - {key: 'shift+O', fn: selectPrev}, - {key: 'shift+P', fn: selectNext}, - {key: [modKey + 'up', true], fn: function () { zoomImage(2); }}, - {key: [modKey + 'down', true], fn: function () { zoomImage(0.5); }}, - {key: [modKey + ']', true], fn: function () { moveUpDownSelected('Up'); }}, - {key: [modKey + '[', true], fn: function () { moveUpDownSelected('Down'); }}, - {key: ['up', true], fn: function () { moveSelected(0, -1); }}, - {key: ['down', true], fn: function () { moveSelected(0, 1); }}, - {key: ['left', true], fn: function () { moveSelected(-1, 0); }}, - {key: ['right', true], fn: function () { moveSelected(1, 0); }}, - {key: 'shift+up', fn: function () { moveSelected(0, -10); }}, - {key: 'shift+down', fn: function () { moveSelected(0, 10); }}, - {key: 'shift+left', fn: function () { moveSelected(-10, 0); }}, - {key: 'shift+right', fn: function () { moveSelected(10, 0); }}, - {key: ['alt+up', true], fn: function () { svgCanvas.cloneSelectedElements(0, -1); }}, - {key: ['alt+down', true], fn: function () { svgCanvas.cloneSelectedElements(0, 1); }}, - {key: ['alt+left', true], fn: function () { svgCanvas.cloneSelectedElements(-1, 0); }}, - {key: ['alt+right', true], fn: function () { svgCanvas.cloneSelectedElements(1, 0); }}, - {key: ['alt+shift+up', true], fn: function () { svgCanvas.cloneSelectedElements(0, -10); }}, - {key: ['alt+shift+down', true], fn: function () { svgCanvas.cloneSelectedElements(0, 10); }}, - {key: ['alt+shift+left', true], fn: function () { svgCanvas.cloneSelectedElements(-10, 0); }}, - {key: ['alt+shift+right', true], fn: function () { svgCanvas.cloneSelectedElements(10, 0); }}, - {key: 'A', fn: function () { svgCanvas.selectAllInCurrentLayer(); }}, - - // Standard shortcuts - {key: modKey + 'z', fn: clickUndo}, - {key: modKey + 'shift+z', fn: clickRedo}, - {key: modKey + 'y', fn: clickRedo}, - - {key: modKey + 'x', fn: cutSelected}, - {key: modKey + 'c', fn: copySelected}, - {key: modKey + 'v', fn: pasteInCenter} - ]; - - // Tooltips not directly associated with a single function - var keyAssocs = { - '4/Shift+4': '#tools_rect_show', - '5/Shift+5': '#tools_ellipse_show' - }; - - return { - setAll: function () { - var flyouts = {}; - - $.each(toolButtons, function (i, opts) { - // Bind function to button - var btn; - if (opts.sel) { - btn = $(opts.sel); - if (btn.length === 0) { return true; } // Skip if markup does not exist - if (opts.evt) { - if (svgedit.browser.isTouch() && opts.evt === 'click') { - opts.evt = 'mousedown'; - } - btn[opts.evt](opts.fn); - } - - // Add to parent flyout menu, if able to be displayed - if (opts.parent && $(opts.parent + '_show').length !== 0) { - var fH = $(opts.parent); - if (!fH.length) { - fH = makeFlyoutHolder(opts.parent.substr(1)); - } - - fH.append(btn); - - if (!$.isArray(flyouts[opts.parent])) { - flyouts[opts.parent] = []; - } - flyouts[opts.parent].push(opts); - } - } - - // Bind function to shortcut key - if (opts.key) { - // Set shortcut based on options - var keyval, - // disInInp = true, - fn = opts.fn, - pd = false; - if ($.isArray(opts.key)) { - keyval = opts.key[0]; - if (opts.key.length > 1) { pd = opts.key[1]; } - // if (opts.key.length > 2) { disInInp = opts.key[2]; } - } else { - keyval = opts.key; - } - keyval += ''; - - $.each(keyval.split('/'), function (i, key) { - $(document).bind('keydown', key, function (e) { - fn(); - if (pd) { - e.preventDefault(); - } - // Prevent default on ALL keys? - return false; - }); - }); - - // Put shortcut in title - if (opts.sel && !opts.hidekey && btn.attr('title')) { - var newTitle = btn.attr('title').split('[')[0] + ' (' + keyval + ')'; - keyAssocs[keyval] = opts.sel; - // Disregard for menu items - if (!btn.parents('#main_menu').length) { - btn.attr('title', newTitle); - } - } - } - }); - - // Setup flyouts - setupFlyouts(flyouts); - - // Misc additional actions - - // Make 'return' keypress trigger the change event - $('.attr_changer, #image_url').bind('keydown', 'return', - function (evt) { - $(this).change(); - evt.preventDefault(); - } - ); - - $(window).bind('keydown', 'tab', function (e) { - if (uiContext === 'canvas') { - e.preventDefault(); - selectNext(); - } - }).bind('keydown', 'shift+tab', function (e) { - if (uiContext === 'canvas') { - e.preventDefault(); - selectPrev(); - } - }); - - $('#tool_zoom').dblclick(dblclickZoom); - }, - setTitles: function () { - $.each(keyAssocs, function (keyval, sel) { - var menu = ($(sel).parents('#main_menu').length); - - $(sel).each(function () { - var t; - if (menu) { - t = $(this).text().split(' [')[0]; - } else { - t = this.title.split(' [')[0]; - } - var keyStr = ''; - // Shift+Up - $.each(keyval.split('/'), function (i, key) { - var modBits = key.split('+'), mod = ''; - if (modBits.length > 1) { - mod = modBits[0] + '+'; - key = modBits[1]; - } - keyStr += (i ? '/' : '') + mod + (uiStrings['key_' + key] || key); - }); - if (menu) { - this.lastChild.textContent = t + ' [' + keyStr + ']'; - } else { - this.title = t + ' [' + keyStr + ']'; - } - }); - }); - }, - getButtonData: function (sel) { - var b; - $.each(toolButtons, function (i, btn) { - if (btn.sel === sel) { b = btn; } - }); - return b; - } - }; - }()); - - Actions.setAll(); - - // Select given tool - editor.ready(function () { - var tool, - itool = curConfig.initTool, - container = $('#tools_left, #svg_editor .tools_flyout'), - preTool = container.find('#tool_' + itool), - regTool = container.find('#' + itool); - if (preTool.length) { - tool = preTool; - } else if (regTool.length) { - tool = regTool; - } else { - tool = $('#tool_select'); - } - tool.click().mouseup(); - - if (curConfig.wireframe) { - $('#tool_wireframe').click(); - } - - if (curConfig.showlayers) { - toggleSidePanel(); - } - - $('#rulers').toggle(!!curConfig.showRulers); - - if (curConfig.showRulers) { - $('#show_rulers')[0].checked = true; - } - - if (curConfig.baseUnit) { - $('#base_unit').val(curConfig.baseUnit); - } - - if (curConfig.gridSnapping) { - $('#grid_snapping_on')[0].checked = true; - } - - if (curConfig.snappingStep) { - $('#grid_snapping_step').val(curConfig.snappingStep); - } - - if (curConfig.gridColor) { - $('#grid_color').val(curConfig.gridColor); - } - }); - - // init SpinButtons - $('#rect_rx').SpinButton({min: 0, max: 1000, callback: changeRectRadius}); - $('#stroke_width').SpinButton({min: 0, max: 99, smallStep: 0.1, callback: changeStrokeWidth}); - $('#angle').SpinButton({min: -180, max: 180, step: 5, callback: changeRotationAngle}); - $('#font_size').SpinButton({min: 0.001, stepfunc: stepFontSize, callback: changeFontSize}); - $('#group_opacity').SpinButton({min: 0, max: 100, step: 5, callback: changeOpacity}); - $('#blur').SpinButton({min: 0, max: 10, step: 0.1, callback: changeBlur}); - $('#zoom').SpinButton({min: 0.001, max: 10000, step: 50, stepfunc: stepZoom, callback: changeZoom}) - // Set default zoom - .val(svgCanvas.getZoom() * 100); - - $('#workarea').contextMenu( - { - menu: 'cmenu_canvas', - inSpeed: 0 - }, - function (action, el, pos) { - switch (action) { - case 'delete': - deleteSelected(); - break; - case 'cut': - cutSelected(); - break; - case 'copy': - copySelected(); - break; - case 'paste': - svgCanvas.pasteElements(); - break; - case 'paste_in_place': - svgCanvas.pasteElements('in_place'); - break; - case 'group': - case 'group_elements': - svgCanvas.groupSelectedElements(); - break; - case 'ungroup': - svgCanvas.ungroupSelectedElement(); - break; - case 'move_front': - moveToTopSelected(); - break; - case 'move_up': - moveUpDownSelected('Up'); - break; - case 'move_down': - moveUpDownSelected('Down'); - break; - case 'move_back': - moveToBottomSelected(); - break; - default: - if (svgedit.contextmenu && svgedit.contextmenu.hasCustomHandler(action)) { - svgedit.contextmenu.getCustomHandler(action).call(); - } - break; - } - } - ); - - var lmenuFunc = function (action, el, pos) { - switch (action) { - case 'dupe': - cloneLayer(); - break; - case 'delete': - deleteLayer(); - break; - case 'merge_down': - mergeLayer(); - break; - case 'merge_all': - svgCanvas.mergeAllLayers(); - updateContextPanel(); - populateLayers(); - break; - } - }; - - $('#layerlist').contextMenu( - { - menu: 'cmenu_layers', - inSpeed: 0 - }, - lmenuFunc - ); - - $('#layer_moreopts').contextMenu( - { - menu: 'cmenu_layers', - inSpeed: 0, - allowLeft: true - }, - lmenuFunc - ); - - $('.contextMenu li').mousedown(function (ev) { - ev.preventDefault(); - }); - - $('#cmenu_canvas li').disableContextMenu(); - canvMenu.enableContextMenuItems('#delete,#cut,#copy'); - - canvMenu[(localStorage.getItem('svgedit_clipboard') ? 'en' : 'dis') + 'ableContextMenuItems']( - '#paste,#paste_in_place' - ); - window.addEventListener('storage', function (e) { - if (e.key !== 'svgedit_clipboard') return; - - canvMenu[(localStorage.getItem('svgedit_clipboard') ? 'en' : 'dis') + 'ableContextMenuItems']( - '#paste,#paste_in_place' - ); - }); - - window.addEventListener('beforeunload', function (e) { - // Suppress warning if page is empty - if (undoMgr.getUndoStackSize() === 0) { - editor.showSaveWarning = false; - } - - // showSaveWarning is set to 'false' when the page is saved. - if (!curConfig.no_save_warning && editor.showSaveWarning) { - // Browser already asks question about closing the page - e.returnValue = uiStrings.notification.unsavedChanges; // Firefox needs this when beforeunload set by addEventListener (even though message is not used) - return uiStrings.notification.unsavedChanges; - } - }, false); - - editor.openPrep = function (func) { - $('#main_menu').hide(); - if (undoMgr.getUndoStackSize() === 0) { - func(true); - } else { - $.confirm(uiStrings.notification.QwantToOpen, func); - } - }; - - function onDragEnter (e) { - e.stopPropagation(); - e.preventDefault(); - // and indicator should be displayed here, such as "drop files here" - } - - function onDragOver (e) { - e.stopPropagation(); - e.preventDefault(); - } - - function onDragLeave (e) { - e.stopPropagation(); - e.preventDefault(); - // hypothetical indicator should be removed here - } - // Use HTML5 File API: http://www.w3.org/TR/FileAPI/ - // if browser has HTML5 File API support, then we will show the open menu item - // and provide a file input to click. When that change event fires, it will - // get the text contents of the file and send it to the canvas - if (window.FileReader) { - var importImage = function (e) { - $.process_cancel(uiStrings.notification.loadingImage); - e.stopPropagation(); - e.preventDefault(); - $('#workarea').removeAttr('style'); - $('#main_menu').hide(); - var file = (e.type === 'drop') ? e.dataTransfer.files[0] : this.files[0]; - if (!file) { - $('#dialog_box').hide(); - return; - } - /* if (file.type === 'application/pdf') { // Todo: Handle PDF imports - - } - else */ - if (file.type.indexOf('image') > -1) { - // Detected an image - // svg handling - var reader; - if (file.type.indexOf('svg') > -1) { - reader = new FileReader(); - reader.onloadend = function (e) { - var newElement = svgCanvas.importSvgString(e.target.result, true); - svgCanvas.ungroupSelectedElement(); - svgCanvas.ungroupSelectedElement(); - svgCanvas.groupSelectedElements(); - svgCanvas.alignSelectedElements('m', 'page'); - svgCanvas.alignSelectedElements('c', 'page'); - // highlight imported element, otherwise we get strange empty selectbox - svgCanvas.selectOnly([newElement]); - $('#dialog_box').hide(); - }; - reader.readAsText(file); - } else { - // bitmap handling - reader = new FileReader(); - reader.onloadend = function (e) { - // let's insert the new image until we know its dimensions - var insertNewImage = function (width, height) { - var newImage = svgCanvas.addSvgElementFromJson({ - element: 'image', - attr: { - x: 0, - y: 0, - width: width, - height: height, - id: svgCanvas.getNextId(), - style: 'pointer-events:inherit' - } - }); - svgCanvas.setHref(newImage, e.target.result); - svgCanvas.selectOnly([newImage]); - svgCanvas.alignSelectedElements('m', 'page'); - svgCanvas.alignSelectedElements('c', 'page'); - updateContextPanel(); - $('#dialog_box').hide(); - }; - // create dummy img so we know the default dimensions - var imgWidth = 100; - var imgHeight = 100; - var img = new Image(); - img.src = e.target.result; - img.style.opacity = 0; - img.onload = function () { - imgWidth = img.offsetWidth; - imgHeight = img.offsetHeight; - insertNewImage(imgWidth, imgHeight); - }; - }; - reader.readAsDataURL(file); - } - } - }; - - workarea[0].addEventListener('dragenter', onDragEnter, false); - workarea[0].addEventListener('dragover', onDragOver, false); - workarea[0].addEventListener('dragleave', onDragLeave, false); - workarea[0].addEventListener('drop', importImage, false); - - var open = $('').change(function () { - var f = this; - editor.openPrep(function (ok) { - if (!ok) { return; } - svgCanvas.clear(); - if (f.files.length === 1) { - $.process_cancel(uiStrings.notification.loadingImage); - var reader = new FileReader(); - reader.onloadend = function (e) { - loadSvgString(e.target.result); - updateCanvas(); - }; - reader.readAsText(f.files[0]); - } - }); - }); - $('#tool_open').show().prepend(open); - - var imgImport = $('').change(importImage); - $('#tool_import').show().prepend(imgImport); - } - - // $(function () { - updateCanvas(true); - // }); - - // var revnums = "svg-editor.js ($Rev$) "; - // revnums += svgCanvas.getVersion(); - // $('#copyright')[0].setAttribute('title', revnums); - - // For Compatibility with older extensions - $(function () { - window.svgCanvas = svgCanvas; - svgCanvas.ready = editor.ready; - }); - - editor.setLang = function (lang, allStrings) { - editor.langChanged = true; - $.pref('lang', lang); - $('#lang_select').val(lang); - if (!allStrings) { - return; - } - // var notif = allStrings.notification; // Currently unused - // $.extend will only replace the given strings - var oldLayerName = $('#layerlist tr.layersel td.layername').text(); - var renameLayer = (oldLayerName === uiStrings.common.layer + ' 1'); - - $.extend(uiStrings, allStrings); - svgCanvas.setUiStrings(allStrings); - Actions.setTitles(); - - if (renameLayer) { - svgCanvas.renameCurrentLayer(uiStrings.common.layer + ' 1'); - populateLayers(); - } - - // In case extensions loaded before the locale, now we execute a callback on them - if (extsPreLang.length) { - while (extsPreLang.length) { - var ext = extsPreLang.shift(); - ext.langReady({lang: lang, uiStrings: uiStrings}); - } - } else { - svgCanvas.runExtensions('langReady', {lang: lang, uiStrings: uiStrings}); - } - svgCanvas.runExtensions('langChanged', lang); - - // Update flyout tooltips - setFlyoutTitles(); - - // Copy title for certain tool elements - var elems = { - '#stroke_color': '#tool_stroke .icon_label, #tool_stroke .color_block', - '#fill_color': '#tool_fill label, #tool_fill .color_block', - '#linejoin_miter': '#cur_linejoin', - '#linecap_butt': '#cur_linecap' - }; - - $.each(elems, function (source, dest) { - $(dest).attr('title', $(source)[0].title); - }); - - // Copy alignment titles - $('#multiselected_panel div[id^=tool_align]').each(function () { - $('#tool_pos' + this.id.substr(10))[0].title = this.title; - }); - }; - }; - - editor.ready = function (cb) { - if (!isReady) { - callbacks.push(cb); - } else { - cb(); - } - }; - - editor.runCallbacks = function () { - $.each(callbacks, function () { - this(); - }); - isReady = true; - }; - - editor.loadFromString = function (str) { - editor.ready(function () { - loadSvgString(str); - }); - }; - - editor.disableUI = function (featList) { - // $(function () { - // $('#tool_wireframe, #tool_image, #main_button, #tool_source, #sidepanels').remove(); - // $('#tools_top').css('left', 5); - // }); - }; - - editor.loadFromURL = function (url, opts) { - if (!opts) { opts = {}; } - - var cache = opts.cache; - var cb = opts.callback; - - editor.ready(function () { - $.ajax({ - 'url': url, - 'dataType': 'text', - cache: !!cache, - beforeSend: function () { - $.process_cancel(uiStrings.notification.loadingImage); - }, - success: function (str) { - loadSvgString(str, cb); - }, - error: function (xhr, stat, err) { - if (xhr.status !== 404 && xhr.responseText) { - loadSvgString(xhr.responseText, cb); - } else { - $.alert(uiStrings.notification.URLloadFail + ': \n' + err, cb); - } - }, - complete: function () { - $('#dialog_box').hide(); - } - }); - }); - }; - - editor.loadFromDataURI = function (str) { - editor.ready(function () { - var base64 = false; - var pre = str.match(/^data:image\/svg\+xml;base64,/); - if (pre) { - base64 = true; - } else { - pre = str.match(/^data:image\/svg\+xml(?:;(?:utf8)?)?,/); - } - if (pre) { - pre = pre[0]; - } - var src = str.slice(pre.length); - loadSvgString(base64 ? Utils.decode64(src) : decodeURIComponent(src)); - }); - }; - - editor.addExtension = function () { - var args = arguments; - - // Note that we don't want this on editor.ready since some extensions - // may want to run before then (like server_opensave). - $(function () { - if (svgCanvas) { svgCanvas.addExtension.apply(this, args); } - }); - }; - - return editor; + var docElem = svgdocbox.documentElement; + + docElem = $(container)[0].appendChild(document.importNode(docElem, true)); + docElem.setAttribute('width', 16.5); + + this.rect = docElem.firstChild; + this.defs = docElem.getElementsByTagName('defs')[0]; + this.grad = this.defs.firstChild; + this.paint = new $.jGraduate.Paint({solidColor: cur.color}); + this.type = type; + + this.setPaint = function (paint, apply) { + this.paint = paint; + + var fillAttr = 'none'; + var ptype = paint.type; + var opac = paint.alpha / 100; + + switch (ptype) { + case 'solidColor': + fillAttr = (paint[ptype] !== 'none') ? '#' + paint[ptype] : paint[ptype]; + break; + case 'linearGradient': + case 'radialGradient': + this.defs.removeChild(this.grad); + this.grad = this.defs.appendChild(paint[ptype]); + var id = this.grad.id = 'gradbox_' + this.type; + fillAttr = 'url(#' + id + ')'; + break; + } + + this.rect.setAttribute('fill', fillAttr); + this.rect.setAttribute('opacity', opac); + + if (apply) { + svgCanvas.setColor(this.type, paintColor, true); + svgCanvas.setPaintOpacity(this.type, paintOpacity, true); + } + }; + + this.update = function (apply) { + if (!selectedElement) { return; } + var i, len; + var type = this.type; + switch (selectedElement.tagName) { + case 'use': + case 'image': + case 'foreignObject': + // These elements don't have fill or stroke, so don't change + // the current value + return; + case 'g': + case 'a': + var gPaint = null; + + var childs = selectedElement.getElementsByTagName('*'); + for (i = 0, len = childs.length; i < len; i++) { + var elem = childs[i]; + var p = elem.getAttribute(type); + if (i === 0) { + gPaint = p; + } else if (gPaint !== p) { + gPaint = null; + break; + } + } + + if (gPaint === null) { + // No common color, don't update anything + paintColor = null; + return; + } + paintColor = gPaint; + paintOpacity = 1; + break; + default: + paintOpacity = parseFloat(selectedElement.getAttribute(type + '-opacity')); + if (isNaN(paintOpacity)) { + paintOpacity = 1.0; + } + + var defColor = type === 'fill' ? 'black' : 'none'; + paintColor = selectedElement.getAttribute(type) || defColor; + } + + if (apply) { + svgCanvas.setColor(type, paintColor, true); + svgCanvas.setPaintOpacity(type, paintOpacity, true); + } + + paintOpacity *= 100; + + var paint = getPaint(paintColor, paintOpacity, type); + // update the rect inside #fill_color/#stroke_color + this.setPaint(paint); + }; + + this.prep = function () { + var ptype = this.paint.type; + + switch (ptype) { + case 'linearGradient': + case 'radialGradient': + var paint = new $.jGraduate.Paint({copy: this.paint}); + svgCanvas.setPaint(type, paint); + break; + } + }; + }; + + paintBox.fill = new PaintBox('#fill_color', 'fill'); + paintBox.stroke = new PaintBox('#stroke_color', 'stroke'); + + $('#stroke_width').val(curConfig.initStroke.width); + $('#group_opacity').val(curConfig.initOpacity * 100); + + // Use this SVG elem to test vectorEffect support + var testEl = paintBox.fill.rect.cloneNode(false); + testEl.setAttribute('style', 'vector-effect:non-scaling-stroke'); + supportsNonSS = (testEl.style.vectorEffect === 'non-scaling-stroke'); + testEl.removeAttribute('style'); + var svgdocbox = paintBox.fill.rect.ownerDocument; + // Use this to test support for blur element. Seems to work to test support in Webkit + var blurTest = svgdocbox.createElementNS(svgedit.NS.SVG, 'feGaussianBlur'); + if (blurTest.stdDeviationX === undefined) { + $('#tool_blur').hide(); + } + $(blurTest).remove(); + + // Test for zoom icon support + (function () { + var pre = '-' + uaPrefix.toLowerCase() + '-zoom-'; + var zoom = pre + 'in'; + workarea.css('cursor', zoom); + if (workarea.css('cursor') === zoom) { + zoomInIcon = zoom; + zoomOutIcon = pre + 'out'; + } + workarea.css('cursor', 'auto'); + }()); + + // Test for embedImage support (use timeout to not interfere with page load) + setTimeout(function () { + svgCanvas.embedImage('images/logo.png', function (datauri) { + if (!datauri) { + // Disable option + $('#image_save_opts [value=embed]').attr('disabled', 'disabled'); + $('#image_save_opts input').val(['ref']); + $.pref('img_save', 'ref'); + $('#image_opt_embed').css('color', '#666').attr('title', uiStrings.notification.featNotSupported); + } + }); + }, 1000); + + $('#fill_color, #tool_fill .icon_label').click(function () { + colorPicker($('#fill_color')); + updateToolButtonState(); + }); + + $('#stroke_color, #tool_stroke .icon_label').click(function () { + colorPicker($('#stroke_color')); + updateToolButtonState(); + }); + + $('#group_opacityLabel').click(function () { + $('#opacity_dropdown button').mousedown(); + $(window).mouseup(); + }); + + $('#zoomLabel').click(function () { + $('#zoom_dropdown button').mousedown(); + $(window).mouseup(); + }); + + $('#tool_move_top').mousedown(function (evt) { + $('#tools_stacking').show(); + evt.preventDefault(); + }); + + $('.layer_button').mousedown(function () { + $(this).addClass('layer_buttonpressed'); + }).mouseout(function () { + $(this).removeClass('layer_buttonpressed'); + }).mouseup(function () { + $(this).removeClass('layer_buttonpressed'); + }); + + $('.push_button').mousedown(function () { + if (!$(this).hasClass('disabled')) { + $(this).addClass('push_button_pressed').removeClass('push_button'); + } + }).mouseout(function () { + $(this).removeClass('push_button_pressed').addClass('push_button'); + }).mouseup(function () { + $(this).removeClass('push_button_pressed').addClass('push_button'); + }); + + // ask for a layer name + $('#layer_new').click(function () { + var uniqName, + i = svgCanvas.getCurrentDrawing().getNumLayers(); + do { + uniqName = uiStrings.layers.layer + ' ' + (++i); + } while (svgCanvas.getCurrentDrawing().hasLayer(uniqName)); + + $.prompt(uiStrings.notification.enterUniqueLayerName, uniqName, function (newName) { + if (!newName) { return; } + if (svgCanvas.getCurrentDrawing().hasLayer(newName)) { + $.alert(uiStrings.notification.dupeLayerName); + return; + } + svgCanvas.createLayer(newName); + updateContextPanel(); + populateLayers(); + }); + }); + + function deleteLayer () { + if (svgCanvas.deleteCurrentLayer()) { + updateContextPanel(); + populateLayers(); + // This matches what SvgCanvas does + // TODO: make this behavior less brittle (svg-editor should get which + // layer is selected from the canvas and then select that one in the UI) + $('#layerlist tr.layer').removeClass('layersel'); + $('#layerlist tr.layer:first').addClass('layersel'); + } + } + + function cloneLayer () { + var name = svgCanvas.getCurrentDrawing().getCurrentLayerName() + ' copy'; + + $.prompt(uiStrings.notification.enterUniqueLayerName, name, function (newName) { + if (!newName) { return; } + if (svgCanvas.getCurrentDrawing().hasLayer(newName)) { + $.alert(uiStrings.notification.dupeLayerName); + return; + } + svgCanvas.cloneLayer(newName); + updateContextPanel(); + populateLayers(); + }); + } + + function mergeLayer () { + if ($('#layerlist tr.layersel').index() === svgCanvas.getCurrentDrawing().getNumLayers() - 1) { + return; + } + svgCanvas.mergeLayer(); + updateContextPanel(); + populateLayers(); + } + + function moveLayer (pos) { + var curIndex = $('#layerlist tr.layersel').index(); + var total = svgCanvas.getCurrentDrawing().getNumLayers(); + if (curIndex > 0 || curIndex < total - 1) { + curIndex += pos; + svgCanvas.setCurrentLayerPosition(total - curIndex - 1); + populateLayers(); + } + } + + $('#layer_delete').click(deleteLayer); + + $('#layer_up').click(function () { + moveLayer(-1); + }); + + $('#layer_down').click(function () { + moveLayer(1); + }); + + $('#layer_rename').click(function () { + // var curIndex = $('#layerlist tr.layersel').prevAll().length; // Currently unused + var oldName = $('#layerlist tr.layersel td.layername').text(); + $.prompt(uiStrings.notification.enterNewLayerName, '', function (newName) { + if (!newName) { return; } + if (oldName === newName || svgCanvas.getCurrentDrawing().hasLayer(newName)) { + $.alert(uiStrings.notification.layerHasThatName); + return; + } + + svgCanvas.renameCurrentLayer(newName); + populateLayers(); + }); + }); + + var SIDEPANEL_MAXWIDTH = 300; + var SIDEPANEL_OPENWIDTH = 150; + var sidedrag = -1, sidedragging = false, allowmove = false; + + var changeSidePanelWidth = function (delta) { + var rulerX = $('#ruler_x'); + $('#sidepanels').width('+=' + delta); + $('#layerpanel').width('+=' + delta); + rulerX.css('right', parseInt(rulerX.css('right'), 10) + delta); + workarea.css('right', parseInt(workarea.css('right'), 10) + delta); + svgCanvas.runExtensions('workareaResized'); + }; + + var resizeSidePanel = function (evt) { + if (!allowmove) { return; } + if (sidedrag === -1) { return; } + sidedragging = true; + var deltaX = sidedrag - evt.pageX; + var sideWidth = $('#sidepanels').width(); + if (sideWidth + deltaX > SIDEPANEL_MAXWIDTH) { + deltaX = SIDEPANEL_MAXWIDTH - sideWidth; + sideWidth = SIDEPANEL_MAXWIDTH; + } else if (sideWidth + deltaX < 2) { + deltaX = 2 - sideWidth; + sideWidth = 2; + } + if (deltaX === 0) { return; } + sidedrag -= deltaX; + changeSidePanelWidth(deltaX); + }; + + // if width is non-zero, then fully close it, otherwise fully open it + // the optional close argument forces the side panel closed + var toggleSidePanel = function (close) { + var w = $('#sidepanels').width(); + var deltaX = (w > 2 || close ? 2 : SIDEPANEL_OPENWIDTH) - w; + changeSidePanelWidth(deltaX); + }; + + $('#sidepanel_handle') + .mousedown(function (evt) { + sidedrag = evt.pageX; + $(window).mousemove(resizeSidePanel); + allowmove = false; + // Silly hack for Chrome, which always runs mousemove right after mousedown + setTimeout(function () { + allowmove = true; + }, 20); + }) + .mouseup(function (evt) { + if (!sidedragging) { toggleSidePanel(); } + sidedrag = -1; + sidedragging = false; + }); + + $(window).mouseup(function () { + sidedrag = -1; + sidedragging = false; + $('#svg_editor').unbind('mousemove', resizeSidePanel); + }); + + populateLayers(); + + // function changeResolution (x,y) { + // var zoom = svgCanvas.getResolution().zoom; + // setResolution(x * zoom, y * zoom); + // } + + var centerCanvas = function () { + // this centers the canvas vertically in the workarea (horizontal handled in CSS) + workarea.css('line-height', workarea.height() + 'px'); + }; + + $(window).bind('load resize', centerCanvas); + + function stepFontSize (elem, step) { + var origVal = Number(elem.value); + var sugVal = origVal + step; + var increasing = sugVal >= origVal; + if (step === 0) { return origVal; } + + if (origVal >= 24) { + if (increasing) { + return Math.round(origVal * 1.1); + } + return Math.round(origVal / 1.1); + } + if (origVal <= 1) { + if (increasing) { + return origVal * 2; + } + return origVal / 2; + } + return sugVal; + } + + function stepZoom (elem, step) { + var origVal = Number(elem.value); + if (origVal === 0) { return 100; } + var sugVal = origVal + step; + if (step === 0) { return origVal; } + + if (origVal >= 100) { + return sugVal; + } + if (sugVal >= origVal) { + return origVal * 2; + } + return origVal / 2; + } + + // function setResolution (w, h, center) { + // updateCanvas(); + // // w -= 0; h -= 0; + // // $('#svgcanvas').css({'width': w, 'height': h}); + // // $('#canvas_width').val(w); + // // $('#canvas_height').val(h); + // // + // // if (center) { + // // var wArea = workarea; + // // var scrollY = h/2 - wArea.height()/2; + // // var scrollX = w/2 - wArea.width()/2; + // // wArea[0].scrollTop = scrollY; + // // wArea[0].scrollLeft = scrollX; + // // } + // } + + $('#resolution').change(function () { + var wh = $('#canvas_width,#canvas_height'); + if (!this.selectedIndex) { + if ($('#canvas_width').val() === 'fit') { + wh.removeAttr('disabled').val(100); + } + } else if (this.value === 'content') { + wh.val('fit').attr('disabled', 'disabled'); + } else { + var dims = this.value.split('x'); + $('#canvas_width').val(dims[0]); + $('#canvas_height').val(dims[1]); + wh.removeAttr('disabled'); + } + }); + + // Prevent browser from erroneously repopulating fields + $('input,select').attr('autocomplete', 'off'); + + // Associate all button actions as well as non-button keyboard shortcuts + Actions = (function () { + // sel:'selector', fn:function, evt:'event', key:[key, preventDefault, NoDisableInInput] + var toolButtons = [ + {sel: '#tool_select', fn: clickSelect, evt: 'click', key: ['V', true]}, + {sel: '#tool_fhpath', fn: clickFHPath, evt: 'click', key: ['Q', true]}, + {sel: '#tool_line', fn: clickLine, evt: 'click', key: ['L', true]}, + {sel: '#tool_rect', fn: clickRect, evt: 'mouseup', key: ['R', true], parent: '#tools_rect', icon: 'rect'}, + {sel: '#tool_square', fn: clickSquare, evt: 'mouseup', parent: '#tools_rect', icon: 'square'}, + {sel: '#tool_fhrect', fn: clickFHRect, evt: 'mouseup', parent: '#tools_rect', icon: 'fh_rect'}, + {sel: '#tool_ellipse', fn: clickEllipse, evt: 'mouseup', key: ['E', true], parent: '#tools_ellipse', icon: 'ellipse'}, + {sel: '#tool_circle', fn: clickCircle, evt: 'mouseup', parent: '#tools_ellipse', icon: 'circle'}, + {sel: '#tool_fhellipse', fn: clickFHEllipse, evt: 'mouseup', parent: '#tools_ellipse', icon: 'fh_ellipse'}, + {sel: '#tool_path', fn: clickPath, evt: 'click', key: ['P', true]}, + {sel: '#tool_text', fn: clickText, evt: 'click', key: ['T', true]}, + {sel: '#tool_image', fn: clickImage, evt: 'mouseup'}, + {sel: '#tool_zoom', fn: clickZoom, evt: 'mouseup', key: ['Z', true]}, + {sel: '#tool_clear', fn: clickClear, evt: 'mouseup', key: ['N', true]}, + {sel: '#tool_save', fn: function () { + if (editingsource) { + saveSourceEditor(); + } else { + clickSave(); + } + }, evt: 'mouseup', key: ['S', true]}, + {sel: '#tool_export', fn: clickExport, evt: 'mouseup'}, + {sel: '#tool_open', fn: clickOpen, evt: 'mouseup', key: ['O', true]}, + {sel: '#tool_import', fn: clickImport, evt: 'mouseup'}, + {sel: '#tool_source', fn: showSourceEditor, evt: 'click', key: ['U', true]}, + {sel: '#tool_wireframe', fn: clickWireframe, evt: 'click', key: ['F', true]}, + {sel: '#tool_source_cancel,.overlay,#tool_docprops_cancel,#tool_prefs_cancel', fn: cancelOverlays, evt: 'click', key: ['esc', false, false], hidekey: true}, + {sel: '#tool_source_save', fn: saveSourceEditor, evt: 'click'}, + {sel: '#tool_docprops_save', fn: saveDocProperties, evt: 'click'}, + {sel: '#tool_docprops', fn: showDocProperties, evt: 'mouseup'}, + {sel: '#tool_prefs_save', fn: savePreferences, evt: 'click'}, + {sel: '#tool_prefs_option', fn: function () { showPreferences(); return false; }, evt: 'mouseup'}, + {sel: '#tool_delete,#tool_delete_multi', fn: deleteSelected, evt: 'click', key: ['del/backspace', true]}, + {sel: '#tool_reorient', fn: reorientPath, evt: 'click'}, + {sel: '#tool_node_link', fn: linkControlPoints, evt: 'click'}, + {sel: '#tool_node_clone', fn: clonePathNode, evt: 'click'}, + {sel: '#tool_node_delete', fn: deletePathNode, evt: 'click'}, + {sel: '#tool_openclose_path', fn: opencloseSubPath, evt: 'click'}, + {sel: '#tool_add_subpath', fn: addSubPath, evt: 'click'}, + {sel: '#tool_move_top', fn: moveToTopSelected, evt: 'click', key: 'ctrl+shift+]'}, + {sel: '#tool_move_bottom', fn: moveToBottomSelected, evt: 'click', key: 'ctrl+shift+['}, + {sel: '#tool_topath', fn: convertToPath, evt: 'click'}, + {sel: '#tool_make_link,#tool_make_link_multi', fn: makeHyperlink, evt: 'click'}, + {sel: '#tool_undo', fn: clickUndo, evt: 'click'}, + {sel: '#tool_redo', fn: clickRedo, evt: 'click'}, + {sel: '#tool_clone,#tool_clone_multi', fn: clickClone, evt: 'click', key: ['D', true]}, + {sel: '#tool_group_elements', fn: clickGroup, evt: 'click', key: ['G', true]}, + {sel: '#tool_ungroup', fn: clickGroup, evt: 'click'}, + {sel: '#tool_unlink_use', fn: clickGroup, evt: 'click'}, + {sel: '[id^=tool_align]', fn: clickAlign, evt: 'click'}, + // these two lines are required to make Opera work properly with the flyout mechanism + // {sel: '#tools_rect_show', fn: clickRect, evt: 'click'}, + // {sel: '#tools_ellipse_show', fn: clickEllipse, evt: 'click'}, + {sel: '#tool_bold', fn: clickBold, evt: 'mousedown'}, + {sel: '#tool_italic', fn: clickItalic, evt: 'mousedown'}, + {sel: '#sidepanel_handle', fn: toggleSidePanel, key: ['X']}, + {sel: '#copy_save_done', fn: cancelOverlays, evt: 'click'}, + + // Shortcuts not associated with buttons + + {key: 'ctrl+left', fn: function () { rotateSelected(0, 1); }}, + {key: 'ctrl+right', fn: function () { rotateSelected(1, 1); }}, + {key: 'ctrl+shift+left', fn: function () { rotateSelected(0, 5); }}, + {key: 'ctrl+shift+right', fn: function () { rotateSelected(1, 5); }}, + {key: 'shift+O', fn: selectPrev}, + {key: 'shift+P', fn: selectNext}, + {key: [modKey + 'up', true], fn: function () { zoomImage(2); }}, + {key: [modKey + 'down', true], fn: function () { zoomImage(0.5); }}, + {key: [modKey + ']', true], fn: function () { moveUpDownSelected('Up'); }}, + {key: [modKey + '[', true], fn: function () { moveUpDownSelected('Down'); }}, + {key: ['up', true], fn: function () { moveSelected(0, -1); }}, + {key: ['down', true], fn: function () { moveSelected(0, 1); }}, + {key: ['left', true], fn: function () { moveSelected(-1, 0); }}, + {key: ['right', true], fn: function () { moveSelected(1, 0); }}, + {key: 'shift+up', fn: function () { moveSelected(0, -10); }}, + {key: 'shift+down', fn: function () { moveSelected(0, 10); }}, + {key: 'shift+left', fn: function () { moveSelected(-10, 0); }}, + {key: 'shift+right', fn: function () { moveSelected(10, 0); }}, + {key: ['alt+up', true], fn: function () { svgCanvas.cloneSelectedElements(0, -1); }}, + {key: ['alt+down', true], fn: function () { svgCanvas.cloneSelectedElements(0, 1); }}, + {key: ['alt+left', true], fn: function () { svgCanvas.cloneSelectedElements(-1, 0); }}, + {key: ['alt+right', true], fn: function () { svgCanvas.cloneSelectedElements(1, 0); }}, + {key: ['alt+shift+up', true], fn: function () { svgCanvas.cloneSelectedElements(0, -10); }}, + {key: ['alt+shift+down', true], fn: function () { svgCanvas.cloneSelectedElements(0, 10); }}, + {key: ['alt+shift+left', true], fn: function () { svgCanvas.cloneSelectedElements(-10, 0); }}, + {key: ['alt+shift+right', true], fn: function () { svgCanvas.cloneSelectedElements(10, 0); }}, + {key: 'A', fn: function () { svgCanvas.selectAllInCurrentLayer(); }}, + + // Standard shortcuts + {key: modKey + 'z', fn: clickUndo}, + {key: modKey + 'shift+z', fn: clickRedo}, + {key: modKey + 'y', fn: clickRedo}, + + {key: modKey + 'x', fn: cutSelected}, + {key: modKey + 'c', fn: copySelected}, + {key: modKey + 'v', fn: pasteInCenter} + ]; + + // Tooltips not directly associated with a single function + var keyAssocs = { + '4/Shift+4': '#tools_rect_show', + '5/Shift+5': '#tools_ellipse_show' + }; + + return { + setAll: function () { + var flyouts = {}; + + $.each(toolButtons, function (i, opts) { + // Bind function to button + var btn; + if (opts.sel) { + btn = $(opts.sel); + if (btn.length === 0) { return true; } // Skip if markup does not exist + if (opts.evt) { + if (svgedit.browser.isTouch() && opts.evt === 'click') { + opts.evt = 'mousedown'; + } + btn[opts.evt](opts.fn); + } + + // Add to parent flyout menu, if able to be displayed + if (opts.parent && $(opts.parent + '_show').length !== 0) { + var fH = $(opts.parent); + if (!fH.length) { + fH = makeFlyoutHolder(opts.parent.substr(1)); + } + + fH.append(btn); + + if (!$.isArray(flyouts[opts.parent])) { + flyouts[opts.parent] = []; + } + flyouts[opts.parent].push(opts); + } + } + + // Bind function to shortcut key + if (opts.key) { + // Set shortcut based on options + var keyval, + // disInInp = true, + fn = opts.fn, + pd = false; + if ($.isArray(opts.key)) { + keyval = opts.key[0]; + if (opts.key.length > 1) { pd = opts.key[1]; } + // if (opts.key.length > 2) { disInInp = opts.key[2]; } + } else { + keyval = opts.key; + } + keyval += ''; + + $.each(keyval.split('/'), function (i, key) { + $(document).bind('keydown', key, function (e) { + fn(); + if (pd) { + e.preventDefault(); + } + // Prevent default on ALL keys? + return false; + }); + }); + + // Put shortcut in title + if (opts.sel && !opts.hidekey && btn.attr('title')) { + var newTitle = btn.attr('title').split('[')[0] + ' (' + keyval + ')'; + keyAssocs[keyval] = opts.sel; + // Disregard for menu items + if (!btn.parents('#main_menu').length) { + btn.attr('title', newTitle); + } + } + } + }); + + // Setup flyouts + setupFlyouts(flyouts); + + // Misc additional actions + + // Make 'return' keypress trigger the change event + $('.attr_changer, #image_url').bind('keydown', 'return', + function (evt) { + $(this).change(); + evt.preventDefault(); + } + ); + + $(window).bind('keydown', 'tab', function (e) { + if (uiContext === 'canvas') { + e.preventDefault(); + selectNext(); + } + }).bind('keydown', 'shift+tab', function (e) { + if (uiContext === 'canvas') { + e.preventDefault(); + selectPrev(); + } + }); + + $('#tool_zoom').dblclick(dblclickZoom); + }, + setTitles: function () { + $.each(keyAssocs, function (keyval, sel) { + var menu = ($(sel).parents('#main_menu').length); + + $(sel).each(function () { + var t; + if (menu) { + t = $(this).text().split(' [')[0]; + } else { + t = this.title.split(' [')[0]; + } + var keyStr = ''; + // Shift+Up + $.each(keyval.split('/'), function (i, key) { + var modBits = key.split('+'), mod = ''; + if (modBits.length > 1) { + mod = modBits[0] + '+'; + key = modBits[1]; + } + keyStr += (i ? '/' : '') + mod + (uiStrings['key_' + key] || key); + }); + if (menu) { + this.lastChild.textContent = t + ' [' + keyStr + ']'; + } else { + this.title = t + ' [' + keyStr + ']'; + } + }); + }); + }, + getButtonData: function (sel) { + var b; + $.each(toolButtons, function (i, btn) { + if (btn.sel === sel) { b = btn; } + }); + return b; + } + }; + }()); + + Actions.setAll(); + + // Select given tool + editor.ready(function () { + var tool, + itool = curConfig.initTool, + container = $('#tools_left, #svg_editor .tools_flyout'), + preTool = container.find('#tool_' + itool), + regTool = container.find('#' + itool); + if (preTool.length) { + tool = preTool; + } else if (regTool.length) { + tool = regTool; + } else { + tool = $('#tool_select'); + } + tool.click().mouseup(); + + if (curConfig.wireframe) { + $('#tool_wireframe').click(); + } + + if (curConfig.showlayers) { + toggleSidePanel(); + } + + $('#rulers').toggle(!!curConfig.showRulers); + + if (curConfig.showRulers) { + $('#show_rulers')[0].checked = true; + } + + if (curConfig.baseUnit) { + $('#base_unit').val(curConfig.baseUnit); + } + + if (curConfig.gridSnapping) { + $('#grid_snapping_on')[0].checked = true; + } + + if (curConfig.snappingStep) { + $('#grid_snapping_step').val(curConfig.snappingStep); + } + + if (curConfig.gridColor) { + $('#grid_color').val(curConfig.gridColor); + } + }); + + // init SpinButtons + $('#rect_rx').SpinButton({min: 0, max: 1000, callback: changeRectRadius}); + $('#stroke_width').SpinButton({min: 0, max: 99, smallStep: 0.1, callback: changeStrokeWidth}); + $('#angle').SpinButton({min: -180, max: 180, step: 5, callback: changeRotationAngle}); + $('#font_size').SpinButton({min: 0.001, stepfunc: stepFontSize, callback: changeFontSize}); + $('#group_opacity').SpinButton({min: 0, max: 100, step: 5, callback: changeOpacity}); + $('#blur').SpinButton({min: 0, max: 10, step: 0.1, callback: changeBlur}); + $('#zoom').SpinButton({min: 0.001, max: 10000, step: 50, stepfunc: stepZoom, callback: changeZoom}) + // Set default zoom + .val(svgCanvas.getZoom() * 100); + + $('#workarea').contextMenu( + { + menu: 'cmenu_canvas', + inSpeed: 0 + }, + function (action, el, pos) { + switch (action) { + case 'delete': + deleteSelected(); + break; + case 'cut': + cutSelected(); + break; + case 'copy': + copySelected(); + break; + case 'paste': + svgCanvas.pasteElements(); + break; + case 'paste_in_place': + svgCanvas.pasteElements('in_place'); + break; + case 'group': + case 'group_elements': + svgCanvas.groupSelectedElements(); + break; + case 'ungroup': + svgCanvas.ungroupSelectedElement(); + break; + case 'move_front': + moveToTopSelected(); + break; + case 'move_up': + moveUpDownSelected('Up'); + break; + case 'move_down': + moveUpDownSelected('Down'); + break; + case 'move_back': + moveToBottomSelected(); + break; + default: + if (svgedit.contextmenu && svgedit.contextmenu.hasCustomHandler(action)) { + svgedit.contextmenu.getCustomHandler(action).call(); + } + break; + } + } + ); + + var lmenuFunc = function (action, el, pos) { + switch (action) { + case 'dupe': + cloneLayer(); + break; + case 'delete': + deleteLayer(); + break; + case 'merge_down': + mergeLayer(); + break; + case 'merge_all': + svgCanvas.mergeAllLayers(); + updateContextPanel(); + populateLayers(); + break; + } + }; + + $('#layerlist').contextMenu( + { + menu: 'cmenu_layers', + inSpeed: 0 + }, + lmenuFunc + ); + + $('#layer_moreopts').contextMenu( + { + menu: 'cmenu_layers', + inSpeed: 0, + allowLeft: true + }, + lmenuFunc + ); + + $('.contextMenu li').mousedown(function (ev) { + ev.preventDefault(); + }); + + $('#cmenu_canvas li').disableContextMenu(); + canvMenu.enableContextMenuItems('#delete,#cut,#copy'); + + canvMenu[(localStorage.getItem('svgedit_clipboard') ? 'en' : 'dis') + 'ableContextMenuItems']( + '#paste,#paste_in_place' + ); + window.addEventListener('storage', function (e) { + if (e.key !== 'svgedit_clipboard') return; + + canvMenu[(localStorage.getItem('svgedit_clipboard') ? 'en' : 'dis') + 'ableContextMenuItems']( + '#paste,#paste_in_place' + ); + }); + + window.addEventListener('beforeunload', function (e) { + // Suppress warning if page is empty + if (undoMgr.getUndoStackSize() === 0) { + editor.showSaveWarning = false; + } + + // showSaveWarning is set to 'false' when the page is saved. + if (!curConfig.no_save_warning && editor.showSaveWarning) { + // Browser already asks question about closing the page + e.returnValue = uiStrings.notification.unsavedChanges; // Firefox needs this when beforeunload set by addEventListener (even though message is not used) + return uiStrings.notification.unsavedChanges; + } + }, false); + + editor.openPrep = function (func) { + $('#main_menu').hide(); + if (undoMgr.getUndoStackSize() === 0) { + func(true); + } else { + $.confirm(uiStrings.notification.QwantToOpen, func); + } + }; + + function onDragEnter (e) { + e.stopPropagation(); + e.preventDefault(); + // and indicator should be displayed here, such as "drop files here" + } + + function onDragOver (e) { + e.stopPropagation(); + e.preventDefault(); + } + + function onDragLeave (e) { + e.stopPropagation(); + e.preventDefault(); + // hypothetical indicator should be removed here + } + // Use HTML5 File API: http://www.w3.org/TR/FileAPI/ + // if browser has HTML5 File API support, then we will show the open menu item + // and provide a file input to click. When that change event fires, it will + // get the text contents of the file and send it to the canvas + if (window.FileReader) { + var importImage = function (e) { + $.process_cancel(uiStrings.notification.loadingImage); + e.stopPropagation(); + e.preventDefault(); + $('#workarea').removeAttr('style'); + $('#main_menu').hide(); + var file = (e.type === 'drop') ? e.dataTransfer.files[0] : this.files[0]; + if (!file) { + $('#dialog_box').hide(); + return; + } + /* if (file.type === 'application/pdf') { // Todo: Handle PDF imports + + } + else */ + if (file.type.indexOf('image') > -1) { + // Detected an image + // svg handling + var reader; + if (file.type.indexOf('svg') > -1) { + reader = new FileReader(); + reader.onloadend = function (e) { + var newElement = svgCanvas.importSvgString(e.target.result, true); + svgCanvas.ungroupSelectedElement(); + svgCanvas.ungroupSelectedElement(); + svgCanvas.groupSelectedElements(); + svgCanvas.alignSelectedElements('m', 'page'); + svgCanvas.alignSelectedElements('c', 'page'); + // highlight imported element, otherwise we get strange empty selectbox + svgCanvas.selectOnly([newElement]); + $('#dialog_box').hide(); + }; + reader.readAsText(file); + } else { + // bitmap handling + reader = new FileReader(); + reader.onloadend = function (e) { + // let's insert the new image until we know its dimensions + var insertNewImage = function (width, height) { + var newImage = svgCanvas.addSvgElementFromJson({ + element: 'image', + attr: { + x: 0, + y: 0, + width: width, + height: height, + id: svgCanvas.getNextId(), + style: 'pointer-events:inherit' + } + }); + svgCanvas.setHref(newImage, e.target.result); + svgCanvas.selectOnly([newImage]); + svgCanvas.alignSelectedElements('m', 'page'); + svgCanvas.alignSelectedElements('c', 'page'); + updateContextPanel(); + $('#dialog_box').hide(); + }; + // create dummy img so we know the default dimensions + var imgWidth = 100; + var imgHeight = 100; + var img = new Image(); + img.src = e.target.result; + img.style.opacity = 0; + img.onload = function () { + imgWidth = img.offsetWidth; + imgHeight = img.offsetHeight; + insertNewImage(imgWidth, imgHeight); + }; + }; + reader.readAsDataURL(file); + } + } + }; + + workarea[0].addEventListener('dragenter', onDragEnter, false); + workarea[0].addEventListener('dragover', onDragOver, false); + workarea[0].addEventListener('dragleave', onDragLeave, false); + workarea[0].addEventListener('drop', importImage, false); + + var open = $('').change(function () { + var f = this; + editor.openPrep(function (ok) { + if (!ok) { return; } + svgCanvas.clear(); + if (f.files.length === 1) { + $.process_cancel(uiStrings.notification.loadingImage); + var reader = new FileReader(); + reader.onloadend = function (e) { + loadSvgString(e.target.result); + updateCanvas(); + }; + reader.readAsText(f.files[0]); + } + }); + }); + $('#tool_open').show().prepend(open); + + var imgImport = $('').change(importImage); + $('#tool_import').show().prepend(imgImport); + } + + // $(function () { + updateCanvas(true); + // }); + + // var revnums = "svg-editor.js ($Rev$) "; + // revnums += svgCanvas.getVersion(); + // $('#copyright')[0].setAttribute('title', revnums); + + // For Compatibility with older extensions + $(function () { + window.svgCanvas = svgCanvas; + svgCanvas.ready = editor.ready; + }); + + editor.setLang = function (lang, allStrings) { + editor.langChanged = true; + $.pref('lang', lang); + $('#lang_select').val(lang); + if (!allStrings) { + return; + } + // var notif = allStrings.notification; // Currently unused + // $.extend will only replace the given strings + var oldLayerName = $('#layerlist tr.layersel td.layername').text(); + var renameLayer = (oldLayerName === uiStrings.common.layer + ' 1'); + + $.extend(uiStrings, allStrings); + svgCanvas.setUiStrings(allStrings); + Actions.setTitles(); + + if (renameLayer) { + svgCanvas.renameCurrentLayer(uiStrings.common.layer + ' 1'); + populateLayers(); + } + + // In case extensions loaded before the locale, now we execute a callback on them + if (extsPreLang.length) { + while (extsPreLang.length) { + var ext = extsPreLang.shift(); + ext.langReady({lang: lang, uiStrings: uiStrings}); + } + } else { + svgCanvas.runExtensions('langReady', {lang: lang, uiStrings: uiStrings}); + } + svgCanvas.runExtensions('langChanged', lang); + + // Update flyout tooltips + setFlyoutTitles(); + + // Copy title for certain tool elements + var elems = { + '#stroke_color': '#tool_stroke .icon_label, #tool_stroke .color_block', + '#fill_color': '#tool_fill label, #tool_fill .color_block', + '#linejoin_miter': '#cur_linejoin', + '#linecap_butt': '#cur_linecap' + }; + + $.each(elems, function (source, dest) { + $(dest).attr('title', $(source)[0].title); + }); + + // Copy alignment titles + $('#multiselected_panel div[id^=tool_align]').each(function () { + $('#tool_pos' + this.id.substr(10))[0].title = this.title; + }); + }; + }; + + editor.ready = function (cb) { + if (!isReady) { + callbacks.push(cb); + } else { + cb(); + } + }; + + editor.runCallbacks = function () { + $.each(callbacks, function () { + this(); + }); + isReady = true; + }; + + editor.loadFromString = function (str) { + editor.ready(function () { + loadSvgString(str); + }); + }; + + editor.disableUI = function (featList) { + // $(function () { + // $('#tool_wireframe, #tool_image, #main_button, #tool_source, #sidepanels').remove(); + // $('#tools_top').css('left', 5); + // }); + }; + + editor.loadFromURL = function (url, opts) { + if (!opts) { opts = {}; } + + var cache = opts.cache; + var cb = opts.callback; + + editor.ready(function () { + $.ajax({ + 'url': url, + 'dataType': 'text', + cache: !!cache, + beforeSend: function () { + $.process_cancel(uiStrings.notification.loadingImage); + }, + success: function (str) { + loadSvgString(str, cb); + }, + error: function (xhr, stat, err) { + if (xhr.status !== 404 && xhr.responseText) { + loadSvgString(xhr.responseText, cb); + } else { + $.alert(uiStrings.notification.URLloadFail + ': \n' + err, cb); + } + }, + complete: function () { + $('#dialog_box').hide(); + } + }); + }); + }; + + editor.loadFromDataURI = function (str) { + editor.ready(function () { + var base64 = false; + var pre = str.match(/^data:image\/svg\+xml;base64,/); + if (pre) { + base64 = true; + } else { + pre = str.match(/^data:image\/svg\+xml(?:;(?:utf8)?)?,/); + } + if (pre) { + pre = pre[0]; + } + var src = str.slice(pre.length); + loadSvgString(base64 ? Utils.decode64(src) : decodeURIComponent(src)); + }); + }; + + editor.addExtension = function () { + var args = arguments; + + // Note that we don't want this on editor.ready since some extensions + // may want to run before then (like server_opensave). + $(function () { + if (svgCanvas) { svgCanvas.addExtension.apply(this, args); } + }); + }; + + return editor; }(jQuery)); // Run init once DOM is loaded diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 2ed645d8..ca72c1f0 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -30,14 +30,14 @@ (function () { if (!window.console) { - window.console = {}; - window.console.log = function (str) {}; - window.console.dir = function (str) {}; + window.console = {}; + window.console.log = function (str) {}; + window.console.dir = function (str) {}; } if (window.opera) { - window.console.log = function (str) { opera.postError(str); }; - window.console.dir = function (str) {}; + window.console.log = function (str) { opera.postError(str); }; + window.console.dir = function (str) {}; } }()); @@ -54,14 +54,14 @@ var NS = svgedit.NS; // Default configuration options var curConfig = { - show_outside_canvas: true, - selectNew: true, - dimensions: [640, 480] + show_outside_canvas: true, + selectNew: true, + dimensions: [640, 480] }; // Update config with new one if given if (config) { - $.extend(curConfig, config); + $.extend(curConfig, config); } // Array with width/height of canvas @@ -75,22 +75,22 @@ var svgdoc = container.ownerDocument; // This is a container for the document being edited, not the document itself. var svgroot = svgdoc.importNode( - svgedit.utilities.text2xml( - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' - ).documentElement, - true + svgedit.utilities.text2xml( + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ).documentElement, + true ); container.appendChild(svgroot); @@ -99,24 +99,24 @@ var svgcontent = svgdoc.createElementNS(NS.SVG, 'svg'); // This function resets the svgcontent element while keeping it in the DOM. var clearSvgContentElement = canvas.clearSvgContentElement = function () { - $(svgcontent).empty(); + $(svgcontent).empty(); - // TODO: Clear out all other attributes first? - $(svgcontent).attr({ - id: 'svgcontent', - width: dimensions[0], - height: dimensions[1], - x: dimensions[0], - y: dimensions[1], - overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden', - xmlns: NS.SVG, - 'xmlns:se': NS.SE, - 'xmlns:xlink': NS.XLINK - }).appendTo(svgroot); + // TODO: Clear out all other attributes first? + $(svgcontent).attr({ + id: 'svgcontent', + width: dimensions[0], + height: dimensions[1], + x: dimensions[0], + y: dimensions[1], + overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden', + xmlns: NS.SVG, + 'xmlns:se': NS.SE, + 'xmlns:xlink': NS.XLINK + }).appendTo(svgroot); - // TODO: make this string optional and set by the client - var comment = svgdoc.createComment(' Created with SVG-edit - https://github.com/SVG-Edit/svgedit'); - svgcontent.appendChild(comment); + // TODO: make this string optional and set by the client + var comment = svgdoc.createComment(' Created with SVG-edit - https://github.com/SVG-Edit/svgedit'); + svgcontent.appendChild(comment); }; clearSvgContentElement(); @@ -129,7 +129,7 @@ var idprefix = 'svg_'; // Parameters: // p - String with the new prefix canvas.setIdPrefix = function (p) { - idprefix = p; + idprefix = p; }; // Current svgedit.draw.Drawing object @@ -140,7 +140,7 @@ canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent, idprefix); // Returns the current Drawing. // @return {svgedit.draw.Drawing} var getCurrentDrawing = canvas.getCurrentDrawing = function () { - return canvas.current_drawing_; + return canvas.current_drawing_; }; // Float displaying the current zoom level (1 = 100%, .5 = 50%, etc) @@ -151,27 +151,27 @@ var currentGroup = null; // Object containing data for the currently selected styles var allProperties = { - shape: { - fill: (curConfig.initFill.color === 'none' ? '' : '#') + curConfig.initFill.color, - fill_paint: null, - fill_opacity: curConfig.initFill.opacity, - stroke: '#' + curConfig.initStroke.color, - stroke_paint: null, - stroke_opacity: curConfig.initStroke.opacity, - stroke_width: curConfig.initStroke.width, - stroke_dasharray: 'none', - stroke_linejoin: 'miter', - stroke_linecap: 'butt', - opacity: curConfig.initOpacity - } + shape: { + fill: (curConfig.initFill.color === 'none' ? '' : '#') + curConfig.initFill.color, + fill_paint: null, + fill_opacity: curConfig.initFill.opacity, + stroke: '#' + curConfig.initStroke.color, + stroke_paint: null, + stroke_opacity: curConfig.initStroke.opacity, + stroke_width: curConfig.initStroke.width, + stroke_dasharray: 'none', + stroke_linejoin: 'miter', + stroke_linecap: 'butt', + opacity: curConfig.initOpacity + } }; allProperties.text = $.extend(true, {}, allProperties.shape); $.extend(allProperties.text, { - fill: '#000000', - stroke_width: curConfig.text.stroke_width, - font_size: curConfig.text.font_size, - font_family: curConfig.text.font_family + fill: '#000000', + stroke_width: curConfig.text.stroke_width, + font_size: curConfig.text.font_size, + font_family: curConfig.text.font_family }); // Current shape style properties @@ -182,27 +182,27 @@ var curShape = allProperties.shape; var selectedElements = []; var getJsonFromSvgElement = this.getJsonFromSvgElement = function (data) { - // Text node - if (data.nodeType === 3) return data.nodeValue; + // Text node + if (data.nodeType === 3) return data.nodeValue; - var retval = { - element: data.tagName, - // namespace: nsMap[data.namespaceURI], - attr: {}, - children: [] - }; + var retval = { + element: data.tagName, + // namespace: nsMap[data.namespaceURI], + attr: {}, + children: [] + }; - // Iterate attributes - for (var i = 0, attr; (attr = data.attributes[i]); i++) { - retval.attr[attr.name] = attr.value; - } + // Iterate attributes + for (var i = 0, attr; (attr = data.attributes[i]); i++) { + retval.attr[attr.name] = attr.value; + } - // Iterate children - for (var i = 0, node; (node = data.childNodes[i]); i++) { - retval.children[i] = getJsonFromSvgElement(node); - } + // Iterate children + for (var i = 0, node; (node = data.childNodes[i]); i++) { + retval.children[i] = getJsonFromSvgElement(node); + } - return retval; + return retval; }; // Function: addSvgElementFromJson @@ -218,46 +218,46 @@ var getJsonFromSvgElement = this.getJsonFromSvgElement = function (data) { // // Returns: The new element var addSvgElementFromJson = this.addSvgElementFromJson = function (data) { - if (typeof data === 'string') return svgdoc.createTextNode(data); + if (typeof data === 'string') return svgdoc.createTextNode(data); - var shape = svgedit.utilities.getElem(data.attr.id); - // if shape is a path but we need to create a rect/ellipse, then remove the path - var currentLayer = getCurrentDrawing().getCurrentLayer(); - if (shape && data.element !== shape.tagName) { - currentLayer.removeChild(shape); - shape = null; - } - if (!shape) { - shape = svgdoc.createElementNS(NS.SVG, data.element); - if (currentLayer) { - (currentGroup || currentLayer).appendChild(shape); - } - } - if (data.curStyles) { - svgedit.utilities.assignAttributes(shape, { - 'fill': curShape.fill, - 'stroke': curShape.stroke, - 'stroke-width': curShape.stroke_width, - 'stroke-dasharray': curShape.stroke_dasharray, - 'stroke-linejoin': curShape.stroke_linejoin, - 'stroke-linecap': curShape.stroke_linecap, - 'stroke-opacity': curShape.stroke_opacity, - 'fill-opacity': curShape.fill_opacity, - 'opacity': curShape.opacity / 2, - 'style': 'pointer-events:inherit' - }, 100); - } - svgedit.utilities.assignAttributes(shape, data.attr, 100); - svgedit.utilities.cleanupElement(shape); + var shape = svgedit.utilities.getElem(data.attr.id); + // if shape is a path but we need to create a rect/ellipse, then remove the path + var currentLayer = getCurrentDrawing().getCurrentLayer(); + if (shape && data.element !== shape.tagName) { + currentLayer.removeChild(shape); + shape = null; + } + if (!shape) { + shape = svgdoc.createElementNS(NS.SVG, data.element); + if (currentLayer) { + (currentGroup || currentLayer).appendChild(shape); + } + } + if (data.curStyles) { + svgedit.utilities.assignAttributes(shape, { + 'fill': curShape.fill, + 'stroke': curShape.stroke, + 'stroke-width': curShape.stroke_width, + 'stroke-dasharray': curShape.stroke_dasharray, + 'stroke-linejoin': curShape.stroke_linejoin, + 'stroke-linecap': curShape.stroke_linecap, + 'stroke-opacity': curShape.stroke_opacity, + 'fill-opacity': curShape.fill_opacity, + 'opacity': curShape.opacity / 2, + 'style': 'pointer-events:inherit' + }, 100); + } + svgedit.utilities.assignAttributes(shape, data.attr, 100); + svgedit.utilities.cleanupElement(shape); - // Children - if (data.children) { - data.children.forEach(function (child) { - shape.appendChild(addSvgElementFromJson(child)); - }); - } + // Children + if (data.children) { + data.children.forEach(function (child) { + shape.appendChild(addSvgElementFromJson(child)); + }); + } - return shape; + return shape; }; // import svgtransformlist.js @@ -274,25 +274,25 @@ var transformListToTransform = canvas.transformListToTransform = svgedit.math.tr // initialize from units.js // send in an object implementing the ElementContainer interface (see units.js) svgedit.units.init({ - getBaseUnit: function () { return curConfig.baseUnit; }, - getElement: svgedit.utilities.getElem, - getHeight: function () { return svgcontent.getAttribute('height') / currentZoom; }, - getWidth: function () { return svgcontent.getAttribute('width') / currentZoom; }, - getRoundDigits: function () { return saveOptions.round_digits; } + getBaseUnit: function () { return curConfig.baseUnit; }, + getElement: svgedit.utilities.getElem, + getHeight: function () { return svgcontent.getAttribute('height') / currentZoom; }, + getWidth: function () { return svgcontent.getAttribute('width') / currentZoom; }, + getRoundDigits: function () { return saveOptions.round_digits; } }); // import from units.js /* var convertToNum = */ canvas.convertToNum = svgedit.units.convertToNum; // import from svgutils.js svgedit.utilities.init({ - getDOMDocument: function () { return svgdoc; }, - getDOMContainer: function () { return container; }, - getSVGRoot: function () { return svgroot; }, - // TODO: replace this mostly with a way to get the current drawing. - getSelectedElements: function () { return selectedElements; }, - getSVGContent: function () { return svgcontent; }, - getBaseUnit: function () { return curConfig.baseUnit; }, - getSnappingStep: function () { return curConfig.snappingStep; } + getDOMDocument: function () { return svgdoc; }, + getDOMContainer: function () { return container; }, + getSVGRoot: function () { return svgroot; }, + // TODO: replace this mostly with a way to get the current drawing. + getSelectedElements: function () { return selectedElements; }, + getSVGContent: function () { return svgcontent; }, + getBaseUnit: function () { return curConfig.baseUnit; }, + getSnappingStep: function () { return curConfig.snappingStep; } }); var findDefs = canvas.findDefs = svgedit.utilities.findDefs; var getUrlFromAttr = canvas.getUrlFromAttr = svgedit.utilities.getUrlFromAttr; @@ -308,16 +308,16 @@ var cleanupElement = this.cleanupElement = svgedit.utilities.cleanupElement; // import from coords.js svgedit.coords.init({ - getDrawing: function () { return getCurrentDrawing(); }, - getGridSnapping: function () { return curConfig.gridSnapping; } + getDrawing: function () { return getCurrentDrawing(); }, + getGridSnapping: function () { return curConfig.gridSnapping; } }); var remapElement = this.remapElement = svgedit.coords.remapElement; // import from recalculate.js svgedit.recalculate.init({ - getSVGRoot: function () { return svgroot; }, - getStartTransform: function () { return startTransform; }, - setStartTransform: function (transform) { startTransform = transform; } + getSVGRoot: function () { return svgroot; }, + getStartTransform: function () { return startTransform; }, + setStartTransform: function (transform) { startTransform = transform; } }); var recalculateDimensions = this.recalculateDimensions = svgedit.recalculate.recalculateDimensions; @@ -334,66 +334,66 @@ var BatchCommand = svgedit.history.BatchCommand; var call; // Implement the svgedit.history.HistoryEventHandler interface. canvas.undoMgr = new svgedit.history.UndoManager({ - handleHistoryEvent: function (eventType, cmd) { - var EventTypes = svgedit.history.HistoryEventTypes; - // TODO: handle setBlurOffsets. - if (eventType === EventTypes.BEFORE_UNAPPLY || eventType === EventTypes.BEFORE_APPLY) { - canvas.clearSelection(); - } else if (eventType === EventTypes.AFTER_APPLY || eventType === EventTypes.AFTER_UNAPPLY) { - var elems = cmd.elements(); - canvas.pathActions.clear(); - call('changed', elems); - var cmdType = cmd.type(); - var isApply = (eventType === EventTypes.AFTER_APPLY); - if (cmdType === MoveElementCommand.type()) { - var parent = isApply ? cmd.newParent : cmd.oldParent; - if (parent === svgcontent) { - canvas.identifyLayers(); - } - } else if (cmdType === InsertElementCommand.type() || - cmdType === RemoveElementCommand.type()) { - if (cmd.parent === svgcontent) { - canvas.identifyLayers(); - } - if (cmdType === InsertElementCommand.type()) { - if (isApply) { restoreRefElems(cmd.elem); } - } else { - if (!isApply) { restoreRefElems(cmd.elem); } - } - if (cmd.elem.tagName === 'use') { - setUseData(cmd.elem); - } - } else if (cmdType === ChangeElementCommand.type()) { - // if we are changing layer names, re-identify all layers - if (cmd.elem.tagName === 'title' && - cmd.elem.parentNode.parentNode === svgcontent - ) { - canvas.identifyLayers(); - } - var values = isApply ? cmd.newValues : cmd.oldValues; - // If stdDeviation was changed, update the blur. - if (values.stdDeviation) { - canvas.setBlurOffsets(cmd.elem.parentNode, values.stdDeviation); - } - // This is resolved in later versions of webkit, perhaps we should - // have a featured detection for correct 'use' behavior? - // —————————— - // 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')) { - // var parent = elem.parentNode; - // var sib = elem.nextSibling; - // parent.removeChild(elem); - // parent.insertBefore(elem, sib); - // } - // } - } - } - } + handleHistoryEvent: function (eventType, cmd) { + var EventTypes = svgedit.history.HistoryEventTypes; + // TODO: handle setBlurOffsets. + if (eventType === EventTypes.BEFORE_UNAPPLY || eventType === EventTypes.BEFORE_APPLY) { + canvas.clearSelection(); + } else if (eventType === EventTypes.AFTER_APPLY || eventType === EventTypes.AFTER_UNAPPLY) { + var elems = cmd.elements(); + canvas.pathActions.clear(); + call('changed', elems); + var cmdType = cmd.type(); + var isApply = (eventType === EventTypes.AFTER_APPLY); + if (cmdType === MoveElementCommand.type()) { + var parent = isApply ? cmd.newParent : cmd.oldParent; + if (parent === svgcontent) { + canvas.identifyLayers(); + } + } else if (cmdType === InsertElementCommand.type() || + cmdType === RemoveElementCommand.type()) { + if (cmd.parent === svgcontent) { + canvas.identifyLayers(); + } + if (cmdType === InsertElementCommand.type()) { + if (isApply) { restoreRefElems(cmd.elem); } + } else { + if (!isApply) { restoreRefElems(cmd.elem); } + } + if (cmd.elem.tagName === 'use') { + setUseData(cmd.elem); + } + } else if (cmdType === ChangeElementCommand.type()) { + // if we are changing layer names, re-identify all layers + if (cmd.elem.tagName === 'title' && + cmd.elem.parentNode.parentNode === svgcontent + ) { + canvas.identifyLayers(); + } + var values = isApply ? cmd.newValues : cmd.oldValues; + // If stdDeviation was changed, update the blur. + if (values.stdDeviation) { + canvas.setBlurOffsets(cmd.elem.parentNode, values.stdDeviation); + } + // This is resolved in later versions of webkit, perhaps we should + // have a featured detection for correct 'use' behavior? + // —————————— + // 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')) { + // var parent = elem.parentNode; + // var sib = elem.nextSibling; + // parent.removeChild(elem); + // parent.insertBefore(elem, sib); + // } + // } + } + } + } }); var addCommandToHistory = function (cmd) { - canvas.undoMgr.addCommandToHistory(cmd); + canvas.undoMgr.addCommandToHistory(cmd); }; /** @@ -402,33 +402,33 @@ var addCommandToHistory = function (cmd) { * @returns {svgedit.history.HistoryRecordingService} */ function historyRecordingService (hrService) { - return hrService || new svgedit.history.HistoryRecordingService(canvas.undoMgr); + return hrService || new svgedit.history.HistoryRecordingService(canvas.undoMgr); } // import from select.js svgedit.select.init(curConfig, { - createSVGElement: function (jsonMap) { return canvas.addSvgElementFromJson(jsonMap); }, - svgRoot: function () { return svgroot; }, - svgContent: function () { return svgcontent; }, - currentZoom: function () { return currentZoom; }, - // TODO(codedread): Remove when getStrokedBBox() has been put into svgutils.js. - getStrokedBBox: function (elems) { return canvas.getStrokedBBox([elems]); } + createSVGElement: function (jsonMap) { return canvas.addSvgElementFromJson(jsonMap); }, + svgRoot: function () { return svgroot; }, + svgContent: function () { return svgcontent; }, + currentZoom: function () { return currentZoom; }, + // TODO(codedread): Remove when getStrokedBBox() has been put into svgutils.js. + getStrokedBBox: function (elems) { return canvas.getStrokedBBox([elems]); } }); // this object manages selectors for us var selectorManager = this.selectorManager = svgedit.select.getSelectorManager(); // Import from path.js svgedit.path.init({ - getCurrentZoom: function () { return currentZoom; }, - getSVGRoot: function () { return svgroot; } + getCurrentZoom: function () { return currentZoom; }, + getSVGRoot: function () { return svgroot; } }); // Interface strings, usually for title elements var uiStrings = { - exportNoBlur: 'Blurred elements will appear as un-blurred', - exportNoforeignObject: 'foreignObject elements will not appear', - exportNoDashArray: 'Strokes will appear filled', - exportNoText: 'Text may not appear as expected' + exportNoBlur: 'Blurred elements will appear as un-blurred', + exportNoforeignObject: 'foreignObject elements will not appear', + exportNoDashArray: 'Strokes will appear filled', + exportNoText: 'Text may not appear as expected' }; var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use'; @@ -439,35 +439,35 @@ var elData = $.data; // Animation element to change the opacity of any newly created element var opacAni = document.createElementNS(NS.SVG, 'animate'); $(opacAni).attr({ - attributeName: 'opacity', - begin: 'indefinite', - dur: 1, - fill: 'freeze' + attributeName: 'opacity', + begin: 'indefinite', + dur: 1, + fill: 'freeze' }).appendTo(svgroot); var restoreRefElems = function (elem) { - // Look for missing reference elements, restore any found - var o, i, l, - attrs = $(elem).attr(refAttrs); - for (o in attrs) { - var val = attrs[o]; - if (val && val.indexOf('url(') === 0) { - var id = svgedit.utilities.getUrlFromAttr(val).substr(1); - var ref = getElem(id); - if (!ref) { - svgedit.utilities.findDefs().appendChild(removedElements[id]); - delete removedElements[id]; - } - } - } + // Look for missing reference elements, restore any found + var o, i, l, + attrs = $(elem).attr(refAttrs); + for (o in attrs) { + var val = attrs[o]; + if (val && val.indexOf('url(') === 0) { + var id = svgedit.utilities.getUrlFromAttr(val).substr(1); + var ref = getElem(id); + if (!ref) { + svgedit.utilities.findDefs().appendChild(removedElements[id]); + delete removedElements[id]; + } + } + } - var childs = elem.getElementsByTagName('*'); + var childs = elem.getElementsByTagName('*'); - if (childs.length) { - for (i = 0, l = childs.length; i < l; i++) { - restoreRefElems(childs[i]); - } - } + if (childs.length) { + for (i = 0, l = childs.length; i < l; i++) { + restoreRefElems(childs[i]); + } + } }; // (function () { @@ -482,70 +482,70 @@ var restoreRefElems = function (elem) { // Object to contain image data for raster images that were found encodable var encodableImages = {}, - // String with image URL of last loadable image - lastGoodImgUrl = curConfig.imgPath + 'logo.png', + // String with image URL of last loadable image + lastGoodImgUrl = curConfig.imgPath + 'logo.png', - // Array with current disabled elements (for in-group editing) - disabledElems = [], + // Array with current disabled elements (for in-group editing) + disabledElems = [], - // Object with save options - saveOptions = {round_digits: 5}, + // Object with save options + saveOptions = {round_digits: 5}, - // Boolean indicating whether or not a draw action has been started - started = false, + // Boolean indicating whether or not a draw action has been started + started = false, - // String with an element's initial transform attribute value - startTransform = null, + // String with an element's initial transform attribute value + startTransform = null, - // String indicating the current editor mode - currentMode = 'select', + // String indicating the current editor mode + currentMode = 'select', - // String with the current direction in which an element is being resized - currentResizeMode = 'none', + // String with the current direction in which an element is being resized + currentResizeMode = 'none', - // Object with IDs for imported files, to see if one was already added - importIds = {}, + // Object with IDs for imported files, to see if one was already added + importIds = {}, - // Current text style properties - curText = allProperties.text, + // Current text style properties + curText = allProperties.text, - // Current general properties - curProperties = curShape, + // Current general properties + curProperties = curShape, - // Array with selected elements' Bounding box object - // selectedBBoxes = new Array(1), + // Array with selected elements' Bounding box object + // selectedBBoxes = new Array(1), - // The DOM element that was just selected - justSelected = null, + // The DOM element that was just selected + justSelected = null, - // DOM element for selection rectangle drawn by the user - rubberBox = null, + // DOM element for selection rectangle drawn by the user + rubberBox = null, - // Array of current BBoxes, used in getIntersectionList(). - curBBoxes = [], + // Array of current BBoxes, used in getIntersectionList(). + curBBoxes = [], - // Object to contain all included extensions - extensions = {}, + // Object to contain all included extensions + extensions = {}, - // Canvas point for the most recent right click - lastClickPoint = null, + // Canvas point for the most recent right click + lastClickPoint = null, - // Map of deleted reference elements - removedElements = {}; + // Map of deleted reference elements + removedElements = {}; // Should this return an array by default, so extension results aren't overwritten? var runExtensions = this.runExtensions = function (action, vars, returnArray) { - var result = returnArray ? [] : false; - $.each(extensions, function (name, opts) { - if (opts && action in opts) { - if (returnArray) { - result.push(opts[action](vars)); - } else { - result = opts[action](vars); - } - } - }); - return result; + var result = returnArray ? [] : false; + $.each(extensions, function (name, opts) { + if (opts && action in opts) { + if (returnArray) { + result.push(opts[action](vars)); + } else { + result = opts[action](vars); + } + } + }); + return result; }; // Function: addExtension @@ -555,29 +555,29 @@ var runExtensions = this.runExtensions = function (action, vars, returnArray) { // name - String with the ID of the extension // extFunc - Function supplied by the extension with its data this.addExtension = function (name, extFunc) { - var ext; - if (!(name in extensions)) { - // Provide private vars/funcs here. Is there a better way to do this? - if ($.isFunction(extFunc)) { - ext = extFunc($.extend(canvas.getPrivateMethods(), { - svgroot: svgroot, - svgcontent: svgcontent, - nonce: getCurrentDrawing().getNonce(), - selectorManager: selectorManager - })); - } else { - ext = extFunc; - } - extensions[name] = ext; - call('extension_added', ext); - } else { - console.log('Cannot add extension "' + name + '", an extension by that name already exists.'); - } + var ext; + if (!(name in extensions)) { + // Provide private vars/funcs here. Is there a better way to do this? + if ($.isFunction(extFunc)) { + ext = extFunc($.extend(canvas.getPrivateMethods(), { + svgroot: svgroot, + svgcontent: svgcontent, + nonce: getCurrentDrawing().getNonce(), + selectorManager: selectorManager + })); + } else { + ext = extFunc; + } + extensions[name] = ext; + call('extension_added', ext); + } else { + 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 currentZoom var round = this.round = function (val) { - return parseInt(val * currentZoom, 10) / currentZoom; + return parseInt(val * currentZoom, 10) / currentZoom; }; // This method sends back an array or a NodeList full of elements that @@ -588,58 +588,58 @@ var round = this.round = function (val) { // Reference: // Firefox does not implement getIntersectionList(), see https://bugzilla.mozilla.org/show_bug.cgi?id=501421 var getIntersectionList = this.getIntersectionList = function (rect) { - if (rubberBox == null) { return null; } + if (rubberBox == null) { return null; } - var parent = currentGroup || getCurrentDrawing().getCurrentLayer(); + var parent = currentGroup || getCurrentDrawing().getCurrentLayer(); - var rubberBBox; - if (!rect) { - rubberBBox = rubberBox.getBBox(); - var o, bb = svgcontent.createSVGRect(); + var rubberBBox; + if (!rect) { + rubberBBox = rubberBox.getBBox(); + var o, bb = svgcontent.createSVGRect(); - for (o in rubberBBox) { - bb[o] = rubberBBox[o] / currentZoom; - } - rubberBBox = bb; - } else { - rubberBBox = svgcontent.createSVGRect(); - rubberBBox.x = rect.x; - rubberBBox.y = rect.y; - rubberBBox.width = rect.width; - rubberBBox.height = rect.height; - } + for (o in rubberBBox) { + bb[o] = rubberBBox[o] / currentZoom; + } + rubberBBox = bb; + } else { + rubberBBox = svgcontent.createSVGRect(); + rubberBBox.x = rect.x; + rubberBBox.y = rect.y; + rubberBBox.width = rect.width; + rubberBBox.height = rect.height; + } - var resultList = null; - if (!svgedit.browser.isIE) { - if (typeof svgroot.getIntersectionList === 'function') { - // Offset the bbox of the rubber box by the offset of the svgcontent element. - rubberBBox.x += parseInt(svgcontent.getAttribute('x'), 10); - rubberBBox.y += parseInt(svgcontent.getAttribute('y'), 10); + var resultList = null; + if (!svgedit.browser.isIE) { + if (typeof svgroot.getIntersectionList === 'function') { + // Offset the bbox of the rubber box by the offset of the svgcontent element. + rubberBBox.x += parseInt(svgcontent.getAttribute('x'), 10); + rubberBBox.y += parseInt(svgcontent.getAttribute('y'), 10); - resultList = svgroot.getIntersectionList(rubberBBox, parent); - } - } + resultList = svgroot.getIntersectionList(rubberBBox, parent); + } + } - if (resultList == null || typeof resultList.item !== 'function') { - resultList = []; + if (resultList == null || typeof resultList.item !== 'function') { + resultList = []; - if (!curBBoxes.length) { - // Cache all bboxes - curBBoxes = getVisibleElementsAndBBoxes(parent); - } - var i = curBBoxes.length; - while (i--) { - if (!rubberBBox.width) { continue; } - if (svgedit.math.rectsIntersect(rubberBBox, curBBoxes[i].bbox)) { - resultList.push(curBBoxes[i].elem); - } - } - } + if (!curBBoxes.length) { + // Cache all bboxes + curBBoxes = getVisibleElementsAndBBoxes(parent); + } + var i = curBBoxes.length; + while (i--) { + if (!rubberBBox.width) { continue; } + if (svgedit.math.rectsIntersect(rubberBBox, curBBoxes[i].bbox)) { + resultList.push(curBBoxes[i].elem); + } + } + } - // 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; + // 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; }; // TODO(codedread): Migrate this into svgutils.js @@ -652,8 +652,8 @@ var getIntersectionList = this.getIntersectionList = function (rect) { // Returns: // A single bounding box object var getStrokedBBox = this.getStrokedBBox = function (elems) { - if (!elems) { elems = getVisibleElements(); } - return svgedit.utilities.getStrokedBBox(elems, addSvgElementFromJson, pathActions); + if (!elems) { elems = getVisibleElements(); } + return svgedit.utilities.getStrokedBBox(elems, addSvgElementFromJson, pathActions); }; // Function: getVisibleElements @@ -667,17 +667,17 @@ var getStrokedBBox = this.getStrokedBBox = function (elems) { // Returns: // An array with all "visible" elements. var getVisibleElements = this.getVisibleElements = function (parent) { - if (!parent) { - parent = $(svgcontent).children(); // Prevent layers from being included - } + if (!parent) { + parent = $(svgcontent).children(); // Prevent layers from being included + } - var contentElems = []; - $(parent).children().each(function (i, elem) { - if (elem.getBBox) { - contentElems.push(elem); - } - }); - return contentElems.reverse(); + var contentElems = []; + $(parent).children().each(function (i, elem) { + if (elem.getBBox) { + contentElems.push(elem); + } + }); + return contentElems.reverse(); }; // Function: getVisibleElementsAndBBoxes @@ -693,16 +693,16 @@ var getVisibleElements = this.getVisibleElements = function (parent) { // * elem - The element // * 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) { - if (elem.getBBox) { - contentElems.push({elem: elem, bbox: getStrokedBBox([elem])}); - } - }); - return contentElems.reverse(); + if (!parent) { + parent = $(svgcontent).children(); // Prevent layers from being included + } + var contentElems = []; + $(parent).children().each(function (i, elem) { + if (elem.getBBox) { + contentElems.push({elem: elem, bbox: getStrokedBBox([elem])}); + } + }); + return contentElems.reverse(); }; // Function: groupSvgElem @@ -711,9 +711,9 @@ var getVisibleElementsAndBBoxes = this.getVisibleElementsAndBBoxes = function (p // Parameters: // elem - SVG element to wrap var groupSvgElem = this.groupSvgElem = function (elem) { - var g = document.createElementNS(NS.SVG, 'g'); - elem.parentNode.replaceChild(g, elem); - $(g).append(elem).data('gsvg', elem)[0].id = getNextId(); + var g = document.createElementNS(NS.SVG, 'g'); + elem.parentNode.replaceChild(g, elem); + $(g).append(elem).data('gsvg', elem)[0].id = getNextId(); }; // Set scope for these functions @@ -734,9 +734,9 @@ getNextId = c.getNextId = function () { return getCurrentDrawing().getNextId(); // event - String with the event name // arg - Argument to pass through to the callback function call = c.call = function (event, arg) { - if (events[event]) { - return events[event](this, arg); - } + if (events[event]) { + return events[event](this, arg); + } }; // Function: bind @@ -749,9 +749,9 @@ call = c.call = function (event, arg) { // Return: // The previous event c.bind = function (event, f) { - var old = events[event]; - events[event] = f; - return old; + var old = events[event]; + events[event] = f; + return old; }; }(canvas)); @@ -761,16 +761,16 @@ c.bind = function (event, f) { // Parameters: // newDoc - The SVG DOM document this.prepareSvg = function (newDoc) { - this.sanitizeSvg(newDoc.documentElement); + this.sanitizeSvg(newDoc.documentElement); - // convert paths into absolute commands - var i, path, len, - paths = newDoc.getElementsByTagNameNS(NS.SVG, 'path'); - for (i = 0, len = paths.length; i < len; ++i) { - path = paths[i]; - path.setAttribute('d', pathActions.convertPath(path)); - pathActions.fixEnd(path); - } + // convert paths into absolute commands + var i, path, len, + paths = newDoc.getElementsByTagNameNS(NS.SVG, 'path'); + for (i = 0, len = paths.length; i < len; ++i) { + path = paths[i]; + path.setAttribute('d', pathActions.convertPath(path)); + pathActions.fixEnd(path); + } }; // Function: ffClone @@ -783,14 +783,14 @@ this.prepareSvg = function (newDoc) { // Parameters: // elem - The (text) DOM element to clone var ffClone = function (elem) { - if (!svgedit.browser.isGecko()) { return elem; } - var clone = elem.cloneNode(true); - elem.parentNode.insertBefore(clone, elem); - elem.parentNode.removeChild(elem); - selectorManager.releaseSelector(elem); - selectedElements[0] = clone; - selectorManager.requestSelector(clone).showGrips(true); - return clone; + if (!svgedit.browser.isGecko()) { return elem; } + var clone = elem.cloneNode(true); + elem.parentNode.insertBefore(clone, elem); + elem.parentNode.removeChild(elem); + selectorManager.releaseSelector(elem); + selectedElements[0] = clone; + selectorManager.requestSelector(clone).showGrips(true); + return clone; }; // this.each is deprecated, if any extension used this it can be recreated by doing this: @@ -808,78 +808,78 @@ var ffClone = function (elem) { // val - The new rotation angle in degrees // preventUndo - Boolean indicating whether the action should be undoable or not this.setRotationAngle = function (val, preventUndo) { - // ensure val is the proper type - val = parseFloat(val); - var elem = selectedElements[0]; - var oldTransform = elem.getAttribute('transform'); - var bbox = svgedit.utilities.getBBox(elem); - var cx = bbox.x + bbox.width / 2, cy = bbox.y + bbox.height / 2; - var tlist = svgedit.transformlist.getTransformList(elem); + // ensure val is the proper type + val = parseFloat(val); + var elem = selectedElements[0]; + var oldTransform = elem.getAttribute('transform'); + var bbox = svgedit.utilities.getBBox(elem); + var cx = bbox.x + bbox.width / 2, cy = bbox.y + bbox.height / 2; + var tlist = svgedit.transformlist.getTransformList(elem); - // only remove the real rotational transform if present (i.e. at index=0) - if (tlist.numberOfItems > 0) { - var xform = tlist.getItem(0); - if (xform.type === 4) { - tlist.removeItem(0); - } - } - // find Rnc and insert it - if (val !== 0) { - var center = svgedit.math.transformPoint(cx, cy, svgedit.math.transformListToTransform(tlist).matrix); - var Rnc = svgroot.createSVGTransform(); - Rnc.setRotate(val, center.x, center.y); - if (tlist.numberOfItems) { - tlist.insertItemBefore(Rnc, 0); - } else { - tlist.appendItem(Rnc); - } - } else if (tlist.numberOfItems === 0) { - elem.removeAttribute('transform'); - } + // only remove the real rotational transform if present (i.e. at index=0) + if (tlist.numberOfItems > 0) { + var xform = tlist.getItem(0); + if (xform.type === 4) { + tlist.removeItem(0); + } + } + // find Rnc and insert it + if (val !== 0) { + var center = svgedit.math.transformPoint(cx, cy, svgedit.math.transformListToTransform(tlist).matrix); + var Rnc = svgroot.createSVGTransform(); + Rnc.setRotate(val, center.x, center.y); + if (tlist.numberOfItems) { + tlist.insertItemBefore(Rnc, 0); + } else { + tlist.appendItem(Rnc); + } + } 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? - var newTransform = elem.getAttribute('transform'); - elem.setAttribute('transform', oldTransform); - changeSelectedAttribute('transform', newTransform, selectedElements); - call('changed', selectedElements); - } - // var pointGripContainer = svgedit.utilities.getElem('pathpointgrip_container'); - // if (elem.nodeName === 'path' && pointGripContainer) { - // pathActions.setPointContainerTransform(elem.getAttribute('transform')); - // } - var selector = selectorManager.requestSelector(selectedElements[0]); - selector.resize(); - selector.updateGripCursors(val); + 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? + var newTransform = elem.getAttribute('transform'); + elem.setAttribute('transform', oldTransform); + changeSelectedAttribute('transform', newTransform, selectedElements); + call('changed', selectedElements); + } + // var pointGripContainer = svgedit.utilities.getElem('pathpointgrip_container'); + // if (elem.nodeName === 'path' && pointGripContainer) { + // pathActions.setPointContainerTransform(elem.getAttribute('transform')); + // } + var selector = selectorManager.requestSelector(selectedElements[0]); + selector.resize(); + selector.updateGripCursors(val); }; // Function: recalculateAllSelectedDimensions // Runs recalculateDimensions on the selected elements, // adding the changes to a single batch command var recalculateAllSelectedDimensions = this.recalculateAllSelectedDimensions = function () { - var text = (currentResizeMode === 'none' ? 'position' : 'size'); - var batchCmd = new svgedit.history.BatchCommand(text); + var text = (currentResizeMode === 'none' ? 'position' : 'size'); + var batchCmd = new svgedit.history.BatchCommand(text); - var i = selectedElements.length; - while (i--) { - var elem = selectedElements[i]; - // if (svgedit.utilities.getRotationAngle(elem) && !svgedit.math.hasMatrixTransform(getTransformList(elem))) { continue; } - var cmd = svgedit.recalculate.recalculateDimensions(elem); - if (cmd) { - batchCmd.addSubCommand(cmd); - } - } + var i = selectedElements.length; + while (i--) { + var elem = selectedElements[i]; + // if (svgedit.utilities.getRotationAngle(elem) && !svgedit.math.hasMatrixTransform(getTransformList(elem))) { continue; } + var cmd = svgedit.recalculate.recalculateDimensions(elem); + if (cmd) { + batchCmd.addSubCommand(cmd); + } + } - if (!batchCmd.isEmpty()) { - addCommandToHistory(batchCmd); - call('changed', selectedElements); - } + if (!batchCmd.isEmpty()) { + addCommandToHistory(batchCmd); + call('changed', selectedElements); + } }; // 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]); + console.log([m.a, m.b, m.c, m.d, m.e, m.f]); }; // Root Current Transformation Matrix in user units @@ -892,14 +892,14 @@ var rootSctm = null; // Parameters: // noCall - Optional boolean that when true does not call the "selected" handler var clearSelection = this.clearSelection = function (noCall) { - selectedElements.map(function (elem) { - if (elem == null) return; + selectedElements.map(function (elem) { + if (elem == null) return; - selectorManager.releaseSelector(elem); - }); - selectedElements = []; + selectorManager.releaseSelector(elem); + }); + selectedElements = []; - if (!noCall) { call('selected', selectedElements); } + if (!noCall) { call('selected', selectedElements); } }; // TODO: do we need to worry about selectedBBoxes here? @@ -911,68 +911,68 @@ var clearSelection = this.clearSelection = function (noCall) { // elemsToAdd - an array of DOM elements to add to the selection // showGrips - a boolean flag indicating whether the resize grips should be shown var addToSelection = this.addToSelection = function (elemsToAdd, showGrips) { - if (elemsToAdd.length === 0) { return; } - // find the first null in our selectedElements array - var j = 0; + if (elemsToAdd.length === 0) { return; } + // find the first null in our selectedElements array + var j = 0; - while (j < selectedElements.length) { - if (selectedElements[j] == null) { - break; - } - ++j; - } + while (j < selectedElements.length) { + if (selectedElements[j] == null) { + break; + } + ++j; + } - // now add each element consecutively - var i = elemsToAdd.length; - while (i--) { - var elem = elemsToAdd[i]; - if (!elem) { continue; } - var bbox = svgedit.utilities.getBBox(elem); - if (!bbox) { continue; } + // now add each element consecutively + var i = elemsToAdd.length; + while (i--) { + var elem = elemsToAdd[i]; + if (!elem) { continue; } + var bbox = svgedit.utilities.getBBox(elem); + if (!bbox) { continue; } - if (elem.tagName === 'a' && elem.childNodes.length === 1) { - // Make "a" element's child be the selected element - elem = elem.firstChild; - } + if (elem.tagName === 'a' && elem.childNodes.length === 1) { + // Make "a" element's child be the selected element + elem = elem.firstChild; + } - // if it's not already there, add it - if (selectedElements.indexOf(elem) === -1) { - selectedElements[j] = elem; + // if it's not already there, add it + if (selectedElements.indexOf(elem) === -1) { + selectedElements[j] = elem; - // only the first selectedBBoxes element is ever used in the codebase these days - // if (j === 0) selectedBBoxes[0] = svgedit.utilities.getBBox(elem); - j++; - var sel = selectorManager.requestSelector(elem, bbox); + // only the first selectedBBoxes element is ever used in the codebase these days + // if (j === 0) selectedBBoxes[0] = svgedit.utilities.getBBox(elem); + j++; + var sel = selectorManager.requestSelector(elem, bbox); - if (selectedElements.length > 1) { - sel.showGrips(false); - } - } - } - call('selected', selectedElements); + if (selectedElements.length > 1) { + sel.showGrips(false); + } + } + } + call('selected', selectedElements); - if (showGrips || selectedElements.length === 1) { - selectorManager.requestSelector(selectedElements[0]).showGrips(true); - } else { - selectorManager.requestSelector(selectedElements[0]).showGrips(false); - } + if (showGrips || selectedElements.length === 1) { + selectorManager.requestSelector(selectedElements[0]).showGrips(true); + } else { + selectorManager.requestSelector(selectedElements[0]).showGrips(false); + } - // make sure the elements are in the correct order - // See: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition + // make sure the elements are in the correct order + // See: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition - selectedElements.sort(function (a, b) { - if (a && b && a.compareDocumentPosition) { - return 3 - (b.compareDocumentPosition(a) & 6); - } - if (a == null) { - return 1; - } - }); + selectedElements.sort(function (a, b) { + if (a && b && a.compareDocumentPosition) { + return 3 - (b.compareDocumentPosition(a) & 6); + } + if (a == null) { + return 1; + } + }); - // Make sure first elements are not null - while (selectedElements[0] == null) { - selectedElements.shift(0); - } + // Make sure first elements are not null + while (selectedElements[0] == null) { + selectedElements.shift(0); + } }; // Function: selectOnly() @@ -981,8 +981,8 @@ var addToSelection = this.addToSelection = function (elemsToAdd, showGrips) { // Parameters: // elems - an array of DOM elements to be selected var selectOnly = this.selectOnly = function (elems, showGrips) { - clearSelection(true); - addToSelection(elems, showGrips); + clearSelection(true); + addToSelection(elems, showGrips); }; // TODO: could use slice here to make this faster? @@ -994,39 +994,39 @@ var selectOnly = this.selectOnly = function (elems, showGrips) { // Parameters: // elemsToRemove - an array of elements to remove from selection /* var removeFromSelection = */ this.removeFromSelection = function (elemsToRemove) { - if (selectedElements[0] == null) { return; } - if (elemsToRemove.length === 0) { return; } + if (selectedElements[0] == null) { return; } + if (elemsToRemove.length === 0) { return; } - // find every element and remove it from our array copy - var i, - j = 0, - newSelectedItems = [], - len = selectedElements.length; - newSelectedItems.length = len; - for (i = 0; i < len; ++i) { - var elem = selectedElements[i]; - if (elem) { - // keep the item - if (elemsToRemove.indexOf(elem) === -1) { - newSelectedItems[j] = elem; - j++; - } else { // remove the item and its selector - selectorManager.releaseSelector(elem); - } - } - } - // the copy becomes the master now - selectedElements = newSelectedItems; + // find every element and remove it from our array copy + var i, + j = 0, + newSelectedItems = [], + len = selectedElements.length; + newSelectedItems.length = len; + for (i = 0; i < len; ++i) { + var elem = selectedElements[i]; + if (elem) { + // keep the item + if (elemsToRemove.indexOf(elem) === -1) { + newSelectedItems[j] = elem; + j++; + } else { // remove the item and its selector + selectorManager.releaseSelector(elem); + } + } + } + // the copy becomes the master now + selectedElements = newSelectedItems; }; // Function: selectAllInCurrentLayer // Clears the selection, then adds all elements in the current layer to the selection. this.selectAllInCurrentLayer = function () { - var currentLayer = getCurrentDrawing().getCurrentLayer(); - if (currentLayer) { - currentMode = 'select'; - selectOnly($(currentGroup || currentLayer).children()); - } + var currentLayer = getCurrentDrawing().getCurrentLayer(); + if (currentLayer) { + currentMode = 'select'; + selectOnly($(currentGroup || currentLayer).children()); + } }; // Function: getMouseTarget @@ -1038,57 +1038,57 @@ this.selectAllInCurrentLayer = function () { // Returns: // DOM element we want var getMouseTarget = this.getMouseTarget = function (evt) { - if (evt == null) { - return null; - } - var mouseTarget = evt.target; + if (evt == null) { + return null; + } + var mouseTarget = evt.target; - // if it was a , Opera and WebKit return the SVGElementInstance - if (mouseTarget.correspondingUseElement) { mouseTarget = mouseTarget.correspondingUseElement; } + // if it was a , Opera and WebKit return the SVGElementInstance + if (mouseTarget.correspondingUseElement) { mouseTarget = mouseTarget.correspondingUseElement; } - // for foreign content, go up until we find the foreignObject - // WebKit browsers set the mouse target to the svgcanvas div - if ([NS.MATH, NS.HTML].indexOf(mouseTarget.namespaceURI) >= 0 && - mouseTarget.id !== 'svgcanvas' - ) { - while (mouseTarget.nodeName !== 'foreignObject') { - mouseTarget = mouseTarget.parentNode; - if (!mouseTarget) { return svgroot; } - } - } + // for foreign content, go up until we find the foreignObject + // WebKit browsers set the mouse target to the svgcanvas div + if ([NS.MATH, NS.HTML].indexOf(mouseTarget.namespaceURI) >= 0 && + mouseTarget.id !== 'svgcanvas' + ) { + while (mouseTarget.nodeName !== 'foreignObject') { + mouseTarget = mouseTarget.parentNode; + if (!mouseTarget) { return svgroot; } + } + } - // Get the desired mouseTarget with jQuery selector-fu - // If it's root-like, select the root - var currentLayer = getCurrentDrawing().getCurrentLayer(); - if ([svgroot, container, svgcontent, currentLayer].indexOf(mouseTarget) >= 0) { - return svgroot; - } + // Get the desired mouseTarget with jQuery selector-fu + // If it's root-like, select the root + var currentLayer = getCurrentDrawing().getCurrentLayer(); + if ([svgroot, container, svgcontent, currentLayer].indexOf(mouseTarget) >= 0) { + return svgroot; + } - var $target = $(mouseTarget); + var $target = $(mouseTarget); - // If it's a selection grip, return the grip parent - if ($target.closest('#selectorParentGroup').length) { - // While we could instead have just returned mouseTarget, - // this makes it easier to indentify as being a selector grip - return selectorManager.selectorParentGroup; - } + // If it's a selection grip, return the grip parent + if ($target.closest('#selectorParentGroup').length) { + // While we could instead have just returned mouseTarget, + // this makes it easier to indentify as being a selector grip + return selectorManager.selectorParentGroup; + } - while (mouseTarget.parentNode !== (currentGroup || currentLayer)) { - mouseTarget = mouseTarget.parentNode; - } + while (mouseTarget.parentNode !== (currentGroup || currentLayer)) { + mouseTarget = mouseTarget.parentNode; + } - // - // // go up until we hit a child of a layer - // while (mouseTarget.parentNode.parentNode.tagName == 'g') { - // mouseTarget = mouseTarget.parentNode; - // } - // Webkit bubbles the mouse event all the way up to the div, so we - // set the mouseTarget to the svgroot like the other browsers - // if (mouseTarget.nodeName.toLowerCase() == 'div') { - // mouseTarget = svgroot; - // } + // + // // go up until we hit a child of a layer + // while (mouseTarget.parentNode.parentNode.tagName == 'g') { + // mouseTarget = mouseTarget.parentNode; + // } + // Webkit bubbles the mouse event all the way up to the div, so we + // set the mouseTarget to the svgroot like the other browsers + // if (mouseTarget.nodeName.toLowerCase() == 'div') { + // mouseTarget = svgroot; + // } - return mouseTarget; + return mouseTarget; }; var drawnPath = null; @@ -1096,853 +1096,853 @@ var drawnPath = null; // Mouse events (function () { var dAttr = null, - startX = null, - startY = null, - rStartX = null, - rStartY = null, - initBbox = {}, - freehand = { - minx: null, - miny: null, - maxx: null, - maxy: null - }, - sumDistance = 0, - controllPoint2 = {x: 0, y: 0}, - controllPoint1 = {x: 0, y: 0}, - start = {x: 0, y: 0}, - end = {x: 0, y: 0}, - parameter, - nextParameter, - bSpline = {x: 0, y: 0}, - nextPos = {x: 0, y: 0}, - THRESHOLD_DIST = 0.8, - STEP_COUNT = 10; + startX = null, + startY = null, + rStartX = null, + rStartY = null, + initBbox = {}, + freehand = { + minx: null, + miny: null, + maxx: null, + maxy: null + }, + sumDistance = 0, + controllPoint2 = {x: 0, y: 0}, + controllPoint1 = {x: 0, y: 0}, + start = {x: 0, y: 0}, + end = {x: 0, y: 0}, + parameter, + nextParameter, + bSpline = {x: 0, y: 0}, + nextPos = {x: 0, y: 0}, + THRESHOLD_DIST = 0.8, + STEP_COUNT = 10; var getBsplinePoint = function (t) { - var spline = {x: 0, y: 0}, - p0 = controllPoint2, - p1 = controllPoint1, - p2 = start, - p3 = end, - S = 1.0 / 6.0, - t2 = t * t, - t3 = t2 * t; + var spline = {x: 0, y: 0}, + p0 = controllPoint2, + p1 = controllPoint1, + p2 = start, + p3 = end, + S = 1.0 / 6.0, + t2 = t * t, + t3 = t2 * t; - var m = [ - [-1, 3, -3, 1], - [3, -6, 3, 0], - [-3, 0, 3, 0], - [1, 4, 1, 0] - ]; + var m = [ + [-1, 3, -3, 1], + [3, -6, 3, 0], + [-3, 0, 3, 0], + [1, 4, 1, 0] + ]; - spline.x = S * ( - (p0.x * m[0][0] + p1.x * m[0][1] + p2.x * m[0][2] + p3.x * m[0][3]) * t3 + - (p0.x * m[1][0] + p1.x * m[1][1] + p2.x * m[1][2] + p3.x * m[1][3]) * t2 + - (p0.x * m[2][0] + p1.x * m[2][1] + p2.x * m[2][2] + p3.x * m[2][3]) * t + - (p0.x * m[3][0] + p1.x * m[3][1] + p2.x * m[3][2] + p3.x * m[3][3]) - ); - spline.y = S * ( - (p0.y * m[0][0] + p1.y * m[0][1] + p2.y * m[0][2] + p3.y * m[0][3]) * t3 + - (p0.y * m[1][0] + p1.y * m[1][1] + p2.y * m[1][2] + p3.y * m[1][3]) * t2 + - (p0.y * m[2][0] + p1.y * m[2][1] + p2.y * m[2][2] + p3.y * m[2][3]) * t + - (p0.y * m[3][0] + p1.y * m[3][1] + p2.y * m[3][2] + p3.y * m[3][3]) - ); + spline.x = S * ( + (p0.x * m[0][0] + p1.x * m[0][1] + p2.x * m[0][2] + p3.x * m[0][3]) * t3 + + (p0.x * m[1][0] + p1.x * m[1][1] + p2.x * m[1][2] + p3.x * m[1][3]) * t2 + + (p0.x * m[2][0] + p1.x * m[2][1] + p2.x * m[2][2] + p3.x * m[2][3]) * t + + (p0.x * m[3][0] + p1.x * m[3][1] + p2.x * m[3][2] + p3.x * m[3][3]) + ); + spline.y = S * ( + (p0.y * m[0][0] + p1.y * m[0][1] + p2.y * m[0][2] + p3.y * m[0][3]) * t3 + + (p0.y * m[1][0] + p1.y * m[1][1] + p2.y * m[1][2] + p3.y * m[1][3]) * t2 + + (p0.y * m[2][0] + p1.y * m[2][1] + p2.y * m[2][2] + p3.y * m[2][3]) * t + + (p0.y * m[3][0] + p1.y * m[3][1] + p2.y * m[3][2] + p3.y * m[3][3]) + ); - return { - x: spline.x, - y: spline.y - }; + return { + x: spline.x, + y: spline.y + }; }; // - 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 // and do nothing else var mouseDown = function (evt) { - if (canvas.spaceKey || evt.button === 1) { return; } + if (canvas.spaceKey || evt.button === 1) { return; } - var rightClick = evt.button === 2; + var rightClick = evt.button === 2; - if (evt.altKey) { // duplicate when dragging - svgCanvas.cloneSelectedElements(0, 0); - } + if (evt.altKey) { // duplicate when dragging + svgCanvas.cloneSelectedElements(0, 0); + } - rootSctm = $('#svgcontent g')[0].getScreenCTM().inverse(); + rootSctm = $('#svgcontent g')[0].getScreenCTM().inverse(); - var pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm), - mouseX = pt.x * currentZoom, - mouseY = pt.y * currentZoom; + var pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm), + mouseX = pt.x * currentZoom, + mouseY = pt.y * currentZoom; - evt.preventDefault(); + evt.preventDefault(); - if (rightClick) { - currentMode = 'select'; - lastClickPoint = pt; - } + if (rightClick) { + currentMode = 'select'; + lastClickPoint = pt; + } - // This would seem to be unnecessary... - // if (['select', 'resize'].indexOf(currentMode) === -1) { - // setGradient(); - // } + // This would seem to be unnecessary... + // if (['select', 'resize'].indexOf(currentMode) === -1) { + // setGradient(); + // } - var x = mouseX / currentZoom, - y = mouseY / currentZoom, - mouseTarget = getMouseTarget(evt); + var x = mouseX / currentZoom, + y = mouseY / currentZoom, + mouseTarget = getMouseTarget(evt); - if (mouseTarget.tagName === 'a' && mouseTarget.childNodes.length === 1) { - mouseTarget = mouseTarget.firstChild; - } + if (mouseTarget.tagName === 'a' && mouseTarget.childNodes.length === 1) { + mouseTarget = mouseTarget.firstChild; + } - // realX/y ignores grid-snap value - var realX = x; - rStartX = startX = x; - var realY = y; - rStartY = startY = y; + // realX/y ignores grid-snap value + var realX = x; + rStartX = startX = x; + var realY = y; + rStartY = startY = y; - if (curConfig.gridSnapping) { - x = svgedit.utilities.snapToGrid(x); - y = svgedit.utilities.snapToGrid(y); - startX = svgedit.utilities.snapToGrid(startX); - startY = svgedit.utilities.snapToGrid(startY); - } + if (curConfig.gridSnapping) { + x = svgedit.utilities.snapToGrid(x); + y = svgedit.utilities.snapToGrid(y); + startX = svgedit.utilities.snapToGrid(startX); + startY = svgedit.utilities.snapToGrid(startY); + } - // if it is a selector grip, then it must be a single element selected, - // set the mouseTarget to that and update the mode to rotate/resize + // if it is a selector grip, then it must be a single element selected, + // set the mouseTarget to that and update the mode to rotate/resize - if (mouseTarget === selectorManager.selectorParentGroup && selectedElements[0] != null) { - var grip = evt.target; - var griptype = elData(grip, 'type'); - // rotating - if (griptype === 'rotate') { - currentMode = 'rotate'; - // resizing - } else if (griptype === 'resize') { - currentMode = 'resize'; - currentResizeMode = elData(grip, 'dir'); - } - mouseTarget = selectedElements[0]; - } + if (mouseTarget === selectorManager.selectorParentGroup && selectedElements[0] != null) { + var grip = evt.target; + var griptype = elData(grip, 'type'); + // rotating + if (griptype === 'rotate') { + currentMode = 'rotate'; + // resizing + } else if (griptype === 'resize') { + currentMode = 'resize'; + currentResizeMode = elData(grip, 'dir'); + } + mouseTarget = selectedElements[0]; + } - startTransform = mouseTarget.getAttribute('transform'); - var i, strokeW, - tlist = svgedit.transformlist.getTransformList(mouseTarget); - switch (currentMode) { - case 'select': - started = true; - currentResizeMode = 'none'; - if (rightClick) { started = false; } + startTransform = mouseTarget.getAttribute('transform'); + var i, strokeW, + tlist = svgedit.transformlist.getTransformList(mouseTarget); + switch (currentMode) { + case 'select': + started = true; + currentResizeMode = 'none'; + if (rightClick) { started = false; } - if (mouseTarget !== svgroot) { - // if this element is not yet selected, clear selection and select it - if (selectedElements.indexOf(mouseTarget) === -1) { - // 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 - clearSelection(true); - } - addToSelection([mouseTarget]); - justSelected = mouseTarget; - pathActions.clear(); - } - // else if it's a path, go into pathedit mode in mouseup + if (mouseTarget !== svgroot) { + // if this element is not yet selected, clear selection and select it + if (selectedElements.indexOf(mouseTarget) === -1) { + // 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 + clearSelection(true); + } + addToSelection([mouseTarget]); + justSelected = mouseTarget; + pathActions.clear(); + } + // else if it's a path, go into pathedit mode in mouseup - if (!rightClick) { - // insert a dummy transform so if the element(s) are moved it will have - // a transform to use for its translate - for (i = 0; i < selectedElements.length; ++i) { - if (selectedElements[i] == null) { continue; } - var slist = svgedit.transformlist.getTransformList(selectedElements[i]); - if (slist.numberOfItems) { - slist.insertItemBefore(svgroot.createSVGTransform(), 0); - } else { - slist.appendItem(svgroot.createSVGTransform()); - } - } - } - } else if (!rightClick) { - clearSelection(); - currentMode = 'multiselect'; - if (rubberBox == null) { - rubberBox = selectorManager.getRubberBandBox(); - } - rStartX *= currentZoom; - rStartY *= currentZoom; - // console.log('p',[evt.pageX, evt.pageY]); - // console.log('c',[evt.clientX, evt.clientY]); - // console.log('o',[evt.offsetX, evt.offsetY]); - // console.log('s',[startX, startY]); + if (!rightClick) { + // insert a dummy transform so if the element(s) are moved it will have + // a transform to use for its translate + for (i = 0; i < selectedElements.length; ++i) { + if (selectedElements[i] == null) { continue; } + var slist = svgedit.transformlist.getTransformList(selectedElements[i]); + if (slist.numberOfItems) { + slist.insertItemBefore(svgroot.createSVGTransform(), 0); + } else { + slist.appendItem(svgroot.createSVGTransform()); + } + } + } + } else if (!rightClick) { + clearSelection(); + currentMode = 'multiselect'; + if (rubberBox == null) { + rubberBox = selectorManager.getRubberBandBox(); + } + rStartX *= currentZoom; + rStartY *= currentZoom; + // console.log('p',[evt.pageX, evt.pageY]); + // console.log('c',[evt.clientX, evt.clientY]); + // console.log('o',[evt.offsetX, evt.offsetY]); + // console.log('s',[startX, startY]); - svgedit.utilities.assignAttributes(rubberBox, { - 'x': rStartX, - 'y': rStartY, - 'width': 0, - 'height': 0, - 'display': 'inline' - }, 100); - } - break; - case 'zoom': - started = true; - if (rubberBox == null) { - rubberBox = selectorManager.getRubberBandBox(); - } - svgedit.utilities.assignAttributes(rubberBox, { - 'x': realX * currentZoom, - 'y': realX * currentZoom, - 'width': 0, - 'height': 0, - 'display': 'inline' - }, 100); - break; - case 'resize': - started = true; - startX = x; - startY = y; + svgedit.utilities.assignAttributes(rubberBox, { + 'x': rStartX, + 'y': rStartY, + 'width': 0, + 'height': 0, + 'display': 'inline' + }, 100); + } + break; + case 'zoom': + started = true; + if (rubberBox == null) { + rubberBox = selectorManager.getRubberBandBox(); + } + svgedit.utilities.assignAttributes(rubberBox, { + 'x': realX * currentZoom, + 'y': realX * currentZoom, + 'width': 0, + 'height': 0, + 'display': 'inline' + }, 100); + break; + case 'resize': + started = true; + startX = x; + startY = y; - // Getting the BBox from the selection box, since we know we - // want to orient around it - initBbox = svgedit.utilities.getBBox($('#selectedBox0')[0]); - var bb = {}; - $.each(initBbox, function (key, val) { - bb[key] = val / currentZoom; - }); - initBbox = bb; + // Getting the BBox from the selection box, since we know we + // want to orient around it + initBbox = svgedit.utilities.getBBox($('#selectedBox0')[0]); + var bb = {}; + $.each(initBbox, function (key, val) { + bb[key] = val / currentZoom; + }); + initBbox = bb; - // append three dummy transforms to the tlist so that - // we can translate,scale,translate in mousemove - var pos = svgedit.utilities.getRotationAngle(mouseTarget) ? 1 : 0; + // append three dummy transforms to the tlist so that + // we can translate,scale,translate in mousemove + var pos = svgedit.utilities.getRotationAngle(mouseTarget) ? 1 : 0; - if (svgedit.math.hasMatrixTransform(tlist)) { - tlist.insertItemBefore(svgroot.createSVGTransform(), pos); - tlist.insertItemBefore(svgroot.createSVGTransform(), pos); - tlist.insertItemBefore(svgroot.createSVGTransform(), pos); - } else { - tlist.appendItem(svgroot.createSVGTransform()); - tlist.appendItem(svgroot.createSVGTransform()); - tlist.appendItem(svgroot.createSVGTransform()); + if (svgedit.math.hasMatrixTransform(tlist)) { + tlist.insertItemBefore(svgroot.createSVGTransform(), pos); + tlist.insertItemBefore(svgroot.createSVGTransform(), pos); + tlist.insertItemBefore(svgroot.createSVGTransform(), pos); + } else { + tlist.appendItem(svgroot.createSVGTransform()); + tlist.appendItem(svgroot.createSVGTransform()); + tlist.appendItem(svgroot.createSVGTransform()); - if (svgedit.browser.supportsNonScalingStroke()) { - // Handle crash for newer Chrome and Safari 6 (Mobile and Desktop): - // https://code.google.com/p/svg-edit/issues/detail?id=904 - // Chromium issue: https://code.google.com/p/chromium/issues/detail?id=114625 - // TODO: Remove this workaround once vendor fixes the issue - var isWebkit = svgedit.browser.isWebkit(); + if (svgedit.browser.supportsNonScalingStroke()) { + // Handle crash for newer Chrome and Safari 6 (Mobile and Desktop): + // https://code.google.com/p/svg-edit/issues/detail?id=904 + // Chromium issue: https://code.google.com/p/chromium/issues/detail?id=114625 + // TODO: Remove this workaround once vendor fixes the issue + var isWebkit = svgedit.browser.isWebkit(); - if (isWebkit) { - var delayedStroke = function (ele) { - var _stroke = ele.getAttributeNS(null, 'stroke'); - ele.removeAttributeNS(null, 'stroke'); - // Re-apply stroke after delay. Anything higher than 1 seems to cause flicker - if (_stroke !== null) setTimeout(function () { ele.setAttributeNS(null, 'stroke', _stroke); }, 0); - }; - } - mouseTarget.style.vectorEffect = 'non-scaling-stroke'; - if (isWebkit) { delayedStroke(mouseTarget); } + if (isWebkit) { + var delayedStroke = function (ele) { + var _stroke = ele.getAttributeNS(null, 'stroke'); + ele.removeAttributeNS(null, 'stroke'); + // Re-apply stroke after delay. Anything higher than 1 seems to cause flicker + if (_stroke !== null) setTimeout(function () { ele.setAttributeNS(null, 'stroke', _stroke); }, 0); + }; + } + mouseTarget.style.vectorEffect = 'non-scaling-stroke'; + if (isWebkit) { delayedStroke(mouseTarget); } - var all = mouseTarget.getElementsByTagName('*'), - len = all.length; - for (i = 0; i < len; i++) { - all[i].style.vectorEffect = 'non-scaling-stroke'; - if (isWebkit) { delayedStroke(all[i]); } - } - } - } - break; - case 'fhellipse': - case 'fhrect': - case 'fhpath': - start.x = realX; - start.y = realY; - started = true; - dAttr = realX + ',' + realY + ' '; - strokeW = parseFloat(curShape.stroke_width) === 0 ? 1 : curShape.stroke_width; - addSvgElementFromJson({ - element: 'polyline', - curStyles: true, - attr: { - points: dAttr, - id: getNextId(), - fill: 'none', - opacity: curShape.opacity / 2, - 'stroke-linecap': 'round', - style: 'pointer-events:none' - } - }); - freehand.minx = realX; - freehand.maxx = realX; - freehand.miny = realY; - freehand.maxy = realY; - break; - case 'image': - started = true; - var newImage = addSvgElementFromJson({ - element: 'image', - attr: { - x: x, - y: y, - width: 0, - height: 0, - id: getNextId(), - opacity: curShape.opacity / 2, - style: 'pointer-events:inherit' - } - }); - setHref(newImage, lastGoodImgUrl); - svgedit.utilities.preventClickDefault(newImage); - break; - case 'square': - // FIXME: once we create the rect, we lose information that this was a square - // (for resizing purposes this could be important) - // Fallthrough - case 'rect': - started = true; - startX = x; - startY = y; - addSvgElementFromJson({ - element: 'rect', - curStyles: true, - attr: { - x: x, - y: y, - width: 0, - height: 0, - id: getNextId(), - opacity: curShape.opacity / 2 - } - }); - break; - case 'line': - started = true; - strokeW = Number(curShape.stroke_width) === 0 ? 1 : curShape.stroke_width; - addSvgElementFromJson({ - element: 'line', - curStyles: true, - attr: { - x1: x, - y1: y, - x2: x, - y2: y, - id: getNextId(), - stroke: curShape.stroke, - 'stroke-width': strokeW, - 'stroke-dasharray': curShape.stroke_dasharray, - 'stroke-linejoin': curShape.stroke_linejoin, - 'stroke-linecap': curShape.stroke_linecap, - 'stroke-opacity': curShape.stroke_opacity, - fill: 'none', - opacity: curShape.opacity / 2, - style: 'pointer-events:none' - } - }); - break; - case 'circle': - started = true; - addSvgElementFromJson({ - element: 'circle', - curStyles: true, - attr: { - cx: x, - cy: y, - r: 0, - id: getNextId(), - opacity: curShape.opacity / 2 - } - }); - break; - case 'ellipse': - started = true; - addSvgElementFromJson({ - element: 'ellipse', - curStyles: true, - attr: { - cx: x, - cy: y, - rx: 0, - ry: 0, - id: getNextId(), - opacity: curShape.opacity / 2 - } - }); - break; - case 'text': - started = true; - /* var newText = */ addSvgElementFromJson({ - element: 'text', - curStyles: true, - attr: { - x: x, - y: y, - id: getNextId(), - fill: curText.fill, - 'stroke-width': curText.stroke_width, - 'font-size': curText.font_size, - 'font-family': curText.font_family, - 'text-anchor': 'middle', - 'xml:space': 'preserve', - opacity: curShape.opacity - } - }); - // newText.textContent = 'text'; - break; - case 'path': - // Fall through - case 'pathedit': - startX *= currentZoom; - startY *= currentZoom; - pathActions.mouseDown(evt, mouseTarget, startX, startY); - started = true; - break; - case 'textedit': - startX *= currentZoom; - startY *= currentZoom; - textActions.mouseDown(evt, mouseTarget, startX, startY); - started = true; - break; - case 'rotate': - started = true; - // we are starting an undoable change (a drag-rotation) - canvas.undoMgr.beginUndoableChange('transform', selectedElements); - break; - default: - // This could occur in an extension - break; - } + var all = mouseTarget.getElementsByTagName('*'), + len = all.length; + for (i = 0; i < len; i++) { + all[i].style.vectorEffect = 'non-scaling-stroke'; + if (isWebkit) { delayedStroke(all[i]); } + } + } + } + break; + case 'fhellipse': + case 'fhrect': + case 'fhpath': + start.x = realX; + start.y = realY; + started = true; + dAttr = realX + ',' + realY + ' '; + strokeW = parseFloat(curShape.stroke_width) === 0 ? 1 : curShape.stroke_width; + addSvgElementFromJson({ + element: 'polyline', + curStyles: true, + attr: { + points: dAttr, + id: getNextId(), + fill: 'none', + opacity: curShape.opacity / 2, + 'stroke-linecap': 'round', + style: 'pointer-events:none' + } + }); + freehand.minx = realX; + freehand.maxx = realX; + freehand.miny = realY; + freehand.maxy = realY; + break; + case 'image': + started = true; + var newImage = addSvgElementFromJson({ + element: 'image', + attr: { + x: x, + y: y, + width: 0, + height: 0, + id: getNextId(), + opacity: curShape.opacity / 2, + style: 'pointer-events:inherit' + } + }); + setHref(newImage, lastGoodImgUrl); + svgedit.utilities.preventClickDefault(newImage); + break; + case 'square': + // FIXME: once we create the rect, we lose information that this was a square + // (for resizing purposes this could be important) + // Fallthrough + case 'rect': + started = true; + startX = x; + startY = y; + addSvgElementFromJson({ + element: 'rect', + curStyles: true, + attr: { + x: x, + y: y, + width: 0, + height: 0, + id: getNextId(), + opacity: curShape.opacity / 2 + } + }); + break; + case 'line': + started = true; + strokeW = Number(curShape.stroke_width) === 0 ? 1 : curShape.stroke_width; + addSvgElementFromJson({ + element: 'line', + curStyles: true, + attr: { + x1: x, + y1: y, + x2: x, + y2: y, + id: getNextId(), + stroke: curShape.stroke, + 'stroke-width': strokeW, + 'stroke-dasharray': curShape.stroke_dasharray, + 'stroke-linejoin': curShape.stroke_linejoin, + 'stroke-linecap': curShape.stroke_linecap, + 'stroke-opacity': curShape.stroke_opacity, + fill: 'none', + opacity: curShape.opacity / 2, + style: 'pointer-events:none' + } + }); + break; + case 'circle': + started = true; + addSvgElementFromJson({ + element: 'circle', + curStyles: true, + attr: { + cx: x, + cy: y, + r: 0, + id: getNextId(), + opacity: curShape.opacity / 2 + } + }); + break; + case 'ellipse': + started = true; + addSvgElementFromJson({ + element: 'ellipse', + curStyles: true, + attr: { + cx: x, + cy: y, + rx: 0, + ry: 0, + id: getNextId(), + opacity: curShape.opacity / 2 + } + }); + break; + case 'text': + started = true; + /* var newText = */ addSvgElementFromJson({ + element: 'text', + curStyles: true, + attr: { + x: x, + y: y, + id: getNextId(), + fill: curText.fill, + 'stroke-width': curText.stroke_width, + 'font-size': curText.font_size, + 'font-family': curText.font_family, + 'text-anchor': 'middle', + 'xml:space': 'preserve', + opacity: curShape.opacity + } + }); + // newText.textContent = 'text'; + break; + case 'path': + // Fall through + case 'pathedit': + startX *= currentZoom; + startY *= currentZoom; + pathActions.mouseDown(evt, mouseTarget, startX, startY); + started = true; + break; + case 'textedit': + startX *= currentZoom; + startY *= currentZoom; + textActions.mouseDown(evt, mouseTarget, startX, startY); + started = true; + break; + case 'rotate': + started = true; + // we are starting an undoable change (a drag-rotation) + canvas.undoMgr.beginUndoableChange('transform', selectedElements); + break; + default: + // This could occur in an extension + break; + } - var extResult = runExtensions('mouseDown', { - event: evt, - start_x: startX, - start_y: startY, - selectedElements: selectedElements - }, true); + var extResult = runExtensions('mouseDown', { + event: evt, + start_x: startX, + start_y: startY, + selectedElements: selectedElements + }, true); - $.each(extResult, function (i, r) { - if (r && r.started) { - started = true; - } - }); + $.each(extResult, function (i, r) { + if (r && r.started) { + started = true; + } + }); }; // 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) { - if (!started) { return; } - if (evt.button === 1 || canvas.spaceKey) { return; } + if (!started) { return; } + if (evt.button === 1 || canvas.spaceKey) { return; } - var i, xya, c, cx, cy, dx, dy, len, angle, box, - selected = selectedElements[0], - pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm), - mouseX = pt.x * currentZoom, - mouseY = pt.y * currentZoom, - shape = svgedit.utilities.getElem(getId()); + var i, xya, c, cx, cy, dx, dy, len, angle, box, + selected = selectedElements[0], + pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm), + mouseX = pt.x * currentZoom, + mouseY = pt.y * currentZoom, + shape = svgedit.utilities.getElem(getId()); - var realX = mouseX / currentZoom; - var x = realX; - var realY = mouseY / currentZoom; - var y = realY; + var realX = mouseX / currentZoom; + var x = realX; + var realY = mouseY / currentZoom; + var y = realY; - if (curConfig.gridSnapping) { - x = svgedit.utilities.snapToGrid(x); - y = svgedit.utilities.snapToGrid(y); - } + if (curConfig.gridSnapping) { + x = svgedit.utilities.snapToGrid(x); + y = svgedit.utilities.snapToGrid(y); + } - evt.preventDefault(); - var tlist; - switch (currentMode) { - case 'select': - // we temporarily use a translate on the element(s) being dragged - // this transform is removed upon mousing up and the element is - // relocated to the new location - if (selectedElements[0] !== null) { - dx = x - startX; - dy = y - startY; + evt.preventDefault(); + var tlist; + switch (currentMode) { + case 'select': + // we temporarily use a translate on the element(s) being dragged + // this transform is removed upon mousing up and the element is + // relocated to the new location + if (selectedElements[0] !== null) { + dx = x - startX; + dy = y - startY; - if (curConfig.gridSnapping) { - dx = svgedit.utilities.snapToGrid(dx); - dy = svgedit.utilities.snapToGrid(dy); - } + if (curConfig.gridSnapping) { + dx = svgedit.utilities.snapToGrid(dx); + dy = svgedit.utilities.snapToGrid(dy); + } - if (evt.shiftKey) { - xya = svgedit.math.snapToAngle(startX, startY, x, y); - x = xya.x; - y = xya.y; - } + if (evt.shiftKey) { + xya = svgedit.math.snapToAngle(startX, startY, x, y); + x = xya.x; + y = xya.y; + } - if (dx !== 0 || dy !== 0) { - len = selectedElements.length; - for (i = 0; i < len; ++i) { - selected = selectedElements[i]; - if (selected == null) { break; } - // if (i === 0) { - // var box = svgedit.utilities.getBBox(selected); - // selectedBBoxes[i].x = box.x + dx; - // selectedBBoxes[i].y = box.y + dy; - // } + if (dx !== 0 || dy !== 0) { + len = selectedElements.length; + for (i = 0; i < len; ++i) { + selected = selectedElements[i]; + if (selected == null) { break; } + // if (i === 0) { + // var box = svgedit.utilities.getBBox(selected); + // selectedBBoxes[i].x = box.x + dx; + // selectedBBoxes[i].y = box.y + dy; + // } - // update the dummy transform in our transform list - // to be a translate - var xform = svgroot.createSVGTransform(); - tlist = svgedit.transformlist.getTransformList(selected); - // Note that if Webkit and there's no ID for this - // element, the dummy transform may have gotten lost. - // This results in unexpected behaviour + // update the dummy transform in our transform list + // to be a translate + var xform = svgroot.createSVGTransform(); + tlist = svgedit.transformlist.getTransformList(selected); + // Note that if Webkit and there's no ID for this + // element, the dummy transform may have gotten lost. + // This results in unexpected behaviour - xform.setTranslate(dx, dy); - if (tlist.numberOfItems) { - tlist.replaceItem(xform, 0); - } else { - tlist.appendItem(xform); - } + xform.setTranslate(dx, dy); + if (tlist.numberOfItems) { + tlist.replaceItem(xform, 0); + } else { + tlist.appendItem(xform); + } - // update our internal bbox that we're tracking while dragging - selectorManager.requestSelector(selected).resize(); - } + // update our internal bbox that we're tracking while dragging + selectorManager.requestSelector(selected).resize(); + } - call('transition', selectedElements); - } - } - break; - case 'multiselect': - realX *= currentZoom; - realY *= currentZoom; - svgedit.utilities.assignAttributes(rubberBox, { - 'x': Math.min(rStartX, realX), - 'y': Math.min(rStartY, realY), - 'width': Math.abs(realX - rStartX), - 'height': Math.abs(realY - rStartY) - }, 100); + call('transition', selectedElements); + } + } + break; + case 'multiselect': + realX *= currentZoom; + realY *= currentZoom; + svgedit.utilities.assignAttributes(rubberBox, { + 'x': Math.min(rStartX, realX), + 'y': Math.min(rStartY, realY), + 'width': Math.abs(realX - rStartX), + 'height': Math.abs(realY - rStartY) + }, 100); - // for each selected: - // - if newList contains selected, do nothing - // - if newList doesn't contain selected, remove it from selected - // - for any newList that was not in selectedElements, add it to selected - var elemsToRemove = selectedElements.slice(), elemsToAdd = [], - newList = getIntersectionList(); + // for each selected: + // - if newList contains selected, do nothing + // - if newList doesn't contain selected, remove it from selected + // - for any newList that was not in selectedElements, add it to selected + var elemsToRemove = selectedElements.slice(), elemsToAdd = [], + newList = getIntersectionList(); - // For every element in the intersection, add if not present in selectedElements. - len = newList.length; - for (i = 0; i < len; ++i) { - var intElem = newList[i]; - // Found an element that was not selected before, so we should add it. - if (selectedElements.indexOf(intElem) === -1) { - elemsToAdd.push(intElem); - } - // Found an element that was already selected, so we shouldn't remove it. - var foundInd = elemsToRemove.indexOf(intElem); - if (foundInd !== -1) { - elemsToRemove.splice(foundInd, 1); - } - } + // For every element in the intersection, add if not present in selectedElements. + len = newList.length; + for (i = 0; i < len; ++i) { + var intElem = newList[i]; + // Found an element that was not selected before, so we should add it. + if (selectedElements.indexOf(intElem) === -1) { + elemsToAdd.push(intElem); + } + // Found an element that was already selected, so we shouldn't remove it. + var foundInd = elemsToRemove.indexOf(intElem); + if (foundInd !== -1) { + elemsToRemove.splice(foundInd, 1); + } + } - if (elemsToRemove.length > 0) { - canvas.removeFromSelection(elemsToRemove); - } + if (elemsToRemove.length > 0) { + canvas.removeFromSelection(elemsToRemove); + } - if (elemsToAdd.length > 0) { - canvas.addToSelection(elemsToAdd); - } + if (elemsToAdd.length > 0) { + canvas.addToSelection(elemsToAdd); + } - break; - case 'resize': - // we track the resize bounding box and translate/scale the selected element - // while the mouse is down, when mouse goes up, we use this to recalculate - // the shape's coordinates - tlist = svgedit.transformlist.getTransformList(selected); - var hasMatrix = svgedit.math.hasMatrixTransform(tlist); - box = hasMatrix ? initBbox : svgedit.utilities.getBBox(selected); - var left = box.x, top = box.y, width = box.width, - height = box.height; - dx = (x - startX); - dy = (y - startY); + break; + case 'resize': + // we track the resize bounding box and translate/scale the selected element + // while the mouse is down, when mouse goes up, we use this to recalculate + // the shape's coordinates + tlist = svgedit.transformlist.getTransformList(selected); + var hasMatrix = svgedit.math.hasMatrixTransform(tlist); + box = hasMatrix ? initBbox : svgedit.utilities.getBBox(selected); + var left = box.x, top = box.y, width = box.width, + height = box.height; + dx = (x - startX); + dy = (y - startY); - if (curConfig.gridSnapping) { - dx = svgedit.utilities.snapToGrid(dx); - dy = svgedit.utilities.snapToGrid(dy); - height = svgedit.utilities.snapToGrid(height); - width = svgedit.utilities.snapToGrid(width); - } + if (curConfig.gridSnapping) { + dx = svgedit.utilities.snapToGrid(dx); + dy = svgedit.utilities.snapToGrid(dy); + height = svgedit.utilities.snapToGrid(height); + width = svgedit.utilities.snapToGrid(width); + } - // if rotated, adjust the dx,dy values - angle = svgedit.utilities.getRotationAngle(selected); - if (angle) { - var r = Math.sqrt(dx * dx + dy * dy), - theta = Math.atan2(dy, dx) - angle * Math.PI / 180.0; - dx = r * Math.cos(theta); - dy = r * Math.sin(theta); - } + // if rotated, adjust the dx,dy values + angle = svgedit.utilities.getRotationAngle(selected); + if (angle) { + var r = Math.sqrt(dx * dx + dy * dy), + theta = Math.atan2(dy, dx) - angle * Math.PI / 180.0; + dx = r * Math.cos(theta); + dy = r * Math.sin(theta); + } - // if not stretching in y direction, set dy to 0 - // if not stretching in x direction, set dx to 0 - if (currentResizeMode.indexOf('n') === -1 && currentResizeMode.indexOf('s') === -1) { - dy = 0; - } - if (currentResizeMode.indexOf('e') === -1 && currentResizeMode.indexOf('w') === -1) { - dx = 0; - } + // if not stretching in y direction, set dy to 0 + // if not stretching in x direction, set dx to 0 + if (currentResizeMode.indexOf('n') === -1 && currentResizeMode.indexOf('s') === -1) { + dy = 0; + } + if (currentResizeMode.indexOf('e') === -1 && currentResizeMode.indexOf('w') === -1) { + dx = 0; + } - var // ts = null, - tx = 0, ty = 0, - 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 (currentResizeMode.indexOf('n') >= 0) { - sy = height ? (height - dy) / height : 1; - ty = height; - } + var // ts = null, + tx = 0, ty = 0, + 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 (currentResizeMode.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 (currentResizeMode.indexOf('w') >= 0) { - sx = width ? (width - dx) / width : 1; - tx = width; - } + // if we dragging on the east side, then adjust the scale factor and tx + if (currentResizeMode.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(), - translateBack = svgroot.createSVGTransform(); + // update the transform list with translate,scale,translate + var translateOrigin = svgroot.createSVGTransform(), + scale = svgroot.createSVGTransform(), + translateBack = svgroot.createSVGTransform(); - if (curConfig.gridSnapping) { - left = svgedit.utilities.snapToGrid(left); - tx = svgedit.utilities.snapToGrid(tx); - top = svgedit.utilities.snapToGrid(top); - ty = svgedit.utilities.snapToGrid(ty); - } + if (curConfig.gridSnapping) { + left = svgedit.utilities.snapToGrid(left); + tx = svgedit.utilities.snapToGrid(tx); + top = svgedit.utilities.snapToGrid(top); + ty = svgedit.utilities.snapToGrid(ty); + } - translateOrigin.setTranslate(-(left + tx), -(top + ty)); - if (evt.shiftKey) { - if (sx === 1) { - sx = sy; - } else { sy = sx; } - } - scale.setScale(sx, sy); + translateOrigin.setTranslate(-(left + tx), -(top + ty)); + if (evt.shiftKey) { + if (sx === 1) { + sx = sy; + } else { sy = sx; } + } + scale.setScale(sx, sy); - translateBack.setTranslate(left + tx, top + ty); - if (hasMatrix) { - var diff = angle ? 1 : 0; - tlist.replaceItem(translateOrigin, 2 + diff); - tlist.replaceItem(scale, 1 + diff); - tlist.replaceItem(translateBack, Number(diff)); - } else { - var N = tlist.numberOfItems; - tlist.replaceItem(translateBack, N - 3); - tlist.replaceItem(scale, N - 2); - tlist.replaceItem(translateOrigin, N - 1); - } + translateBack.setTranslate(left + tx, top + ty); + if (hasMatrix) { + var diff = angle ? 1 : 0; + tlist.replaceItem(translateOrigin, 2 + diff); + tlist.replaceItem(scale, 1 + diff); + tlist.replaceItem(translateBack, Number(diff)); + } else { + var N = tlist.numberOfItems; + tlist.replaceItem(translateBack, N - 3); + tlist.replaceItem(scale, N - 2); + tlist.replaceItem(translateOrigin, N - 1); + } - selectorManager.requestSelector(selected).resize(); + selectorManager.requestSelector(selected).resize(); - call('transition', selectedElements); + call('transition', selectedElements); - break; - case 'zoom': - realX *= currentZoom; - realY *= currentZoom; - svgedit.utilities.assignAttributes(rubberBox, { - 'x': Math.min(rStartX * currentZoom, realX), - 'y': Math.min(rStartY * currentZoom, realY), - 'width': Math.abs(realX - rStartX * currentZoom), - 'height': Math.abs(realY - rStartY * currentZoom) - }, 100); - break; - case 'text': - svgedit.utilities.assignAttributes(shape, { - 'x': x, - 'y': y - }, 1000); - break; - case 'line': - if (curConfig.gridSnapping) { - x = svgedit.utilities.snapToGrid(x); - y = svgedit.utilities.snapToGrid(y); - } + break; + case 'zoom': + realX *= currentZoom; + realY *= currentZoom; + svgedit.utilities.assignAttributes(rubberBox, { + 'x': Math.min(rStartX * currentZoom, realX), + 'y': Math.min(rStartY * currentZoom, realY), + 'width': Math.abs(realX - rStartX * currentZoom), + 'height': Math.abs(realY - rStartY * currentZoom) + }, 100); + break; + case 'text': + svgedit.utilities.assignAttributes(shape, { + 'x': x, + 'y': y + }, 1000); + break; + case 'line': + if (curConfig.gridSnapping) { + x = svgedit.utilities.snapToGrid(x); + y = svgedit.utilities.snapToGrid(y); + } - var x2 = x; - var y2 = y; + var x2 = x; + var y2 = y; - if (evt.shiftKey) { - xya = svgedit.math.snapToAngle(startX, startY, x2, y2); - x2 = xya.x; - y2 = xya.y; - } + if (evt.shiftKey) { + xya = svgedit.math.snapToAngle(startX, startY, x2, y2); + x2 = xya.x; + y2 = xya.y; + } - shape.setAttributeNS(null, 'x2', x2); - shape.setAttributeNS(null, 'y2', y2); - break; - case 'foreignObject': - // fall through - case 'square': - // fall through - case 'rect': - // fall through - case 'image': - var square = (currentMode === 'square') || evt.shiftKey, - w = Math.abs(x - startX), - h = Math.abs(y - startY), - newX, newY; - if (square) { - w = h = Math.max(w, h); - newX = startX < x ? startX : startX - w; - newY = startY < y ? startY : startY - h; - } else { - newX = Math.min(startX, x); - newY = Math.min(startY, y); - } + shape.setAttributeNS(null, 'x2', x2); + shape.setAttributeNS(null, 'y2', y2); + break; + case 'foreignObject': + // fall through + case 'square': + // fall through + case 'rect': + // fall through + case 'image': + var square = (currentMode === 'square') || evt.shiftKey, + w = Math.abs(x - startX), + h = Math.abs(y - startY), + newX, newY; + if (square) { + w = h = Math.max(w, h); + newX = startX < x ? startX : startX - w; + newY = startY < y ? startY : startY - h; + } else { + newX = Math.min(startX, x); + newY = Math.min(startY, y); + } - if (curConfig.gridSnapping) { - w = svgedit.utilities.snapToGrid(w); - h = svgedit.utilities.snapToGrid(h); - newX = svgedit.utilities.snapToGrid(newX); - newY = svgedit.utilities.snapToGrid(newY); - } + if (curConfig.gridSnapping) { + w = svgedit.utilities.snapToGrid(w); + h = svgedit.utilities.snapToGrid(h); + newX = svgedit.utilities.snapToGrid(newX); + newY = svgedit.utilities.snapToGrid(newY); + } - svgedit.utilities.assignAttributes(shape, { - 'width': w, - 'height': h, - 'x': newX, - 'y': newY - }, 1000); + svgedit.utilities.assignAttributes(shape, { + 'width': w, + 'height': h, + 'x': newX, + 'y': newY + }, 1000); - break; - case 'circle': - c = $(shape).attr(['cx', 'cy']); - cx = c.cx; - cy = c.cy; - var rad = Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)); - if (curConfig.gridSnapping) { - rad = svgedit.utilities.snapToGrid(rad); - } - shape.setAttributeNS(null, 'r', rad); - break; - case 'ellipse': - c = $(shape).attr(['cx', 'cy']); - cx = c.cx; - cy = c.cy; - if (curConfig.gridSnapping) { - x = svgedit.utilities.snapToGrid(x); - cx = svgedit.utilities.snapToGrid(cx); - y = svgedit.utilities.snapToGrid(y); - cy = svgedit.utilities.snapToGrid(cy); - } - shape.setAttributeNS(null, 'rx', Math.abs(x - cx)); - var ry = Math.abs(evt.shiftKey ? (x - cx) : (y - cy)); - shape.setAttributeNS(null, 'ry', ry); - break; - case 'fhellipse': - case 'fhrect': - freehand.minx = Math.min(realX, freehand.minx); - freehand.maxx = Math.max(realX, freehand.maxx); - freehand.miny = Math.min(realY, freehand.miny); - freehand.maxy = Math.max(realY, freehand.maxy); - // Fallthrough - case 'fhpath': - // dAttr += + realX + ',' + realY + ' '; - // shape.setAttributeNS(null, 'points', dAttr); - end.x = realX; end.y = realY; - if (controllPoint2.x && controllPoint2.y) { - for (i = 0; i < STEP_COUNT - 1; i++) { - parameter = i / STEP_COUNT; - nextParameter = (i + 1) / STEP_COUNT; - bSpline = getBsplinePoint(nextParameter); - nextPos = bSpline; - bSpline = getBsplinePoint(parameter); - sumDistance += Math.sqrt((nextPos.x - bSpline.x) * (nextPos.x - bSpline.x) + (nextPos.y - bSpline.y) * (nextPos.y - bSpline.y)); - if (sumDistance > THRESHOLD_DIST) { - dAttr += +bSpline.x + ',' + bSpline.y + ' '; - shape.setAttributeNS(null, 'points', dAttr); - sumDistance -= THRESHOLD_DIST; - } - } - } - controllPoint2 = {x: controllPoint1.x, y: controllPoint1.y}; - controllPoint1 = {x: start.x, y: start.y}; - start = {x: end.x, y: end.y}; - break; - // update path stretch line coordinates - case 'path': - // fall through - case 'pathedit': - x *= currentZoom; - y *= currentZoom; + break; + case 'circle': + c = $(shape).attr(['cx', 'cy']); + cx = c.cx; + cy = c.cy; + var rad = Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)); + if (curConfig.gridSnapping) { + rad = svgedit.utilities.snapToGrid(rad); + } + shape.setAttributeNS(null, 'r', rad); + break; + case 'ellipse': + c = $(shape).attr(['cx', 'cy']); + cx = c.cx; + cy = c.cy; + if (curConfig.gridSnapping) { + x = svgedit.utilities.snapToGrid(x); + cx = svgedit.utilities.snapToGrid(cx); + y = svgedit.utilities.snapToGrid(y); + cy = svgedit.utilities.snapToGrid(cy); + } + shape.setAttributeNS(null, 'rx', Math.abs(x - cx)); + var ry = Math.abs(evt.shiftKey ? (x - cx) : (y - cy)); + shape.setAttributeNS(null, 'ry', ry); + break; + case 'fhellipse': + case 'fhrect': + freehand.minx = Math.min(realX, freehand.minx); + freehand.maxx = Math.max(realX, freehand.maxx); + freehand.miny = Math.min(realY, freehand.miny); + freehand.maxy = Math.max(realY, freehand.maxy); + // Fallthrough + case 'fhpath': + // dAttr += + realX + ',' + realY + ' '; + // shape.setAttributeNS(null, 'points', dAttr); + end.x = realX; end.y = realY; + if (controllPoint2.x && controllPoint2.y) { + for (i = 0; i < STEP_COUNT - 1; i++) { + parameter = i / STEP_COUNT; + nextParameter = (i + 1) / STEP_COUNT; + bSpline = getBsplinePoint(nextParameter); + nextPos = bSpline; + bSpline = getBsplinePoint(parameter); + sumDistance += Math.sqrt((nextPos.x - bSpline.x) * (nextPos.x - bSpline.x) + (nextPos.y - bSpline.y) * (nextPos.y - bSpline.y)); + if (sumDistance > THRESHOLD_DIST) { + dAttr += +bSpline.x + ',' + bSpline.y + ' '; + shape.setAttributeNS(null, 'points', dAttr); + sumDistance -= THRESHOLD_DIST; + } + } + } + controllPoint2 = {x: controllPoint1.x, y: controllPoint1.y}; + controllPoint1 = {x: start.x, y: start.y}; + start = {x: end.x, y: end.y}; + break; + // update path stretch line coordinates + case 'path': + // fall through + case 'pathedit': + x *= currentZoom; + y *= currentZoom; - if (curConfig.gridSnapping) { - x = svgedit.utilities.snapToGrid(x); - y = svgedit.utilities.snapToGrid(y); - startX = svgedit.utilities.snapToGrid(startX); - startY = svgedit.utilities.snapToGrid(startY); - } - if (evt.shiftKey) { - var path = svgedit.path.path; - var x1, y1; - if (path) { - x1 = path.dragging ? path.dragging[0] : startX; - y1 = path.dragging ? path.dragging[1] : startY; - } else { - x1 = startX; - y1 = startY; - } - xya = svgedit.math.snapToAngle(x1, y1, x, y); - x = xya.x; - y = xya.y; - } + if (curConfig.gridSnapping) { + x = svgedit.utilities.snapToGrid(x); + y = svgedit.utilities.snapToGrid(y); + startX = svgedit.utilities.snapToGrid(startX); + startY = svgedit.utilities.snapToGrid(startY); + } + if (evt.shiftKey) { + var path = svgedit.path.path; + var x1, y1; + if (path) { + x1 = path.dragging ? path.dragging[0] : startX; + y1 = path.dragging ? path.dragging[1] : startY; + } else { + x1 = startX; + y1 = startY; + } + xya = svgedit.math.snapToAngle(x1, y1, x, y); + x = xya.x; + y = xya.y; + } - if (rubberBox && rubberBox.getAttribute('display') !== 'none') { - realX *= currentZoom; - realY *= currentZoom; - svgedit.utilities.assignAttributes(rubberBox, { - 'x': Math.min(rStartX * currentZoom, realX), - 'y': Math.min(rStartY * currentZoom, realY), - 'width': Math.abs(realX - rStartX * currentZoom), - 'height': Math.abs(realY - rStartY * currentZoom) - }, 100); - } - pathActions.mouseMove(x, y); + if (rubberBox && rubberBox.getAttribute('display') !== 'none') { + realX *= currentZoom; + realY *= currentZoom; + svgedit.utilities.assignAttributes(rubberBox, { + 'x': Math.min(rStartX * currentZoom, realX), + 'y': Math.min(rStartY * currentZoom, realY), + 'width': Math.abs(realX - rStartX * currentZoom), + 'height': Math.abs(realY - rStartY * currentZoom) + }, 100); + } + pathActions.mouseMove(x, y); - break; - case 'textedit': - x *= currentZoom; - y *= currentZoom; - // if (rubberBox && rubberBox.getAttribute('display') !== 'none') { - // svgedit.utilities.assignAttributes(rubberBox, { - // 'x': Math.min(startX, x), - // 'y': Math.min(startY, y), - // 'width': Math.abs(x - startX), - // 'height': Math.abs(y - startY) - // }, 100); - // } + break; + case 'textedit': + x *= currentZoom; + y *= currentZoom; + // if (rubberBox && rubberBox.getAttribute('display') !== 'none') { + // svgedit.utilities.assignAttributes(rubberBox, { + // 'x': Math.min(startX, x), + // 'y': Math.min(startY, y), + // 'width': Math.abs(x - startX), + // 'height': Math.abs(y - startY) + // }, 100); + // } - textActions.mouseMove(mouseX, mouseY); + textActions.mouseMove(mouseX, mouseY); - break; - case 'rotate': - box = svgedit.utilities.getBBox(selected); - cx = box.x + box.width / 2; - cy = box.y + box.height / 2; - var m = svgedit.math.getMatrix(selected), - center = svgedit.math.transformPoint(cx, cy, m); - cx = center.x; - cy = center.y; - angle = ((Math.atan2(cy - y, cx - x) * (180 / Math.PI)) - 90) % 360; - if (curConfig.gridSnapping) { - angle = svgedit.utilities.snapToGrid(angle); - } - if (evt.shiftKey) { // restrict rotations to nice angles (WRS) - var snap = 45; - angle = Math.round(angle / snap) * snap; - } + break; + case 'rotate': + box = svgedit.utilities.getBBox(selected); + cx = box.x + box.width / 2; + cy = box.y + box.height / 2; + var m = svgedit.math.getMatrix(selected), + center = svgedit.math.transformPoint(cx, cy, m); + cx = center.x; + cy = center.y; + angle = ((Math.atan2(cy - y, cx - x) * (180 / Math.PI)) - 90) % 360; + if (curConfig.gridSnapping) { + angle = svgedit.utilities.snapToGrid(angle); + } + if (evt.shiftKey) { // restrict rotations to nice angles (WRS) + var snap = 45; + angle = Math.round(angle / snap) * snap; + } - canvas.setRotationAngle(angle < -180 ? (360 + angle) : angle, true); - call('transition', selectedElements); - break; - default: - break; - } + canvas.setRotationAngle(angle < -180 ? (360 + angle) : angle, true); + call('transition', selectedElements); + break; + default: + break; + } - runExtensions('mouseMove', { - event: evt, - mouse_x: mouseX, - mouse_y: mouseY, - selected: selected - }); + runExtensions('mouseMove', { + event: evt, + mouse_x: mouseX, + mouse_y: mouseY, + selected: selected + }); }; // mouseMove() // - in create mode, the element's opacity is set properly, we create an InsertElementCommand @@ -1951,362 +1951,362 @@ var mouseMove = function (evt) { // identified, a ChangeElementCommand is created and stored on the stack for those attrs // this is done in when we recalculate the selected dimensions() var mouseUp = function (evt) { - if (evt.button === 2) { return; } - var tempJustSelected = justSelected; - justSelected = null; - if (!started) { return; } - var pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm), - mouseX = pt.x * currentZoom, - mouseY = pt.y * currentZoom, - x = mouseX / currentZoom, - y = mouseY / currentZoom, - element = svgedit.utilities.getElem(getId()), - keep = false; + if (evt.button === 2) { return; } + var tempJustSelected = justSelected; + justSelected = null; + if (!started) { return; } + var pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm), + mouseX = pt.x * currentZoom, + mouseY = pt.y * currentZoom, + x = mouseX / currentZoom, + y = mouseY / currentZoom, + element = svgedit.utilities.getElem(getId()), + keep = false; - var realX = x; - var realY = y; + var realX = x; + var realY = y; - // TODO: Make true when in multi-unit mode - var useUnit = false; // (curConfig.baseUnit !== 'px'); - started = false; - var attrs, t; - switch (currentMode) { - // intentionally fall-through to select here - case 'resize': - case 'multiselect': - if (rubberBox != null) { - rubberBox.setAttribute('display', 'none'); - curBBoxes = []; - } - currentMode = 'select'; - // Fallthrough - case 'select': - if (selectedElements[0] != null) { - // if we only have one selected element - if (selectedElements[1] == null) { - // set our current stroke/fill properties to the element's - var selected = selectedElements[0]; - switch (selected.tagName) { - case 'g': - case 'use': - case 'image': - case 'foreignObject': - break; - default: - curProperties.fill = selected.getAttribute('fill'); - curProperties.fill_opacity = selected.getAttribute('fill-opacity'); - curProperties.stroke = selected.getAttribute('stroke'); - curProperties.stroke_opacity = selected.getAttribute('stroke-opacity'); - curProperties.stroke_width = selected.getAttribute('stroke-width'); - curProperties.stroke_dasharray = selected.getAttribute('stroke-dasharray'); - curProperties.stroke_linejoin = selected.getAttribute('stroke-linejoin'); - curProperties.stroke_linecap = selected.getAttribute('stroke-linecap'); - } + // TODO: Make true when in multi-unit mode + var useUnit = false; // (curConfig.baseUnit !== 'px'); + started = false; + var attrs, t; + switch (currentMode) { + // intentionally fall-through to select here + case 'resize': + case 'multiselect': + if (rubberBox != null) { + rubberBox.setAttribute('display', 'none'); + curBBoxes = []; + } + currentMode = 'select'; + // Fallthrough + case 'select': + if (selectedElements[0] != null) { + // if we only have one selected element + if (selectedElements[1] == null) { + // set our current stroke/fill properties to the element's + var selected = selectedElements[0]; + switch (selected.tagName) { + case 'g': + case 'use': + case 'image': + case 'foreignObject': + break; + default: + curProperties.fill = selected.getAttribute('fill'); + curProperties.fill_opacity = selected.getAttribute('fill-opacity'); + curProperties.stroke = selected.getAttribute('stroke'); + curProperties.stroke_opacity = selected.getAttribute('stroke-opacity'); + curProperties.stroke_width = selected.getAttribute('stroke-width'); + curProperties.stroke_dasharray = selected.getAttribute('stroke-dasharray'); + curProperties.stroke_linejoin = selected.getAttribute('stroke-linejoin'); + curProperties.stroke_linecap = selected.getAttribute('stroke-linecap'); + } - if (selected.tagName === 'text') { - curText.font_size = selected.getAttribute('font-size'); - curText.font_family = selected.getAttribute('font-family'); - } - selectorManager.requestSelector(selected).showGrips(true); + if (selected.tagName === 'text') { + curText.font_size = selected.getAttribute('font-size'); + curText.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]); - } - // always recalculate dimensions to strip off stray identity transforms - recalculateAllSelectedDimensions(); - // if it was being dragged/resized - if (realX !== rStartX || realY !== rStartY) { - var i, len = selectedElements.length; - for (i = 0; i < len; ++i) { - if (selectedElements[i] == null) { break; } - if (!selectedElements[i].firstChild) { - // Not needed for groups (incorrectly resizes elems), possibly not needed at all? - selectorManager.requestSelector(selectedElements[i]).resize(); - } - } - // no change in position/size, so maybe we should move to pathedit - } else { - t = evt.target; - if (selectedElements[0].nodeName === 'path' && selectedElements[1] == null) { - pathActions.select(selectedElements[0]); - // if it was a path - // else, if it was selected and this is a shift-click, remove it from selection - } else if (evt.shiftKey) { - if (tempJustSelected !== t) { - canvas.removeFromSelection([t]); - } - } - } // no change in mouse position + // This shouldn't be necessary as it was done on mouseDown... + // call('selected', [selected]); + } + // always recalculate dimensions to strip off stray identity transforms + recalculateAllSelectedDimensions(); + // if it was being dragged/resized + if (realX !== rStartX || realY !== rStartY) { + var i, len = selectedElements.length; + for (i = 0; i < len; ++i) { + if (selectedElements[i] == null) { break; } + if (!selectedElements[i].firstChild) { + // Not needed for groups (incorrectly resizes elems), possibly not needed at all? + selectorManager.requestSelector(selectedElements[i]).resize(); + } + } + // no change in position/size, so maybe we should move to pathedit + } else { + t = evt.target; + if (selectedElements[0].nodeName === 'path' && selectedElements[1] == null) { + pathActions.select(selectedElements[0]); + // if it was a path + // else, if it was selected and this is a shift-click, remove it from selection + } else if (evt.shiftKey) { + if (tempJustSelected !== t) { + canvas.removeFromSelection([t]); + } + } + } // no change in mouse position - // Remove non-scaling stroke - if (svgedit.browser.supportsNonScalingStroke()) { - var elem = selectedElements[0]; - if (elem) { - elem.removeAttribute('style'); - svgedit.utilities.walkTree(elem, function (elem) { - elem.removeAttribute('style'); - }); - } - } - } - return; - case 'zoom': - if (rubberBox != null) { - rubberBox.setAttribute('display', 'none'); - } - var factor = evt.shiftKey ? 0.5 : 2; - call('zoomed', { - 'x': Math.min(rStartX, realX), - 'y': Math.min(rStartY, realY), - 'width': Math.abs(realX - rStartX), - 'height': Math.abs(realY - rStartY), - 'factor': factor - }); - return; - case 'fhpath': - // Check that the path contains at least 2 points; a degenerate one-point path - // causes problems. - // Webkit ignores how we set the points attribute with commas and uses space - // to separate all coordinates, see https://bugs.webkit.org/show_bug.cgi?id=29870 - sumDistance = 0; - controllPoint2 = {x: 0, y: 0}; - controllPoint1 = {x: 0, y: 0}; - start = {x: 0, y: 0}; - end = {x: 0, y: 0}; - var coords = element.getAttribute('points'); - var commaIndex = coords.indexOf(','); - if (commaIndex >= 0) { - keep = coords.indexOf(',', commaIndex + 1) >= 0; - } else { - keep = coords.indexOf(' ', coords.indexOf(' ') + 1) >= 0; - } - if (keep) { - element = pathActions.smoothPolylineIntoPath(element); - } - break; - case 'line': - attrs = $(element).attr(['x1', 'x2', 'y1', 'y2']); - keep = (attrs.x1 !== attrs.x2 || attrs.y1 !== attrs.y2); - break; - case 'foreignObject': - case 'square': - case 'rect': - case 'image': - attrs = $(element).attr(['width', 'height']); - // Image should be kept regardless of size (use inherit dimensions later) - keep = (attrs.width !== '0' || attrs.height !== '0') || currentMode === 'image'; - break; - case 'circle': - keep = (element.getAttribute('r') !== '0'); - break; - case 'ellipse': - attrs = $(element).attr(['rx', 'ry']); - keep = (attrs.rx != null || attrs.ry != null); - break; - case 'fhellipse': - if ((freehand.maxx - freehand.minx) > 0 && - (freehand.maxy - freehand.miny) > 0) { - element = addSvgElementFromJson({ - element: 'ellipse', - curStyles: true, - attr: { - cx: (freehand.minx + freehand.maxx) / 2, - cy: (freehand.miny + freehand.maxy) / 2, - rx: (freehand.maxx - freehand.minx) / 2, - ry: (freehand.maxy - freehand.miny) / 2, - id: getId() - } - }); - call('changed', [element]); - keep = true; - } - break; - case 'fhrect': - if ((freehand.maxx - freehand.minx) > 0 && - (freehand.maxy - freehand.miny) > 0) { - element = addSvgElementFromJson({ - element: 'rect', - curStyles: true, - attr: { - x: freehand.minx, - y: freehand.miny, - width: (freehand.maxx - freehand.minx), - height: (freehand.maxy - freehand.miny), - id: getId() - } - }); - call('changed', [element]); - keep = true; - } - break; - case 'text': - keep = true; - selectOnly([element]); - textActions.start(element); - break; - case 'path': - // set element to null here so that it is not removed nor finalized - element = null; - // continue to be set to true so that mouseMove happens - started = true; + // Remove non-scaling stroke + if (svgedit.browser.supportsNonScalingStroke()) { + var elem = selectedElements[0]; + if (elem) { + elem.removeAttribute('style'); + svgedit.utilities.walkTree(elem, function (elem) { + elem.removeAttribute('style'); + }); + } + } + } + return; + case 'zoom': + if (rubberBox != null) { + rubberBox.setAttribute('display', 'none'); + } + var factor = evt.shiftKey ? 0.5 : 2; + call('zoomed', { + 'x': Math.min(rStartX, realX), + 'y': Math.min(rStartY, realY), + 'width': Math.abs(realX - rStartX), + 'height': Math.abs(realY - rStartY), + 'factor': factor + }); + return; + case 'fhpath': + // Check that the path contains at least 2 points; a degenerate one-point path + // causes problems. + // Webkit ignores how we set the points attribute with commas and uses space + // to separate all coordinates, see https://bugs.webkit.org/show_bug.cgi?id=29870 + sumDistance = 0; + controllPoint2 = {x: 0, y: 0}; + controllPoint1 = {x: 0, y: 0}; + start = {x: 0, y: 0}; + end = {x: 0, y: 0}; + var coords = element.getAttribute('points'); + var commaIndex = coords.indexOf(','); + if (commaIndex >= 0) { + keep = coords.indexOf(',', commaIndex + 1) >= 0; + } else { + keep = coords.indexOf(' ', coords.indexOf(' ') + 1) >= 0; + } + if (keep) { + element = pathActions.smoothPolylineIntoPath(element); + } + break; + case 'line': + attrs = $(element).attr(['x1', 'x2', 'y1', 'y2']); + keep = (attrs.x1 !== attrs.x2 || attrs.y1 !== attrs.y2); + break; + case 'foreignObject': + case 'square': + case 'rect': + case 'image': + attrs = $(element).attr(['width', 'height']); + // Image should be kept regardless of size (use inherit dimensions later) + keep = (attrs.width !== '0' || attrs.height !== '0') || currentMode === 'image'; + break; + case 'circle': + keep = (element.getAttribute('r') !== '0'); + break; + case 'ellipse': + attrs = $(element).attr(['rx', 'ry']); + keep = (attrs.rx != null || attrs.ry != null); + break; + case 'fhellipse': + if ((freehand.maxx - freehand.minx) > 0 && + (freehand.maxy - freehand.miny) > 0) { + element = addSvgElementFromJson({ + element: 'ellipse', + curStyles: true, + attr: { + cx: (freehand.minx + freehand.maxx) / 2, + cy: (freehand.miny + freehand.maxy) / 2, + rx: (freehand.maxx - freehand.minx) / 2, + ry: (freehand.maxy - freehand.miny) / 2, + id: getId() + } + }); + call('changed', [element]); + keep = true; + } + break; + case 'fhrect': + if ((freehand.maxx - freehand.minx) > 0 && + (freehand.maxy - freehand.miny) > 0) { + element = addSvgElementFromJson({ + element: 'rect', + curStyles: true, + attr: { + x: freehand.minx, + y: freehand.miny, + width: (freehand.maxx - freehand.minx), + height: (freehand.maxy - freehand.miny), + id: getId() + } + }); + call('changed', [element]); + keep = true; + } + break; + case 'text': + keep = true; + selectOnly([element]); + textActions.start(element); + break; + case 'path': + // set element to null here so that it is not removed nor finalized + element = null; + // continue to be set to true so that mouseMove happens + started = true; - var res = pathActions.mouseUp(evt, element, mouseX, mouseY); - element = res.element; - keep = res.keep; - break; - case 'pathedit': - keep = true; - element = null; - pathActions.mouseUp(evt); - break; - case 'textedit': - keep = false; - element = null; - textActions.mouseUp(evt, mouseX, mouseY); - break; - case 'rotate': - keep = true; - element = null; - currentMode = 'select'; - var batchCmd = canvas.undoMgr.finishUndoableChange(); - if (!batchCmd.isEmpty()) { - addCommandToHistory(batchCmd); - } - // perform recalculation to weed out any stray identity transforms that might get stuck - recalculateAllSelectedDimensions(); - call('changed', selectedElements); - break; - default: - // This could occur in an extension - break; - } + var res = pathActions.mouseUp(evt, element, mouseX, mouseY); + element = res.element; + keep = res.keep; + break; + case 'pathedit': + keep = true; + element = null; + pathActions.mouseUp(evt); + break; + case 'textedit': + keep = false; + element = null; + textActions.mouseUp(evt, mouseX, mouseY); + break; + case 'rotate': + keep = true; + element = null; + currentMode = 'select'; + var batchCmd = canvas.undoMgr.finishUndoableChange(); + if (!batchCmd.isEmpty()) { + addCommandToHistory(batchCmd); + } + // perform recalculation to weed out any stray identity transforms that might get stuck + recalculateAllSelectedDimensions(); + call('changed', selectedElements); + break; + default: + // This could occur in an extension + break; + } - var extResult = runExtensions('mouseUp', { - event: evt, - mouse_x: mouseX, - mouse_y: mouseY - }, true); + var extResult = runExtensions('mouseUp', { + event: evt, + mouse_x: mouseX, + mouse_y: mouseY + }, true); - $.each(extResult, function (i, r) { - if (r) { - keep = r.keep || keep; - element = r.element; - started = r.started || started; - } - }); + $.each(extResult, function (i, r) { + if (r) { + keep = r.keep || keep; + element = r.element; + started = r.started || started; + } + }); - if (!keep && element != null) { - getCurrentDrawing().releaseId(getId()); - element.parentNode.removeChild(element); - element = null; + if (!keep && element != null) { + getCurrentDrawing().releaseId(getId()); + element.parentNode.removeChild(element); + element = null; - t = evt.target; + t = evt.target; - // if this element is in a group, go up until we reach the top-level group - // just below the layer groups - // TODO: once we implement links, we also would have to check for elements - while (t && t.parentNode && t.parentNode.parentNode && 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, - // then go to Select mode. - // WebKit returns
        when the canvas is clicked, Firefox/Opera return - if ((currentMode !== 'path' || !drawnPath) && - t && t.parentNode && - t.parentNode.id !== 'selectorParentGroup' && - 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 this element is in a group, go up until we reach the top-level group + // just below the layer groups + // TODO: once we implement links, we also would have to check for elements + while (t && t.parentNode && t.parentNode.parentNode && 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, + // then go to Select mode. + // WebKit returns
        when the canvas is clicked, Firefox/Opera return + if ((currentMode !== 'path' || !drawnPath) && + t && t.parentNode && + t.parentNode.id !== 'selectorParentGroup' && + 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); } + if (useUnit) { svgedit.units.convertAttrs(element); } - var aniDur = 0.2, cAni; - if (opacAni.beginElement && parseFloat(element.getAttribute('opacity')) !== curShape.opacity) { - cAni = $(opacAni).clone().attr({ - to: curShape.opacity, - dur: aniDur - }).appendTo(element); - try { - // Fails in FF4 on foreignObject - cAni[0].beginElement(); - } catch (e) {} - } else { - aniDur = 0; - } + var aniDur = 0.2, cAni; + if (opacAni.beginElement && parseFloat(element.getAttribute('opacity')) !== curShape.opacity) { + cAni = $(opacAni).clone().attr({ + to: curShape.opacity, + dur: aniDur + }).appendTo(element); + try { + // Fails in FF4 on foreignObject + cAni[0].beginElement(); + } catch (e) {} + } else { + aniDur = 0; + } - // Ideally this would be done on the endEvent of the animation, - // but that doesn't seem to be supported in Webkit - setTimeout(function () { - if (cAni) { cAni.remove(); } - element.setAttribute('opacity', curShape.opacity); - element.setAttribute('style', 'pointer-events:inherit'); - cleanupElement(element); - if (currentMode === 'path') { - pathActions.toEditMode(element); - } else if (curConfig.selectNew) { - selectOnly([element], true); - } - // 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 svgedit.history.InsertElementCommand(element)); + // Ideally this would be done on the endEvent of the animation, + // but that doesn't seem to be supported in Webkit + setTimeout(function () { + if (cAni) { cAni.remove(); } + element.setAttribute('opacity', curShape.opacity); + element.setAttribute('style', 'pointer-events:inherit'); + cleanupElement(element); + if (currentMode === 'path') { + pathActions.toEditMode(element); + } else if (curConfig.selectNew) { + selectOnly([element], true); + } + // 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 svgedit.history.InsertElementCommand(element)); - call('changed', [element]); - }, aniDur * 1000); - } + call('changed', [element]); + }, aniDur * 1000); + } - startTransform = null; + startTransform = null; }; var dblClick = function (evt) { - var evtTarget = evt.target; - var parent = evtTarget.parentNode; + var evtTarget = evt.target; + var parent = evtTarget.parentNode; - // Do nothing if already in current group - if (parent === currentGroup) { return; } + // Do nothing if already in current group + if (parent === currentGroup) { return; } - var mouseTarget = getMouseTarget(evt); - var tagName = mouseTarget.tagName; + var mouseTarget = getMouseTarget(evt); + var tagName = mouseTarget.tagName; - if (tagName === 'text' && currentMode !== 'textedit') { - var pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm); - textActions.select(mouseTarget, pt.x, pt.y); - } + if (tagName === 'text' && currentMode !== 'textedit') { + var pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm); + textActions.select(mouseTarget, pt.x, pt.y); + } - if ((tagName === 'g' || tagName === 'a') && - svgedit.utilities.getRotationAngle(mouseTarget) - ) { - // TODO: Allow method of in-group editing without having to do - // this (similar to editing rotated paths) + if ((tagName === 'g' || tagName === 'a') && + svgedit.utilities.getRotationAngle(mouseTarget) + ) { + // TODO: Allow method of in-group editing without having to do + // this (similar to editing rotated paths) - // Ungroup and regroup - pushGroupProperties(mouseTarget); - mouseTarget = selectedElements[0]; - clearSelection(true); - } - // Reset context - if (currentGroup) { - leaveContext(); - } + // Ungroup and regroup + pushGroupProperties(mouseTarget); + mouseTarget = selectedElements[0]; + clearSelection(true); + } + // Reset context + if (currentGroup) { + leaveContext(); + } - if ((parent.tagName !== 'g' && parent.tagName !== 'a') || - parent === getCurrentDrawing().getCurrentLayer() || - mouseTarget === selectorManager.selectorParentGroup - ) { - // Escape from in-group edit - return; - } - setContext(mouseTarget); + if ((parent.tagName !== 'g' && parent.tagName !== 'a') || + parent === getCurrentDrawing().getCurrentLayer() || + mouseTarget === selectorManager.selectorParentGroup + ) { + // Escape from in-group edit + return; + } + setContext(mouseTarget); }; // prevent links from being followed in the canvas var handleLinkInCanvas = function (e) { - e.preventDefault(); - return false; + e.preventDefault(); + return false; }; // Added mouseup to the container here. @@ -2316,26 +2316,26 @@ $(container).mousedown(mouseDown).mousemove(mouseMove).click(handleLinkInCanvas) // TODO(rafaelcastrocouto): User preference for shift key and zoom factor $(container).bind('mousewheel DOMMouseScroll', function (e) { - // if (!e.shiftKey) { return; } - e.preventDefault(); - var evt = e.originalEvent; + // if (!e.shiftKey) { return; } + e.preventDefault(); + var evt = e.originalEvent; - rootSctm = $('#svgcontent g')[0].getScreenCTM().inverse(); - var pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm); + rootSctm = $('#svgcontent g')[0].getScreenCTM().inverse(); + var pt = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm); - var bbox = { - 'x': pt.x, - 'y': pt.y, - 'width': 0, - 'height': 0 - }; + var bbox = { + 'x': pt.x, + 'y': pt.y, + 'width': 0, + 'height': 0 + }; - var delta = (evt.wheelDelta) ? evt.wheelDelta : (evt.detail) ? -evt.detail : 0; - if (!delta) { return; } + var delta = (evt.wheelDelta) ? evt.wheelDelta : (evt.detail) ? -evt.detail : 0; + if (!delta) { return; } - bbox.factor = Math.max(3 / 4, Math.min(4 / 3, (delta))); + bbox.factor = Math.max(3 / 4, Math.min(4 / 3, (delta))); - call('zoomed', bbox); + call('zoomed', bbox); }); }()); @@ -2354,386 +2354,386 @@ var lastX, lastY; var allowDbl; function setCursor (index) { - var empty = (textinput.value === ''); - $(textinput).focus(); + var empty = (textinput.value === ''); + $(textinput).focus(); - if (!arguments.length) { - if (empty) { - index = 0; - } else { - if (textinput.selectionEnd !== textinput.selectionStart) { return; } - index = textinput.selectionEnd; - } - } + if (!arguments.length) { + if (empty) { + index = 0; + } else { + if (textinput.selectionEnd !== textinput.selectionStart) { return; } + index = textinput.selectionEnd; + } + } - var charbb; - charbb = chardata[index]; - if (!empty) { - textinput.setSelectionRange(index, index); - } - cursor = svgedit.utilities.getElem('text_cursor'); - if (!cursor) { - cursor = document.createElementNS(NS.SVG, 'line'); - svgedit.utilities.assignAttributes(cursor, { - id: 'text_cursor', - stroke: '#333', - 'stroke-width': 1 - }); - cursor = svgedit.utilities.getElem('selectorParentGroup').appendChild(cursor); - } + var charbb; + charbb = chardata[index]; + if (!empty) { + textinput.setSelectionRange(index, index); + } + cursor = svgedit.utilities.getElem('text_cursor'); + if (!cursor) { + cursor = document.createElementNS(NS.SVG, 'line'); + svgedit.utilities.assignAttributes(cursor, { + id: 'text_cursor', + stroke: '#333', + 'stroke-width': 1 + }); + cursor = svgedit.utilities.getElem('selectorParentGroup').appendChild(cursor); + } - if (!blinker) { - blinker = setInterval(function () { - var show = (cursor.getAttribute('display') === 'none'); - cursor.setAttribute('display', show ? 'inline' : 'none'); - }, 600); - } + if (!blinker) { + blinker = setInterval(function () { + var show = (cursor.getAttribute('display') === 'none'); + cursor.setAttribute('display', show ? 'inline' : 'none'); + }, 600); + } - var startPt = ptToScreen(charbb.x, textbb.y); - var endPt = ptToScreen(charbb.x, (textbb.y + textbb.height)); + var startPt = ptToScreen(charbb.x, textbb.y); + var endPt = ptToScreen(charbb.x, (textbb.y + textbb.height)); - svgedit.utilities.assignAttributes(cursor, { - x1: startPt.x, - y1: startPt.y, - x2: endPt.x, - y2: endPt.y, - visibility: 'visible', - display: 'inline' - }); + svgedit.utilities.assignAttributes(cursor, { + x1: startPt.x, + y1: startPt.y, + x2: endPt.x, + y2: endPt.y, + visibility: 'visible', + display: 'inline' + }); - if (selblock) { selblock.setAttribute('d', ''); } + if (selblock) { selblock.setAttribute('d', ''); } } function setSelection (start, end, skipInput) { - if (start === end) { - setCursor(end); - return; - } + if (start === end) { + setCursor(end); + return; + } - if (!skipInput) { - textinput.setSelectionRange(start, end); - } + if (!skipInput) { + textinput.setSelectionRange(start, end); + } - selblock = svgedit.utilities.getElem('text_selectblock'); - if (!selblock) { - selblock = document.createElementNS(NS.SVG, 'path'); - svgedit.utilities.assignAttributes(selblock, { - id: 'text_selectblock', - fill: 'green', - opacity: 0.5, - style: 'pointer-events:none' - }); - svgedit.utilities.getElem('selectorParentGroup').appendChild(selblock); - } + selblock = svgedit.utilities.getElem('text_selectblock'); + if (!selblock) { + selblock = document.createElementNS(NS.SVG, 'path'); + svgedit.utilities.assignAttributes(selblock, { + id: 'text_selectblock', + fill: 'green', + opacity: 0.5, + style: 'pointer-events:none' + }); + svgedit.utilities.getElem('selectorParentGroup').appendChild(selblock); + } - var startbb = chardata[start]; - var endbb = chardata[end]; + var startbb = chardata[start]; + var endbb = chardata[end]; - cursor.setAttribute('visibility', 'hidden'); + 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 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'; + var dstr = 'M' + tl.x + ',' + tl.y + + ' L' + tr.x + ',' + tr.y + + ' ' + br.x + ',' + br.y + + ' ' + bl.x + ',' + bl.y + 'z'; - svgedit.utilities.assignAttributes(selblock, { - d: dstr, - 'display': 'inline' - }); + svgedit.utilities.assignAttributes(selblock, { + d: dstr, + 'display': 'inline' + }); } function getIndexFromPoint (mouseX, mouseY) { - // Position cursor here - var pt = svgroot.createSVGPoint(); - pt.x = mouseX; - pt.y = mouseY; + // Position cursor here + var pt = svgroot.createSVGPoint(); + pt.x = mouseX; + pt.y = mouseY; - // No content, so return 0 - if (chardata.length === 1) { return 0; } - // Determine if cursor should be on left or right of character - var charpos = curtext.getCharNumAtPosition(pt); - if (charpos < 0) { - // Out of text range, look at mouse coords - charpos = chardata.length - 2; - if (mouseX <= chardata[0].x) { - charpos = 0; - } - } else if (charpos >= chardata.length - 2) { - charpos = chardata.length - 2; - } - var charbb = chardata[charpos]; - var mid = charbb.x + (charbb.width / 2); - if (mouseX > mid) { - charpos++; - } - return charpos; + // No content, so return 0 + if (chardata.length === 1) { return 0; } + // Determine if cursor should be on left or right of character + var charpos = curtext.getCharNumAtPosition(pt); + if (charpos < 0) { + // Out of text range, look at mouse coords + charpos = chardata.length - 2; + if (mouseX <= chardata[0].x) { + charpos = 0; + } + } else if (charpos >= chardata.length - 2) { + charpos = chardata.length - 2; + } + var charbb = chardata[charpos]; + var mid = charbb.x + (charbb.width / 2); + if (mouseX > mid) { + charpos++; + } + return charpos; } function setCursorFromPoint (mouseX, mouseY) { - setCursor(getIndexFromPoint(mouseX, mouseY)); + setCursor(getIndexFromPoint(mouseX, mouseY)); } function setEndSelectionFromPoint (x, y, apply) { - var i1 = textinput.selectionStart; - var i2 = getIndexFromPoint(x, y); + 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); + var start = Math.min(i1, i2); + var end = Math.max(i1, i2); + setSelection(start, end, !apply); } function screenToPt (xIn, yIn) { - var out = { - x: xIn, - y: yIn - }; + var out = { + x: xIn, + y: yIn + }; - out.x /= currentZoom; - out.y /= currentZoom; + out.x /= currentZoom; + out.y /= currentZoom; - if (matrix) { - var pt = svgedit.math.transformPoint(out.x, out.y, matrix.inverse()); - out.x = pt.x; - out.y = pt.y; - } + if (matrix) { + var pt = svgedit.math.transformPoint(out.x, out.y, matrix.inverse()); + out.x = pt.x; + out.y = pt.y; + } - return out; + return out; } function ptToScreen (xIn, yIn) { - var out = { - x: xIn, - y: yIn - }; + var out = { + x: xIn, + y: yIn + }; - if (matrix) { - var pt = svgedit.math.transformPoint(out.x, out.y, matrix); - out.x = pt.x; - out.y = pt.y; - } + if (matrix) { + var pt = svgedit.math.transformPoint(out.x, out.y, matrix); + out.x = pt.x; + out.y = pt.y; + } - out.x *= currentZoom; - out.y *= currentZoom; + out.x *= currentZoom; + out.y *= currentZoom; - return out; + return out; } /* // Not currently in use function hideCursor () { - if (cursor) { - cursor.setAttribute('visibility', 'hidden'); - } + if (cursor) { + cursor.setAttribute('visibility', 'hidden'); + } } */ function selectAll (evt) { - setSelection(0, curtext.textContent.length); - $(this).unbind(evt); + setSelection(0, curtext.textContent.length); + $(this).unbind(evt); } function selectWord (evt) { - if (!allowDbl || !curtext) { return; } + if (!allowDbl || !curtext) { return; } - var ept = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm), - mouseX = ept.x * currentZoom, - mouseY = ept.y * currentZoom; - var pt = screenToPt(mouseX, mouseY); + var ept = svgedit.math.transformPoint(evt.pageX, evt.pageY, rootSctm), + mouseX = ept.x * currentZoom, + mouseY = ept.y * currentZoom; + var pt = screenToPt(mouseX, mouseY); - 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); + 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); + // Set tripleclick + $(evt.target).click(selectAll); + setTimeout(function () { + $(evt.target).unbind('click', selectAll); + }, 300); } return { - select: function (target, x, y) { - curtext = target; - textActions.toEditMode(x, y); - }, - start: function (elem) { - curtext = elem; - textActions.toEditMode(); - }, - mouseDown: function (evt, mouseTarget, startX, startY) { - var pt = screenToPt(startX, startY); + select: function (target, x, y) { + curtext = target; + textActions.toEditMode(x, y); + }, + start: function (elem) { + curtext = elem; + textActions.toEditMode(); + }, + mouseDown: function (evt, mouseTarget, startX, startY) { + var pt = screenToPt(startX, startY); - textinput.focus(); - setCursorFromPoint(pt.x, pt.y); - lastX = startX; - lastY = startY; + textinput.focus(); + setCursorFromPoint(pt.x, pt.y); + lastX = startX; + lastY = startY; - // TODO: Find way to block native selection - }, - mouseMove: function (mouseX, mouseY) { - var pt = screenToPt(mouseX, mouseY); - setEndSelectionFromPoint(pt.x, pt.y); - }, - mouseUp: function (evt, mouseX, mouseY) { - var pt = screenToPt(mouseX, mouseY); + // TODO: Find way to block native selection + }, + mouseMove: function (mouseX, mouseY) { + var pt = screenToPt(mouseX, mouseY); + setEndSelectionFromPoint(pt.x, pt.y); + }, + mouseUp: function (evt, mouseX, mouseY) { + var pt = screenToPt(mouseX, mouseY); - setEndSelectionFromPoint(pt.x, pt.y, true); + setEndSelectionFromPoint(pt.x, pt.y, true); - // TODO: Find a way to make this work: Use transformed BBox instead of evt.target - // if (lastX === mouseX && lastY === mouseY - // && !svgedit.math.rectsIntersect(transbb, {x: pt.x, y: pt.y, width: 0, height: 0})) { - // textActions.toSelectMode(true); - // } + // TODO: Find a way to make this work: Use transformed BBox instead of evt.target + // if (lastX === mouseX && lastY === mouseY + // && !svgedit.math.rectsIntersect(transbb, {x: pt.x, y: pt.y, width: 0, height: 0})) { + // textActions.toSelectMode(true); + // } - if ( - evt.target !== curtext && - mouseX < lastX + 2 && - mouseX > lastX - 2 && - mouseY < lastY + 2 && - mouseY > lastY - 2 - ) { - textActions.toSelectMode(true); - } - }, - setCursor: setCursor, - toEditMode: function (x, y) { - allowDbl = false; - currentMode = 'textedit'; - selectorManager.requestSelector(curtext).showGrips(false); - // Make selector group accept clicks - /* var selector = */ selectorManager.requestSelector(curtext); // Do we need this? Has side effect of setting lock, so keeping for now, but next line wasn't being used - // var sel = selector.selectorRect; + if ( + evt.target !== curtext && + mouseX < lastX + 2 && + mouseX > lastX - 2 && + mouseY < lastY + 2 && + mouseY > lastY - 2 + ) { + textActions.toSelectMode(true); + } + }, + setCursor: setCursor, + toEditMode: function (x, y) { + allowDbl = false; + currentMode = 'textedit'; + selectorManager.requestSelector(curtext).showGrips(false); + // Make selector group accept clicks + /* var selector = */ selectorManager.requestSelector(curtext); // Do we need this? Has side effect of setting lock, so keeping for now, but next line wasn't being used + // var sel = selector.selectorRect; - textActions.init(); + textActions.init(); - $(curtext).css('cursor', 'text'); + $(curtext).css('cursor', 'text'); - // if (svgedit.browser.supportsEditableText()) { - // curtext.setAttribute('editable', 'simple'); - // return; - // } + // if (svgedit.browser.supportsEditableText()) { + // curtext.setAttribute('editable', 'simple'); + // return; + // } - if (!arguments.length) { - setCursor(); - } else { - var pt = screenToPt(x, y); - setCursorFromPoint(pt.x, pt.y); - } + if (!arguments.length) { + setCursor(); + } else { + var pt = screenToPt(x, y); + setCursorFromPoint(pt.x, pt.y); + } - setTimeout(function () { - allowDbl = true; - }, 300); - }, - toSelectMode: function (selectElem) { - currentMode = 'select'; - clearInterval(blinker); - blinker = null; - if (selblock) { $(selblock).attr('display', 'none'); } - if (cursor) { $(cursor).attr('visibility', 'hidden'); } - $(curtext).css('cursor', 'move'); + setTimeout(function () { + allowDbl = true; + }, 300); + }, + toSelectMode: function (selectElem) { + currentMode = 'select'; + clearInterval(blinker); + blinker = null; + if (selblock) { $(selblock).attr('display', 'none'); } + if (cursor) { $(cursor).attr('visibility', 'hidden'); } + $(curtext).css('cursor', 'move'); - if (selectElem) { - clearSelection(); - $(curtext).css('cursor', 'move'); + if (selectElem) { + clearSelection(); + $(curtext).css('cursor', 'move'); - call('selected', [curtext]); - addToSelection([curtext], true); - } - if (curtext && !curtext.textContent.length) { - // No content, so delete - canvas.deleteSelectedElements(); - } + call('selected', [curtext]); + addToSelection([curtext], true); + } + if (curtext && !curtext.textContent.length) { + // No content, so delete + canvas.deleteSelectedElements(); + } - $(textinput).blur(); + $(textinput).blur(); - curtext = false; + curtext = false; - // if (svgedit.browser.supportsEditableText()) { - // curtext.removeAttribute('editable'); - // } - }, - setInputElem: function (elem) { - textinput = elem; - // $(textinput).blur(hideCursor); - }, - clear: function () { - if (currentMode === 'textedit') { - textActions.toSelectMode(); - } - }, - init: function (inputElem) { - if (!curtext) { return; } - var i, end; - // if (svgedit.browser.supportsEditableText()) { - // curtext.select(); - // return; - // } + // if (svgedit.browser.supportsEditableText()) { + // curtext.removeAttribute('editable'); + // } + }, + setInputElem: function (elem) { + textinput = elem; + // $(textinput).blur(hideCursor); + }, + clear: function () { + if (currentMode === 'textedit') { + textActions.toSelectMode(); + } + }, + init: function (inputElem) { + if (!curtext) { return; } + var i, end; + // if (svgedit.browser.supportsEditableText()) { + // curtext.select(); + // return; + // } - if (!curtext.parentNode) { - // Result of the ffClone, need to get correct element - curtext = selectedElements[0]; - selectorManager.requestSelector(curtext).showGrips(false); - } + 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 str = curtext.textContent; + var len = str.length; - var xform = curtext.getAttribute('transform'); + var xform = curtext.getAttribute('transform'); - textbb = svgedit.utilities.getBBox(curtext); + textbb = svgedit.utilities.getBBox(curtext); - matrix = xform ? svgedit.math.getMatrix(curtext) : null; + matrix = xform ? svgedit.math.getMatrix(curtext) : null; - chardata = []; - chardata.length = len; - textinput.focus(); + chardata = []; + chardata.length = len; + textinput.focus(); - $(curtext).unbind('dblclick', selectWord).dblclick(selectWord); + $(curtext).unbind('dblclick', selectWord).dblclick(selectWord); - if (!len) { - end = {x: textbb.x + (textbb.width / 2), width: 0}; - } + if (!len) { + end = {x: textbb.x + (textbb.width / 2), width: 0}; + } - for (i = 0; i < len; i++) { - var start = curtext.getStartPositionOfChar(i); - end = curtext.getEndPositionOfChar(i); + for (i = 0; i < len; i++) { + var start = curtext.getStartPositionOfChar(i); + end = curtext.getEndPositionOfChar(i); - if (!svgedit.browser.supportsGoodTextCharPos()) { - var offset = canvas.contentW * currentZoom; - start.x -= offset; - end.x -= offset; + if (!svgedit.browser.supportsGoodTextCharPos()) { + var offset = canvas.contentW * currentZoom; + start.x -= offset; + end.x -= offset; - start.x /= currentZoom; - end.x /= currentZoom; - } + start.x /= currentZoom; + end.x /= currentZoom; + } - // Get a "bbox" equivalent for each character. Uses the - // bbox data of the actual text for y, height purposes + // 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, - y: textbb.y, // start.y? - width: end.x - start.x, - height: textbb.height - }; - } + // TODO: Decide if y, width and height are actually necessary + chardata[i] = { + x: start.x, + y: textbb.y, // start.y? + width: end.x - start.x, + height: textbb.height + }; + } - // Add a last bbox for cursor at end of text - chardata.push({ - x: end.x, - width: 0 - }); - setSelection(textinput.selectionStart, textinput.selectionEnd, true); - } + // Add a last bbox for cursor at end of text + chardata.push({ + x: end.x, + width: 0 + }); + setSelection(textinput.selectionStart, textinput.selectionEnd, true); + } }; }()); @@ -2746,46 +2746,46 @@ var currentPath; var newPoint, firstCtrl; function resetD (p) { - p.setAttribute('d', pathActions.convertPath(p)); + p.setAttribute('d', pathActions.convertPath(p)); } // TODO: Move into path.js svgedit.path.Path.prototype.endChanges = function (text) { - if (svgedit.browser.isWebkit()) { resetD(this.elem); } - var cmd = new svgedit.history.ChangeElementCommand(this.elem, {d: this.last_d}, text); - addCommandToHistory(cmd); - call('changed', [this.elem]); + if (svgedit.browser.isWebkit()) { resetD(this.elem); } + var cmd = new svgedit.history.ChangeElementCommand(this.elem, {d: this.last_d}, text); + addCommandToHistory(cmd); + call('changed', [this.elem]); }; svgedit.path.Path.prototype.addPtsToSelection = function (indexes) { - var i, seg; - if (!$.isArray(indexes)) { indexes = [indexes]; } - for (i = 0; i < indexes.length; i++) { - var index = indexes[i]; - seg = this.segs[index]; - if (seg.ptgrip) { - if (this.selected_pts.indexOf(index) === -1 && index >= 0) { - this.selected_pts.push(index); - } - } - } - this.selected_pts.sort(); - i = this.selected_pts.length; - var grips = []; - grips.length = i; - // Loop through points to be selected and highlight each - while (i--) { - var pt = this.selected_pts[i]; - seg = this.segs[pt]; - seg.select(true); - grips[i] = seg.ptgrip; - } - // TODO: Correct this: - pathActions.canDeleteNodes = true; + var i, seg; + if (!$.isArray(indexes)) { indexes = [indexes]; } + for (i = 0; i < indexes.length; i++) { + var index = indexes[i]; + seg = this.segs[index]; + if (seg.ptgrip) { + if (this.selected_pts.indexOf(index) === -1 && index >= 0) { + this.selected_pts.push(index); + } + } + } + this.selected_pts.sort(); + i = this.selected_pts.length; + var grips = []; + grips.length = i; + // Loop through points to be selected and highlight each + while (i--) { + var pt = this.selected_pts[i]; + seg = this.segs[pt]; + seg.select(true); + grips[i] = seg.ptgrip; + } + // TODO: Correct this: + pathActions.canDeleteNodes = true; - pathActions.closed_subpath = this.subpathIsClosed(this.selected_pts[0]); + pathActions.closed_subpath = this.subpathIsClosed(this.selected_pts[0]); - call('selected', grips); + call('selected', grips); }; currentPath = null; @@ -2796,867 +2796,867 @@ var hasMoved = false; // a path element and coverts every three line segments into a single bezier // curve in an attempt to smooth out the free-hand var smoothPolylineIntoPath = function (element) { - var i, points = element.points; - 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 - // 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 - // - http://www.ian-ko.com/ET_GeoWizards/UserGuide/smooth.htm - // - http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html - var curpos = points.getItem(0), prevCtlPt = null; - var d = []; - d.push(['M', curpos.x, ',', curpos.y, ' C'].join('')); - for (i = 1; i <= (N - 4); i += 3) { - var ct1 = points.getItem(i); - var ct2 = points.getItem(i + 1); - var end = points.getItem(i + 2); + var i, points = element.points; + 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 + // 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 + // - http://www.ian-ko.com/ET_GeoWizards/UserGuide/smooth.htm + // - http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html + var curpos = points.getItem(0), prevCtlPt = null; + var d = []; + d.push(['M', curpos.x, ',', curpos.y, ' C'].join('')); + for (i = 1; i <= (N - 4); i += 3) { + 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) { - var newpts = svgedit.path.smoothControlPoints(prevCtlPt, ct1, curpos); - if (newpts && newpts.length === 2) { - var prevArr = d[d.length - 1].split(','); - prevArr[2] = newpts[0].x; - prevArr[3] = newpts[0].y; - d[d.length - 1] = prevArr.join(','); - ct1 = newpts[1]; - } - } + // if the previous segment had a control point, we want to smooth out + // the control points on both sides + if (prevCtlPt) { + var newpts = svgedit.path.smoothControlPoints(prevCtlPt, ct1, curpos); + if (newpts && newpts.length === 2) { + var prevArr = d[d.length - 1].split(','); + prevArr[2] = newpts[0].x; + prevArr[3] = newpts[0].y; + d[d.length - 1] = prevArr.join(','); + ct1 = newpts[1]; + } + } - d.push([ct1.x, ct1.y, ct2.x, ct2.y, end.x, end.y].join(',')); + d.push([ct1.x, ct1.y, ct2.x, ct2.y, end.x, end.y].join(',')); - curpos = end; - prevCtlPt = ct2; - } - // handle remaining line segments - d.push('L'); - while (i < N) { - var pt = points.getItem(i); - d.push([pt.x, pt.y].join(',')); - i++; - } - d = d.join(' '); + curpos = end; + prevCtlPt = ct2; + } + // handle remaining line segments + d.push('L'); + while (i < N) { + var pt = points.getItem(i); + d.push([pt.x, pt.y].join(',')); + i++; + } + d = d.join(' '); - // create new path element - element = addSvgElementFromJson({ - element: 'path', - curStyles: true, - attr: { - id: getId(), - d: d, - fill: 'none' - } - }); - // No need to call "changed", as this is already done under mouseUp - } - return element; + // create new path element + element = addSvgElementFromJson({ + element: 'path', + curStyles: true, + attr: { + id: getId(), + d: d, + fill: 'none' + } + }); + // No need to call "changed", as this is already done under mouseUp + } + return element; }; return { - mouseDown: function (evt, mouseTarget, startX, startY) { - var id; - if (currentMode === 'path') { - var mouseX = startX; // Was this meant to work with the other `mouseX`? (was defined globally so adding `var` to at least avoid a global) - var mouseY = startY; // Was this meant to work with the other `mouseY`? (was defined globally so adding `var` to at least avoid a global) - - var x = mouseX / currentZoom, - y = mouseY / currentZoom, - stretchy = svgedit.utilities.getElem('path_stretch_line'); - newPoint = [x, y]; - - if (curConfig.gridSnapping) { - x = svgedit.utilities.snapToGrid(x); - y = svgedit.utilities.snapToGrid(y); - mouseX = svgedit.utilities.snapToGrid(mouseX); - mouseY = svgedit.utilities.snapToGrid(mouseY); - } - - if (!stretchy) { - stretchy = document.createElementNS(NS.SVG, 'path'); - svgedit.utilities.assignAttributes(stretchy, { - id: 'path_stretch_line', - stroke: '#22C', - 'stroke-width': '0.5', - fill: 'none' - }); - stretchy = svgedit.utilities.getElem('selectorParentGroup').appendChild(stretchy); - } - stretchy.setAttribute('display', 'inline'); - - var keep = null; - var index; - // if pts array is empty, create path element with M at current point - if (!drawnPath) { - var dAttr = 'M' + x + ',' + y + ' '; // Was this meant to work with the other `dAttr`? (was defined globally so adding `var` to at least avoid a global) - drawnPath = addSvgElementFromJson({ - element: 'path', - curStyles: true, - attr: { - d: dAttr, - id: getNextId(), - opacity: curShape.opacity / 2 - } - }); - // set stretchy line to first point - stretchy.setAttribute('d', ['M', mouseX, mouseY, mouseX, mouseY].join(' ')); - index = subpath ? svgedit.path.path.segs.length : 0; - svgedit.path.addPointGrip(index, mouseX, mouseY); - } else { - // determine if we clicked on an existing point - var seglist = drawnPath.pathSegList; - var i = seglist.numberOfItems; - var FUZZ = 6 / currentZoom; - var clickOnPoint = false; - while (i) { - i--; - var item = seglist.getItem(i); - var px = item.x, py = item.y; - // found a matching point - if (x >= (px - FUZZ) && x <= (px + FUZZ) && - y >= (py - FUZZ) && y <= (py + FUZZ) - ) { - clickOnPoint = true; - break; - } - } - - // get path element that we are in the process of creating - id = getId(); - - // Remove previous path object if previously created - svgedit.path.removePath_(id); - - var newpath = svgedit.utilities.getElem(id); - var newseg; - var sSeg; - 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 - if (clickOnPoint) { - // if clicked on any other point but the first OR - // the first point was clicked on and there are less than 3 points - // then leave the path open - // otherwise, close the path - if (i <= 1 && len >= 2) { - // Create end segment - var absX = seglist.getItem(0).x; - var absY = seglist.getItem(0).y; - - sSeg = stretchy.pathSegList.getItem(1); - if (sSeg.pathSegType === 4) { - newseg = drawnPath.createSVGPathSegLinetoAbs(absX, absY); - } else { - newseg = drawnPath.createSVGPathSegCurvetoCubicAbs( - absX, - absY, - sSeg.x1 / currentZoom, - sSeg.y1 / currentZoom, - absX, - absY - ); - } - - var endseg = drawnPath.createSVGPathSegClosePath(); - seglist.appendItem(newseg); - seglist.appendItem(endseg); - } else if (len < 3) { - keep = false; - return keep; - } - $(stretchy).remove(); - - // This will signal to commit the path - // var element = newpath; // Other event handlers define own `element`, so this was probably not meant to interact with them or one which shares state (as there were none); I therefore adding a missing `var` to avoid a global - drawnPath = null; - started = false; - - if (subpath) { - if (svgedit.path.path.matrix) { - svgedit.coords.remapElement(newpath, {}, svgedit.path.path.matrix.inverse()); - } - - var newD = newpath.getAttribute('d'); - var origD = $(svgedit.path.path.elem).attr('d'); - $(svgedit.path.path.elem).attr('d', origD + newD); - $(newpath).remove(); - if (svgedit.path.path.matrix) { - svgedit.path.recalcRotatedPath(); - } - svgedit.path.path.init(); - pathActions.toEditMode(svgedit.path.path.elem); - svgedit.path.path.selectPt(); - return false; - } - // else, create a new point, update path element - } else { - // Checks if current target or parents are #svgcontent - if (!$.contains(container, getMouseTarget(evt))) { - // Clicked outside canvas, so don't make point - console.log('Clicked outside canvas'); - return false; - } - - var num = drawnPath.pathSegList.numberOfItems; - var last = drawnPath.pathSegList.getItem(num - 1); - var lastx = last.x, lasty = last.y; - - if (evt.shiftKey) { - var xya = svgedit.math.snapToAngle(lastx, lasty, x, y); - x = xya.x; - y = xya.y; - } - - // Use the segment defined by stretchy - sSeg = stretchy.pathSegList.getItem(1); - if (sSeg.pathSegType === 4) { - newseg = drawnPath.createSVGPathSegLinetoAbs(round(x), round(y)); - } else { - newseg = drawnPath.createSVGPathSegCurvetoCubicAbs( - round(x), - round(y), - sSeg.x1 / currentZoom, - sSeg.y1 / currentZoom, - sSeg.x2 / currentZoom, - sSeg.y2 / currentZoom - ); - } - - drawnPath.pathSegList.appendItem(newseg); - - x *= currentZoom; - y *= currentZoom; - - // set stretchy line to latest point - stretchy.setAttribute('d', ['M', x, y, x, y].join(' ')); - index = num; - if (subpath) { index += svgedit.path.path.segs.length; } - svgedit.path.addPointGrip(index, x, y); - } - // keep = true; - } - - return; - } - - // TODO: Make sure currentPath isn't null at this point - if (!svgedit.path.path) { return; } - - svgedit.path.path.storeD(); - - id = evt.target.id; - var curPt; - if (id.substr(0, 14) === 'pathpointgrip_') { - // Select this point - curPt = svgedit.path.path.cur_pt = parseInt(id.substr(14), 10); - svgedit.path.path.dragging = [startX, startY]; - var seg = svgedit.path.path.segs[curPt]; - - // 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) { - svgedit.path.path.clearSelection(); - } - svgedit.path.path.addPtsToSelection(curPt); - } else if (seg.selected) { - svgedit.path.path.removePtFromSelection(curPt); - } else { - svgedit.path.path.addPtsToSelection(curPt); - } - } else if (id.indexOf('ctrlpointgrip_') === 0) { - svgedit.path.path.dragging = [startX, startY]; - - var parts = id.split('_')[1].split('c'); - curPt = Number(parts[0]); - var ctrlNum = Number(parts[1]); - svgedit.path.path.selectPt(curPt, ctrlNum); - } - - // Start selection box - if (!svgedit.path.path.dragging) { - if (rubberBox == null) { - rubberBox = selectorManager.getRubberBandBox(); - } - svgedit.utilities.assignAttributes(rubberBox, { - 'x': startX * currentZoom, - 'y': startY * currentZoom, - 'width': 0, - 'height': 0, - 'display': 'inline' - }, 100); - } - }, - mouseMove: function (mouseX, mouseY) { - hasMoved = true; - if (currentMode === 'path') { - if (!drawnPath) { return; } - var seglist = drawnPath.pathSegList; - var index = seglist.numberOfItems - 1; - - if (newPoint) { - // First point - // if (!index) { return; } - - // Set control points - var pointGrip1 = svgedit.path.addCtrlGrip('1c1'); - var pointGrip2 = svgedit.path.addCtrlGrip('0c2'); - - // dragging pointGrip1 - pointGrip1.setAttribute('cx', mouseX); - pointGrip1.setAttribute('cy', mouseY); - pointGrip1.setAttribute('display', 'inline'); - - var ptX = newPoint[0]; - var ptY = newPoint[1]; - - // set curve - // var seg = seglist.getItem(index); - var curX = mouseX / currentZoom; - var curY = mouseY / currentZoom; - var altX = (ptX + (ptX - curX)); - var altY = (ptY + (ptY - curY)); - - pointGrip2.setAttribute('cx', altX * currentZoom); - pointGrip2.setAttribute('cy', altY * currentZoom); - pointGrip2.setAttribute('display', 'inline'); - - var ctrlLine = svgedit.path.getCtrlLine(1); - svgedit.utilities.assignAttributes(ctrlLine, { - x1: mouseX, - y1: mouseY, - x2: altX * currentZoom, - y2: altY * currentZoom, - display: 'inline' - }); - - if (index === 0) { - firstCtrl = [mouseX, mouseY]; - } else { - var last = seglist.getItem(index - 1); - var lastX = last.x; - var lastY = last.y; - - if (last.pathSegType === 6) { - lastX += (lastX - last.x2); - lastY += (lastY - last.y2); - } else if (firstCtrl) { - lastX = firstCtrl[0] / currentZoom; - lastY = firstCtrl[1] / currentZoom; - } - svgedit.path.replacePathSeg(6, index, [ptX, ptY, lastX, lastY, altX, altY], drawnPath); - } - } else { - var stretchy = svgedit.utilities.getElem('path_stretch_line'); - if (stretchy) { - var prev = seglist.getItem(index); - if (prev.pathSegType === 6) { - var prevX = prev.x + (prev.x - prev.x2); - var prevY = prev.y + (prev.y - prev.y2); - svgedit.path.replacePathSeg(6, 1, [mouseX, mouseY, prevX * currentZoom, prevY * currentZoom, mouseX, mouseY], stretchy); - } else if (firstCtrl) { - svgedit.path.replacePathSeg(6, 1, [mouseX, mouseY, firstCtrl[0], firstCtrl[1], mouseX, mouseY], stretchy); - } else { - svgedit.path.replacePathSeg(4, 1, [mouseX, mouseY], stretchy); - } - } - } - return; - } - // if we are dragging a point, let's move it - if (svgedit.path.path.dragging) { - var pt = svgedit.path.getPointFromGrip({ - x: svgedit.path.path.dragging[0], - y: svgedit.path.path.dragging[1] - }, svgedit.path.path); - var mpt = svgedit.path.getPointFromGrip({ - x: mouseX, - y: mouseY - }, svgedit.path.path); - var diffX = mpt.x - pt.x; - var diffY = mpt.y - pt.y; - svgedit.path.path.dragging = [mouseX, mouseY]; - - if (svgedit.path.path.dragctrl) { - svgedit.path.path.moveCtrl(diffX, diffY); - } else { - svgedit.path.path.movePts(diffX, diffY); - } - } else { - svgedit.path.path.selected_pts = []; - svgedit.path.path.eachSeg(function (i) { - var seg = this; - if (!seg.next && !seg.prev) { return; } - - // var item = seg.item; - var rbb = rubberBox.getBBox(); - - var pt = svgedit.path.getGripPt(seg); - var ptBb = { - x: pt.x, - y: pt.y, - width: 0, - height: 0 - }; - - var sel = svgedit.math.rectsIntersect(rbb, ptBb); - - this.select(sel); - // Note that addPtsToSelection is not being run - if (sel) { svgedit.path.path.selected_pts.push(seg.index); } - }); - } - }, - mouseUp: function (evt, element, mouseX, mouseY) { - // Create mode - if (currentMode === 'path') { - newPoint = null; - if (!drawnPath) { - element = svgedit.utilities.getElem(getId()); - started = false; - firstCtrl = null; - } - - return { - keep: true, - element: element - }; - } - - // Edit mode - - if (svgedit.path.path.dragging) { - var lastPt = 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(lastPt); - } - } 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 { - pathActions.toSelectMode(evt.target); - } - hasMoved = false; - }, - toEditMode: function (element) { - svgedit.path.path = svgedit.path.getPath_(element); - currentMode = 'pathedit'; - clearSelection(); - svgedit.path.path.show(true).update(); - svgedit.path.path.oldbbox = svgedit.utilities.getBBox(svgedit.path.path.elem); - subpath = false; - }, - toSelectMode: function (elem) { - var selPath = (elem === svgedit.path.path.elem); - currentMode = 'select'; - svgedit.path.path.show(false); - currentPath = 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); - } - }, - addSubPath: function (on) { - if (on) { - // Internally we go into "path" mode, but in the UI it will - // still appear as if in "pathedit" mode. - currentMode = 'path'; - subpath = true; - } else { - pathActions.clear(true); - pathActions.toEditMode(svgedit.path.path.elem); - } - }, - select: function (target) { - if (currentPath === target) { - pathActions.toEditMode(target); - currentMode = 'pathedit'; - // going into pathedit mode - } else { - currentPath = target; - } - }, - reorient: function () { - var elem = selectedElements[0]; - if (!elem) { return; } - var angle = svgedit.utilities.getRotationAngle(elem); - if (angle === 0) { return; } - - var batchCmd = new svgedit.history.BatchCommand('Reorient path'); - var changes = { - d: elem.getAttribute('d'), - transform: elem.getAttribute('transform') - }; - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes)); - clearSelection(); - this.resetOrientation(elem); - - addCommandToHistory(batchCmd); - - // Set matrix to null - svgedit.path.getPath_(elem).show(false).matrix = null; - - this.clear(); - - addToSelection([elem], true); - call('changed', selectedElements); - }, - - clear: function (remove) { - currentPath = null; - if (drawnPath) { - var elem = svgedit.utilities.getElem(getId()); - $(svgedit.utilities.getElem('path_stretch_line')).remove(); - $(elem).remove(); - $(svgedit.utilities.getElem('pathpointgrip_container')).find('*').attr('display', 'none'); - drawnPath = firstCtrl = null; - started = false; - } else if (currentMode === 'pathedit') { - this.toSelectMode(); - } - if (svgedit.path.path) { svgedit.path.path.init().show(false); } - }, - resetOrientation: function (path) { - if (path == null || path.nodeName !== 'path') { return false; } - var tlist = svgedit.transformlist.getTransformList(path); - var m = svgedit.math.transformListToTransform(tlist).matrix; - 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) { - // var fixed_d = pathActions.convertPath(path); - // path.setAttribute('d', fixed_d); - // segList = path.pathSegList; - // var len = segList.numberOfItems; - // } - var i; // , lastX, lastY; - for (i = 0; i < len; ++i) { - var seg = segList.getItem(i); - var type = seg.pathSegType; - if (type === 1) { continue; } - var pts = []; - $.each(['', 1, 2], function (j, n) { - var x = seg['x' + n], y = seg['y' + n]; - if (x !== undefined && y !== undefined) { - var pt = svgedit.math.transformPoint(x, y, m); - pts.splice(pts.length, 0, pt.x, pt.y); - } - }); - svgedit.path.replacePathSeg(type, i, pts, path); - } - - reorientGrads(path, m); - }, - zoomChange: function () { - if (currentMode === 'pathedit') { - svgedit.path.path.update(); - } - }, - getNodePoint: function () { - var selPt = svgedit.path.path.selected_pts.length ? svgedit.path.path.selected_pts[0] : 1; - - var seg = svgedit.path.path.segs[selPt]; - return { - x: seg.item.x, - y: seg.item.y, - type: seg.type - }; - }, - linkControlPoints: function (linkPoints) { - svgedit.path.setLinkControlPoints(linkPoints); - }, - clonePathNode: function () { - svgedit.path.path.storeD(); - - var selPts = svgedit.path.path.selected_pts; - // var segs = svgedit.path.path.segs; - - var i = selPts.length; - var nums = []; - - while (i--) { - var pt = selPts[i]; - svgedit.path.path.addSeg(pt); - - nums.push(pt + i); - nums.push(pt + i + 1); - } - svgedit.path.path.init().addPtsToSelection(nums); - - svgedit.path.path.endChanges('Clone path node(s)'); - }, - opencloseSubPath: function () { - var selPts = svgedit.path.path.selected_pts; - // Only allow one selected node for now - if (selPts.length !== 1) { return; } - - var elem = svgedit.path.path.elem; - var list = elem.pathSegList; - - // var len = list.numberOfItems; - - var index = selPts[0]; - - var openPt = null; - var startItem = null; - - // Check if subpath is already open - svgedit.path.path.eachSeg(function (i) { - if (this.type === 2 && i <= index) { - startItem = this.item; - } - if (i <= index) { return true; } - if (this.type === 2) { - // Found M first, so open - openPt = i; - return false; - } - if (this.type === 1) { - // Found Z first, so closed - openPt = false; - return false; - } - }); - - if (openPt == null) { - // Single path, so close last seg - openPt = svgedit.path.path.segs.length - 1; - } - - if (openPt !== false) { - // Close this path - - // Create a line going to the previous "M" - var newseg = elem.createSVGPathSegLinetoAbs(startItem.x, startItem.y); - - var closer = elem.createSVGPathSegClosePath(); - if (openPt === svgedit.path.path.segs.length - 1) { - list.appendItem(newseg); - list.appendItem(closer); - } else { - svgedit.path.insertItemBefore(elem, closer, openPt); - svgedit.path.insertItemBefore(elem, newseg, openPt); - } - - svgedit.path.path.init().selectPt(openPt + 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 - - 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 i, lastM, zSeg; - - // Find this sub-path's closing point and remove - for (i = 0; i < list.numberOfItems; i++) { - var item = list.getItem(i); - - if (item.pathSegType === 2) { - // Find the preceding M - lastM = i; - } else if (i === index) { - // Remove it - list.removeItem(lastM); - // index--; - } else if (item.pathSegType === 1 && index < i) { - // Remove the closing seg of this subpath - zSeg = i - 1; - list.removeItem(i); - break; - } - } - - var num = (index - lastM) - 1; - - while (num--) { - svgedit.path.insertItemBefore(elem, list.getItem(lastM), zSeg); - } - - var pt = list.getItem(lastM); - - // Make this point the new "M" - svgedit.path.replacePathSeg(2, lastM, [pt.x, pt.y]); - - i = index; // i is local here, so has no effect; what is the reason for this? - - svgedit.path.path.init().selectPt(0); - }, - deletePathNode: function () { - if (!pathActions.canDeleteNodes) { return; } - svgedit.path.path.storeD(); - - var selPts = svgedit.path.path.selected_pts; - var i = selPts.length; - - while (i--) { - var pt = selPts[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); - } - }; - - if (len <= 1) { return true; } - - while (len--) { - var item = segList.getItem(len); - if (item.pathSegType === 1) { - var prev = segList.getItem(len - 1); - var nprev = segList.getItem(len - 2); - if (prev.pathSegType === 2) { - remItems(len - 1, 2); - cleanup(); - break; - } else if (nprev.pathSegType === 2) { - remItems(len - 2, 3); - cleanup(); - break; - } - } else if (item.pathSegType === 2) { - if (len > 0) { - var prevType = segList.getItem(len - 1).pathSegType; - // Path has M M - if (prevType === 2) { - remItems(len - 1, 1); - cleanup(); - break; - // Entire path ends with Z M - } else if (prevType === 1 && segList.numberOfItems - 1 === len) { - remItems(len, 1); - cleanup(); - break; - } - } - } - } - return false; - }; - - cleanup(); - - // Completely delete a path with 1 or 0 segments - if (svgedit.path.path.elem.pathSegList.numberOfItems <= 1) { - pathActions.toSelectMode(svgedit.path.path.elem); - canvas.deleteSelectedElements(); - return; - } - - svgedit.path.path.init(); - svgedit.path.path.clearSelection(); - - // TODO: Find right way to select point now - // path.selectPt(selPt); - if (window.opera) { // Opera repaints incorrectly - var cp = $(svgedit.path.path.elem); - cp.attr('d', cp.attr('d')); - } - svgedit.path.path.endChanges('Delete path node(s)'); - }, - smoothPolylineIntoPath: smoothPolylineIntoPath, - setSegType: function (v) { - svgedit.path.path.setSegType(v); - }, - moveNode: function (attr, newValue) { - var selPts = svgedit.path.path.selected_pts; - if (!selPts.length) { return; } - - svgedit.path.path.storeD(); - - // Get first selected point - var seg = svgedit.path.path.segs[selPts[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'); - }, - fixEnd: function (elem) { - // Adds an extra segment if the last seg before a Z doesn't end - // at its M point - // M0,0 L0,100 L100,100 z - var segList = elem.pathSegList; - var len = segList.numberOfItems; - var i, lastM; - for (i = 0; i < len; ++i) { - var item = segList.getItem(i); - if (item.pathSegType === 2) { - lastM = item; - } - - if (item.pathSegType === 1) { - var prev = segList.getItem(i - 1); - if (prev.x !== lastM.x || prev.y !== lastM.y) { - // Add an L segment here - var newseg = elem.createSVGPathSegLinetoAbs(lastM.x, lastM.y); - svgedit.path.insertItemBefore(elem, newseg, i); - // Can this be done better? - pathActions.fixEnd(elem); - break; - } - } - } - if (svgedit.browser.isWebkit()) { resetD(elem); } - }, - // Convert a path to one with only absolute or relative values - convertPath: svgedit.utilities.convertPath + mouseDown: function (evt, mouseTarget, startX, startY) { + var id; + if (currentMode === 'path') { + var mouseX = startX; // Was this meant to work with the other `mouseX`? (was defined globally so adding `var` to at least avoid a global) + var mouseY = startY; // Was this meant to work with the other `mouseY`? (was defined globally so adding `var` to at least avoid a global) + + var x = mouseX / currentZoom, + y = mouseY / currentZoom, + stretchy = svgedit.utilities.getElem('path_stretch_line'); + newPoint = [x, y]; + + if (curConfig.gridSnapping) { + x = svgedit.utilities.snapToGrid(x); + y = svgedit.utilities.snapToGrid(y); + mouseX = svgedit.utilities.snapToGrid(mouseX); + mouseY = svgedit.utilities.snapToGrid(mouseY); + } + + if (!stretchy) { + stretchy = document.createElementNS(NS.SVG, 'path'); + svgedit.utilities.assignAttributes(stretchy, { + id: 'path_stretch_line', + stroke: '#22C', + 'stroke-width': '0.5', + fill: 'none' + }); + stretchy = svgedit.utilities.getElem('selectorParentGroup').appendChild(stretchy); + } + stretchy.setAttribute('display', 'inline'); + + var keep = null; + var index; + // if pts array is empty, create path element with M at current point + if (!drawnPath) { + var dAttr = 'M' + x + ',' + y + ' '; // Was this meant to work with the other `dAttr`? (was defined globally so adding `var` to at least avoid a global) + drawnPath = addSvgElementFromJson({ + element: 'path', + curStyles: true, + attr: { + d: dAttr, + id: getNextId(), + opacity: curShape.opacity / 2 + } + }); + // set stretchy line to first point + stretchy.setAttribute('d', ['M', mouseX, mouseY, mouseX, mouseY].join(' ')); + index = subpath ? svgedit.path.path.segs.length : 0; + svgedit.path.addPointGrip(index, mouseX, mouseY); + } else { + // determine if we clicked on an existing point + var seglist = drawnPath.pathSegList; + var i = seglist.numberOfItems; + var FUZZ = 6 / currentZoom; + var clickOnPoint = false; + while (i) { + i--; + var item = seglist.getItem(i); + var px = item.x, py = item.y; + // found a matching point + if (x >= (px - FUZZ) && x <= (px + FUZZ) && + y >= (py - FUZZ) && y <= (py + FUZZ) + ) { + clickOnPoint = true; + break; + } + } + + // get path element that we are in the process of creating + id = getId(); + + // Remove previous path object if previously created + svgedit.path.removePath_(id); + + var newpath = svgedit.utilities.getElem(id); + var newseg; + var sSeg; + 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 + if (clickOnPoint) { + // if clicked on any other point but the first OR + // the first point was clicked on and there are less than 3 points + // then leave the path open + // otherwise, close the path + if (i <= 1 && len >= 2) { + // Create end segment + var absX = seglist.getItem(0).x; + var absY = seglist.getItem(0).y; + + sSeg = stretchy.pathSegList.getItem(1); + if (sSeg.pathSegType === 4) { + newseg = drawnPath.createSVGPathSegLinetoAbs(absX, absY); + } else { + newseg = drawnPath.createSVGPathSegCurvetoCubicAbs( + absX, + absY, + sSeg.x1 / currentZoom, + sSeg.y1 / currentZoom, + absX, + absY + ); + } + + var endseg = drawnPath.createSVGPathSegClosePath(); + seglist.appendItem(newseg); + seglist.appendItem(endseg); + } else if (len < 3) { + keep = false; + return keep; + } + $(stretchy).remove(); + + // This will signal to commit the path + // var element = newpath; // Other event handlers define own `element`, so this was probably not meant to interact with them or one which shares state (as there were none); I therefore adding a missing `var` to avoid a global + drawnPath = null; + started = false; + + if (subpath) { + if (svgedit.path.path.matrix) { + svgedit.coords.remapElement(newpath, {}, svgedit.path.path.matrix.inverse()); + } + + var newD = newpath.getAttribute('d'); + var origD = $(svgedit.path.path.elem).attr('d'); + $(svgedit.path.path.elem).attr('d', origD + newD); + $(newpath).remove(); + if (svgedit.path.path.matrix) { + svgedit.path.recalcRotatedPath(); + } + svgedit.path.path.init(); + pathActions.toEditMode(svgedit.path.path.elem); + svgedit.path.path.selectPt(); + return false; + } + // else, create a new point, update path element + } else { + // Checks if current target or parents are #svgcontent + if (!$.contains(container, getMouseTarget(evt))) { + // Clicked outside canvas, so don't make point + console.log('Clicked outside canvas'); + return false; + } + + var num = drawnPath.pathSegList.numberOfItems; + var last = drawnPath.pathSegList.getItem(num - 1); + var lastx = last.x, lasty = last.y; + + if (evt.shiftKey) { + var xya = svgedit.math.snapToAngle(lastx, lasty, x, y); + x = xya.x; + y = xya.y; + } + + // Use the segment defined by stretchy + sSeg = stretchy.pathSegList.getItem(1); + if (sSeg.pathSegType === 4) { + newseg = drawnPath.createSVGPathSegLinetoAbs(round(x), round(y)); + } else { + newseg = drawnPath.createSVGPathSegCurvetoCubicAbs( + round(x), + round(y), + sSeg.x1 / currentZoom, + sSeg.y1 / currentZoom, + sSeg.x2 / currentZoom, + sSeg.y2 / currentZoom + ); + } + + drawnPath.pathSegList.appendItem(newseg); + + x *= currentZoom; + y *= currentZoom; + + // set stretchy line to latest point + stretchy.setAttribute('d', ['M', x, y, x, y].join(' ')); + index = num; + if (subpath) { index += svgedit.path.path.segs.length; } + svgedit.path.addPointGrip(index, x, y); + } + // keep = true; + } + + return; + } + + // TODO: Make sure currentPath isn't null at this point + if (!svgedit.path.path) { return; } + + svgedit.path.path.storeD(); + + id = evt.target.id; + var curPt; + if (id.substr(0, 14) === 'pathpointgrip_') { + // Select this point + curPt = svgedit.path.path.cur_pt = parseInt(id.substr(14), 10); + svgedit.path.path.dragging = [startX, startY]; + var seg = svgedit.path.path.segs[curPt]; + + // 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) { + svgedit.path.path.clearSelection(); + } + svgedit.path.path.addPtsToSelection(curPt); + } else if (seg.selected) { + svgedit.path.path.removePtFromSelection(curPt); + } else { + svgedit.path.path.addPtsToSelection(curPt); + } + } else if (id.indexOf('ctrlpointgrip_') === 0) { + svgedit.path.path.dragging = [startX, startY]; + + var parts = id.split('_')[1].split('c'); + curPt = Number(parts[0]); + var ctrlNum = Number(parts[1]); + svgedit.path.path.selectPt(curPt, ctrlNum); + } + + // Start selection box + if (!svgedit.path.path.dragging) { + if (rubberBox == null) { + rubberBox = selectorManager.getRubberBandBox(); + } + svgedit.utilities.assignAttributes(rubberBox, { + 'x': startX * currentZoom, + 'y': startY * currentZoom, + 'width': 0, + 'height': 0, + 'display': 'inline' + }, 100); + } + }, + mouseMove: function (mouseX, mouseY) { + hasMoved = true; + if (currentMode === 'path') { + if (!drawnPath) { return; } + var seglist = drawnPath.pathSegList; + var index = seglist.numberOfItems - 1; + + if (newPoint) { + // First point + // if (!index) { return; } + + // Set control points + var pointGrip1 = svgedit.path.addCtrlGrip('1c1'); + var pointGrip2 = svgedit.path.addCtrlGrip('0c2'); + + // dragging pointGrip1 + pointGrip1.setAttribute('cx', mouseX); + pointGrip1.setAttribute('cy', mouseY); + pointGrip1.setAttribute('display', 'inline'); + + var ptX = newPoint[0]; + var ptY = newPoint[1]; + + // set curve + // var seg = seglist.getItem(index); + var curX = mouseX / currentZoom; + var curY = mouseY / currentZoom; + var altX = (ptX + (ptX - curX)); + var altY = (ptY + (ptY - curY)); + + pointGrip2.setAttribute('cx', altX * currentZoom); + pointGrip2.setAttribute('cy', altY * currentZoom); + pointGrip2.setAttribute('display', 'inline'); + + var ctrlLine = svgedit.path.getCtrlLine(1); + svgedit.utilities.assignAttributes(ctrlLine, { + x1: mouseX, + y1: mouseY, + x2: altX * currentZoom, + y2: altY * currentZoom, + display: 'inline' + }); + + if (index === 0) { + firstCtrl = [mouseX, mouseY]; + } else { + var last = seglist.getItem(index - 1); + var lastX = last.x; + var lastY = last.y; + + if (last.pathSegType === 6) { + lastX += (lastX - last.x2); + lastY += (lastY - last.y2); + } else if (firstCtrl) { + lastX = firstCtrl[0] / currentZoom; + lastY = firstCtrl[1] / currentZoom; + } + svgedit.path.replacePathSeg(6, index, [ptX, ptY, lastX, lastY, altX, altY], drawnPath); + } + } else { + var stretchy = svgedit.utilities.getElem('path_stretch_line'); + if (stretchy) { + var prev = seglist.getItem(index); + if (prev.pathSegType === 6) { + var prevX = prev.x + (prev.x - prev.x2); + var prevY = prev.y + (prev.y - prev.y2); + svgedit.path.replacePathSeg(6, 1, [mouseX, mouseY, prevX * currentZoom, prevY * currentZoom, mouseX, mouseY], stretchy); + } else if (firstCtrl) { + svgedit.path.replacePathSeg(6, 1, [mouseX, mouseY, firstCtrl[0], firstCtrl[1], mouseX, mouseY], stretchy); + } else { + svgedit.path.replacePathSeg(4, 1, [mouseX, mouseY], stretchy); + } + } + } + return; + } + // if we are dragging a point, let's move it + if (svgedit.path.path.dragging) { + var pt = svgedit.path.getPointFromGrip({ + x: svgedit.path.path.dragging[0], + y: svgedit.path.path.dragging[1] + }, svgedit.path.path); + var mpt = svgedit.path.getPointFromGrip({ + x: mouseX, + y: mouseY + }, svgedit.path.path); + var diffX = mpt.x - pt.x; + var diffY = mpt.y - pt.y; + svgedit.path.path.dragging = [mouseX, mouseY]; + + if (svgedit.path.path.dragctrl) { + svgedit.path.path.moveCtrl(diffX, diffY); + } else { + svgedit.path.path.movePts(diffX, diffY); + } + } else { + svgedit.path.path.selected_pts = []; + svgedit.path.path.eachSeg(function (i) { + var seg = this; + if (!seg.next && !seg.prev) { return; } + + // var item = seg.item; + var rbb = rubberBox.getBBox(); + + var pt = svgedit.path.getGripPt(seg); + var ptBb = { + x: pt.x, + y: pt.y, + width: 0, + height: 0 + }; + + var sel = svgedit.math.rectsIntersect(rbb, ptBb); + + this.select(sel); + // Note that addPtsToSelection is not being run + if (sel) { svgedit.path.path.selected_pts.push(seg.index); } + }); + } + }, + mouseUp: function (evt, element, mouseX, mouseY) { + // Create mode + if (currentMode === 'path') { + newPoint = null; + if (!drawnPath) { + element = svgedit.utilities.getElem(getId()); + started = false; + firstCtrl = null; + } + + return { + keep: true, + element: element + }; + } + + // Edit mode + + if (svgedit.path.path.dragging) { + var lastPt = 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(lastPt); + } + } 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 { + pathActions.toSelectMode(evt.target); + } + hasMoved = false; + }, + toEditMode: function (element) { + svgedit.path.path = svgedit.path.getPath_(element); + currentMode = 'pathedit'; + clearSelection(); + svgedit.path.path.show(true).update(); + svgedit.path.path.oldbbox = svgedit.utilities.getBBox(svgedit.path.path.elem); + subpath = false; + }, + toSelectMode: function (elem) { + var selPath = (elem === svgedit.path.path.elem); + currentMode = 'select'; + svgedit.path.path.show(false); + currentPath = 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); + } + }, + addSubPath: function (on) { + if (on) { + // Internally we go into "path" mode, but in the UI it will + // still appear as if in "pathedit" mode. + currentMode = 'path'; + subpath = true; + } else { + pathActions.clear(true); + pathActions.toEditMode(svgedit.path.path.elem); + } + }, + select: function (target) { + if (currentPath === target) { + pathActions.toEditMode(target); + currentMode = 'pathedit'; + // going into pathedit mode + } else { + currentPath = target; + } + }, + reorient: function () { + var elem = selectedElements[0]; + if (!elem) { return; } + var angle = svgedit.utilities.getRotationAngle(elem); + if (angle === 0) { return; } + + var batchCmd = new svgedit.history.BatchCommand('Reorient path'); + var changes = { + d: elem.getAttribute('d'), + transform: elem.getAttribute('transform') + }; + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes)); + clearSelection(); + this.resetOrientation(elem); + + addCommandToHistory(batchCmd); + + // Set matrix to null + svgedit.path.getPath_(elem).show(false).matrix = null; + + this.clear(); + + addToSelection([elem], true); + call('changed', selectedElements); + }, + + clear: function (remove) { + currentPath = null; + if (drawnPath) { + var elem = svgedit.utilities.getElem(getId()); + $(svgedit.utilities.getElem('path_stretch_line')).remove(); + $(elem).remove(); + $(svgedit.utilities.getElem('pathpointgrip_container')).find('*').attr('display', 'none'); + drawnPath = firstCtrl = null; + started = false; + } else if (currentMode === 'pathedit') { + this.toSelectMode(); + } + if (svgedit.path.path) { svgedit.path.path.init().show(false); } + }, + resetOrientation: function (path) { + if (path == null || path.nodeName !== 'path') { return false; } + var tlist = svgedit.transformlist.getTransformList(path); + var m = svgedit.math.transformListToTransform(tlist).matrix; + 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) { + // var fixed_d = pathActions.convertPath(path); + // path.setAttribute('d', fixed_d); + // segList = path.pathSegList; + // var len = segList.numberOfItems; + // } + var i; // , lastX, lastY; + for (i = 0; i < len; ++i) { + var seg = segList.getItem(i); + var type = seg.pathSegType; + if (type === 1) { continue; } + var pts = []; + $.each(['', 1, 2], function (j, n) { + var x = seg['x' + n], y = seg['y' + n]; + if (x !== undefined && y !== undefined) { + var pt = svgedit.math.transformPoint(x, y, m); + pts.splice(pts.length, 0, pt.x, pt.y); + } + }); + svgedit.path.replacePathSeg(type, i, pts, path); + } + + reorientGrads(path, m); + }, + zoomChange: function () { + if (currentMode === 'pathedit') { + svgedit.path.path.update(); + } + }, + getNodePoint: function () { + var selPt = svgedit.path.path.selected_pts.length ? svgedit.path.path.selected_pts[0] : 1; + + var seg = svgedit.path.path.segs[selPt]; + return { + x: seg.item.x, + y: seg.item.y, + type: seg.type + }; + }, + linkControlPoints: function (linkPoints) { + svgedit.path.setLinkControlPoints(linkPoints); + }, + clonePathNode: function () { + svgedit.path.path.storeD(); + + var selPts = svgedit.path.path.selected_pts; + // var segs = svgedit.path.path.segs; + + var i = selPts.length; + var nums = []; + + while (i--) { + var pt = selPts[i]; + svgedit.path.path.addSeg(pt); + + nums.push(pt + i); + nums.push(pt + i + 1); + } + svgedit.path.path.init().addPtsToSelection(nums); + + svgedit.path.path.endChanges('Clone path node(s)'); + }, + opencloseSubPath: function () { + var selPts = svgedit.path.path.selected_pts; + // Only allow one selected node for now + if (selPts.length !== 1) { return; } + + var elem = svgedit.path.path.elem; + var list = elem.pathSegList; + + // var len = list.numberOfItems; + + var index = selPts[0]; + + var openPt = null; + var startItem = null; + + // Check if subpath is already open + svgedit.path.path.eachSeg(function (i) { + if (this.type === 2 && i <= index) { + startItem = this.item; + } + if (i <= index) { return true; } + if (this.type === 2) { + // Found M first, so open + openPt = i; + return false; + } + if (this.type === 1) { + // Found Z first, so closed + openPt = false; + return false; + } + }); + + if (openPt == null) { + // Single path, so close last seg + openPt = svgedit.path.path.segs.length - 1; + } + + if (openPt !== false) { + // Close this path + + // Create a line going to the previous "M" + var newseg = elem.createSVGPathSegLinetoAbs(startItem.x, startItem.y); + + var closer = elem.createSVGPathSegClosePath(); + if (openPt === svgedit.path.path.segs.length - 1) { + list.appendItem(newseg); + list.appendItem(closer); + } else { + svgedit.path.insertItemBefore(elem, closer, openPt); + svgedit.path.insertItemBefore(elem, newseg, openPt); + } + + svgedit.path.path.init().selectPt(openPt + 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 + + 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 i, lastM, zSeg; + + // Find this sub-path's closing point and remove + for (i = 0; i < list.numberOfItems; i++) { + var item = list.getItem(i); + + if (item.pathSegType === 2) { + // Find the preceding M + lastM = i; + } else if (i === index) { + // Remove it + list.removeItem(lastM); + // index--; + } else if (item.pathSegType === 1 && index < i) { + // Remove the closing seg of this subpath + zSeg = i - 1; + list.removeItem(i); + break; + } + } + + var num = (index - lastM) - 1; + + while (num--) { + svgedit.path.insertItemBefore(elem, list.getItem(lastM), zSeg); + } + + var pt = list.getItem(lastM); + + // Make this point the new "M" + svgedit.path.replacePathSeg(2, lastM, [pt.x, pt.y]); + + i = index; // i is local here, so has no effect; what is the reason for this? + + svgedit.path.path.init().selectPt(0); + }, + deletePathNode: function () { + if (!pathActions.canDeleteNodes) { return; } + svgedit.path.path.storeD(); + + var selPts = svgedit.path.path.selected_pts; + var i = selPts.length; + + while (i--) { + var pt = selPts[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); + } + }; + + if (len <= 1) { return true; } + + while (len--) { + var item = segList.getItem(len); + if (item.pathSegType === 1) { + var prev = segList.getItem(len - 1); + var nprev = segList.getItem(len - 2); + if (prev.pathSegType === 2) { + remItems(len - 1, 2); + cleanup(); + break; + } else if (nprev.pathSegType === 2) { + remItems(len - 2, 3); + cleanup(); + break; + } + } else if (item.pathSegType === 2) { + if (len > 0) { + var prevType = segList.getItem(len - 1).pathSegType; + // Path has M M + if (prevType === 2) { + remItems(len - 1, 1); + cleanup(); + break; + // Entire path ends with Z M + } else if (prevType === 1 && segList.numberOfItems - 1 === len) { + remItems(len, 1); + cleanup(); + break; + } + } + } + } + return false; + }; + + cleanup(); + + // Completely delete a path with 1 or 0 segments + if (svgedit.path.path.elem.pathSegList.numberOfItems <= 1) { + pathActions.toSelectMode(svgedit.path.path.elem); + canvas.deleteSelectedElements(); + return; + } + + svgedit.path.path.init(); + svgedit.path.path.clearSelection(); + + // TODO: Find right way to select point now + // path.selectPt(selPt); + if (window.opera) { // Opera repaints incorrectly + var cp = $(svgedit.path.path.elem); + cp.attr('d', cp.attr('d')); + } + svgedit.path.path.endChanges('Delete path node(s)'); + }, + smoothPolylineIntoPath: smoothPolylineIntoPath, + setSegType: function (v) { + svgedit.path.path.setSegType(v); + }, + moveNode: function (attr, newValue) { + var selPts = svgedit.path.path.selected_pts; + if (!selPts.length) { return; } + + svgedit.path.path.storeD(); + + // Get first selected point + var seg = svgedit.path.path.segs[selPts[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'); + }, + fixEnd: function (elem) { + // Adds an extra segment if the last seg before a Z doesn't end + // at its M point + // M0,0 L0,100 L100,100 z + var segList = elem.pathSegList; + var len = segList.numberOfItems; + var i, lastM; + for (i = 0; i < len; ++i) { + var item = segList.getItem(i); + if (item.pathSegType === 2) { + lastM = item; + } + + if (item.pathSegType === 1) { + var prev = segList.getItem(i - 1); + if (prev.x !== lastM.x || prev.y !== lastM.y) { + // Add an L segment here + var newseg = elem.createSVGPathSegLinetoAbs(lastM.x, lastM.y); + svgedit.path.insertItemBefore(elem, newseg, i); + // Can this be done better? + pathActions.fixEnd(elem); + break; + } + } + } + if (svgedit.browser.isWebkit()) { resetD(elem); } + }, + // Convert a path to one with only absolute or relative values + convertPath: svgedit.utilities.convertPath }; })(); // end pathActions @@ -3670,50 +3670,50 @@ return { // Returns: // The amount of elements that were removed var removeUnusedDefElems = this.removeUnusedDefElems = function () { - var defs = svgcontent.getElementsByTagNameNS(NS.SVG, 'defs'); - if (!defs || !defs.length) { return 0; } + var defs = svgcontent.getElementsByTagNameNS(NS.SVG, 'defs'); + if (!defs || !defs.length) { return 0; } - // if (!defs.firstChild) { return; } + // if (!defs.firstChild) { return; } - var defelemUses = [], - numRemoved = 0; - var attrs = ['fill', 'stroke', 'filter', 'marker-start', 'marker-mid', 'marker-end']; - var alen = attrs.length; + var defelemUses = [], + numRemoved = 0; + var attrs = ['fill', 'stroke', 'filter', 'marker-start', 'marker-mid', 'marker-end']; + var alen = attrs.length; - var allEls = svgcontent.getElementsByTagNameNS(NS.SVG, '*'); - var allLen = allEls.length; + var allEls = svgcontent.getElementsByTagNameNS(NS.SVG, '*'); + var allLen = allEls.length; - var i, j; - for (i = 0; i < allLen; i++) { - var el = allEls[i]; - for (j = 0; j < alen; j++) { - var ref = svgedit.utilities.getUrlFromAttr(el.getAttribute(attrs[j])); - if (ref) { - defelemUses.push(ref.substr(1)); - } - } + var i, j; + for (i = 0; i < allLen; i++) { + var el = allEls[i]; + for (j = 0; j < alen; j++) { + var ref = svgedit.utilities.getUrlFromAttr(el.getAttribute(attrs[j])); + if (ref) { + defelemUses.push(ref.substr(1)); + } + } - // gradients can refer to other gradients - var href = getHref(el); - if (href && href.indexOf('#') === 0) { - defelemUses.push(href.substr(1)); - } - } + // gradients can refer to other gradients + var href = getHref(el); + if (href && href.indexOf('#') === 0) { + defelemUses.push(href.substr(1)); + } + } - var defelems = $(defs).find('linearGradient, radialGradient, filter, marker, svg, symbol'); - i = defelems.length; - while (i--) { - var defelem = defelems[i]; - var id = defelem.id; - if (defelemUses.indexOf(id) < 0) { - // Not found, so remove (but remember) - removedElements[id] = defelem; - defelem.parentNode.removeChild(defelem); - numRemoved++; - } - } + var defelems = $(defs).find('linearGradient, radialGradient, filter, marker, svg, symbol'); + i = defelems.length; + while (i--) { + var defelem = defelems[i]; + var id = defelem.id; + if (defelemUses.indexOf(id) < 0) { + // Not found, so remove (but remember) + removedElements[id] = defelem; + defelem.parentNode.removeChild(defelem); + numRemoved++; + } + } - return numRemoved; + return numRemoved; }; // Function: svgCanvasToString @@ -3722,53 +3722,53 @@ var removeUnusedDefElems = this.removeUnusedDefElems = function () { // Returns: // String containing the SVG image for output this.svgCanvasToString = function () { - // keep calling it until there are none to remove - while (removeUnusedDefElems() > 0) {} + // keep calling it until there are none to remove + while (removeUnusedDefElems() > 0) {} - pathActions.clear(true); + 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); - } - }); + // 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 (currentGroup) { - leaveContext(); - selectOnly([currentGroup]); - } + // Move out of in-group editing mode + if (currentGroup) { + leaveContext(); + selectOnly([currentGroup]); + } - var nakedSvgs = []; + var nakedSvgs = []; - // Unwrap gsvg if it has no special attributes (only id and style) - $(svgcontent).find('g:data(gsvg)').each(function () { - var attrs = this.attributes; - var len = attrs.length; - var i; - for (i = 0; i < len; i++) { - if (attrs[i].nodeName === 'id' || attrs[i].nodeName === 'style') { - len--; - } - } - // No significant attributes, so ungroup - if (len <= 0) { - var svg = this.firstChild; - nakedSvgs.push(svg); - $(this).replaceWith(svg); - } - }); - var output = this.svgToString(svgcontent, 0); + // Unwrap gsvg if it has no special attributes (only id and style) + $(svgcontent).find('g:data(gsvg)').each(function () { + var attrs = this.attributes; + var len = attrs.length; + var i; + for (i = 0; i < len; i++) { + if (attrs[i].nodeName === 'id' || attrs[i].nodeName === 'style') { + len--; + } + } + // No significant attributes, so ungroup + if (len <= 0) { + var svg = this.firstChild; + nakedSvgs.push(svg); + $(this).replaceWith(svg); + } + }); + var output = this.svgToString(svgcontent, 0); - // Rewrap gsvg - if (nakedSvgs.length) { - $(nakedSvgs).each(function () { - groupSvgElem(this); - }); - } + // Rewrap gsvg + if (nakedSvgs.length) { + $(nakedSvgs).each(function () { + groupSvgElem(this); + }); + } - return output; + return output; }; // Function: svgToString @@ -3781,171 +3781,171 @@ this.svgCanvasToString = function () { // Returns: // String with the given element as an SVG tag this.svgToString = function (elem, indent) { - var out = [], - toXml = svgedit.utilities.toXml; - var unit = curConfig.baseUnit; - var unitRe = new RegExp('^-?[\\d\\.]+' + unit + '$'); + var out = [], + toXml = svgedit.utilities.toXml; + var unit = curConfig.baseUnit; + var unitRe = new RegExp('^-?[\\d\\.]+' + unit + '$'); - if (elem) { - cleanupElement(elem); - var attrs = elem.attributes, - attr, - i, - childs = elem.childNodes; + if (elem) { + cleanupElement(elem); + var attrs = elem.attributes, + attr, + i, + childs = elem.childNodes; - for (i = 0; i < indent; i++) { out.push(' '); } - out.push('<'); out.push(elem.nodeName); - if (elem.id === 'svgcontent') { - // Process root element separately - var res = getResolution(); + for (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 - // if (curConfig.baseUnit !== 'px') { - // var unit = curConfig.baseUnit; - // var unitM = svgedit.units.getTypeMap()[unit]; - // res.w = svgedit.units.shortFloat(res.w / unitM); - // res.h = svgedit.units.shortFloat(res.h / unitM); - // vb = ' viewBox="' + [0, 0, res.w, res.h].join(' ') + '"'; - // res.w += unit; - // res.h += unit; - // } + 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 + // if (curConfig.baseUnit !== 'px') { + // var unit = curConfig.baseUnit; + // var unitM = svgedit.units.getTypeMap()[unit]; + // res.w = svgedit.units.shortFloat(res.w / unitM); + // res.h = svgedit.units.shortFloat(res.h / unitM); + // 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; - } + 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="' + NS.SVG + '"'); + out.push(' width="' + res.w + '" height="' + res.h + '"' + vb + ' xmlns="' + NS.SVG + '"'); - var nsuris = {}; + var nsuris = {}; - // Check elements for namespaces, add if found - $(elem).find('*').andSelf().each(function () { - // var el = this; - // for some elements have no attribute - var uri = this.namespaceURI; - if (uri && !nsuris[uri] && nsMap[uri] && nsMap[uri] !== 'xmlns' && nsMap[uri] !== 'xml') { - nsuris[uri] = true; - out.push(' xmlns:' + nsMap[uri] + '="' + uri + '"'); - } + // Check elements for namespaces, add if found + $(elem).find('*').andSelf().each(function () { + // var el = this; + // for some elements have no attribute + var uri = this.namespaceURI; + if (uri && !nsuris[uri] && nsMap[uri] && nsMap[uri] !== 'xmlns' && nsMap[uri] !== 'xml') { + nsuris[uri] = true; + out.push(' xmlns:' + nsMap[uri] + '="' + uri + '"'); + } - $.each(this.attributes, function (i, attr) { - var uri = attr.namespaceURI; - if (uri && !nsuris[uri] && nsMap[uri] !== 'xmlns' && nsMap[uri] !== 'xml') { - nsuris[uri] = true; - out.push(' xmlns:' + nsMap[uri] + '="' + uri + '"'); - } - }); - }); + $.each(this.attributes, function (i, attr) { + var uri = attr.namespaceURI; + if (uri && !nsuris[uri] && nsMap[uri] !== 'xmlns' && nsMap[uri] !== 'xml') { + nsuris[uri] = true; + out.push(' xmlns:' + nsMap[uri] + '="' + uri + '"'); + } + }); + }); - i = attrs.length; - var attrNames = ['width', 'height', 'xmlns', 'x', 'y', 'viewBox', 'id', 'overflow']; - while (i--) { - attr = attrs.item(i); - var attrVal = toXml(attr.value); + i = attrs.length; + var attrNames = ['width', 'height', 'xmlns', 'x', 'y', 'viewBox', 'id', 'overflow']; + while (i--) { + attr = attrs.item(i); + var attrVal = toXml(attr.value); - // Namespaces have already been dealt with, so skip - if (attr.nodeName.indexOf('xmlns:') === 0) { continue; } + // 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 !== '' && attrNames.indexOf(attr.localName) === -1) { - if (!attr.namespaceURI || nsMap[attr.namespaceURI]) { - out.push(' '); - out.push(attr.nodeName); out.push('="'); - out.push(attrVal); out.push('"'); - } - } - } - } else { - // Skip empty defs - if (elem.nodeName === 'defs' && !elem.firstChild) { return; } + // only serialize attributes we don't use internally + if (attrVal !== '' && attrNames.indexOf(attr.localName) === -1) { + if (!attr.namespaceURI || nsMap[attr.namespaceURI]) { + out.push(' '); + out.push(attr.nodeName); out.push('="'); + out.push(attrVal); out.push('"'); + } + } + } + } else { + // Skip empty defs + if (elem.nodeName === 'defs' && !elem.firstChild) { return; } - var mozAttrs = ['-moz-math-font-style', '_moz-math-font-style']; - for (i = attrs.length - 1; i >= 0; i--) { - attr = attrs.item(i); - var attrVal = toXml(attr.value); - // remove bogus attributes added by Gecko - if (mozAttrs.indexOf(attr.localName) >= 0) { continue; } - if (attrVal !== '') { - if (attrVal.indexOf('pointer-events') === 0) { continue; } - if (attr.localName === 'class' && attrVal.indexOf('se_') === 0) { continue; } - out.push(' '); - if (attr.localName === 'd') { attrVal = pathActions.convertPath(elem, true); } - if (!isNaN(attrVal)) { - attrVal = svgedit.units.shortFloat(attrVal); - } else if (unitRe.test(attrVal)) { - attrVal = svgedit.units.shortFloat(attrVal) + unit; - } + var mozAttrs = ['-moz-math-font-style', '_moz-math-font-style']; + for (i = attrs.length - 1; i >= 0; i--) { + attr = attrs.item(i); + var attrVal = toXml(attr.value); + // remove bogus attributes added by Gecko + if (mozAttrs.indexOf(attr.localName) >= 0) { continue; } + if (attrVal !== '') { + if (attrVal.indexOf('pointer-events') === 0) { continue; } + if (attr.localName === 'class' && attrVal.indexOf('se_') === 0) { continue; } + out.push(' '); + if (attr.localName === 'd') { attrVal = pathActions.convertPath(elem, true); } + if (!isNaN(attrVal)) { + attrVal = svgedit.units.shortFloat(attrVal); + } else if (unitRe.test(attrVal)) { + attrVal = svgedit.units.shortFloat(attrVal) + unit; + } - // Embed images when saving - if (saveOptions.apply && - elem.nodeName === 'image' && - attr.localName === 'href' && - saveOptions.images && - saveOptions.images === 'embed' - ) { - var img = encodableImages[attrVal]; - if (img) { attrVal = img; } - } + // Embed images when saving + if (saveOptions.apply && + elem.nodeName === 'image' && + attr.localName === 'href' && + saveOptions.images && + saveOptions.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 === NS.SVG || nsMap[attr.namespaceURI]) { - out.push(attr.nodeName); out.push('="'); - out.push(attrVal); out.push('"'); - } - } - } - } + // map various namespaces to our fixed namespace prefixes + // (the default xmlns attribute itself does not get a prefix) + if (!attr.namespaceURI || attr.namespaceURI === NS.SVG || nsMap[attr.namespaceURI]) { + out.push(attr.nodeName); out.push('="'); + out.push(attrVal); out.push('"'); + } + } + } + } - if (elem.hasChildNodes()) { - out.push('>'); - indent++; - var bOneLine = false; + if (elem.hasChildNodes()) { + out.push('>'); + indent++; + var bOneLine = false; - for (i = 0; i < childs.length; i++) { - var child = childs.item(i); - switch (child.nodeType) { - case 1: // element node - out.push('\n'); - out.push(this.svgToString(childs.item(i), indent)); - break; - case 3: // text node - var str = child.nodeValue.replace(/^\s+|\s+$/g, ''); - if (str !== '') { - bOneLine = true; - out.push(String(toXml(str))); - } - break; - case 4: // cdata node - out.push('\n'); - out.push(new Array(indent + 1).join(' ')); - out.push(''); - out.push(child.nodeValue); - out.push(''); - break; - case 8: // comment - out.push('\n'); - out.push(new Array(indent + 1).join(' ')); - out.push(''); - break; - } // switch on node type - } - indent--; - if (!bOneLine) { - out.push('\n'); - for (i = 0; i < indent; i++) { out.push(' '); } - } - out.push(''); - } else { - out.push('/>'); - } - } - return out.join(''); + for (i = 0; i < childs.length; i++) { + var child = childs.item(i); + switch (child.nodeType) { + case 1: // element node + out.push('\n'); + out.push(this.svgToString(childs.item(i), indent)); + break; + case 3: // text node + var str = child.nodeValue.replace(/^\s+|\s+$/g, ''); + if (str !== '') { + bOneLine = true; + out.push(String(toXml(str))); + } + break; + case 4: // cdata node + out.push('\n'); + out.push(new Array(indent + 1).join(' ')); + out.push(''); + out.push(child.nodeValue); + out.push(''); + break; + case 8: // comment + out.push('\n'); + out.push(new Array(indent + 1).join(' ')); + out.push(''); + break; + } // switch on node type + } + indent--; + if (!bOneLine) { + out.push('\n'); + for (i = 0; i < indent; i++) { out.push(' '); } + } + out.push(''); + } else { + out.push('/>'); + } + } + return out.join(''); }; // end svgToString() // Function: embedImage @@ -3956,35 +3956,35 @@ this.svgToString = function (elem, indent) { // callback - Optional function to run when image data is found, supplies the // result (data URL or false) as first parameter. this.embedImage = function (val, callback) { - // load in the image and once it's loaded, get the dimensions - $(new Image()).load(function () { - // create a canvas the same size as the raster image - var canvas = document.createElement('canvas'); - canvas.width = this.width; - canvas.height = this.height; - // load the raster image into the canvas - canvas.getContext('2d').drawImage(this, 0, 0); - // retrieve the data: URL - try { - var urldata = ';svgedit_url=' + encodeURIComponent(val); - urldata = canvas.toDataURL().replace(';base64', urldata + ';base64'); - encodableImages[val] = urldata; - } catch (e) { - encodableImages[val] = false; - } - lastGoodImgUrl = val; - if (callback) { callback(encodableImages[val]); } - }).attr('src', val); + // load in the image and once it's loaded, get the dimensions + $(new Image()).load(function () { + // create a canvas the same size as the raster image + var canvas = document.createElement('canvas'); + canvas.width = this.width; + canvas.height = this.height; + // load the raster image into the canvas + canvas.getContext('2d').drawImage(this, 0, 0); + // retrieve the data: URL + try { + var urldata = ';svgedit_url=' + encodeURIComponent(val); + urldata = canvas.toDataURL().replace(';base64', urldata + ';base64'); + encodableImages[val] = urldata; + } catch (e) { + encodableImages[val] = false; + } + lastGoodImgUrl = val; + if (callback) { callback(encodableImages[val]); } + }).attr('src', val); }; // Function: setGoodImage // Sets a given URL to be a "last good image" URL this.setGoodImage = function (val) { - lastGoodImgUrl = val; + lastGoodImgUrl = val; }; this.open = function () { - // Nothing by default, handled by optional widget/extension + // Nothing by default, handled by optional widget/extension }; // Function: save @@ -3995,111 +3995,111 @@ this.open = function () { // Returns: // Nothing this.save = function (opts) { - // remove the selected outline before serializing - clearSelection(); - // Update save options if provided - if (opts) { $.extend(saveOptions, opts); } - saveOptions.apply = true; + // remove the selected outline before serializing + clearSelection(); + // Update save options if provided + if (opts) { $.extend(saveOptions, opts); } + saveOptions.apply = true; - // no need for doctype, see http://jwatt.org/svg/authoring/#doctype-declaration - var str = this.svgCanvasToString(); - call('saved', str); + // no need for doctype, see http://jwatt.org/svg/authoring/#doctype-declaration + var str = this.svgCanvasToString(); + call('saved', str); }; function getIssues () { - // remove the selected outline before serializing - clearSelection(); + // remove the selected outline before serializing + clearSelection(); - // Check for known CanVG issues - var issues = []; + // Check for known CanVG issues + var issues = []; - // Selector and notice - var issueList = { - 'feGaussianBlur': uiStrings.exportNoBlur, - 'foreignObject': uiStrings.exportNoforeignObject, - '[stroke-dasharray]': uiStrings.exportNoDashArray - }; - var content = $(svgcontent); + // Selector and notice + var issueList = { + 'feGaussianBlur': uiStrings.exportNoBlur, + 'foreignObject': uiStrings.exportNoforeignObject, + '[stroke-dasharray]': uiStrings.exportNoDashArray + }; + var content = $(svgcontent); - // Add font/text check if Canvas Text API is not implemented - if (!('font' in $('')[0].getContext('2d'))) { - issueList.text = uiStrings.exportNoText; - } + // Add font/text check if Canvas Text API is not implemented + if (!('font' in $('')[0].getContext('2d'))) { + issueList.text = uiStrings.exportNoText; + } - $.each(issueList, function (sel, descr) { - if (content.find(sel).length) { - issues.push(descr); - } - }); - return issues; + $.each(issueList, function (sel, descr) { + if (content.find(sel).length) { + issues.push(descr); + } + }); + return issues; } // Function: rasterExport // Generates a Data URL based on the current image, then calls "exported" // with an object including the string, image information, and any issues found this.rasterExport = function (imgType, quality, exportWindowName) { - var mimeType = 'image/' + imgType.toLowerCase(); - var issues = getIssues(); - var str = this.svgCanvasToString(); + var mimeType = 'image/' + imgType.toLowerCase(); + var issues = getIssues(); + var str = this.svgCanvasToString(); - svgedit.utilities.buildCanvgCallback(function () { - var type = imgType || 'PNG'; - if (!$('#export_canvas').length) { - $('', {id: 'export_canvas'}).hide().appendTo('body'); - } - var c = $('#export_canvas')[0]; - c.width = svgCanvas.contentW; - c.height = svgCanvas.contentH; + svgedit.utilities.buildCanvgCallback(function () { + var type = imgType || 'PNG'; + if (!$('#export_canvas').length) { + $('', {id: 'export_canvas'}).hide().appendTo('body'); + } + var c = $('#export_canvas')[0]; + c.width = svgCanvas.contentW; + c.height = svgCanvas.contentH; - canvg(c, str, {renderCallback: function () { - var dataURLType = (type === 'ICO' ? 'BMP' : type).toLowerCase(); - var datauri = quality ? c.toDataURL('image/' + dataURLType, quality) : c.toDataURL('image/' + dataURLType); - if (c.toBlob) { - c.toBlob(function (blob) { - var bloburl = svgedit.utilities.createObjectURL(blob); - call('exported', {datauri: datauri, bloburl: bloburl, svg: str, issues: issues, type: imgType, mimeType: mimeType, quality: quality, exportWindowName: exportWindowName}); - }, mimeType, quality); - return; - } - var bloburl = svgedit.utilities.dataURLToObjectURL(datauri); - call('exported', {datauri: datauri, bloburl: bloburl, svg: str, issues: issues, type: imgType, mimeType: mimeType, quality: quality, exportWindowName: exportWindowName}); - }}); - })(); + canvg(c, str, {renderCallback: function () { + var dataURLType = (type === 'ICO' ? 'BMP' : type).toLowerCase(); + var datauri = quality ? c.toDataURL('image/' + dataURLType, quality) : c.toDataURL('image/' + dataURLType); + if (c.toBlob) { + c.toBlob(function (blob) { + var bloburl = svgedit.utilities.createObjectURL(blob); + call('exported', {datauri: datauri, bloburl: bloburl, svg: str, issues: issues, type: imgType, mimeType: mimeType, quality: quality, exportWindowName: exportWindowName}); + }, mimeType, quality); + return; + } + var bloburl = svgedit.utilities.dataURLToObjectURL(datauri); + call('exported', {datauri: datauri, bloburl: bloburl, svg: str, issues: issues, type: imgType, mimeType: mimeType, quality: quality, exportWindowName: exportWindowName}); + }}); + })(); }; this.exportPDF = function (exportWindowName, outputType) { - var that = this; - svgedit.utilities.buildJSPDFCallback(function () { - var res = getResolution(); - var orientation = res.w > res.h ? 'landscape' : 'portrait'; - var units = 'pt'; // curConfig.baseUnit; // We could use baseUnit, but that is presumably not intended for export purposes - var doc = jsPDF({ - orientation: orientation, - unit: units, - format: [res.w, res.h] - // , compressPdf: true - }); // Todo: Give options to use predefined jsPDF formats like "a4", etc. from pull-down (with option to keep customizable) - var docTitle = getDocumentTitle(); - doc.setProperties({ - title: docTitle /* , - subject: '', - author: '', - keywords: '', - creator: '' */ - }); - var issues = getIssues(); - var str = that.svgCanvasToString(); - doc.addSVG(str, 0, 0); + var that = this; + svgedit.utilities.buildJSPDFCallback(function () { + var res = getResolution(); + var orientation = res.w > res.h ? 'landscape' : 'portrait'; + var units = 'pt'; // curConfig.baseUnit; // We could use baseUnit, but that is presumably not intended for export purposes + var doc = jsPDF({ + orientation: orientation, + unit: units, + format: [res.w, res.h] + // , compressPdf: true + }); // Todo: Give options to use predefined jsPDF formats like "a4", etc. from pull-down (with option to keep customizable) + var docTitle = getDocumentTitle(); + doc.setProperties({ + title: docTitle /* , + subject: '', + author: '', + keywords: '', + creator: '' */ + }); + var issues = getIssues(); + var str = that.svgCanvasToString(); + doc.addSVG(str, 0, 0); - // doc.output('save'); // Works to open in a new - // window; todo: configure this and other export - // options to optionally work in this manner as - // opposed to opening a new tab - var obj = {svg: str, issues: issues, exportWindowName: exportWindowName}; - var method = outputType || 'dataurlstring'; - obj[method] = doc.output(method); - call('exportedPDF', obj); - })(); + // doc.output('save'); // Works to open in a new + // window; todo: configure this and other export + // options to optionally work in this manner as + // opposed to opening a new tab + var obj = {svg: str, issues: issues, exportWindowName: exportWindowName}; + var method = outputType || 'dataurlstring'; + obj[method] = doc.output(method); + call('exportedPDF', obj); + })(); }; // Function: getSvgString @@ -4108,8 +4108,8 @@ this.exportPDF = function (exportWindowName, outputType) { // Returns: // The current drawing as raw SVG XML text. this.getSvgString = function () { - saveOptions.apply = false; - return this.svgCanvasToString(); + saveOptions.apply = false; + return this.svgCanvasToString(); }; // Function: randomizeIds @@ -4124,11 +4124,11 @@ this.getSvgString = function () { // this BEFORE calling svgCanvas.setSvgString // this.randomizeIds = function (enableRandomization) { - if (arguments.length > 0 && enableRandomization === false) { - svgedit.draw.randomizeIds(false, getCurrentDrawing()); - } else { - svgedit.draw.randomizeIds(true, getCurrentDrawing()); - } + if (arguments.length > 0 && enableRandomization === false) { + svgedit.draw.randomizeIds(false, getCurrentDrawing()); + } else { + svgedit.draw.randomizeIds(true, getCurrentDrawing()); + } }; // Function: uniquifyElems @@ -4137,314 +4137,314 @@ this.randomizeIds = function (enableRandomization) { // Parameters: // g - The parent element of the tree to give unique IDs var uniquifyElems = this.uniquifyElems = function (g) { - var ids = {}; - // TODO: Handle markers and connectors. These are not yet re-identified properly - // as their referring elements do not get remapped. - // - // - // - // - // 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 refElems = ['filter', 'linearGradient', 'pattern', 'radialGradient', 'symbol', 'textPath', 'use']; + var ids = {}; + // TODO: Handle markers and connectors. These are not yet re-identified properly + // as their referring elements do not get remapped. + // + // + // + // + // 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 refElems = ['filter', 'linearGradient', 'pattern', 'radialGradient', 'symbol', 'textPath', 'use']; - svgedit.utilities.walkTree(g, function (n) { - // if it's an element node - if (n.nodeType === 1) { - // and the element has an ID - if (n.id) { - // and we haven't tracked this ID yet - if (!(n.id in ids)) { - // add this id to our map - ids[n.id] = {elem: null, attrs: [], hrefs: []}; - } - ids[n.id].elem = n; - } + svgedit.utilities.walkTree(g, function (n) { + // if it's an element node + if (n.nodeType === 1) { + // and the element has an ID + if (n.id) { + // and we haven't tracked this ID yet + if (!(n.id in ids)) { + // add this id to our map + ids[n.id] = {elem: null, attrs: [], hrefs: []}; + } + ids[n.id].elem = n; + } - // now search for all attributes on this element that might refer - // to other elements - $.each(refAttrs, function (i, attr) { - var attrnode = n.getAttributeNode(attr); - if (attrnode) { - // the incoming file has been sanitized, so we should be able to safely just strip off the leading # - var url = svgedit.utilities.getUrlFromAttr(attrnode.value), - refid = url ? url.substr(1) : null; - if (refid) { - if (!(refid in ids)) { - // add this id to our map - ids[refid] = {elem: null, attrs: [], hrefs: []}; - } - ids[refid].attrs.push(attrnode); - } - } - }); + // now search for all attributes on this element that might refer + // to other elements + $.each(refAttrs, function (i, attr) { + var attrnode = n.getAttributeNode(attr); + if (attrnode) { + // the incoming file has been sanitized, so we should be able to safely just strip off the leading # + var url = svgedit.utilities.getUrlFromAttr(attrnode.value), + refid = url ? url.substr(1) : null; + if (refid) { + if (!(refid in ids)) { + // add this id to our map + ids[refid] = {elem: null, attrs: [], hrefs: []}; + } + ids[refid].attrs.push(attrnode); + } + } + }); - // check xlink:href now - var href = svgedit.utilities.getHref(n); - // TODO: what if an or element refers to an element internally? - if (href && refElems.indexOf(n.nodeName) >= 0) { - var refid = href.substr(1); - if (refid) { - if (!(refid in ids)) { - // add this id to our map - ids[refid] = {elem: null, attrs: [], hrefs: []}; - } - ids[refid].hrefs.push(n); - } - } - } - }); + // check xlink:href now + var href = svgedit.utilities.getHref(n); + // TODO: what if an or element refers to an element internally? + if (href && refElems.indexOf(n.nodeName) >= 0) { + var refid = href.substr(1); + if (refid) { + if (!(refid in ids)) { + // add this id to our map + ids[refid] = {elem: null, attrs: [], hrefs: []}; + } + ids[refid].hrefs.push(n); + } + } + } + }); - // in ids, we now have a map of ids, elements and attributes, let's re-identify - var oldid; - for (oldid in ids) { - if (!oldid) { continue; } - var elem = ids[oldid].elem; - if (elem) { - var newid = getNextId(); + // in ids, we now have a map of ids, elements and attributes, let's re-identify + var oldid; + for (oldid in ids) { + if (!oldid) { continue; } + var elem = ids[oldid].elem; + if (elem) { + var newid = getNextId(); - // assign element its new id - elem.id = newid; + // assign element its new id + elem.id = newid; - // remap all url() attributes - var attrs = ids[oldid].attrs; - var j = attrs.length; - while (j--) { - var attr = attrs[j]; - attr.ownerElement.setAttribute(attr.name, 'url(#' + newid + ')'); - } + // remap all url() attributes + var attrs = ids[oldid].attrs; + var j = attrs.length; + while (j--) { + var attr = attrs[j]; + attr.ownerElement.setAttribute(attr.name, 'url(#' + newid + ')'); + } - // remap all href attributes - var hreffers = ids[oldid].hrefs; - var k = hreffers.length; - while (k--) { - var hreffer = hreffers[k]; - svgedit.utilities.setHref(hreffer, '#' + newid); - } - } - } + // remap all href attributes + var hreffers = ids[oldid].hrefs; + var k = hreffers.length; + while (k--) { + var hreffer = hreffers[k]; + svgedit.utilities.setHref(hreffer, '#' + newid); + } + } + } }; // Function setUseData // Assigns reference data for each use element var setUseData = this.setUseData = function (parent) { - var elems = $(parent); + var elems = $(parent); - if (parent.tagName !== 'use') { - elems = elems.find('use'); - } + if (parent.tagName !== 'use') { + elems = elems.find('use'); + } - elems.each(function () { - var id = getHref(this).substr(1); - var refElem = svgedit.utilities.getElem(id); - if (!refElem) { return; } - $(this).data('ref', refElem); - if (refElem.tagName === 'symbol' || refElem.tagName === 'svg') { - $(this).data('symbol', refElem).data('ref', refElem); - } - }); + elems.each(function () { + var id = getHref(this).substr(1); + var refElem = svgedit.utilities.getElem(id); + if (!refElem) { return; } + $(this).data('ref', refElem); + if (refElem.tagName === 'symbol' || refElem.tagName === 'svg') { + $(this).data('symbol', refElem).data('ref', refElem); + } + }); }; // Function convertGradients // Converts gradients from userSpaceOnUse to objectBoundingBox var convertGradients = this.convertGradients = function (elem) { - var elems = $(elem).find('linearGradient, radialGradient'); - if (!elems.length && svgedit.browser.isWebkit()) { - // Bug in webkit prevents regular *Gradient selector search - elems = $(elem).find('*').filter(function () { - return (this.tagName.indexOf('Gradient') >= 0); - }); - } + var elems = $(elem).find('linearGradient, radialGradient'); + if (!elems.length && svgedit.browser.isWebkit()) { + // Bug in webkit prevents regular *Gradient selector search + elems = $(elem).find('*').filter(function () { + return (this.tagName.indexOf('Gradient') >= 0); + }); + } - elems.each(function () { - var grad = this; - if ($(grad).attr('gradientUnits') === 'userSpaceOnUse') { - // TODO: Support more than one element with this ref by duplicating parent grad - var elems = $(svgcontent).find('[fill="url(#' + grad.id + ')"],[stroke="url(#' + grad.id + ')"]'); - if (!elems.length) { return; } + 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]); + // get object's bounding box + var bb = svgedit.utilities.getBBox(elems[0]); - // This will occur if the element is inside a or a , - // in which we shouldn't need to convert anyway. - if (!bb) { return; } + // This will occur if the element is inside a or a , + // in which we shouldn't need to convert anyway. + if (!bb) { return; } - if (grad.tagName === 'linearGradient') { - var gCoords = $(grad).attr(['x1', 'y1', 'x2', 'y2']); + if (grad.tagName === 'linearGradient') { + var gCoords = $(grad).attr(['x1', 'y1', 'x2', 'y2']); - // If has transform, convert - var tlist = grad.gradientTransform.baseVal; - if (tlist && tlist.numberOfItems > 0) { - var m = svgedit.math.transformListToTransform(tlist).matrix; - var pt1 = svgedit.math.transformPoint(gCoords.x1, gCoords.y1, m); - var pt2 = svgedit.math.transformPoint(gCoords.x2, gCoords.y2, m); + // If has transform, convert + var tlist = grad.gradientTransform.baseVal; + if (tlist && tlist.numberOfItems > 0) { + var m = svgedit.math.transformListToTransform(tlist).matrix; + var pt1 = svgedit.math.transformPoint(gCoords.x1, gCoords.y1, m); + var pt2 = svgedit.math.transformPoint(gCoords.x2, gCoords.y2, m); - gCoords.x1 = pt1.x; - gCoords.y1 = pt1.y; - gCoords.x2 = pt2.x; - gCoords.y2 = pt2.y; - grad.removeAttribute('gradientTransform'); - } + gCoords.x1 = pt1.x; + gCoords.y1 = pt1.y; + gCoords.x2 = pt2.x; + gCoords.y2 = pt2.y; + grad.removeAttribute('gradientTransform'); + } - $(grad).attr({ - x1: (gCoords.x1 - bb.x) / bb.width, - y1: (gCoords.y1 - bb.y) / bb.height, - x2: (gCoords.x2 - bb.x) / bb.width, - y2: (gCoords.y2 - bb.y) / bb.height - }); - grad.removeAttribute('gradientUnits'); - } - // else { - // 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. - // - // For now we'll do nothing, though we should probably have - // the gradient be updated as the element is moved, as - // inkscape/illustrator do. - // - // var gCoords = $(grad).attr(['cx', 'cy', 'r']); - // - // $(grad).attr({ - // cx: (gCoords.cx - bb.x) / bb.width, - // cy: (gCoords.cy - bb.y) / bb.height, - // r: gCoords.r - // }); - // - // grad.removeAttribute('gradientUnits'); - // } - } - }); + $(grad).attr({ + x1: (gCoords.x1 - bb.x) / bb.width, + y1: (gCoords.y1 - bb.y) / bb.height, + x2: (gCoords.x2 - bb.x) / bb.width, + y2: (gCoords.y2 - bb.y) / bb.height + }); + grad.removeAttribute('gradientUnits'); + } + // else { + // 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. + // + // For now we'll do nothing, though we should probably have + // the gradient be updated as the element is moved, as + // inkscape/illustrator do. + // + // var gCoords = $(grad).attr(['cx', 'cy', 'r']); + // + // $(grad).attr({ + // cx: (gCoords.cx - bb.x) / bb.width, + // cy: (gCoords.cy - bb.y) / bb.height, + // r: gCoords.r + // }); + // + // grad.removeAttribute('gradientUnits'); + // } + } + }); }; // Function: convertToGroup // Converts selected/given or child SVG element to a group var convertToGroup = this.convertToGroup = function (elem) { - if (!elem) { - elem = selectedElements[0]; - } - var $elem = $(elem); - var batchCmd = new svgedit.history.BatchCommand(); - var ts; + if (!elem) { + elem = selectedElements[0]; + } + var $elem = $(elem); + var batchCmd = new svgedit.history.BatchCommand(); + var ts; - if ($elem.data('gsvg')) { - // Use the gsvg as the new group - var svg = elem.firstChild; - var pt = $(svg).attr(['x', 'y']); + 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'); + $(elem.firstChild.firstChild).unwrap(); + $(elem).removeData('gsvg'); - var tlist = svgedit.transformlist.getTransformList(elem); - var xform = svgroot.createSVGTransform(); - xform.setTranslate(pt.x, pt.y); - tlist.appendItem(xform); - svgedit.recalculate.recalculateDimensions(elem); - call('selected', [elem]); - } else if ($elem.data('symbol')) { - elem = $elem.data('symbol'); + var tlist = svgedit.transformlist.getTransformList(elem); + var xform = svgroot.createSVGTransform(); + xform.setTranslate(pt.x, pt.y); + tlist.appendItem(xform); + svgedit.recalculate.recalculateDimensions(elem); + call('selected', [elem]); + } else if ($elem.data('symbol')) { + elem = $elem.data('symbol'); - ts = $elem.attr('transform'); - var pos = $elem.attr(['x', 'y']); + ts = $elem.attr('transform'); + var pos = $elem.attr(['x', 'y']); - var vb = elem.getAttribute('viewBox'); + var vb = elem.getAttribute('viewBox'); - if (vb) { - var nums = vb.split(' '); - pos.x -= +nums[0]; - pos.y -= +nums[1]; - } + 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) + ')'; + // Not ideal, but works + ts += ' translate(' + (pos.x || 0) + ',' + (pos.y || 0) + ')'; - var prev = $elem.prev(); + var prev = $elem.prev(); - // Remove element - batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand($elem[0], $elem[0].nextSibling, $elem[0].parentNode)); - $elem.remove(); + // Remove element + batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand($elem[0], $elem[0].nextSibling, $elem[0].parentNode)); + $elem.remove(); - // See if other elements reference this symbol - var hasMore = $(svgcontent).find('use:data(symbol)').length; + // See if other elements reference this symbol + var hasMore = $(svgcontent).find('use:data(symbol)').length; - var g = svgdoc.createElementNS(NS.SVG, 'g'); - var childs = elem.childNodes; + var g = svgdoc.createElementNS(NS.SVG, 'g'); + var childs = elem.childNodes; - var i; - for (i = 0; i < childs.length; i++) { - g.appendChild(childs[i].cloneNode(true)); - } + var i; + for (i = 0; i < childs.length; i++) { + g.appendChild(childs[i].cloneNode(true)); + } - // Duplicate the gradients for Gecko, since they weren't included in the - if (svgedit.browser.isGecko()) { - var dupeGrads = $(svgedit.utilities.findDefs()).children('linearGradient,radialGradient,pattern').clone(); - $(g).append(dupeGrads); - } + // Duplicate the gradients for Gecko, since they weren't included in the + if (svgedit.browser.isGecko()) { + var dupeGrads = $(svgedit.utilities.findDefs()).children('linearGradient,radialGradient,pattern').clone(); + $(g).append(dupeGrads); + } - if (ts) { - g.setAttribute('transform', ts); - } + if (ts) { + g.setAttribute('transform', ts); + } - var parent = elem.parentNode; + var parent = elem.parentNode; - uniquifyElems(g); + uniquifyElems(g); - // Put the dupe gradients back into (after uniquifying them) - if (svgedit.browser.isGecko()) { - $(findDefs()).append($(g).find('linearGradient,radialGradient,pattern')); - } + // Put the dupe gradients back into (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(); + // now give the g itself a new id + g.id = getNextId(); - prev.after(g); + prev.after(g); - if (parent) { - if (!hasMore) { - // remove symbol/svg element - var nextSibling = elem.nextSibling; - parent.removeChild(elem); - batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(elem, nextSibling, parent)); - } - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(g)); - } + if (parent) { + if (!hasMore) { + // remove symbol/svg element + var nextSibling = elem.nextSibling; + parent.removeChild(elem); + batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(elem, nextSibling, parent)); + } + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(g)); + } - setUseData(g); + setUseData(g); - if (svgedit.browser.isGecko()) { - convertGradients(svgedit.utilities.findDefs()); - } else { - convertGradients(g); - } + if (svgedit.browser.isGecko()) { + convertGradients(svgedit.utilities.findDefs()); + } else { + convertGradients(g); + } - // recalculate dimensions on the top-level children so that unnecessary transforms - // are removed - svgedit.utilities.walkTreePost(g, function (n) { - try { - svgedit.recalculate.recalculateDimensions(n); - } catch (e) { - console.log(e); - } - }); + // recalculate dimensions on the top-level children so that unnecessary transforms + // are removed + svgedit.utilities.walkTreePost(g, function (n) { + try { + svgedit.recalculate.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(); } - }); + // Give ID for any visible element missing one + $(g).find(visElems).each(function () { + if (!this.id) { this.id = getNextId(); } + }); - selectOnly([g]); + selectOnly([g]); - var cm = pushGroupProperties(g, true); - if (cm) { - batchCmd.addSubCommand(cm); - } + var cm = pushGroupProperties(g, true); + if (cm) { + batchCmd.addSubCommand(cm); + } - addCommandToHistory(batchCmd); - } else { - console.log('Unexpected element to ungroup:', elem); - } + addCommandToHistory(batchCmd); + } else { + console.log('Unexpected element to ungroup:', elem); + } }; // @@ -4460,165 +4460,165 @@ var convertToGroup = this.convertToGroup = function (elem) { // Returns: // This function returns false if the set was unsuccessful, true otherwise. this.setSvgString = function (xmlString, preventUndo) { - try { - // convert string into XML document - var newDoc = svgedit.utilities.text2xml(xmlString); + try { + // convert string into XML document + var newDoc = svgedit.utilities.text2xml(xmlString); - this.prepareSvg(newDoc); + this.prepareSvg(newDoc); - var batchCmd = new svgedit.history.BatchCommand('Change Source'); + var batchCmd = new svgedit.history.BatchCommand('Change Source'); - // remove old svg document - var nextSibling = svgcontent.nextSibling; - var oldzoom = svgroot.removeChild(svgcontent); - batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(oldzoom, nextSibling, svgroot)); + // remove old svg document + var nextSibling = svgcontent.nextSibling; + var oldzoom = svgroot.removeChild(svgcontent); + batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(oldzoom, nextSibling, svgroot)); - // set new svg document - // If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode() - if (svgdoc.adoptNode) { - svgcontent = svgdoc.adoptNode(newDoc.documentElement); - } else { - svgcontent = svgdoc.importNode(newDoc.documentElement, true); - } + // set new svg document + // If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode() + if (svgdoc.adoptNode) { + svgcontent = svgdoc.adoptNode(newDoc.documentElement); + } else { + svgcontent = svgdoc.importNode(newDoc.documentElement, true); + } - svgroot.appendChild(svgcontent); - var content = $(svgcontent); + svgroot.appendChild(svgcontent); + var content = $(svgcontent); - canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent, idprefix); + canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent, idprefix); - // retrieve or set the nonce - var nonce = getCurrentDrawing().getNonce(); - if (nonce) { - call('setnonce', nonce); - } else { - call('unsetnonce'); - } + // retrieve or set the nonce + var nonce = getCurrentDrawing().getNonce(); + if (nonce) { + call('setnonce', nonce); + } else { + call('unsetnonce'); + } - // change image href vals if possible - content.find('image').each(function () { - var image = this; - svgedit.utilities.preventClickDefault(image); - var val = getHref(this); - if (val) { - if (val.indexOf('data:') === 0) { - // Check if an SVG-edit data URI - var m = val.match(/svgedit_url=(.*?);/); - if (m) { - var url = decodeURIComponent(m[1]); - $(new Image()).load(function () { - image.setAttributeNS(NS.XLINK, 'xlink:href', url); - }).attr('src', url); - } - } - // Add to encodableImages if it loads - canvas.embedImage(val); - } - }); + // change image href vals if possible + content.find('image').each(function () { + var image = this; + svgedit.utilities.preventClickDefault(image); + var val = getHref(this); + if (val) { + if (val.indexOf('data:') === 0) { + // Check if an SVG-edit data URI + var m = val.match(/svgedit_url=(.*?);/); + if (m) { + var url = decodeURIComponent(m[1]); + $(new Image()).load(function () { + image.setAttributeNS(NS.XLINK, 'xlink:href', url); + }).attr('src', url); + } + } + // 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 - if ($(this).closest('defs').length) { return; } + // Wrap child SVGs in group elements + content.find('svg').each(function () { + // Skip if it's in a + if ($(this).closest('defs').length) { return; } - uniquifyElems(this); + uniquifyElems(this); - // Check if it already has a gsvg group - var pa = this.parentNode; - if (pa.childNodes.length === 1 && pa.nodeName === 'g') { - $(pa).data('gsvg', this); - pa.id = pa.id || getNextId(); - } else { - groupSvgElem(this); - } - }); + // Check if it already has a gsvg group + var pa = this.parentNode; + if (pa.childNodes.length === 1 && pa.nodeName === 'g') { + $(pa).data('gsvg', this); + pa.id = pa.id || getNextId(); + } else { + groupSvgElem(this); + } + }); - // For Firefox: Put all paint elems in defs - if (svgedit.browser.isGecko()) { - content.find('linearGradient, radialGradient, pattern').appendTo(svgedit.utilities.findDefs()); - } + // For Firefox: Put all paint elems in defs + if (svgedit.browser.isGecko()) { + content.find('linearGradient, radialGradient, pattern').appendTo(svgedit.utilities.findDefs()); + } - // Set ref element for elements + // Set ref element for elements - // TODO: This should also be done if the object is re-added through "redo" - setUseData(content); + // TODO: This should also be done if the object is re-added through "redo" + setUseData(content); - convertGradients(content[0]); + convertGradients(content[0]); - var attrs = { - id: 'svgcontent', - overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden' - }; + var attrs = { + id: 'svgcontent', + overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden' + }; - var percs = false; + var percs = false; - // determine proper size - if (content.attr('viewBox')) { - var vb = content.attr('viewBox').split(' '); - attrs.width = vb[2]; - attrs.height = vb[3]; - // handle content that doesn't have a viewBox - } else { - $.each(['width', 'height'], function (i, dim) { - // Set to 100 if not given - var val = content.attr(dim); + // determine proper size + if (content.attr('viewBox')) { + var vb = content.attr('viewBox').split(' '); + attrs.width = vb[2]; + attrs.height = vb[3]; + // handle content that doesn't have a viewBox + } else { + $.each(['width', 'height'], function (i, dim) { + // Set to 100 if not given + var val = content.attr(dim); - if (!val) { val = '100%'; } + if (!val) { val = '100%'; } - if (String(val).substr(-1) === '%') { - // Use user units if percentage given - percs = true; - } else { - attrs[dim] = svgedit.units.convertToNum(dim, val); - } - }); - } + if (String(val).substr(-1) === '%') { + // Use user units if percentage given + percs = true; + } else { + attrs[dim] = svgedit.units.convertToNum(dim, val); + } + }); + } - // identify layers - identifyLayers(); + // identify layers + identifyLayers(); - // Give ID for any visible layer children missing one - content.children().find(visElems).each(function () { - if (!this.id) { this.id = getNextId(); } - }); + // 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; - } + // 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 - // result from the percs calculation - if (attrs.width <= 0) { attrs.width = 100; } - if (attrs.height <= 0) { attrs.height = 100; } + // Just in case negative numbers are given or + // result from the percs calculation + if (attrs.width <= 0) { attrs.width = 100; } + if (attrs.height <= 0) { attrs.height = 100; } - content.attr(attrs); - this.contentW = attrs.width; - this.contentH = attrs.height; + content.attr(attrs); + this.contentW = attrs.width; + this.contentH = attrs.height; - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(svgcontent)); - // update root to the correct size - var changes = content.attr(['width', 'height']); - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(svgroot, changes)); + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(svgcontent)); + // update root to the correct size + var changes = content.attr(['width', 'height']); + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(svgroot, changes)); - // reset zoom - currentZoom = 1; + // reset zoom + currentZoom = 1; - // reset transform lists - svgedit.transformlist.resetListMap(); - clearSelection(); - svgedit.path.clearData(); - svgroot.appendChild(selectorManager.selectorParentGroup); + // reset transform lists + svgedit.transformlist.resetListMap(); + clearSelection(); + svgedit.path.clearData(); + svgroot.appendChild(selectorManager.selectorParentGroup); - if (!preventUndo) addCommandToHistory(batchCmd); - call('changed', [svgcontent]); - } catch (e) { - console.log(e); - return false; - } + if (!preventUndo) addCommandToHistory(batchCmd); + call('changed', [svgcontent]); + } catch (e) { + console.log(e); + return false; + } - return true; + return true; }; // Function: importSvgString @@ -4638,123 +4638,123 @@ this.setSvgString = function (xmlString, preventUndo) { // was obtained // * import should happen in top-left of current zoomed viewport this.importSvgString = function (xmlString) { - var j, ts; - try { - // Get unique ID - var uid = svgedit.utilities.encode64(xmlString.length + xmlString).substr(0, 32); + var j, ts; + try { + // Get unique ID + var uid = svgedit.utilities.encode64(xmlString.length + xmlString).substr(0, 32); - var useExisting = false; + var useExisting = false; - // Look for symbol and make sure symbol exists in image - if (importIds[uid]) { - if ($(importIds[uid].symbol).parents('#svgroot').length) { - useExisting = true; - } - } + // Look for symbol and make sure symbol exists in image + if (importIds[uid]) { + if ($(importIds[uid].symbol).parents('#svgroot').length) { + useExisting = true; + } + } - var batchCmd = new svgedit.history.BatchCommand('Import Image'); - var symbol; - if (useExisting) { - symbol = importIds[uid].symbol; - ts = importIds[uid].xform; - } else { - // convert string into XML document - var newDoc = svgedit.utilities.text2xml(xmlString); + var batchCmd = new svgedit.history.BatchCommand('Import Image'); + var symbol; + if (useExisting) { + symbol = importIds[uid].symbol; + ts = importIds[uid].xform; + } else { + // convert string into XML document + var newDoc = svgedit.utilities.text2xml(xmlString); - this.prepareSvg(newDoc); + this.prepareSvg(newDoc); - // import new svg document into our document - var svg; - // If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode() - if (svgdoc.adoptNode) { - svg = svgdoc.adoptNode(newDoc.documentElement); - } else { - svg = svgdoc.importNode(newDoc.documentElement, true); - } + // import new svg document into our document + var svg; + // If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode() + if (svgdoc.adoptNode) { + svg = svgdoc.adoptNode(newDoc.documentElement); + } else { + svg = svgdoc.importNode(newDoc.documentElement, true); + } - uniquifyElems(svg); + uniquifyElems(svg); - var innerw = svgedit.units.convertToNum('width', svg.getAttribute('width')), - innerh = svgedit.units.convertToNum('height', svg.getAttribute('height')), - innervb = svg.getAttribute('viewBox'), - // if no explicit viewbox, create one out of the width and height - vb = innervb ? innervb.split(' ') : [0, 0, innerw, innerh]; - for (j = 0; j < 4; ++j) { - vb[j] = +(vb[j]); - } + var innerw = svgedit.units.convertToNum('width', svg.getAttribute('width')), + innerh = svgedit.units.convertToNum('height', svg.getAttribute('height')), + innervb = svg.getAttribute('viewBox'), + // if no explicit viewbox, create one out of the width and height + vb = innervb ? innervb.split(' ') : [0, 0, innerw, innerh]; + for (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 + // 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) { - ts = 'scale(' + (canvash / 3) / vb[3] + ')'; - } else { - ts = 'scale(' + (canvash / 3) / vb[2] + ')'; - } + if (innerh > innerw) { + ts = 'scale(' + (canvash / 3) / vb[3] + ')'; + } else { + ts = 'scale(' + (canvash / 3) / vb[2] + ')'; + } - // Hack to make recalculateDimensions understand how to scale - ts = 'translate(0) ' + ts + ' translate(0)'; + // Hack to make recalculateDimensions understand how to scale + ts = 'translate(0) ' + ts + ' translate(0)'; - symbol = svgdoc.createElementNS(NS.SVG, 'symbol'); - var defs = svgedit.utilities.findDefs(); + symbol = svgdoc.createElementNS(NS.SVG, 'symbol'); + var defs = svgedit.utilities.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); - } + 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); - } - var attrs = svg.attributes; - var i; - for (i = 0; i < attrs.length; i++) { - var attr = attrs[i]; - symbol.setAttribute(attr.nodeName, attr.value); - } - symbol.id = getNextId(); + while (svg.firstChild) { + var first = svg.firstChild; + symbol.appendChild(first); + } + var attrs = svg.attributes; + var i; + for (i = 0; i < attrs.length; i++) { + var attr = attrs[i]; + symbol.setAttribute(attr.nodeName, attr.value); + } + symbol.id = getNextId(); - // Store data - importIds[uid] = { - symbol: symbol, - xform: ts - }; + // Store data + importIds[uid] = { + symbol: symbol, + xform: ts + }; - svgedit.utilities.findDefs().appendChild(symbol); - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(symbol)); - } + svgedit.utilities.findDefs().appendChild(symbol); + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(symbol)); + } - var useEl = svgdoc.createElementNS(NS.SVG, 'use'); - useEl.id = getNextId(); - setHref(useEl, '#' + symbol.id); + var useEl = svgdoc.createElementNS(NS.SVG, 'use'); + useEl.id = getNextId(); + setHref(useEl, '#' + symbol.id); - (currentGroup || getCurrentDrawing().getCurrentLayer()).appendChild(useEl); - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(useEl)); - clearSelection(); + (currentGroup || getCurrentDrawing().getCurrentLayer()).appendChild(useEl); + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(useEl)); + clearSelection(); - useEl.setAttribute('transform', ts); - svgedit.recalculate.recalculateDimensions(useEl); - $(useEl).data('symbol', symbol).data('ref', symbol); - addToSelection([useEl]); + useEl.setAttribute('transform', ts); + svgedit.recalculate.recalculateDimensions(useEl); + $(useEl).data('symbol', symbol).data('ref', symbol); + addToSelection([useEl]); - // 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; - // } - addCommandToHistory(batchCmd); - call('changed', [svgcontent]); - } catch (e) { - console.log(e); - return null; - } + // 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; + // } + addCommandToHistory(batchCmd); + call('changed', [svgcontent]); + } catch (e) { + console.log(e); + return null; + } - // we want to return the element so we can automatically select it - return useEl; + // we want to return the element so we can automatically select it + return useEl; }; // TODO(codedread): Move all layer/context functions in draw.js @@ -4765,8 +4765,8 @@ this.importSvgString = function (xmlString) { // Function: identifyLayers // Updates layer system var identifyLayers = canvas.identifyLayers = function () { - leaveContext(); - getCurrentDrawing().identifyLayers(); + leaveContext(); + getCurrentDrawing().identifyLayers(); }; // Function: createLayer @@ -4777,9 +4777,9 @@ var identifyLayers = canvas.identifyLayers = function () { // Parameters: // name - The given name this.createLayer = function (name, hrService) { - var newLayer = getCurrentDrawing().createLayer(name, historyRecordingService(hrService)); - clearSelection(); - call('changed', [newLayer]); + var newLayer = getCurrentDrawing().createLayer(name, historyRecordingService(hrService)); + clearSelection(); + call('changed', [newLayer]); }; /** @@ -4790,32 +4790,32 @@ this.createLayer = function (name, hrService) { * @param {svgedit.history.HistoryRecordingService} hrService - History recording service */ this.cloneLayer = function (name, hrService) { - // Clone the current layer and make the cloned layer the new current layer - var newLayer = getCurrentDrawing().cloneLayer(name, historyRecordingService(hrService)); + // Clone the current layer and make the cloned layer the new current layer + var newLayer = getCurrentDrawing().cloneLayer(name, historyRecordingService(hrService)); - clearSelection(); - leaveContext(); - call('changed', [newLayer]); + clearSelection(); + leaveContext(); + call('changed', [newLayer]); }; // Function: deleteCurrentLayer // 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 currentLayer = getCurrentDrawing().getCurrentLayer(); - var nextSibling = currentLayer.nextSibling; - var parent = currentLayer.parentNode; - currentLayer = getCurrentDrawing().deleteCurrentLayer(); - if (currentLayer) { - var batchCmd = new svgedit.history.BatchCommand('Delete Layer'); - // store in our Undo History - batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(currentLayer, nextSibling, parent)); - addCommandToHistory(batchCmd); - clearSelection(); - call('changed', [parent]); - return true; - } - return false; + var currentLayer = getCurrentDrawing().getCurrentLayer(); + var nextSibling = currentLayer.nextSibling; + var parent = currentLayer.parentNode; + currentLayer = getCurrentDrawing().deleteCurrentLayer(); + if (currentLayer) { + var batchCmd = new svgedit.history.BatchCommand('Delete Layer'); + // store in our Undo History + batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(currentLayer, nextSibling, parent)); + addCommandToHistory(batchCmd); + clearSelection(); + call('changed', [parent]); + return true; + } + return false; }; // Function: setCurrentLayer @@ -4828,11 +4828,11 @@ this.deleteCurrentLayer = function () { // Returns: // true if the current layer was switched, otherwise false this.setCurrentLayer = function (name) { - var result = getCurrentDrawing().setCurrentLayer(svgedit.utilities.toXml(name)); - if (result) { - clearSelection(); - } - return result; + var result = getCurrentDrawing().setCurrentLayer(svgedit.utilities.toXml(name)); + if (result) { + clearSelection(); + } + return result; }; // Function: renameCurrentLayer @@ -4846,16 +4846,16 @@ this.setCurrentLayer = function (name) { // Returns: // true if the rename succeeded, false otherwise. this.renameCurrentLayer = function (newname) { - var drawing = getCurrentDrawing(); - var layer = drawing.getCurrentLayer(); - if (layer) { - var result = drawing.setCurrentLayerName(newname, historyRecordingService()); - if (result) { - call('changed', [layer]); - return true; - } - } - return false; + var drawing = getCurrentDrawing(); + var layer = drawing.getCurrentLayer(); + if (layer) { + var result = drawing.setCurrentLayerName(newname, historyRecordingService()); + if (result) { + call('changed', [layer]); + return true; + } + } + return false; }; // Function: setCurrentLayerPosition @@ -4870,13 +4870,13 @@ this.renameCurrentLayer = function (newname) { // Returns: // true if the current layer position was changed, false otherwise. this.setCurrentLayerPosition = function (newpos) { - var drawing = getCurrentDrawing(); - var result = drawing.setCurrentLayerPosition(newpos); - if (result) { - addCommandToHistory(new svgedit.history.MoveElementCommand(result.currentGroup, result.oldNextSibling, svgcontent)); - return true; - } - return false; + var drawing = getCurrentDrawing(); + var result = drawing.setCurrentLayerPosition(newpos); + if (result) { + addCommandToHistory(new svgedit.history.MoveElementCommand(result.currentGroup, result.oldNextSibling, svgcontent)); + return true; + } + return false; }; // Function: setLayerVisibility @@ -4890,22 +4890,22 @@ this.setCurrentLayerPosition = function (newpos) { // Returns: // true if the layer's visibility was set, false otherwise this.setLayerVisibility = function (layername, bVisible) { - var drawing = getCurrentDrawing(); - var prevVisibility = drawing.getLayerVisibility(layername); - var layer = drawing.setLayerVisibility(layername, bVisible); - if (layer) { - var oldDisplay = prevVisibility ? 'inline' : 'none'; - addCommandToHistory(new svgedit.history.ChangeElementCommand(layer, {'display': oldDisplay}, 'Layer Visibility')); - } else { - return false; - } + var drawing = getCurrentDrawing(); + var prevVisibility = drawing.getLayerVisibility(layername); + var layer = drawing.setLayerVisibility(layername, bVisible); + if (layer) { + var oldDisplay = prevVisibility ? 'inline' : 'none'; + addCommandToHistory(new svgedit.history.ChangeElementCommand(layer, {'display': oldDisplay}, 'Layer Visibility')); + } else { + return false; + } - if (layer === drawing.getCurrentLayer()) { - clearSelection(); - pathActions.clear(); - } - // call('changed', [selected]); - return true; + if (layer === drawing.getCurrentLayer()) { + clearSelection(); + pathActions.clear(); + } + // call('changed', [selected]); + return true; }; // Function: moveSelectedToLayer @@ -4918,92 +4918,92 @@ this.setLayerVisibility = function (layername, bVisible) { // Returns: // true if the selected elements were moved to the layer, false otherwise. this.moveSelectedToLayer = function (layername) { - // find the layer - var i; - var drawing = getCurrentDrawing(); - var layer = drawing.getLayerByName(layername); - if (!layer) { return false; } + // find the layer + var i; + var drawing = getCurrentDrawing(); + var layer = drawing.getLayerByName(layername); + if (!layer) { return false; } - var batchCmd = new svgedit.history.BatchCommand('Move Elements to Layer'); + var batchCmd = new svgedit.history.BatchCommand('Move Elements to Layer'); - // loop for each selected element and move it - var selElems = selectedElements; - i = selElems.length; - while (i--) { - var elem = selElems[i]; - if (!elem) { continue; } - var oldNextSibling = elem.nextSibling; - // TODO: this is pretty brittle! - var oldLayer = elem.parentNode; - layer.appendChild(elem); - batchCmd.addSubCommand(new svgedit.history.MoveElementCommand(elem, oldNextSibling, oldLayer)); - } + // loop for each selected element and move it + var selElems = selectedElements; + i = selElems.length; + while (i--) { + var elem = selElems[i]; + if (!elem) { continue; } + var oldNextSibling = elem.nextSibling; + // TODO: this is pretty brittle! + var oldLayer = elem.parentNode; + layer.appendChild(elem); + batchCmd.addSubCommand(new svgedit.history.MoveElementCommand(elem, oldNextSibling, oldLayer)); + } - addCommandToHistory(batchCmd); + addCommandToHistory(batchCmd); - return true; + return true; }; this.mergeLayer = function (hrService) { - getCurrentDrawing().mergeLayer(historyRecordingService(hrService)); - clearSelection(); - leaveContext(); - call('changed', [svgcontent]); + getCurrentDrawing().mergeLayer(historyRecordingService(hrService)); + clearSelection(); + leaveContext(); + call('changed', [svgcontent]); }; this.mergeAllLayers = function (hrService) { - getCurrentDrawing().mergeAllLayers(historyRecordingService(hrService)); - clearSelection(); - leaveContext(); - call('changed', [svgcontent]); + getCurrentDrawing().mergeAllLayers(historyRecordingService(hrService)); + clearSelection(); + leaveContext(); + call('changed', [svgcontent]); }; // Function: leaveContext // Return from a group context to the regular kind, make any previously // disabled elements enabled again var leaveContext = this.leaveContext = function () { - var i, len = disabledElems.length; - if (len) { - for (i = 0; i < len; i++) { - var elem = disabledElems[i]; - var orig = elData(elem, 'orig_opac'); - if (orig !== 1) { - elem.setAttribute('opacity', orig); - } else { - elem.removeAttribute('opacity'); - } - elem.setAttribute('style', 'pointer-events: inherit'); - } - disabledElems = []; - clearSelection(true); - call('contextset', null); - } - currentGroup = null; + var i, len = disabledElems.length; + if (len) { + for (i = 0; i < len; i++) { + var elem = disabledElems[i]; + var orig = elData(elem, 'orig_opac'); + if (orig !== 1) { + elem.setAttribute('opacity', orig); + } else { + elem.removeAttribute('opacity'); + } + elem.setAttribute('style', 'pointer-events: inherit'); + } + disabledElems = []; + clearSelection(true); + call('contextset', null); + } + currentGroup = null; }; // Function: setContext // Set the current context (for in-group editing) var setContext = this.setContext = function (elem) { - leaveContext(); - if (typeof elem === 'string') { - elem = svgedit.utilities.getElem(elem); - } + leaveContext(); + if (typeof elem === 'string') { + elem = svgedit.utilities.getElem(elem); + } - // Edit inside this group - currentGroup = elem; + // Edit inside this group + currentGroup = elem; - // Disable other elements - $(elem).parentsUntil('#svgcontent').andSelf().siblings().each(function () { - var opac = this.getAttribute('opacity') || 1; - // Store the original's opacity - elData(this, 'orig_opac', opac); - this.setAttribute('opacity', opac * 0.33); - this.setAttribute('style', 'pointer-events: none'); - disabledElems.push(this); - }); + // Disable other elements + $(elem).parentsUntil('#svgcontent').andSelf().siblings().each(function () { + var opac = this.getAttribute('opacity') || 1; + // Store the original's opacity + elData(this, 'orig_opac', opac); + this.setAttribute('opacity', opac * 0.33); + this.setAttribute('style', 'pointer-events: none'); + disabledElems.push(this); + }); - clearSelection(); - call('contextset', currentGroup); + clearSelection(); + call('contextset', currentGroup); }; // Group: Document functions @@ -5011,29 +5011,29 @@ var setContext = this.setContext = function (elem) { // Function: clear // Clears the current document. This is not an undoable action. this.clear = function () { - pathActions.clear(); + pathActions.clear(); - clearSelection(); + clearSelection(); - // clear the svgcontent node - canvas.clearSvgContentElement(); + // clear the svgcontent node + canvas.clearSvgContentElement(); - // create new document - canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent); + // create new document + canvas.current_drawing_ = new svgedit.draw.Drawing(svgcontent); - // create empty first layer - canvas.createLayer('Layer 1'); + // create empty first layer + canvas.createLayer('Layer 1'); - // clear the undo stack - canvas.undoMgr.resetUndoStack(); + // clear the undo stack + canvas.undoMgr.resetUndoStack(); - // reset the selector manager - selectorManager.initGroup(); + // reset the selector manager + selectorManager.initGroup(); - // reset the rubber band box - rubberBox = selectorManager.getRubberBandBox(); + // reset the rubber band box + rubberBox = selectorManager.getRubberBandBox(); - call('cleared'); + call('cleared'); }; // Function: linkControlPoints @@ -5058,14 +5058,14 @@ var getResolution = this.getResolution = function () { // var vb = svgcontent.getAttribute('viewBox').split(' '); // return {'w':vb[2], 'h':vb[3], 'zoom': currentZoom}; - var width = svgcontent.getAttribute('width') / currentZoom; - var height = svgcontent.getAttribute('height') / currentZoom; + var width = svgcontent.getAttribute('width') / currentZoom; + var height = svgcontent.getAttribute('height') / currentZoom; - return { - 'w': width, - 'h': height, - 'zoom': currentZoom - }; + return { + 'w': width, + 'h': height, + 'zoom': currentZoom + }; }; // Function: getZoom @@ -5079,7 +5079,7 @@ this.getSnapToGrid = function () { return curConfig.gridSnapping; }; // Function: getVersion // Returns a string which describes the revision number of SvgCanvas. this.getVersion = function () { - return 'svgcanvas.js ($Rev$)'; + return 'svgcanvas.js ($Rev$)'; }; // Function: setUiStrings @@ -5088,7 +5088,7 @@ this.getVersion = function () { // Parameters: // strs - Object with strings (see uiStrings for examples) this.setUiStrings = function (strs) { - $.extend(uiStrings, strs.notification); + $.extend(uiStrings, strs.notification); }; // Function: setConfig @@ -5097,61 +5097,61 @@ this.setUiStrings = function (strs) { // Parameters: // opts - Object with options (see curConfig for examples) this.setConfig = function (opts) { - $.extend(curConfig, opts); + $.extend(curConfig, opts); }; // Function: getTitle // Returns the current group/SVG's title contents this.getTitle = function (elem) { - var i; - elem = elem || selectedElements[0]; - if (!elem) { return; } - elem = $(elem).data('gsvg') || $(elem).data('symbol') || elem; - var childs = elem.childNodes; - for (i = 0; i < childs.length; i++) { - if (childs[i].nodeName === 'title') { - return childs[i].textContent; - } - } - return ''; + var i; + elem = elem || selectedElements[0]; + if (!elem) { return; } + elem = $(elem).data('gsvg') || $(elem).data('symbol') || elem; + var childs = elem.childNodes; + for (i = 0; i < childs.length; i++) { + if (childs[i].nodeName === 'title') { + return childs[i].textContent; + } + } + return ''; }; // Function: setGroupTitle // Sets the group/SVG's title content // TODO: Combine this with setDocumentTitle this.setGroupTitle = function (val) { - var elem = selectedElements[0]; - elem = $(elem).data('gsvg') || elem; + var elem = selectedElements[0]; + elem = $(elem).data('gsvg') || elem; - var ts = $(elem).children('title'); + var ts = $(elem).children('title'); - var batchCmd = new svgedit.history.BatchCommand('Set Label'); + var batchCmd = new svgedit.history.BatchCommand('Set Label'); - if (!val.length) { - // Remove title element - var tsNextSibling = ts.nextSibling; - batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(ts[0], tsNextSibling, elem)); - ts.remove(); - } else if (ts.length) { - // Change title contents - var title = ts[0]; - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(title, {'#text': title.textContent})); - title.textContent = val; - } else { - // Add title element - title = svgdoc.createElementNS(NS.SVG, 'title'); - title.textContent = val; - $(elem).prepend(title); - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(title)); - } + if (!val.length) { + // Remove title element + var tsNextSibling = ts.nextSibling; + batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(ts[0], tsNextSibling, elem)); + ts.remove(); + } else if (ts.length) { + // Change title contents + var title = ts[0]; + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(title, {'#text': title.textContent})); + title.textContent = val; + } else { + // Add title element + title = svgdoc.createElementNS(NS.SVG, 'title'); + title.textContent = val; + $(elem).prepend(title); + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(title)); + } - addCommandToHistory(batchCmd); + addCommandToHistory(batchCmd); }; // Function: getDocumentTitle // Returns the current document title or an empty string if not found var getDocumentTitle = this.getDocumentTitle = function () { - return canvas.getTitle(svgcontent); + return canvas.getTitle(svgcontent); }; // Function: setDocumentTitle @@ -5161,31 +5161,31 @@ var getDocumentTitle = this.getDocumentTitle = function () { // Parameters: // newtitle - String with the new title this.setDocumentTitle = function (newtitle) { - var i; - var childs = svgcontent.childNodes, docTitle = false, oldTitle = ''; + var i; + var childs = svgcontent.childNodes, docTitle = false, oldTitle = ''; - var batchCmd = new svgedit.history.BatchCommand('Change Image Title'); + var batchCmd = new svgedit.history.BatchCommand('Change Image Title'); - for (i = 0; i < childs.length; i++) { - if (childs[i].nodeName === 'title') { - docTitle = childs[i]; - oldTitle = docTitle.textContent; - break; - } - } - if (!docTitle) { - docTitle = svgdoc.createElementNS(NS.SVG, 'title'); - svgcontent.insertBefore(docTitle, svgcontent.firstChild); - } + for (i = 0; i < childs.length; i++) { + if (childs[i].nodeName === 'title') { + docTitle = childs[i]; + oldTitle = docTitle.textContent; + break; + } + } + if (!docTitle) { + docTitle = svgdoc.createElementNS(NS.SVG, 'title'); + svgcontent.insertBefore(docTitle, svgcontent.firstChild); + } - if (newtitle.length) { - docTitle.textContent = newtitle; - } else { - // No title given, so element is not necessary - docTitle.parentNode.removeChild(docTitle); - } - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(docTitle, {'#text': oldTitle})); - addCommandToHistory(batchCmd); + if (newtitle.length) { + docTitle.textContent = newtitle; + } else { + // No title given, so element is not necessary + docTitle.parentNode.removeChild(docTitle); + } + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(docTitle, {'#text': oldTitle})); + addCommandToHistory(batchCmd); }; // Function: getEditorNS @@ -5194,10 +5194,10 @@ this.setDocumentTitle = function (newtitle) { // Parameters: // add - Boolean to indicate whether or not to add the namespace value this.getEditorNS = function (add) { - if (add) { - svgcontent.setAttribute('xmlns:se', NS.SE); - } - return NS.SE; + if (add) { + svgcontent.setAttribute('xmlns:se', NS.SE); + } + return NS.SE; }; // Function: setResolution @@ -5212,63 +5212,63 @@ this.getEditorNS = function (add) { // 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(); - var w = res.w, h = res.h; - var batchCmd; + var res = getResolution(); + var w = res.w, h = res.h; + var batchCmd; - if (x === 'fit') { - // Get bounding box - var bbox = getStrokedBBox(); + if (x === 'fit') { + // Get bounding box + var bbox = getStrokedBBox(); - if (bbox) { - batchCmd = new svgedit.history.BatchCommand('Fit Canvas to Content'); - var visEls = getVisibleElements(); - addToSelection(visEls); - var dx = [], dy = []; - $.each(visEls, function (i, item) { - dx.push(bbox.x * -1); - dy.push(bbox.y * -1); - }); + if (bbox) { + batchCmd = new svgedit.history.BatchCommand('Fit Canvas to Content'); + var visEls = getVisibleElements(); + addToSelection(visEls); + var dx = [], dy = []; + $.each(visEls, function (i, item) { + dx.push(bbox.x * -1); + dy.push(bbox.y * -1); + }); - var cmd = canvas.moveSelectedElements(dx, dy, true); - batchCmd.addSubCommand(cmd); - clearSelection(); + var cmd = canvas.moveSelectedElements(dx, dy, true); + batchCmd.addSubCommand(cmd); + clearSelection(); - x = Math.round(bbox.width); - y = Math.round(bbox.height); - } else { - return false; - } - } - if (x !== w || y !== h) { - if (!batchCmd) { - batchCmd = new svgedit.history.BatchCommand('Change Image Dimensions'); - } + x = Math.round(bbox.width); + y = Math.round(bbox.height); + } else { + return false; + } + } + if (x !== w || y !== h) { + if (!batchCmd) { + batchCmd = new svgedit.history.BatchCommand('Change Image Dimensions'); + } - x = svgedit.units.convertToNum('width', x); - y = svgedit.units.convertToNum('height', y); + x = svgedit.units.convertToNum('width', x); + y = svgedit.units.convertToNum('height', y); - svgcontent.setAttribute('width', x); - svgcontent.setAttribute('height', y); + svgcontent.setAttribute('width', x); + svgcontent.setAttribute('height', y); - this.contentW = x; - this.contentH = y; - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(svgcontent, {'width': w, 'height': h})); + this.contentW = x; + this.contentH = y; + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(svgcontent, {'width': w, 'height': h})); - svgcontent.setAttribute('viewBox', [0, 0, x / currentZoom, y / currentZoom].join(' ')); - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(svgcontent, {'viewBox': ['0 0', w, h].join(' ')})); + svgcontent.setAttribute('viewBox', [0, 0, x / currentZoom, y / currentZoom].join(' ')); + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(svgcontent, {'viewBox': ['0 0', w, h].join(' ')})); - addCommandToHistory(batchCmd); - call('changed', [svgcontent]); - } - return true; + addCommandToHistory(batchCmd); + call('changed', [svgcontent]); + } + return true; }; // Function: getOffset // Returns an object with x, y values indicating the svgcontent element's // position in the editor's canvas. this.getOffset = function () { - return $(svgcontent).attr(['x', 'y']); + return $(svgcontent).attr(['x', 'y']); }; // Function: setBBoxZoom @@ -5279,48 +5279,48 @@ this.getOffset = function () { // editorW - Integer with the editor's workarea box's width // editorH - Integer with the editor's workarea box's height this.setBBoxZoom = function (val, editorW, editorH) { - var spacer = 0.85; - var bb; - var calcZoom = function (bb) { - if (!bb) { return false; } - var wZoom = Math.round((editorW / bb.width) * 100 * spacer) / 100; - var hZoom = Math.round((editorH / bb.height) * 100 * spacer) / 100; - var zoomlevel = Math.min(wZoom, hZoom); - canvas.setZoom(zoomlevel); - return {'zoom': zoomlevel, 'bbox': bb}; - }; + var spacer = 0.85; + var bb; + var calcZoom = function (bb) { + if (!bb) { return false; } + var wZoom = Math.round((editorW / bb.width) * 100 * spacer) / 100; + var hZoom = Math.round((editorH / bb.height) * 100 * spacer) / 100; + var zoomlevel = Math.min(wZoom, hZoom); + canvas.setZoom(zoomlevel); + return {'zoom': zoomlevel, 'bbox': bb}; + }; - if (typeof val === 'object') { - bb = val; - if (bb.width === 0 || bb.height === 0) { - var newzoom = bb.zoom ? bb.zoom : currentZoom * bb.factor; - canvas.setZoom(newzoom); - return {'zoom': currentZoom, 'bbox': bb}; - } - return calcZoom(bb); - } + if (typeof val === 'object') { + bb = val; + if (bb.width === 0 || bb.height === 0) { + var newzoom = bb.zoom ? bb.zoom : currentZoom * bb.factor; + canvas.setZoom(newzoom); + return {'zoom': currentZoom, 'bbox': bb}; + } + return calcZoom(bb); + } - switch (val) { - case 'selection': - if (!selectedElements[0]) { return; } - var selectedElems = $.map(selectedElements, function (n) { if (n) { return n; } }); - bb = getStrokedBBox(selectedElems); - break; - case 'canvas': - var res = getResolution(); - spacer = 0.95; - bb = {width: res.w, height: res.h, x: 0, y: 0}; - break; - case 'content': - bb = getStrokedBBox(); - break; - case 'layer': - bb = getStrokedBBox(getVisibleElements(getCurrentDrawing().getCurrentLayer())); - break; - default: - return; - } - return calcZoom(bb); + switch (val) { + case 'selection': + if (!selectedElements[0]) { return; } + var selectedElems = $.map(selectedElements, function (n) { if (n) { return n; } }); + bb = getStrokedBBox(selectedElems); + break; + case 'canvas': + var res = getResolution(); + spacer = 0.95; + bb = {width: res.w, height: res.h, x: 0, y: 0}; + break; + case 'content': + bb = getStrokedBBox(); + break; + case 'layer': + bb = getStrokedBBox(getVisibleElements(getCurrentDrawing().getCurrentLayer())); + break; + default: + return; + } + return calcZoom(bb); }; // Function: setZoom @@ -5329,21 +5329,21 @@ this.setBBoxZoom = function (val, editorW, editorH) { // Parameters: // zoomlevel - Float indicating the zoom level to change to this.setZoom = function (zoomlevel) { - var res = getResolution(); - svgcontent.setAttribute('viewBox', '0 0 ' + res.w / zoomlevel + ' ' + res.h / zoomlevel); - currentZoom = zoomlevel; - $.each(selectedElements, function (i, elem) { - if (!elem) { return; } - selectorManager.requestSelector(elem).resize(); - }); - pathActions.zoomChange(); - runExtensions('zoomChanged', zoomlevel); + var res = getResolution(); + svgcontent.setAttribute('viewBox', '0 0 ' + res.w / zoomlevel + ' ' + res.h / zoomlevel); + currentZoom = zoomlevel; + $.each(selectedElements, function (i, elem) { + if (!elem) { return; } + selectorManager.requestSelector(elem).resize(); + }); + pathActions.zoomChange(); + runExtensions('zoomChanged', zoomlevel); }; // Function: getMode // Returns the current editor mode string this.getMode = function () { - return currentMode; + return currentMode; }; // Function: setMode @@ -5352,10 +5352,10 @@ this.getMode = function () { // Parameters: // name - String with the new mode to change to this.setMode = function (name) { - pathActions.clear(true); - textActions.clear(); - curProperties = (selectedElements[0] && selectedElements[0].nodeName === 'text') ? curText : curShape; - currentMode = name; + pathActions.clear(true); + textActions.clear(); + curProperties = (selectedElements[0] && selectedElements[0].nodeName === 'text') ? curText : curShape; + currentMode = name; }; // Group: Element Styling @@ -5363,7 +5363,7 @@ this.setMode = function (name) { // Function: getColor // Returns the current fill/stroke option this.getColor = function (type) { - return curProperties[type]; + return curProperties[type]; }; // Function: setColor @@ -5374,39 +5374,39 @@ this.getColor = function (type) { // val - The value to set the stroke attribute to // preventUndo - Boolean indicating whether or not this should be and undoable option this.setColor = function (type, val, preventUndo) { - curShape[type] = val; - curProperties[type + '_paint'] = {type: 'solidColor'}; - var elems = []; - function addNonG (e) { - if (e.nodeName !== 'g') { - elems.push(e); - } - } - var i = selectedElements.length; - while (i--) { - var elem = selectedElements[i]; - if (elem) { - if (elem.tagName === 'g') { - svgedit.utilities.walkTree(elem, addNonG); - } else { - if (type === 'fill') { - if (elem.tagName !== 'polyline' && elem.tagName !== 'line') { - elems.push(elem); - } - } else { - elems.push(elem); - } - } - } - } - if (elems.length > 0) { - if (!preventUndo) { - changeSelectedAttribute(type, val, elems); - call('changed', elems); - } else { - changeSelectedAttributeNoUndo(type, val, elems); - } - } + curShape[type] = val; + curProperties[type + '_paint'] = {type: 'solidColor'}; + var elems = []; + function addNonG (e) { + if (e.nodeName !== 'g') { + elems.push(e); + } + } + var i = selectedElements.length; + while (i--) { + var elem = selectedElements[i]; + if (elem) { + if (elem.tagName === 'g') { + svgedit.utilities.walkTree(elem, addNonG); + } else { + if (type === 'fill') { + if (elem.tagName !== 'polyline' && elem.tagName !== 'line') { + elems.push(elem); + } + } else { + elems.push(elem); + } + } + } + } + if (elems.length > 0) { + if (!preventUndo) { + changeSelectedAttribute(type, val, elems); + call('changed', elems); + } else { + changeSelectedAttributeNoUndo(type, val, elems); + } + } }; // Function: setGradient @@ -5415,21 +5415,21 @@ this.setColor = function (type, val, preventUndo) { // Parameters // type - String indicating "fill" or "stroke" to apply to an element var setGradient = this.setGradient = function (type) { - if (!curProperties[type + '_paint'] || curProperties[type + '_paint'].type === 'solidColor') { return; } - var grad = canvas[type + 'Grad']; - // find out if there is a duplicate gradient already in the defs - var duplicateGrad = findDuplicateGradient(grad); - var defs = svgedit.utilities.findDefs(); - // no duplicate found, so import gradient into defs - if (!duplicateGrad) { - // var origGrad = grad; - grad = defs.appendChild(svgdoc.importNode(grad, true)); - // get next id and set it on the grad - grad.id = getNextId(); - } else { // use existing gradient - grad = duplicateGrad; - } - canvas.setColor(type, 'url(#' + grad.id + ')'); + if (!curProperties[type + '_paint'] || curProperties[type + '_paint'].type === 'solidColor') { return; } + var grad = canvas[type + 'Grad']; + // find out if there is a duplicate gradient already in the defs + var duplicateGrad = findDuplicateGradient(grad); + var defs = svgedit.utilities.findDefs(); + // no duplicate found, so import gradient into defs + if (!duplicateGrad) { + // var origGrad = grad; + grad = defs.appendChild(svgdoc.importNode(grad, true)); + // get next id and set it on the grad + grad.id = getNextId(); + } else { // use existing gradient + grad = duplicateGrad; + } + canvas.setColor(type, 'url(#' + grad.id + ')'); }; // Function: findDuplicateGradient @@ -5441,101 +5441,101 @@ var setGradient = this.setGradient = function (type) { // Returns: // The existing gradient if found, null if not var findDuplicateGradient = function (grad) { - var defs = svgedit.utilities.findDefs(); - var existingGrads = $(defs).find('linearGradient, radialGradient'); - var i = existingGrads.length; - var radAttrs = ['r', 'cx', 'cy', 'fx', 'fy']; - while (i--) { - var og = existingGrads[i]; - if (grad.tagName === 'linearGradient') { - 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') - ) { - continue; - } - } else { - var gradAttrs = $(grad).attr(radAttrs); - var ogAttrs = $(og).attr(radAttrs); + var defs = svgedit.utilities.findDefs(); + var existingGrads = $(defs).find('linearGradient, radialGradient'); + var i = existingGrads.length; + var radAttrs = ['r', 'cx', 'cy', 'fx', 'fy']; + while (i--) { + var og = existingGrads[i]; + if (grad.tagName === 'linearGradient') { + 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') + ) { + continue; + } + } else { + var gradAttrs = $(grad).attr(radAttrs); + var ogAttrs = $(og).attr(radAttrs); - var diff = false; - $.each(radAttrs, function (i, attr) { - if (gradAttrs[attr] !== ogAttrs[attr]) { diff = true; } - }); + var diff = false; + $.each(radAttrs, function (i, attr) { + if (gradAttrs[attr] !== ogAttrs[attr]) { diff = true; } + }); - if (diff) { continue; } - } + if (diff) { continue; } + } - // else could be a duplicate, iterate through stops - var stops = grad.getElementsByTagNameNS(NS.SVG, 'stop'); - var ostops = og.getElementsByTagNameNS(NS.SVG, 'stop'); + // else could be a duplicate, iterate through stops + var stops = grad.getElementsByTagNameNS(NS.SVG, 'stop'); + var ostops = og.getElementsByTagNameNS(NS.SVG, 'stop'); - if (stops.length !== ostops.length) { - continue; - } + if (stops.length !== ostops.length) { + continue; + } - var j = stops.length; - while (j--) { - var stop = stops[j]; - var ostop = ostops[j]; + var j = stops.length; + while (j--) { + var stop = stops[j]; + var ostop = ostops[j]; - if (stop.getAttribute('offset') !== ostop.getAttribute('offset') || - stop.getAttribute('stop-opacity') !== ostop.getAttribute('stop-opacity') || - stop.getAttribute('stop-color') !== ostop.getAttribute('stop-color')) { - break; - } - } + if (stop.getAttribute('offset') !== ostop.getAttribute('offset') || + stop.getAttribute('stop-opacity') !== ostop.getAttribute('stop-opacity') || + stop.getAttribute('stop-color') !== ostop.getAttribute('stop-color')) { + break; + } + } - if (j === -1) { - return og; - } - } // for each gradient in defs + if (j === -1) { + return og; + } + } // for each gradient in defs - return null; + return null; }; function reorientGrads (elem, m) { - var i; - var bb = svgedit.utilities.getBBox(elem); - for (i = 0; i < 2; i++) { - var type = i === 0 ? 'fill' : 'stroke'; - var attrVal = elem.getAttribute(type); - if (attrVal && attrVal.indexOf('url(') === 0) { - var grad = svgedit.utilities.getRefElem(attrVal); - if (grad.tagName === 'linearGradient') { - var x1 = grad.getAttribute('x1') || 0; - var y1 = grad.getAttribute('y1') || 0; - var x2 = grad.getAttribute('x2') || 1; - var y2 = grad.getAttribute('y2') || 0; + var i; + var bb = svgedit.utilities.getBBox(elem); + for (i = 0; i < 2; i++) { + var type = i === 0 ? 'fill' : 'stroke'; + var attrVal = elem.getAttribute(type); + if (attrVal && attrVal.indexOf('url(') === 0) { + var grad = svgedit.utilities.getRefElem(attrVal); + if (grad.tagName === 'linearGradient') { + var x1 = grad.getAttribute('x1') || 0; + 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; + // 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 = svgedit.math.transformPoint(x1, y1, m); - var pt2 = svgedit.math.transformPoint(x2, y2, m); + // Transform those points + var pt1 = svgedit.math.transformPoint(x1, y1, m); + var pt2 = svgedit.math.transformPoint(x2, y2, m); - // Convert back to BB points - var gCoords = {}; + // Convert back to BB points + var gCoords = {}; - gCoords.x1 = (pt1.x - bb.x) / bb.width; - gCoords.y1 = (pt1.y - bb.y) / bb.height; - gCoords.x2 = (pt2.x - bb.x) / bb.width; - gCoords.y2 = (pt2.y - bb.y) / bb.height; + gCoords.x1 = (pt1.x - bb.x) / bb.width; + gCoords.y1 = (pt1.y - bb.y) / bb.height; + gCoords.x2 = (pt2.x - bb.x) / bb.width; + gCoords.y2 = (pt2.y - bb.y) / bb.height; - var newgrad = grad.cloneNode(true); - $(newgrad).attr(gCoords); + var newgrad = grad.cloneNode(true); + $(newgrad).attr(gCoords); - newgrad.id = getNextId(); - svgedit.utilities.findDefs().appendChild(newgrad); - elem.setAttribute(type, 'url(#' + newgrad.id + ')'); - } - } - } + newgrad.id = getNextId(); + svgedit.utilities.findDefs().appendChild(newgrad); + elem.setAttribute(type, 'url(#' + newgrad.id + ')'); + } + } + } } // Function: setPaint @@ -5545,37 +5545,37 @@ function reorientGrads (elem, m) { // type - String with "fill" or "stroke" // paint - The jGraduate paint object to apply this.setPaint = function (type, paint) { - // make a copy - var p = new $.jGraduate.Paint(paint); - this.setPaintOpacity(type, p.alpha / 100, true); + // make a copy + var p = new $.jGraduate.Paint(paint); + this.setPaintOpacity(type, p.alpha / 100, true); - // now set the current paint object - curProperties[type + '_paint'] = p; - switch (p.type) { - case 'solidColor': - this.setColor(type, p.solidColor !== 'none' ? '#' + p.solidColor : 'none'); - break; - case 'linearGradient': - case 'radialGradient': - canvas[type + 'Grad'] = p[p.type]; - setGradient(type); - break; - } + // now set the current paint object + curProperties[type + '_paint'] = p; + switch (p.type) { + case 'solidColor': + this.setColor(type, p.solidColor !== 'none' ? '#' + p.solidColor : 'none'); + break; + case 'linearGradient': + case 'radialGradient': + canvas[type + 'Grad'] = p[p.type]; + setGradient(type); + break; + } }; // alias this.setStrokePaint = function (paint) { - this.setPaint('stroke', paint); + this.setPaint('stroke', paint); }; this.setFillPaint = function (paint) { - this.setPaint('fill', paint); + this.setPaint('fill', paint); }; // Function: getStrokeWidth // Returns the current stroke-width value this.getStrokeWidth = function () { - return curProperties.stroke_width; + return curProperties.stroke_width; }; // Function: setStrokeWidth @@ -5585,33 +5585,33 @@ this.getStrokeWidth = function () { // Parameters: // val - A Float indicating the new stroke width value this.setStrokeWidth = function (val) { - if (val === 0 && ['line', 'path'].indexOf(currentMode) >= 0) { - canvas.setStrokeWidth(1); - return; - } - curProperties.stroke_width = val; + if (val === 0 && ['line', 'path'].indexOf(currentMode) >= 0) { + canvas.setStrokeWidth(1); + return; + } + curProperties.stroke_width = val; - var elems = []; - function addNonG (e) { - if (e.nodeName !== 'g') { - elems.push(e); - } - } - var i = selectedElements.length; - while (i--) { - var elem = selectedElements[i]; - if (elem) { - if (elem.tagName === 'g') { - svgedit.utilities.walkTree(elem, addNonG); - } else { - elems.push(elem); - } - } - } - if (elems.length > 0) { - changeSelectedAttribute('stroke-width', val, elems); - call('changed', selectedElements); - } + var elems = []; + function addNonG (e) { + if (e.nodeName !== 'g') { + elems.push(e); + } + } + var i = selectedElements.length; + while (i--) { + var elem = selectedElements[i]; + if (elem) { + if (elem.tagName === 'g') { + svgedit.utilities.walkTree(elem, addNonG); + } else { + elems.push(elem); + } + } + } + if (elems.length > 0) { + changeSelectedAttribute('stroke-width', val, elems); + call('changed', selectedElements); + } }; // Function: setStrokeAttr @@ -5621,55 +5621,55 @@ this.setStrokeWidth = function (val) { // attr - String with the attribute name // val - String or number with the attribute value this.setStrokeAttr = function (attr, val) { - curShape[attr.replace('-', '_')] = val; - var elems = []; + curShape[attr.replace('-', '_')] = val; + var elems = []; - var i = selectedElements.length; - while (i--) { - var elem = selectedElements[i]; - if (elem) { - if (elem.tagName === 'g') { - svgedit.utilities.walkTree(elem, function (e) { if (e.nodeName !== 'g') { elems.push(e); } }); - } else { - elems.push(elem); - } - } - } - if (elems.length > 0) { - changeSelectedAttribute(attr, val, elems); - call('changed', selectedElements); - } + var i = selectedElements.length; + while (i--) { + var elem = selectedElements[i]; + if (elem) { + if (elem.tagName === 'g') { + svgedit.utilities.walkTree(elem, function (e) { if (e.nodeName !== 'g') { elems.push(e); } }); + } else { + elems.push(elem); + } + } + } + if (elems.length > 0) { + changeSelectedAttribute(attr, val, elems); + call('changed', selectedElements); + } }; // Function: getStyle // Returns current style options this.getStyle = function () { - return curShape; + return curShape; }; // Function: getOpacity // Returns the current opacity this.getOpacity = function () { - return curShape.opacity; + return curShape.opacity; }; // Function: setOpacity // Sets the given opacity to the current selected elements this.setOpacity = function (val) { - curShape.opacity = val; - changeSelectedAttribute('opacity', val); + curShape.opacity = val; + changeSelectedAttribute('opacity', val); }; // Function: getOpacity // Returns the current fill opacity this.getFillOpacity = function () { - return curShape.fill_opacity; + return curShape.fill_opacity; }; // Function: getStrokeOpacity // Returns the current stroke opacity this.getStrokeOpacity = function () { - return curShape.stroke_opacity; + return curShape.stroke_opacity; }; // Function: setPaintOpacity @@ -5680,12 +5680,12 @@ this.getStrokeOpacity = function () { // val - Float with the new opacity value // preventUndo - Boolean indicating whether or not this should be an undoable action this.setPaintOpacity = function (type, val, preventUndo) { - curShape[type + '_opacity'] = val; - if (!preventUndo) { - changeSelectedAttribute(type + '-opacity', val); - } else { - changeSelectedAttributeNoUndo(type + '-opacity', val); - } + curShape[type + '_opacity'] = val; + if (!preventUndo) { + changeSelectedAttribute(type + '-opacity', val); + } else { + changeSelectedAttributeNoUndo(type + '-opacity', val); + } }; // Function: getPaintOpacity @@ -5694,7 +5694,7 @@ this.setPaintOpacity = function (type, val, preventUndo) { // Parameters: // type - String with "fill" or "stroke" this.getPaintOpacity = function (type) { - return type === 'fill' ? this.getFillOpacity() : this.getStrokeOpacity(); + return type === 'fill' ? this.getFillOpacity() : this.getStrokeOpacity(); }; // Function: getBlur @@ -5703,19 +5703,19 @@ this.getPaintOpacity = function (type) { // Parameters: // elem - The element to check the blur value for this.getBlur = function (elem) { - var val = 0; - // var elem = selectedElements[0]; + var val = 0; + // var elem = selectedElements[0]; - if (elem) { - var filterUrl = elem.getAttribute('filter'); - if (filterUrl) { - var blur = svgedit.utilities.getElem(elem.id + '_blur'); - if (blur) { - val = blur.firstChild.getAttribute('stdDeviation'); - } - } - } - return val; + if (elem) { + var filterUrl = elem.getAttribute('filter'); + if (filterUrl) { + var blur = svgedit.utilities.getElem(elem.id + '_blur'); + if (blur) { + val = blur.firstChild.getAttribute('stdDeviation'); + } + } + } + return val; }; (function () { @@ -5729,36 +5729,36 @@ var filterHidden = false; // Parameters: // val - The new stdDeviation value canvas.setBlurNoUndo = function (val) { - if (!filter) { - canvas.setBlur(val); - return; - } - if (val === 0) { - // Don't change the StdDev, as that will hide the element. - // Instead, just remove the value for "filter" - changeSelectedAttributeNoUndo('filter', ''); - filterHidden = true; - } else { - var elem = selectedElements[0]; - if (filterHidden) { - changeSelectedAttributeNoUndo('filter', 'url(#' + elem.id + '_blur)'); - } - if (svgedit.browser.isWebkit()) { - console.log('e', elem); - elem.removeAttribute('filter'); - elem.setAttribute('filter', 'url(#' + elem.id + '_blur)'); - } - changeSelectedAttributeNoUndo('stdDeviation', val, [filter.firstChild]); - canvas.setBlurOffsets(filter, val); - } + if (!filter) { + canvas.setBlur(val); + return; + } + if (val === 0) { + // Don't change the StdDev, as that will hide the element. + // Instead, just remove the value for "filter" + changeSelectedAttributeNoUndo('filter', ''); + filterHidden = true; + } else { + var elem = selectedElements[0]; + if (filterHidden) { + changeSelectedAttributeNoUndo('filter', 'url(#' + elem.id + '_blur)'); + } + if (svgedit.browser.isWebkit()) { + console.log('e', elem); + elem.removeAttribute('filter'); + elem.setAttribute('filter', 'url(#' + elem.id + '_blur)'); + } + changeSelectedAttributeNoUndo('stdDeviation', val, [filter.firstChild]); + canvas.setBlurOffsets(filter, val); + } }; function finishChange () { - var bCmd = canvas.undoMgr.finishUndoableChange(); - curCommand.addSubCommand(bCmd); - addCommandToHistory(curCommand); - curCommand = null; - filter = null; + var bCmd = canvas.undoMgr.finishUndoableChange(); + curCommand.addSubCommand(bCmd); + addCommandToHistory(curCommand); + curCommand = null; + filter = null; } // Function: setBlurOffsets @@ -5769,23 +5769,23 @@ function finishChange () { // filter - The filter DOM element to update // stdDev - The standard deviation value on which to base the offset size canvas.setBlurOffsets = function (filter, stdDev) { - if (stdDev > 3) { - // TODO: Create algorithm here where size is based on expected blur - svgedit.utilities.assignAttributes(filter, { - x: '-50%', - y: '-50%', - width: '200%', - height: '200%' - }, 100); - } else { - // Removing these attributes hides text in Chrome (see Issue 579) - if (!svgedit.browser.isWebkit()) { - filter.removeAttribute('x'); - filter.removeAttribute('y'); - filter.removeAttribute('width'); - filter.removeAttribute('height'); - } - } + if (stdDev > 3) { + // TODO: Create algorithm here where size is based on expected blur + svgedit.utilities.assignAttributes(filter, { + x: '-50%', + y: '-50%', + width: '200%', + height: '200%' + }, 100); + } else { + // Removing these attributes hides text in Chrome (see Issue 579) + if (!svgedit.browser.isWebkit()) { + filter.removeAttribute('x'); + filter.removeAttribute('y'); + filter.removeAttribute('width'); + filter.removeAttribute('height'); + } + } }; // Function: setBlur @@ -5795,64 +5795,64 @@ canvas.setBlurOffsets = function (filter, stdDev) { // val - Float with the new stdDeviation blur value // complete - Boolean indicating whether or not the action should be completed (to add to the undo manager) canvas.setBlur = function (val, complete) { - if (curCommand) { - finishChange(); - return; - } + if (curCommand) { + finishChange(); + return; + } - // Looks for associated blur, creates one if not found - var elem = selectedElements[0]; - var elemId = elem.id; - filter = svgedit.utilities.getElem(elemId + '_blur'); + // Looks for associated blur, creates one if not found + var elem = selectedElements[0]; + var elemId = elem.id; + filter = svgedit.utilities.getElem(elemId + '_blur'); - val -= 0; + val -= 0; - var batchCmd = new svgedit.history.BatchCommand(); + var batchCmd = new svgedit.history.BatchCommand(); - // Blur found! - if (filter) { - if (val === 0) { - filter = null; - } - } else { - // Not found, so create - var newblur = addSvgElementFromJson({ 'element': 'feGaussianBlur', - 'attr': { - 'in': 'SourceGraphic', - 'stdDeviation': val - } - }); + // Blur found! + if (filter) { + if (val === 0) { + filter = null; + } + } else { + // Not found, so create + var newblur = addSvgElementFromJson({ 'element': 'feGaussianBlur', + 'attr': { + 'in': 'SourceGraphic', + 'stdDeviation': val + } + }); - filter = addSvgElementFromJson({ 'element': 'filter', - 'attr': { - 'id': elemId + '_blur' - } - }); + filter = addSvgElementFromJson({ 'element': 'filter', + 'attr': { + 'id': elemId + '_blur' + } + }); - filter.appendChild(newblur); - svgedit.utilities.findDefs().appendChild(filter); + filter.appendChild(newblur); + svgedit.utilities.findDefs().appendChild(filter); - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(filter)); - } + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(filter)); + } - var changes = {filter: elem.getAttribute('filter')}; + var changes = {filter: elem.getAttribute('filter')}; - if (val === 0) { - elem.removeAttribute('filter'); - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes)); - return; - } + if (val === 0) { + elem.removeAttribute('filter'); + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes)); + return; + } - changeSelectedAttribute('filter', 'url(#' + elemId + '_blur)'); - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes)); - canvas.setBlurOffsets(filter, val); + changeSelectedAttribute('filter', 'url(#' + elemId + '_blur)'); + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes)); + canvas.setBlurOffsets(filter, val); - curCommand = batchCmd; - canvas.undoMgr.beginUndoableChange('stdDeviation', [filter ? filter.firstChild : null]); - if (complete) { - canvas.setBlurNoUndo(val); - finishChange(); - } + curCommand = batchCmd; + canvas.undoMgr.beginUndoableChange('stdDeviation', [filter ? filter.firstChild : null]); + if (complete) { + canvas.setBlurNoUndo(val); + finishChange(); + } }; }()); @@ -5862,13 +5862,13 @@ canvas.setBlur = function (val, complete) { // Returns: // Boolean indicating whether or not element is bold this.getBold = function () { - // should only have one element selected - var selected = selectedElements[0]; - if (selected != null && selected.tagName === 'text' && - selectedElements[1] == null) { - return (selected.getAttribute('font-weight') === 'bold'); - } - return false; + // should only have one element selected + var selected = selectedElements[0]; + if (selected != null && selected.tagName === 'text' && + selectedElements[1] == null) { + return (selected.getAttribute('font-weight') === 'bold'); + } + return false; }; // Function: setBold @@ -5877,14 +5877,14 @@ this.getBold = function () { // Parameters: // b - Boolean indicating bold (true) or normal (false) this.setBold = function (b) { - var selected = selectedElements[0]; - if (selected != null && selected.tagName === 'text' && - selectedElements[1] == null) { - changeSelectedAttribute('font-weight', b ? 'bold' : 'normal'); - } - if (!selectedElements[0].textContent) { - textActions.setCursor(); - } + var selected = selectedElements[0]; + if (selected != null && selected.tagName === 'text' && + selectedElements[1] == null) { + changeSelectedAttribute('font-weight', b ? 'bold' : 'normal'); + } + if (!selectedElements[0].textContent) { + textActions.setCursor(); + } }; // Function: getItalic @@ -5893,12 +5893,12 @@ this.setBold = function (b) { // Returns: // Boolean indicating whether or not element is italic this.getItalic = function () { - var selected = selectedElements[0]; - if (selected != null && selected.tagName === 'text' && - selectedElements[1] == null) { - return (selected.getAttribute('font-style') === 'italic'); - } - return false; + var selected = selectedElements[0]; + if (selected != null && selected.tagName === 'text' && + selectedElements[1] == null) { + return (selected.getAttribute('font-style') === 'italic'); + } + return false; }; // Function: setItalic @@ -5907,20 +5907,20 @@ this.getItalic = function () { // Parameters: // b - Boolean indicating italic (true) or normal (false) this.setItalic = function (i) { - var selected = selectedElements[0]; - if (selected != null && selected.tagName === 'text' && - selectedElements[1] == null) { - changeSelectedAttribute('font-style', i ? 'italic' : 'normal'); - } - if (!selectedElements[0].textContent) { - textActions.setCursor(); - } + var selected = selectedElements[0]; + if (selected != null && selected.tagName === 'text' && + selectedElements[1] == null) { + changeSelectedAttribute('font-style', i ? 'italic' : 'normal'); + } + if (!selectedElements[0].textContent) { + textActions.setCursor(); + } }; // Function: getFontFamily // Returns the current font family this.getFontFamily = function () { - return curText.font_family; + return curText.font_family; }; // Function: setFontFamily @@ -5929,11 +5929,11 @@ this.getFontFamily = function () { // Parameters: // val - String with the new font family this.setFontFamily = function (val) { - curText.font_family = val; - changeSelectedAttribute('font-family', val); - if (selectedElements[0] && !selectedElements[0].textContent) { - textActions.setCursor(); - } + curText.font_family = val; + changeSelectedAttribute('font-family', val); + if (selectedElements[0] && !selectedElements[0].textContent) { + textActions.setCursor(); + } }; // Function: setFontColor @@ -5942,20 +5942,20 @@ this.setFontFamily = function (val) { // Parameters: // val - String with the new font color this.setFontColor = function (val) { - curText.fill = val; - changeSelectedAttribute('fill', val); + curText.fill = val; + changeSelectedAttribute('fill', val); }; // Function: getFontColor // Returns the current font color this.getFontColor = function () { - return curText.fill; + return curText.fill; }; // Function: getFontSize // Returns the current font size this.getFontSize = function () { - return curText.font_size; + return curText.font_size; }; // Function: setFontSize @@ -5964,19 +5964,19 @@ this.getFontSize = function () { // Parameters: // val - Float with the new font size this.setFontSize = function (val) { - curText.font_size = val; - changeSelectedAttribute('font-size', val); - if (!selectedElements[0].textContent) { - textActions.setCursor(); - } + curText.font_size = val; + changeSelectedAttribute('font-size', val); + if (!selectedElements[0].textContent) { + textActions.setCursor(); + } }; // Function: getText // Returns the current text (textContent) of the selected element this.getText = function () { - var selected = selectedElements[0]; - if (selected == null) { return ''; } - return selected.textContent; + var selected = selectedElements[0]; + if (selected == null) { return ''; } + return selected.textContent; }; // Function: setTextContent @@ -5985,9 +5985,9 @@ this.getText = function () { // Parameters: // val - String with the new text this.setTextContent = function (val) { - changeSelectedAttribute('#text', val); - textActions.init(val); - textActions.setCursor(); + changeSelectedAttribute('#text', val); + textActions.init(val); + textActions.setCursor(); }; // Function: setImageURL @@ -5997,44 +5997,44 @@ this.setTextContent = function (val) { // Parameters: // val - String with the image URL/path this.setImageURL = function (val) { - var elem = selectedElements[0]; - if (!elem) { return; } + var elem = selectedElements[0]; + if (!elem) { return; } - var attrs = $(elem).attr(['width', 'height']); - var setsize = (!attrs.width || !attrs.height); + var attrs = $(elem).attr(['width', 'height']); + var setsize = (!attrs.width || !attrs.height); - var curHref = getHref(elem); + var curHref = getHref(elem); - // Do nothing if no URL change or size change - if (curHref !== val) { - setsize = true; - } else if (!setsize) { return; } + // Do nothing if no URL change or size change + if (curHref !== val) { + setsize = true; + } else if (!setsize) { return; } - var batchCmd = new svgedit.history.BatchCommand('Change Image URL'); + var batchCmd = new svgedit.history.BatchCommand('Change Image URL'); - setHref(elem, val); - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, { - '#href': curHref - })); + setHref(elem, val); + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, { + '#href': curHref + })); - if (setsize) { - $(new Image()).load(function () { - var changes = $(elem).attr(['width', 'height']); + if (setsize) { + $(new Image()).load(function () { + var changes = $(elem).attr(['width', 'height']); - $(elem).attr({ - width: this.width, - height: this.height - }); + $(elem).attr({ + width: this.width, + height: this.height + }); - selectorManager.requestSelector(elem).resize(); + selectorManager.requestSelector(elem).resize(); - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes)); - addCommandToHistory(batchCmd); - call('changed', [elem]); - }).attr('src', val); - } else { - addCommandToHistory(batchCmd); - } + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes)); + addCommandToHistory(batchCmd); + call('changed', [elem]); + }).attr('src', val); + } else { + addCommandToHistory(batchCmd); + } }; // Function: setLinkURL @@ -6043,30 +6043,30 @@ this.setImageURL = function (val) { // Parameters: // val - String with the link URL/path this.setLinkURL = function (val) { - var elem = selectedElements[0]; - if (!elem) { return; } - if (elem.tagName !== 'a') { - // See if parent is an anchor - var parentsA = $(elem).parents('a'); - if (parentsA.length) { - elem = parentsA[0]; - } else { - return; - } - } + var elem = selectedElements[0]; + if (!elem) { return; } + if (elem.tagName !== 'a') { + // See if parent is an anchor + var parentsA = $(elem).parents('a'); + if (parentsA.length) { + elem = parentsA[0]; + } else { + return; + } + } - var curHref = getHref(elem); + var curHref = getHref(elem); - if (curHref === val) { return; } + if (curHref === val) { return; } - var batchCmd = new svgedit.history.BatchCommand('Change Link URL'); + var batchCmd = new svgedit.history.BatchCommand('Change Link URL'); - setHref(elem, val); - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, { - '#href': curHref - })); + setHref(elem, val); + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, { + '#href': curHref + })); - addCommandToHistory(batchCmd); + addCommandToHistory(batchCmd); }; // Function: setRectRadius @@ -6075,30 +6075,30 @@ this.setLinkURL = function (val) { // Parameters: // val - The new radius this.setRectRadius = function (val) { - var selected = selectedElements[0]; - if (selected != null && selected.tagName === 'rect') { - var r = selected.getAttribute('rx'); - if (r !== String(val)) { - selected.setAttribute('rx', val); - selected.setAttribute('ry', val); - addCommandToHistory(new svgedit.history.ChangeElementCommand(selected, {'rx': r, 'ry': r}, 'Radius')); - call('changed', [selected]); - } - } + var selected = selectedElements[0]; + if (selected != null && selected.tagName === 'rect') { + var r = selected.getAttribute('rx'); + if (r !== String(val)) { + selected.setAttribute('rx', val); + selected.setAttribute('ry', val); + addCommandToHistory(new svgedit.history.ChangeElementCommand(selected, {'rx': r, 'ry': r}, 'Radius')); + call('changed', [selected]); + } + } }; // Function: makeHyperlink // Wraps the selected element(s) in an anchor element or converts group to one this.makeHyperlink = function (url) { - canvas.groupSelectedElements('a', url); + canvas.groupSelectedElements('a', url); - // TODO: If element is a single "g", convert to "a" - // if (selectedElements.length > 1 && selectedElements[1]) { + // TODO: If element is a single "g", convert to "a" + // if (selectedElements.length > 1 && selectedElements[1]) { }; // Function: removeHyperlink this.removeHyperlink = function () { - canvas.ungroupSelectedElement(); + canvas.ungroupSelectedElement(); }; // Group: Element manipulation @@ -6110,7 +6110,7 @@ this.removeHyperlink = function () { // new_type - Integer with the new segment type // See http://www.w3.org/TR/SVG/paths.html#InterfaceSVGPathSeg for list this.setSegType = function (newType) { - pathActions.setSegType(newType); + pathActions.setSegType(newType); }; // TODO(codedread): Remove the getBBox argument and split this function into two. @@ -6125,32 +6125,32 @@ this.setSegType = function (newType) { // If the getBBox flag is true, the resulting path's bounding box object. // Otherwise the resulting path element is returned. this.convertToPath = function (elem, getBBox) { - if (elem == null) { - var elems = selectedElements; - $.each(elems, function (i, elem) { - if (elem) { canvas.convertToPath(elem); } - }); - return; - } - if (getBBox) { - return svgedit.utilities.getBBoxOfElementAsPath(elem, addSvgElementFromJson, pathActions); - } else { - // TODO: Why is this applying attributes from curShape, then inside utilities.convertToPath it's pulling addition attributes from elem? - // TODO: If convertToPath is called with one elem, curShape and elem are probably the same; but calling with multiple is a bug or cool feature. - var attrs = { - 'fill': curShape.fill, - 'fill-opacity': curShape.fill_opacity, - 'stroke': curShape.stroke, - 'stroke-width': curShape.stroke_width, - 'stroke-dasharray': curShape.stroke_dasharray, - 'stroke-linejoin': curShape.stroke_linejoin, - 'stroke-linecap': curShape.stroke_linecap, - 'stroke-opacity': curShape.stroke_opacity, - 'opacity': curShape.opacity, - 'visibility': 'hidden' - }; - return svgedit.utilities.convertToPath(elem, attrs, addSvgElementFromJson, pathActions, clearSelection, addToSelection, svgedit.history, addCommandToHistory); - } + if (elem == null) { + var elems = selectedElements; + $.each(elems, function (i, elem) { + if (elem) { canvas.convertToPath(elem); } + }); + return; + } + if (getBBox) { + return svgedit.utilities.getBBoxOfElementAsPath(elem, addSvgElementFromJson, pathActions); + } else { + // TODO: Why is this applying attributes from curShape, then inside utilities.convertToPath it's pulling addition attributes from elem? + // TODO: If convertToPath is called with one elem, curShape and elem are probably the same; but calling with multiple is a bug or cool feature. + var attrs = { + 'fill': curShape.fill, + 'fill-opacity': curShape.fill_opacity, + 'stroke': curShape.stroke, + 'stroke-width': curShape.stroke_width, + 'stroke-dasharray': curShape.stroke_dasharray, + 'stroke-linejoin': curShape.stroke_linejoin, + 'stroke-linecap': curShape.stroke_linecap, + 'stroke-opacity': curShape.stroke_opacity, + 'opacity': curShape.opacity, + 'visibility': 'hidden' + }; + return svgedit.utilities.convertToPath(elem, attrs, addSvgElementFromJson, pathActions, clearSelection, addToSelection, svgedit.history, addCommandToHistory); + } }; // Function: changeSelectedAttributeNoUndo @@ -6162,116 +6162,116 @@ this.convertToPath = function (elem, getBBox) { // newValue - String or number with the new attribute value // elems - The DOM elements to apply the change to var changeSelectedAttributeNoUndo = function (attr, newValue, elems) { - if (currentMode === 'pathedit') { - // Editing node - pathActions.moveNode(attr, newValue); - } - elems = elems || selectedElements; - var i = elems.length; - var noXYElems = ['g', 'polyline', 'path']; - var goodGAttrs = ['transform', 'opacity', 'filter']; + if (currentMode === 'pathedit') { + // Editing node + pathActions.moveNode(attr, newValue); + } + elems = elems || selectedElements; + var i = elems.length; + var noXYElems = ['g', 'polyline', 'path']; + var goodGAttrs = ['transform', 'opacity', 'filter']; - while (i--) { - var elem = elems[i]; - if (elem == null) { continue; } + while (i--) { + var elem = elems[i]; + if (elem == null) { continue; } - // Set x,y vals on elements that don't have them - if ((attr === 'x' || attr === 'y') && noXYElems.indexOf(elem.tagName) >= 0) { - var bbox = getStrokedBBox([elem]); - var diffX = attr === 'x' ? newValue - bbox.x : 0; - var diffY = attr === 'y' ? newValue - bbox.y : 0; - canvas.moveSelectedElements(diffX * currentZoom, diffY * currentZoom, true); - continue; - } + // Set x,y vals on elements that don't have them + if ((attr === 'x' || attr === 'y') && noXYElems.indexOf(elem.tagName) >= 0) { + var bbox = getStrokedBBox([elem]); + var diffX = attr === 'x' ? newValue - bbox.x : 0; + var diffY = attr === 'y' ? newValue - bbox.y : 0; + canvas.moveSelectedElements(diffX * currentZoom, diffY * currentZoom, true); + continue; + } - // only allow the transform/opacity/filter attribute to change on elements, slightly hacky - // TODO: FIXME: This doesn't seem right. Where's the body of this if statement? - if (elem.tagName === 'g' && goodGAttrs.indexOf(attr) >= 0) {} - var oldval = attr === '#text' ? elem.textContent : elem.getAttribute(attr); - if (oldval == null) { oldval = ''; } - if (oldval !== String(newValue)) { - if (attr === '#text') { - // var oldW = svgedit.utilities.getBBox(elem).width; - elem.textContent = newValue; + // only allow the transform/opacity/filter attribute to change on elements, slightly hacky + // TODO: FIXME: This doesn't seem right. Where's the body of this if statement? + if (elem.tagName === 'g' && goodGAttrs.indexOf(attr) >= 0) {} + var oldval = attr === '#text' ? elem.textContent : elem.getAttribute(attr); + if (oldval == null) { oldval = ''; } + if (oldval !== String(newValue)) { + if (attr === '#text') { + // var oldW = svgedit.utilities.getBBox(elem).width; + elem.textContent = newValue; - // FF bug occurs on on rotated elements - if ((/rotate/).test(elem.getAttribute('transform'))) { - elem = ffClone(elem); - } - // Hoped to solve the issue of moving text with text-anchor="start", - // but this doesn't actually fix it. Hopefully on the right track, though. -Fyrd - // var box = getBBox(elem), left = box.x, top = box.y, width = box.width, - // height = box.height, dx = width - oldW, dy = 0; - // var angle = svgedit.utilities.getRotationAngle(elem, true); - // if (angle) { - // var r = Math.sqrt(dx * dx + dy * dy); - // var theta = Math.atan2(dy, dx) - angle; - // dx = r * Math.cos(theta); - // dy = r * Math.sin(theta); - // - // elem.setAttribute('x', elem.getAttribute('x') - dx); - // elem.setAttribute('y', elem.getAttribute('y') - dy); - // } - } else if (attr === '#href') { - setHref(elem, newValue); - } else { elem.setAttribute(attr, newValue); } + // FF bug occurs on on rotated elements + if ((/rotate/).test(elem.getAttribute('transform'))) { + elem = ffClone(elem); + } + // Hoped to solve the issue of moving text with text-anchor="start", + // but this doesn't actually fix it. Hopefully on the right track, though. -Fyrd + // var box = getBBox(elem), left = box.x, top = box.y, width = box.width, + // height = box.height, dx = width - oldW, dy = 0; + // var angle = svgedit.utilities.getRotationAngle(elem, true); + // if (angle) { + // var r = Math.sqrt(dx * dx + dy * dy); + // var theta = Math.atan2(dy, dx) - angle; + // dx = r * Math.cos(theta); + // dy = r * Math.sin(theta); + // + // elem.setAttribute('x', elem.getAttribute('x') - dx); + // elem.setAttribute('y', elem.getAttribute('y') - dy); + // } + } else if (attr === '#href') { + setHref(elem, newValue); + } else { elem.setAttribute(attr, newValue); } - // Go into "select" mode for text changes - // NOTE: Important that this happens AFTER elem.setAttribute() or else attributes like - // font-size can get reset to their old value, ultimately by svgEditor.updateContextPanel(), - // after calling textActions.toSelectMode() below - if (currentMode === 'textedit' && attr !== '#text' && elem.textContent.length) { - textActions.toSelectMode(elem); - } + // Go into "select" mode for text changes + // NOTE: Important that this happens AFTER elem.setAttribute() or else attributes like + // font-size can get reset to their old value, ultimately by svgEditor.updateContextPanel(), + // after calling textActions.toSelectMode() below + if (currentMode === 'textedit' && attr !== '#text' && elem.textContent.length) { + textActions.toSelectMode(elem); + } - // if (i === 0) { - // selectedBBoxes[0] = svgedit.utilities.getBBox(elem); - // } + // if (i === 0) { + // selectedBBoxes[0] = svgedit.utilities.getBBox(elem); + // } - // Use the Firefox ffClone hack for text elements with gradients or - // where other text attributes are changed. - if (svgedit.browser.isGecko() && elem.nodeName === 'text' && (/rotate/).test(elem.getAttribute('transform'))) { - if (String(newValue).indexOf('url') === 0 || (['font-size', 'font-family', 'x', 'y'].indexOf(attr) >= 0 && elem.textContent)) { - elem = ffClone(elem); - } - } - // Timeout needed for Opera & Firefox - // codedread: it is now possible for this function to be called with elements - // that are not in the selectedElements array, we need to only request a - // selector if the element is in that array - if (selectedElements.indexOf(elem) >= 0) { - setTimeout(function () { - // Due to element replacement, this element may no longer - // be part of the DOM - if (!elem.parentNode) { return; } - selectorManager.requestSelector(elem).resize(); - }, 0); - } - // if this element was rotated, and we changed the position of this element - // we need to update the rotational transform attribute - var angle = svgedit.utilities.getRotationAngle(elem); - if (angle !== 0 && attr !== 'transform') { - var tlist = svgedit.transformlist.getTransformList(elem); - var n = tlist.numberOfItems; - while (n--) { - var xform = tlist.getItem(n); - if (xform.type === 4) { - // remove old rotate - tlist.removeItem(n); + // Use the Firefox ffClone hack for text elements with gradients or + // where other text attributes are changed. + if (svgedit.browser.isGecko() && elem.nodeName === 'text' && (/rotate/).test(elem.getAttribute('transform'))) { + if (String(newValue).indexOf('url') === 0 || (['font-size', 'font-family', 'x', 'y'].indexOf(attr) >= 0 && elem.textContent)) { + elem = ffClone(elem); + } + } + // Timeout needed for Opera & Firefox + // codedread: it is now possible for this function to be called with elements + // that are not in the selectedElements array, we need to only request a + // selector if the element is in that array + if (selectedElements.indexOf(elem) >= 0) { + setTimeout(function () { + // Due to element replacement, this element may no longer + // be part of the DOM + if (!elem.parentNode) { return; } + selectorManager.requestSelector(elem).resize(); + }, 0); + } + // if this element was rotated, and we changed the position of this element + // we need to update the rotational transform attribute + var angle = svgedit.utilities.getRotationAngle(elem); + if (angle !== 0 && attr !== 'transform') { + var tlist = svgedit.transformlist.getTransformList(elem); + var n = tlist.numberOfItems; + while (n--) { + var xform = tlist.getItem(n); + if (xform.type === 4) { + // remove old rotate + tlist.removeItem(n); - var box = svgedit.utilities.getBBox(elem); - var center = svgedit.math.transformPoint(box.x + box.width / 2, box.y + box.height / 2, svgedit.math.transformListToTransform(tlist).matrix); - var cx = center.x, - cy = center.y; - var newrot = svgroot.createSVGTransform(); - newrot.setRotate(angle, cx, cy); - tlist.insertItemBefore(newrot, n); - break; - } - } - } - } // if oldValue != newValue - } // for each elem + var box = svgedit.utilities.getBBox(elem); + var center = svgedit.math.transformPoint(box.x + box.width / 2, box.y + box.height / 2, svgedit.math.transformListToTransform(tlist).matrix); + var cx = center.x, + cy = center.y; + var newrot = svgroot.createSVGTransform(); + newrot.setRotate(angle, cx, cy); + tlist.insertItemBefore(newrot, n); + break; + } + } + } + } // if oldValue != newValue + } // for each elem }; // Function: changeSelectedAttribute @@ -6285,149 +6285,149 @@ var changeSelectedAttributeNoUndo = function (attr, newValue, elems) { // newValue - String or number with the new attribute value // elems - The DOM elements to apply the change to var changeSelectedAttribute = this.changeSelectedAttribute = function (attr, val, elems) { - elems = elems || selectedElements; - canvas.undoMgr.beginUndoableChange(attr, elems); - // var i = elems.length; + elems = elems || selectedElements; + canvas.undoMgr.beginUndoableChange(attr, elems); + // var i = elems.length; - changeSelectedAttributeNoUndo(attr, val, elems); + changeSelectedAttributeNoUndo(attr, val, elems); - var batchCmd = canvas.undoMgr.finishUndoableChange(); - if (!batchCmd.isEmpty()) { - addCommandToHistory(batchCmd); - } + var batchCmd = canvas.undoMgr.finishUndoableChange(); + if (!batchCmd.isEmpty()) { + addCommandToHistory(batchCmd); + } }; // Function: deleteSelectedElements // Removes all selected elements from the DOM and adds the change to the // history stack this.deleteSelectedElements = function () { - var i; - var batchCmd = new svgedit.history.BatchCommand('Delete Elements'); - var len = selectedElements.length; - var selectedCopy = []; // selectedElements is being deleted + var i; + var batchCmd = new svgedit.history.BatchCommand('Delete Elements'); + var len = selectedElements.length; + var selectedCopy = []; // selectedElements is being deleted - for (i = 0; i < len; ++i) { - var selected = selectedElements[i]; - if (selected == null) { break; } + for (i = 0; i < len; ++i) { + var selected = selectedElements[i]; + if (selected == null) { break; } - var parent = selected.parentNode; - var t = selected; + var parent = selected.parentNode; + var t = selected; - // this will unselect the element and remove the selectedOutline - selectorManager.releaseSelector(t); + // this will unselect the element and remove the selectedOutline + selectorManager.releaseSelector(t); - // Remove the path if present. - svgedit.path.removePath_(t.id); + // 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; - } + // 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 - batchCmd.addSubCommand(new RemoveElementCommand(elem, nextSibling, parent)); - } - selectedElements = []; + var nextSibling = t.nextSibling; + var elem = parent.removeChild(t); + selectedCopy.push(selected); // for the copy + batchCmd.addSubCommand(new RemoveElementCommand(elem, nextSibling, parent)); + } + selectedElements = []; - if (!batchCmd.isEmpty()) { addCommandToHistory(batchCmd); } - call('changed', selectedCopy); - clearSelection(); + if (!batchCmd.isEmpty()) { addCommandToHistory(batchCmd); } + call('changed', selectedCopy); + clearSelection(); }; // Function: cutSelectedElements // Removes all selected elements from the DOM and adds the change to the // history stack. Remembers removed elements on the clipboard this.cutSelectedElements = function () { - svgCanvas.copySelectedElements(); - svgCanvas.deleteSelectedElements(); + svgCanvas.copySelectedElements(); + svgCanvas.deleteSelectedElements(); }; // Function: copySelectedElements // Remembers the current selected elements on the clipboard this.copySelectedElements = function () { - localStorage.setItem('svgedit_clipboard', JSON.stringify( - selectedElements.map(function (x) { return getJsonFromSvgElement(x); }) - )); + localStorage.setItem('svgedit_clipboard', JSON.stringify( + selectedElements.map(function (x) { return getJsonFromSvgElement(x); }) + )); - $('#cmenu_canvas').enableContextMenuItems('#paste,#paste_in_place'); + $('#cmenu_canvas').enableContextMenuItems('#paste,#paste_in_place'); }; this.pasteElements = function (type, x, y) { - var cb = JSON.parse(localStorage.getItem('svgedit_clipboard')); - var len = cb.length; - if (!len) { return; } + var cb = JSON.parse(localStorage.getItem('svgedit_clipboard')); + var len = cb.length; + if (!len) { return; } - var pasted = []; - var batchCmd = new svgedit.history.BatchCommand('Paste elements'); - // var drawing = getCurrentDrawing(); - var changedIDs = {}; + var pasted = []; + var batchCmd = new svgedit.history.BatchCommand('Paste elements'); + // var drawing = getCurrentDrawing(); + var changedIDs = {}; - // Recursively replace IDs and record the changes - function checkIDs (elem) { - if (elem.attr && elem.attr.id) { - changedIDs[elem.attr.id] = getNextId(); - elem.attr.id = changedIDs[elem.attr.id]; - } - if (elem.children) elem.children.forEach(checkIDs); - } - cb.forEach(checkIDs); + // Recursively replace IDs and record the changes + function checkIDs (elem) { + if (elem.attr && elem.attr.id) { + changedIDs[elem.attr.id] = getNextId(); + elem.attr.id = changedIDs[elem.attr.id]; + } + if (elem.children) elem.children.forEach(checkIDs); + } + cb.forEach(checkIDs); - // Give extensions like the connector extension a chance to reflect new IDs and remove invalid elements - runExtensions('IDsUpdated', {elems: cb, changes: changedIDs}, true).forEach(function (extChanges) { - if (!extChanges || !('remove' in extChanges)) return; + // Give extensions like the connector extension a chance to reflect new IDs and remove invalid elements + runExtensions('IDsUpdated', {elems: cb, changes: changedIDs}, true).forEach(function (extChanges) { + if (!extChanges || !('remove' in extChanges)) return; - extChanges.remove.forEach(function (removeID) { - cb = cb.filter(function (cbItem) { - return cbItem.attr.id !== removeID; - }); - }); - }); + extChanges.remove.forEach(function (removeID) { + cb = cb.filter(function (cbItem) { + return cbItem.attr.id !== removeID; + }); + }); + }); - // Move elements to lastClickPoint - while (len--) { - var elem = cb[len]; - if (!elem) { continue; } + // Move elements to lastClickPoint + while (len--) { + var elem = cb[len]; + if (!elem) { continue; } - var copy = addSvgElementFromJson(elem); - pasted.push(copy); - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(copy)); + var copy = addSvgElementFromJson(elem); + pasted.push(copy); + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(copy)); - restoreRefElems(copy); - } + restoreRefElems(copy); + } - selectOnly(pasted); + selectOnly(pasted); - if (type !== 'in_place') { - var ctrX, ctrY; + if (type !== 'in_place') { + var ctrX, ctrY; - if (!type) { - ctrX = lastClickPoint.x; - ctrY = lastClickPoint.y; - } else if (type === 'point') { - ctrX = x; - ctrY = y; - } + if (!type) { + ctrX = lastClickPoint.x; + ctrY = lastClickPoint.y; + } else if (type === 'point') { + ctrX = x; + ctrY = y; + } - var bbox = getStrokedBBox(pasted); - var cx = ctrX - (bbox.x + bbox.width / 2), - cy = ctrY - (bbox.y + bbox.height / 2), - dx = [], - dy = []; + var bbox = getStrokedBBox(pasted); + var cx = ctrX - (bbox.x + bbox.width / 2), + cy = ctrY - (bbox.y + bbox.height / 2), + dx = [], + dy = []; - $.each(pasted, function (i, item) { - dx.push(cx); - dy.push(cy); - }); + $.each(pasted, function (i, item) { + dx.push(cx); + dy.push(cy); + }); - var cmd = canvas.moveSelectedElements(dx, dy, false); - if (cmd) batchCmd.addSubCommand(cmd); - } + var cmd = canvas.moveSelectedElements(dx, dy, false); + if (cmd) batchCmd.addSubCommand(cmd); + } - addCommandToHistory(batchCmd); - call('changed', pasted); + addCommandToHistory(batchCmd); + call('changed', pasted); }; // Function: groupSelectedElements @@ -6436,355 +6436,355 @@ this.pasteElements = function (type, x, y) { // Parameters: // type - type of element to group into, defaults to this.groupSelectedElements = function (type, urlArg) { - if (!type) { type = 'g'; } - var cmdStr = ''; + if (!type) { type = 'g'; } + var cmdStr = ''; - switch (type) { - case 'a': - cmdStr = 'Make hyperlink'; - var url = ''; - if (arguments.length > 1) { - url = urlArg; - } - break; - default: - type = 'g'; - cmdStr = 'Group Elements'; - break; - } + switch (type) { + case 'a': + cmdStr = 'Make hyperlink'; + var url = ''; + if (arguments.length > 1) { + url = urlArg; + } + break; + default: + type = 'g'; + cmdStr = 'Group Elements'; + break; + } - var batchCmd = new svgedit.history.BatchCommand(cmdStr); + var batchCmd = new svgedit.history.BatchCommand(cmdStr); - // create and insert the group element - var g = addSvgElementFromJson({ - 'element': type, - 'attr': { - 'id': getNextId() - } - }); - if (type === 'a') { - setHref(g, url); - } - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(g)); + // create and insert the group element + var g = addSvgElementFromJson({ + 'element': type, + 'attr': { + 'id': getNextId() + } + }); + if (type === 'a') { + setHref(g, url); + } + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(g)); - // now move all children into the group - var i = selectedElements.length; - while (i--) { - var elem = selectedElements[i]; - if (elem == null) { continue; } + // 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; - } + 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 svgedit.history.MoveElementCommand(elem, oldNextSibling, oldParent)); - } - if (!batchCmd.isEmpty()) { addCommandToHistory(batchCmd); } + var oldNextSibling = elem.nextSibling; + var oldParent = elem.parentNode; + g.appendChild(elem); + batchCmd.addSubCommand(new svgedit.history.MoveElementCommand(elem, oldNextSibling, oldParent)); + } + if (!batchCmd.isEmpty()) { addCommandToHistory(batchCmd); } - // update selection - selectOnly([g], true); + // update selection + selectOnly([g], true); }; // Function: pushGroupProperties // Pushes all appropriate parent group properties down to its children, then // removes them from the group var pushGroupProperties = this.pushGroupProperties = function (g, undoable) { - var children = g.childNodes; - var len = children.length; - var xform = g.getAttribute('transform'); + var children = g.childNodes; + var len = children.length; + var xform = g.getAttribute('transform'); - var glist = svgedit.transformlist.getTransformList(g); - var m = svgedit.math.transformListToTransform(glist).matrix; + var glist = svgedit.transformlist.getTransformList(g); + var m = svgedit.math.transformListToTransform(glist).matrix; - var batchCmd = new svgedit.history.BatchCommand('Push group properties'); + var batchCmd = new svgedit.history.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", - // "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 + // 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", + // "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 = svgedit.utilities.getRotationAngle(g); + var i = 0; + var gangle = svgedit.utilities.getRotationAngle(g); - var gattrs = $(g).attr(['filter', 'opacity']); - var gfilter, gblur, changes; - var drawing = getCurrentDrawing(); + var gattrs = $(g).attr(['filter', 'opacity']); + var gfilter, gblur, changes; + var drawing = getCurrentDrawing(); - for (i = 0; i < len; i++) { - var elem = children[i]; + for (i = 0; i < len; i++) { + var elem = children[i]; - if (elem.nodeType !== 1) { continue; } + if (elem.nodeType !== 1) { continue; } - if (gattrs.opacity !== null && gattrs.opacity !== 1) { - // var c_opac = elem.getAttribute('opacity') || 1; - var newOpac = Math.round((elem.getAttribute('opacity') || 1) * gattrs.opacity * 100) / 100; - changeSelectedAttribute('opacity', newOpac, [elem]); - } + if (gattrs.opacity !== null && gattrs.opacity !== 1) { + // var c_opac = elem.getAttribute('opacity') || 1; + var newOpac = Math.round((elem.getAttribute('opacity') || 1) * gattrs.opacity * 100) / 100; + changeSelectedAttribute('opacity', newOpac, [elem]); + } - if (gattrs.filter) { - var cblur = this.getBlur(elem); - var origCblur = cblur; - if (!gblur) { gblur = this.getBlur(g); } - if (cblur) { - // Is this formula correct? - cblur = Number(gblur) + Number(cblur); - } else if (cblur === 0) { - cblur = gblur; - } + if (gattrs.filter) { + var cblur = this.getBlur(elem); + var origCblur = cblur; + if (!gblur) { gblur = this.getBlur(g); } + if (cblur) { + // Is this formula correct? + cblur = Number(gblur) + Number(cblur); + } else if (cblur === 0) { + cblur = gblur; + } - // If child has no current filter, get group's filter or clone it. - if (!origCblur) { - // Set group's filter to use first child's ID - if (!gfilter) { - gfilter = svgedit.utilities.getRefElem(gattrs.filter); - } else { - // Clone the group's filter - gfilter = drawing.copyElem(gfilter); - svgedit.utilities.findDefs().appendChild(gfilter); - } - } else { - gfilter = svgedit.utilities.getRefElem(elem.getAttribute('filter')); - } + // If child has no current filter, get group's filter or clone it. + if (!origCblur) { + // Set group's filter to use first child's ID + if (!gfilter) { + gfilter = svgedit.utilities.getRefElem(gattrs.filter); + } else { + // Clone the group's filter + gfilter = drawing.copyElem(gfilter); + svgedit.utilities.findDefs().appendChild(gfilter); + } + } else { + gfilter = svgedit.utilities.getRefElem(elem.getAttribute('filter')); + } - // Change this in future for different filters - var suffix = (gfilter.firstChild.tagName === 'feGaussianBlur') ? 'blur' : 'filter'; - gfilter.id = elem.id + '_' + suffix; - changeSelectedAttribute('filter', 'url(#' + gfilter.id + ')', [elem]); + // Change this in future for different filters + var suffix = (gfilter.firstChild.tagName === 'feGaussianBlur') ? 'blur' : 'filter'; + gfilter.id = elem.id + '_' + suffix; + changeSelectedAttribute('filter', 'url(#' + gfilter.id + ')', [elem]); - // Update blur value - if (cblur) { - changeSelectedAttribute('stdDeviation', cblur, [gfilter.firstChild]); - canvas.setBlurOffsets(gfilter, cblur); - } - } + // Update blur value + if (cblur) { + changeSelectedAttribute('stdDeviation', cblur, [gfilter.firstChild]); + canvas.setBlurOffsets(gfilter, cblur); + } + } - var chtlist = svgedit.transformlist.getTransformList(elem); + var chtlist = svgedit.transformlist.getTransformList(elem); - // Don't process gradient transforms - if (~elem.tagName.indexOf('Gradient')) { chtlist = null; } + // Don't process gradient transforms + if (~elem.tagName.indexOf('Gradient')) { chtlist = null; } - // Hopefully not a problem to add this. Necessary for elements like - if (!chtlist) { continue; } + // Hopefully not a problem to add this. Necessary for elements like + if (!chtlist) { continue; } - // Apparently can get get a transformlist, but we don't want it to have one! - if (elem.tagName === 'defs') { continue; } + // Apparently 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 - // out any translates) - 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 - // sum of the group and child's rotation angles - // - [Tr] is the equivalent translation that this child - // undergoes if the group wasn't there + 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 + // out any translates) + 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 + // sum of the group and child's rotation angles + // - [Tr] is the equivalent translation that this child + // undergoes if the group wasn't there - // [Tr] = [Rg] [Rc] [Rc2_inv] + // [Tr] = [Rg] [Rc] [Rc2_inv] - // get group's rotation matrix (Rg) - var rgm = glist.getItem(0).matrix; + // get group's rotation matrix (Rg) + var rgm = glist.getItem(0).matrix; - // get child's rotation matrix (Rc) - var rcm = svgroot.createSVGMatrix(); - var cangle = svgedit.utilities.getRotationAngle(elem); - if (cangle) { - rcm = chtlist.getItem(0).matrix; - } + // get child's rotation matrix (Rc) + var rcm = svgroot.createSVGMatrix(); + var cangle = svgedit.utilities.getRotationAngle(elem); + if (cangle) { + rcm = chtlist.getItem(0).matrix; + } - // get child's old center of rotation - var cbox = svgedit.utilities.getBBox(elem); - var ceqm = svgedit.math.transformListToTransform(chtlist).matrix; - var coldc = svgedit.math.transformPoint(cbox.x + cbox.width / 2, cbox.y + cbox.height / 2, ceqm); + // get child's old center of rotation + var cbox = svgedit.utilities.getBBox(elem); + var ceqm = svgedit.math.transformListToTransform(chtlist).matrix; + var coldc = svgedit.math.transformPoint(cbox.x + cbox.width / 2, cbox.y + cbox.height / 2, ceqm); - // sum group and child's angles - var sangle = gangle + cangle; + // 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); + // 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 = svgedit.math.matrixMultiply(rgm, rcm, r2.matrix.inverse()); + // calculate equivalent translate + var trm = svgedit.math.matrixMultiply(rgm, rcm, r2.matrix.inverse()); - // set up tlist - if (cangle) { - chtlist.removeItem(0); - } + // set up tlist + if (cangle) { + chtlist.removeItem(0); + } - if (sangle) { - if (chtlist.numberOfItems) { - chtlist.insertItemBefore(r2, 0); - } else { - chtlist.appendItem(r2); - } - } + if (sangle) { + if (chtlist.numberOfItems) { + chtlist.insertItemBefore(r2, 0); + } else { + chtlist.appendItem(r2); + } + } - if (trm.e || trm.f) { - var tr = svgroot.createSVGTransform(); - tr.setTranslate(trm.e, trm.f); - if (chtlist.numberOfItems) { - chtlist.insertItemBefore(tr, 0); - } else { - chtlist.appendItem(tr); - } - } - } else { // more complicated than just a rotate - // transfer the group's transform down to each child and then - // call svgedit.recalculate.recalculateDimensions() - var oldxform = elem.getAttribute('transform'); - changes = {}; - changes.transform = oldxform || ''; + if (trm.e || trm.f) { + var tr = svgroot.createSVGTransform(); + tr.setTranslate(trm.e, trm.f); + if (chtlist.numberOfItems) { + chtlist.insertItemBefore(tr, 0); + } else { + chtlist.appendItem(tr); + } + } + } else { // more complicated than just a rotate + // transfer the group's transform down to each child and then + // call svgedit.recalculate.recalculateDimensions() + var oldxform = elem.getAttribute('transform'); + changes = {}; + changes.transform = oldxform || ''; - var newxform = svgroot.createSVGTransform(); + var newxform = svgroot.createSVGTransform(); - // [ gm ] [ chm ] = [ chm ] [ gm' ] - // [ gm' ] = [ chmInv ] [ gm ] [ chm ] - var chm = svgedit.math.transformListToTransform(chtlist).matrix, - chmInv = chm.inverse(); - var gm = svgedit.math.matrixMultiply(chmInv, m, chm); - newxform.setMatrix(gm); - chtlist.appendItem(newxform); - } - var cmd = svgedit.recalculate.recalculateDimensions(elem); - if (cmd) { batchCmd.addSubCommand(cmd); } - } - } + // [ gm ] [ chm ] = [ chm ] [ gm' ] + // [ gm' ] = [ chmInv ] [ gm ] [ chm ] + var chm = svgedit.math.transformListToTransform(chtlist).matrix, + chmInv = chm.inverse(); + var gm = svgedit.math.matrixMultiply(chmInv, m, chm); + newxform.setMatrix(gm); + chtlist.appendItem(newxform); + } + var cmd = svgedit.recalculate.recalculateDimensions(elem); + if (cmd) { batchCmd.addSubCommand(cmd); } + } + } - // remove transform and make it undo-able - if (xform) { - changes = {}; - changes.transform = xform; - g.setAttribute('transform', ''); - g.removeAttribute('transform'); - batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(g, changes)); - } + // remove transform and make it undo-able + if (xform) { + changes = {}; + changes.transform = xform; + g.setAttribute('transform', ''); + g.removeAttribute('transform'); + batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(g, changes)); + } - if (undoable && !batchCmd.isEmpty()) { - return batchCmd; - } + if (undoable && !batchCmd.isEmpty()) { + return batchCmd; + } }; // Function: ungroupSelectedElement // Unwraps all the elements in a selected group (g) element. This requires // significant recalculations to apply group's transforms, etc to its children this.ungroupSelectedElement = function () { - var g = selectedElements[0]; - if (!g) { - return; - } - if ($(g).data('gsvg') || $(g).data('symbol')) { - // Is svg, so actually convert to group - convertToGroup(g); - return; - } - if (g.tagName === 'use') { - // Somehow doesn't have data set, so retrieve - var symbol = svgedit.utilities.getElem(getHref(g).substr(1)); - $(g).data('symbol', symbol).data('ref', symbol); - convertToGroup(g); - return; - } - var parentsA = $(g).parents('a'); - if (parentsA.length) { - g = parentsA[0]; - } + var g = selectedElements[0]; + if (!g) { + return; + } + if ($(g).data('gsvg') || $(g).data('symbol')) { + // Is svg, so actually convert to group + convertToGroup(g); + return; + } + if (g.tagName === 'use') { + // Somehow doesn't have data set, so retrieve + var symbol = svgedit.utilities.getElem(getHref(g).substr(1)); + $(g).data('symbol', symbol).data('ref', symbol); + convertToGroup(g); + return; + } + var parentsA = $(g).parents('a'); + if (parentsA.length) { + g = parentsA[0]; + } - // Look for parent "a" - if (g.tagName === 'g' || g.tagName === 'a') { - var batchCmd = new svgedit.history.BatchCommand('Ungroup Elements'); - var cmd = pushGroupProperties(g, true); - if (cmd) { batchCmd.addSubCommand(cmd); } + // Look for parent "a" + if (g.tagName === 'g' || g.tagName === 'a') { + var batchCmd = new svgedit.history.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 parent = g.parentNode; + var anchor = g.nextSibling; + var children = new Array(g.childNodes.length); - var i = 0; + var i = 0; - while (g.firstChild) { - var elem = g.firstChild; - var oldNextSibling = elem.nextSibling; - var oldParent = elem.parentNode; + 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; - batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(elem, nextSibling, oldParent)); - oldParent.removeChild(elem); - continue; - } + // Remove child title elements + if (elem.tagName === 'title') { + var nextSibling = elem.nextSibling; + batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(elem, nextSibling, oldParent)); + oldParent.removeChild(elem); + continue; + } - children[i++] = elem = parent.insertBefore(elem, anchor); - batchCmd.addSubCommand(new svgedit.history.MoveElementCommand(elem, oldNextSibling, oldParent)); - } + children[i++] = elem = parent.insertBefore(elem, anchor); + batchCmd.addSubCommand(new svgedit.history.MoveElementCommand(elem, oldNextSibling, oldParent)); + } - // remove the group from the selection - clearSelection(); + // 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 svgedit.history.RemoveElementCommand(g, gNextSibling, parent)); + // delete the group element (but make undo-able) + var gNextSibling = g.nextSibling; + g = parent.removeChild(g); + batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(g, gNextSibling, parent)); - if (!batchCmd.isEmpty()) { addCommandToHistory(batchCmd); } + if (!batchCmd.isEmpty()) { addCommandToHistory(batchCmd); } - // update selection - addToSelection(children); - } + // update selection + addToSelection(children); + } }; // Function: moveToTopSelectedElement // Repositions the selected element to the bottom in the DOM to appear on top of // other elements this.moveToTopSelectedElement = function () { - var selected = selectedElements[0]; - if (selected != null) { - var t = selected; - var oldParent = t.parentNode; - var oldNextSibling = t.nextSibling; - t = t.parentNode.appendChild(t); - // If the element actually moved position, add the command and fire the changed - // event handler. - if (oldNextSibling !== t.nextSibling) { - addCommandToHistory(new svgedit.history.MoveElementCommand(t, oldNextSibling, oldParent, 'top')); - call('changed', [t]); - } - } + var selected = selectedElements[0]; + if (selected != null) { + var t = selected; + var oldParent = t.parentNode; + var oldNextSibling = t.nextSibling; + t = t.parentNode.appendChild(t); + // If the element actually moved position, add the command and fire the changed + // event handler. + if (oldNextSibling !== t.nextSibling) { + addCommandToHistory(new svgedit.history.MoveElementCommand(t, oldNextSibling, oldParent, 'top')); + call('changed', [t]); + } + } }; // Function: moveToBottomSelectedElement // Repositions the selected element to the top in the DOM to appear under // other elements this.moveToBottomSelectedElement = function () { - var selected = selectedElements[0]; - if (selected != null) { - var t = selected; - var oldParent = t.parentNode; - var oldNextSibling = t.nextSibling; - var firstChild = t.parentNode.firstChild; - if (firstChild.tagName === 'title') { - firstChild = firstChild.nextSibling; - } - // This can probably be removed, as the defs should not ever apppear - // inside a layer group - if (firstChild.tagName === 'defs') { - firstChild = firstChild.nextSibling; - } - t = t.parentNode.insertBefore(t, firstChild); - // If the element actually moved position, add the command and fire the changed - // event handler. - if (oldNextSibling !== t.nextSibling) { - addCommandToHistory(new svgedit.history.MoveElementCommand(t, oldNextSibling, oldParent, 'bottom')); - call('changed', [t]); - } - } + var selected = selectedElements[0]; + if (selected != null) { + var t = selected; + var oldParent = t.parentNode; + var oldNextSibling = t.nextSibling; + var firstChild = t.parentNode.firstChild; + if (firstChild.tagName === 'title') { + firstChild = firstChild.nextSibling; + } + // This can probably be removed, as the defs should not ever apppear + // inside a layer group + if (firstChild.tagName === 'defs') { + firstChild = firstChild.nextSibling; + } + t = t.parentNode.insertBefore(t, firstChild); + // If the element actually moved position, add the command and fire the changed + // event handler. + if (oldNextSibling !== t.nextSibling) { + addCommandToHistory(new svgedit.history.MoveElementCommand(t, oldNextSibling, oldParent, 'bottom')); + call('changed', [t]); + } + } }; // Function: moveUpDownSelected @@ -6794,37 +6794,37 @@ this.moveToBottomSelectedElement = function () { // Parameters: // dir - String that's either 'Up' or 'Down' this.moveUpDownSelected = function (dir) { - var selected = selectedElements[0]; - if (!selected) { return; } + var selected = selectedElements[0]; + if (!selected) { return; } - curBBoxes = []; - var closest, foundCur; - // jQuery sorts this list - var list = $(getIntersectionList(getStrokedBBox([selected]))).toArray(); - if (dir === 'Down') { list.reverse(); } + curBBoxes = []; + var closest, foundCur; + // jQuery sorts this list + var list = $(getIntersectionList(getStrokedBBox([selected]))).toArray(); + if (dir === 'Down') { list.reverse(); } - $.each(list, function () { - if (!foundCur) { - if (this === selected) { - foundCur = true; - } - return; - } - closest = this; - return false; - }); - if (!closest) { return; } + $.each(list, function () { + if (!foundCur) { + if (this === selected) { + foundCur = true; + } + return; + } + closest = this; + return false; + }); + if (!closest) { return; } - var t = selected; - var oldParent = t.parentNode; - var oldNextSibling = t.nextSibling; - $(closest)[dir === 'Down' ? 'before' : 'after'](t); - // If the element actually moved position, add the command and fire the changed - // event handler. - if (oldNextSibling !== t.nextSibling) { - addCommandToHistory(new svgedit.history.MoveElementCommand(t, oldNextSibling, oldParent, 'Move ' + dir)); - call('changed', [t]); - } + var t = selected; + var oldParent = t.parentNode; + var oldNextSibling = t.nextSibling; + $(closest)[dir === 'Down' ? 'before' : 'after'](t); + // If the element actually moved position, add the command and fire the changed + // event handler. + if (oldNextSibling !== t.nextSibling) { + addCommandToHistory(new svgedit.history.MoveElementCommand(t, oldNextSibling, oldParent, 'Move ' + dir)); + call('changed', [t]); + } }; // Function: moveSelectedElements @@ -6838,101 +6838,101 @@ this.moveUpDownSelected = function (dir) { // Returns: // Batch command for the move this.moveSelectedElements = function (dx, dy, undoable) { - // if undoable is not sent, default to true - // if single values, scale them to the zoom - if (dx.constructor !== Array) { - dx /= currentZoom; - dy /= currentZoom; - } - undoable = undoable || true; - var batchCmd = new svgedit.history.BatchCommand('position'); - var i = selectedElements.length; - while (i--) { - var selected = selectedElements[i]; - 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; + // if undoable is not sent, default to true + // if single values, scale them to the zoom + if (dx.constructor !== Array) { + dx /= currentZoom; + dy /= currentZoom; + } + undoable = undoable || true; + var batchCmd = new svgedit.history.BatchCommand('position'); + var i = selectedElements.length; + while (i--) { + var selected = selectedElements[i]; + 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 = svgedit.transformlist.getTransformList(selected); + var xform = svgroot.createSVGTransform(); + var tlist = svgedit.transformlist.getTransformList(selected); - // dx and dy could be arrays - if (dx.constructor === Array) { - // if (i === 0) { - // selectedBBoxes[0].x += dx[0]; - // selectedBBoxes[0].y += dy[0]; - // } - xform.setTranslate(dx[i], dy[i]); - } else { - // if (i === 0) { - // selectedBBoxes[0].x += dx; - // selectedBBoxes[0].y += dy; - // } - xform.setTranslate(dx, dy); - } + // dx and dy could be arrays + if (dx.constructor === Array) { + // if (i === 0) { + // selectedBBoxes[0].x += dx[0]; + // selectedBBoxes[0].y += dy[0]; + // } + xform.setTranslate(dx[i], dy[i]); + } else { + // if (i === 0) { + // selectedBBoxes[0].x += dx; + // selectedBBoxes[0].y += dy; + // } + xform.setTranslate(dx, dy); + } - if (tlist.numberOfItems) { - tlist.insertItemBefore(xform, 0); - } else { - tlist.appendItem(xform); - } + if (tlist.numberOfItems) { + tlist.insertItemBefore(xform, 0); + } else { + tlist.appendItem(xform); + } - var cmd = svgedit.recalculate.recalculateDimensions(selected); - if (cmd) { - batchCmd.addSubCommand(cmd); - } + var cmd = svgedit.recalculate.recalculateDimensions(selected); + if (cmd) { + batchCmd.addSubCommand(cmd); + } - selectorManager.requestSelector(selected).resize(); - } - } - if (!batchCmd.isEmpty()) { - if (undoable) { - addCommandToHistory(batchCmd); - } - call('changed', selectedElements); - return batchCmd; - } + selectorManager.requestSelector(selected).resize(); + } + } + if (!batchCmd.isEmpty()) { + if (undoable) { + addCommandToHistory(batchCmd); + } + call('changed', selectedElements); + return batchCmd; + } }; // Function: cloneSelectedElements // Create deep DOM copies (clones) of all selected elements and move them slightly // from their originals this.cloneSelectedElements = function (x, y) { - var i, elem; - var batchCmd = new svgedit.history.BatchCommand('Clone Elements'); - // find all the elements selected (stop at first null) - var len = selectedElements.length; - function sortfunction (a, b) { - return ($(b).index() - $(a).index()); // causes an array to be sorted numerically and ascending - } - selectedElements.sort(sortfunction); - for (i = 0; i < len; ++i) { - elem = selectedElements[i]; - if (elem == null) { break; } - } - // use slice to quickly get the subset of elements we need - var copiedElements = selectedElements.slice(0, i); - this.clearSelection(true); - // note that we loop in the reverse way because of the way elements are added - // to the selectedElements array (top-first) - var drawing = getCurrentDrawing(); - i = copiedElements.length; - while (i--) { - // clone each element and replace it within copiedElements - elem = copiedElements[i] = drawing.copyElem(copiedElements[i]); - (currentGroup || drawing.getCurrentLayer()).appendChild(elem); - batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(elem)); - } + var i, elem; + var batchCmd = new svgedit.history.BatchCommand('Clone Elements'); + // find all the elements selected (stop at first null) + var len = selectedElements.length; + function sortfunction (a, b) { + return ($(b).index() - $(a).index()); // causes an array to be sorted numerically and ascending + } + selectedElements.sort(sortfunction); + for (i = 0; i < len; ++i) { + elem = selectedElements[i]; + if (elem == null) { break; } + } + // use slice to quickly get the subset of elements we need + var copiedElements = selectedElements.slice(0, i); + this.clearSelection(true); + // note that we loop in the reverse way because of the way elements are added + // to the selectedElements array (top-first) + var drawing = getCurrentDrawing(); + i = copiedElements.length; + while (i--) { + // clone each element and replace it within copiedElements + elem = copiedElements[i] = drawing.copyElem(copiedElements[i]); + (currentGroup || drawing.getCurrentLayer()).appendChild(elem); + batchCmd.addSubCommand(new svgedit.history.InsertElementCommand(elem)); + } - if (!batchCmd.isEmpty()) { - addToSelection(copiedElements.reverse()); // Need to reverse for correct selection-adding - this.moveSelectedElements(x, y, false); - addCommandToHistory(batchCmd); - } + if (!batchCmd.isEmpty()) { + addToSelection(copiedElements.reverse()); // Need to reverse for correct selection-adding + this.moveSelectedElements(x, y, false); + addCommandToHistory(batchCmd); + } }; // Function: alignSelectedElements @@ -6943,93 +6943,93 @@ this.cloneSelectedElements = function (x, y) { // relativeTo - String that must be one of the following: // "selected", "largest", "smallest", "page" this.alignSelectedElements = function (type, relativeTo) { - var i, elem; - var bboxes = []; // angles = []; - var minx = Number.MAX_VALUE, maxx = Number.MIN_VALUE, miny = Number.MAX_VALUE, maxy = Number.MIN_VALUE; - var curwidth = Number.MIN_VALUE, curheight = Number.MIN_VALUE; - var len = selectedElements.length; - if (!len) { return; } - for (i = 0; i < len; ++i) { - if (selectedElements[i] == null) { break; } - elem = selectedElements[i]; - bboxes[i] = getStrokedBBox([elem]); + var i, elem; + var bboxes = []; // angles = []; + var minx = Number.MAX_VALUE, maxx = Number.MIN_VALUE, miny = Number.MAX_VALUE, maxy = Number.MIN_VALUE; + var curwidth = Number.MIN_VALUE, curheight = Number.MIN_VALUE; + var len = selectedElements.length; + if (!len) { return; } + for (i = 0; i < len; ++i) { + if (selectedElements[i] == null) { break; } + elem = selectedElements[i]; + bboxes[i] = getStrokedBBox([elem]); - // now bbox is axis-aligned and handles rotation - switch (relativeTo) { - case 'smallest': - if (((type === 'l' || type === 'c' || type === 'r') && - (curwidth === Number.MIN_VALUE || curwidth > bboxes[i].width)) || - ((type === 't' || type === 'm' || type === 'b') && - (curheight === Number.MIN_VALUE || curheight > bboxes[i].height)) - ) { - minx = bboxes[i].x; - miny = bboxes[i].y; - maxx = bboxes[i].x + bboxes[i].width; - maxy = bboxes[i].y + bboxes[i].height; - curwidth = bboxes[i].width; - curheight = bboxes[i].height; - } - break; - case 'largest': - if (((type === 'l' || type === 'c' || type === 'r') && - (curwidth === Number.MIN_VALUE || curwidth < bboxes[i].width)) || - ((type === 't' || type === 'm' || type === 'b') && - (curheight === Number.MIN_VALUE || curheight < bboxes[i].height)) - ) { - minx = bboxes[i].x; - miny = bboxes[i].y; - maxx = bboxes[i].x + bboxes[i].width; - maxy = bboxes[i].y + bboxes[i].height; - curwidth = bboxes[i].width; - curheight = bboxes[i].height; - } - break; - default: // 'selected' - if (bboxes[i].x < minx) { minx = bboxes[i].x; } - if (bboxes[i].y < miny) { miny = bboxes[i].y; } - if (bboxes[i].x + bboxes[i].width > maxx) { maxx = bboxes[i].x + bboxes[i].width; } - if (bboxes[i].y + bboxes[i].height > maxy) { maxy = bboxes[i].y + bboxes[i].height; } - break; - } - } // loop for each element to find the bbox and adjust min/max + // now bbox is axis-aligned and handles rotation + switch (relativeTo) { + case 'smallest': + if (((type === 'l' || type === 'c' || type === 'r') && + (curwidth === Number.MIN_VALUE || curwidth > bboxes[i].width)) || + ((type === 't' || type === 'm' || type === 'b') && + (curheight === Number.MIN_VALUE || curheight > bboxes[i].height)) + ) { + minx = bboxes[i].x; + miny = bboxes[i].y; + maxx = bboxes[i].x + bboxes[i].width; + maxy = bboxes[i].y + bboxes[i].height; + curwidth = bboxes[i].width; + curheight = bboxes[i].height; + } + break; + case 'largest': + if (((type === 'l' || type === 'c' || type === 'r') && + (curwidth === Number.MIN_VALUE || curwidth < bboxes[i].width)) || + ((type === 't' || type === 'm' || type === 'b') && + (curheight === Number.MIN_VALUE || curheight < bboxes[i].height)) + ) { + minx = bboxes[i].x; + miny = bboxes[i].y; + maxx = bboxes[i].x + bboxes[i].width; + maxy = bboxes[i].y + bboxes[i].height; + curwidth = bboxes[i].width; + curheight = bboxes[i].height; + } + break; + default: // 'selected' + if (bboxes[i].x < minx) { minx = bboxes[i].x; } + if (bboxes[i].y < miny) { miny = bboxes[i].y; } + if (bboxes[i].x + bboxes[i].width > maxx) { maxx = bboxes[i].x + bboxes[i].width; } + if (bboxes[i].y + bboxes[i].height > maxy) { maxy = bboxes[i].y + bboxes[i].height; } + break; + } + } // loop for each element to find the bbox and adjust min/max - if (relativeTo === 'page') { - minx = 0; - miny = 0; - maxx = canvas.contentW; - maxy = canvas.contentH; - } + if (relativeTo === 'page') { + minx = 0; + miny = 0; + maxx = canvas.contentW; + maxy = canvas.contentH; + } - var dx = new Array(len); - var dy = new Array(len); - for (i = 0; i < len; ++i) { - if (selectedElements[i] == null) { break; } - elem = selectedElements[i]; - var bbox = bboxes[i]; - dx[i] = 0; - dy[i] = 0; - switch (type) { - case 'l': // left (horizontal) - dx[i] = minx - bbox.x; - break; - case 'c': // center (horizontal) - dx[i] = (minx + maxx) / 2 - (bbox.x + bbox.width / 2); - break; - case 'r': // right (horizontal) - dx[i] = maxx - (bbox.x + bbox.width); - break; - case 't': // top (vertical) - dy[i] = miny - bbox.y; - break; - case 'm': // middle (vertical) - dy[i] = (miny + maxy) / 2 - (bbox.y + bbox.height / 2); - break; - case 'b': // bottom (vertical) - dy[i] = maxy - (bbox.y + bbox.height); - break; - } - } - this.moveSelectedElements(dx, dy); + var dx = new Array(len); + var dy = new Array(len); + for (i = 0; i < len; ++i) { + if (selectedElements[i] == null) { break; } + elem = selectedElements[i]; + var bbox = bboxes[i]; + dx[i] = 0; + dy[i] = 0; + switch (type) { + case 'l': // left (horizontal) + dx[i] = minx - bbox.x; + break; + case 'c': // center (horizontal) + dx[i] = (minx + maxx) / 2 - (bbox.x + bbox.width / 2); + break; + case 'r': // right (horizontal) + dx[i] = maxx - (bbox.x + bbox.width); + break; + case 't': // top (vertical) + dy[i] = miny - bbox.y; + break; + case 'm': // middle (vertical) + dy[i] = (miny + maxy) / 2 - (bbox.y + bbox.height / 2); + break; + case 'b': // bottom (vertical) + dy[i] = maxy - (bbox.y + bbox.height); + break; + } + } + this.moveSelectedElements(dx, dy); }; // Group: Additional editor tools @@ -7053,40 +7053,40 @@ this.contentH = getResolution().h; // * d_x - The x position difference // * d_y - The y position difference this.updateCanvas = function (w, h) { - svgroot.setAttribute('width', w); - svgroot.setAttribute('height', h); - var bg = $('#canvasBackground')[0]; - var oldX = svgcontent.getAttribute('x'); - var oldY = svgcontent.getAttribute('y'); - var x = (w / 2 - this.contentW * currentZoom / 2); - var y = (h / 2 - this.contentH * currentZoom / 2); + svgroot.setAttribute('width', w); + svgroot.setAttribute('height', h); + var bg = $('#canvasBackground')[0]; + var oldX = svgcontent.getAttribute('x'); + var oldY = svgcontent.getAttribute('y'); + var x = (w / 2 - this.contentW * currentZoom / 2); + var y = (h / 2 - this.contentH * currentZoom / 2); - svgedit.utilities.assignAttributes(svgcontent, { - width: this.contentW * currentZoom, - height: this.contentH * currentZoom, - 'x': x, - 'y': y, - 'viewBox': '0 0 ' + this.contentW + ' ' + this.contentH - }); + svgedit.utilities.assignAttributes(svgcontent, { + width: this.contentW * currentZoom, + height: this.contentH * currentZoom, + 'x': x, + 'y': y, + 'viewBox': '0 0 ' + this.contentW + ' ' + this.contentH + }); - svgedit.utilities.assignAttributes(bg, { - width: svgcontent.getAttribute('width'), - height: svgcontent.getAttribute('height'), - x: x, - y: y - }); + svgedit.utilities.assignAttributes(bg, { + width: svgcontent.getAttribute('width'), + height: svgcontent.getAttribute('height'), + x: x, + y: y + }); - var bgImg = svgedit.utilities.getElem('background_image'); - if (bgImg) { - svgedit.utilities.assignAttributes(bgImg, { - 'width': '100%', - 'height': '100%' - }); - } + var bgImg = svgedit.utilities.getElem('background_image'); + if (bgImg) { + svgedit.utilities.assignAttributes(bgImg, { + 'width': '100%', + 'height': '100%' + }); + } - selectorManager.selectorParentGroup.setAttribute('transform', 'translate(' + x + ',' + y + ')'); - runExtensions('canvasUpdated', {new_x: x, new_y: y, old_x: oldX, old_y: oldY, d_x: x - oldX, d_y: y - oldY}); - return {x: x, y: y, old_x: oldX, old_y: oldY, d_x: x - oldX, d_y: y - oldY}; + selectorManager.selectorParentGroup.setAttribute('transform', 'translate(' + x + ',' + y + ')'); + runExtensions('canvasUpdated', {new_x: x, new_y: y, old_x: oldX, old_y: oldY, d_x: x - oldX, d_y: y - oldY}); + return {x: x, y: y, old_x: oldX, old_y: oldY, d_x: x - oldX, d_y: y - oldY}; }; // Function: setBackground @@ -7096,26 +7096,26 @@ this.updateCanvas = function (w, h) { // color - String with fill color to apply // url - URL or path to image to use this.setBackground = function (color, url) { - var bg = svgedit.utilities.getElem('canvasBackground'); - var border = $(bg).find('rect')[0]; - var bgImg = svgedit.utilities.getElem('background_image'); - border.setAttribute('fill', color); - if (url) { - if (!bgImg) { - bgImg = svgdoc.createElementNS(NS.SVG, 'image'); - svgedit.utilities.assignAttributes(bgImg, { - 'id': 'background_image', - 'width': '100%', - 'height': '100%', - 'preserveAspectRatio': 'xMinYMin', - 'style': 'pointer-events:none' - }); - } - setHref(bgImg, url); - bg.appendChild(bgImg); - } else if (bgImg) { - bgImg.parentNode.removeChild(bgImg); - } + var bg = svgedit.utilities.getElem('canvasBackground'); + var border = $(bg).find('rect')[0]; + var bgImg = svgedit.utilities.getElem('background_image'); + border.setAttribute('fill', color); + if (url) { + if (!bgImg) { + bgImg = svgdoc.createElementNS(NS.SVG, 'image'); + svgedit.utilities.assignAttributes(bgImg, { + 'id': 'background_image', + 'width': '100%', + 'height': '100%', + 'preserveAspectRatio': 'xMinYMin', + 'style': 'pointer-events:none' + }); + } + setHref(bgImg, url); + bg.appendChild(bgImg); + } else if (bgImg) { + bgImg.parentNode.removeChild(bgImg); + } }; // Function: cycleElement @@ -7124,31 +7124,31 @@ this.setBackground = function (color, url) { // Parameters: // next - Boolean where true = next and false = previous element this.cycleElement = function (next) { - var num; - var curElem = selectedElements[0]; - var elem = false; - var allElems = getVisibleElements(currentGroup || getCurrentDrawing().getCurrentLayer()); - if (!allElems.length) { return; } - if (curElem == null) { - num = next ? allElems.length - 1 : 0; - elem = allElems[num]; - } else { - var i = allElems.length; - while (i--) { - if (allElems[i] === curElem) { - num = next ? i - 1 : i + 1; - if (num >= allElems.length) { - num = 0; - } else if (num < 0) { - num = allElems.length - 1; - } - elem = allElems[num]; - break; - } - } - } - selectOnly([elem], true); - call('selected', selectedElements); + var num; + var curElem = selectedElements[0]; + var elem = false; + var allElems = getVisibleElements(currentGroup || getCurrentDrawing().getCurrentLayer()); + if (!allElems.length) { return; } + if (curElem == null) { + num = next ? allElems.length - 1 : 0; + elem = allElems[num]; + } else { + var i = allElems.length; + while (i--) { + if (allElems[i] === curElem) { + num = next ? i - 1 : i + 1; + if (num >= allElems.length) { + num = 0; + } else if (num < 0) { + num = allElems.length - 1; + } + elem = allElems[num]; + break; + } + } + } + selectOnly([elem], true); + call('selected', selectedElements); }; this.clear(); @@ -7160,48 +7160,48 @@ this.clear(); // but currently appears to be the best way to allow testing and provide // access to them to plugins. this.getPrivateMethods = function () { - var obj = { - addCommandToHistory: addCommandToHistory, - setGradient: setGradient, - addSvgElementFromJson: addSvgElementFromJson, - assignAttributes: assignAttributes, - BatchCommand: BatchCommand, - call: call, - ChangeElementCommand: ChangeElementCommand, - copyElem: function (elem) { return getCurrentDrawing().copyElem(elem); }, - ffClone: ffClone, - findDefs: findDefs, - findDuplicateGradient: findDuplicateGradient, - getElem: getElem, - getId: getId, - getIntersectionList: getIntersectionList, - getMouseTarget: getMouseTarget, - getNextId: getNextId, - getPathBBox: getPathBBox, - getUrlFromAttr: getUrlFromAttr, - hasMatrixTransform: hasMatrixTransform, - identifyLayers: identifyLayers, - InsertElementCommand: InsertElementCommand, - isIdentity: svgedit.math.isIdentity, - logMatrix: logMatrix, - matrixMultiply: matrixMultiply, - MoveElementCommand: MoveElementCommand, - preventClickDefault: svgedit.utilities.preventClickDefault, - recalculateAllSelectedDimensions: recalculateAllSelectedDimensions, - recalculateDimensions: recalculateDimensions, - remapElement: remapElement, - RemoveElementCommand: RemoveElementCommand, - removeUnusedDefElems: removeUnusedDefElems, - round: round, - runExtensions: runExtensions, - sanitizeSvg: sanitizeSvg, - SVGEditTransformList: svgedit.transformlist.SVGTransformList, - toString: toString, - transformBox: svgedit.math.transformBox, - transformListToTransform: transformListToTransform, - transformPoint: transformPoint, - walkTree: svgedit.utilities.walkTree - }; - return obj; + var obj = { + addCommandToHistory: addCommandToHistory, + setGradient: setGradient, + addSvgElementFromJson: addSvgElementFromJson, + assignAttributes: assignAttributes, + BatchCommand: BatchCommand, + call: call, + ChangeElementCommand: ChangeElementCommand, + copyElem: function (elem) { return getCurrentDrawing().copyElem(elem); }, + ffClone: ffClone, + findDefs: findDefs, + findDuplicateGradient: findDuplicateGradient, + getElem: getElem, + getId: getId, + getIntersectionList: getIntersectionList, + getMouseTarget: getMouseTarget, + getNextId: getNextId, + getPathBBox: getPathBBox, + getUrlFromAttr: getUrlFromAttr, + hasMatrixTransform: hasMatrixTransform, + identifyLayers: identifyLayers, + InsertElementCommand: InsertElementCommand, + isIdentity: svgedit.math.isIdentity, + logMatrix: logMatrix, + matrixMultiply: matrixMultiply, + MoveElementCommand: MoveElementCommand, + preventClickDefault: svgedit.utilities.preventClickDefault, + recalculateAllSelectedDimensions: recalculateAllSelectedDimensions, + recalculateDimensions: recalculateDimensions, + remapElement: remapElement, + RemoveElementCommand: RemoveElementCommand, + removeUnusedDefElems: removeUnusedDefElems, + round: round, + runExtensions: runExtensions, + sanitizeSvg: sanitizeSvg, + SVGEditTransformList: svgedit.transformlist.SVGTransformList, + toString: toString, + transformBox: svgedit.math.transformBox, + transformListToTransform: transformListToTransform, + transformPoint: transformPoint, + walkTree: svgedit.utilities.walkTree + }; + return obj; }; }; diff --git a/editor/svgedit.js b/editor/svgedit.js index e8611567..065155cc 100644 --- a/editor/svgedit.js +++ b/editor/svgedit.js @@ -7,24 +7,24 @@ */ svgedit = { - // common namepaces constants in alpha order - NS: { - HTML: 'http://www.w3.org/1999/xhtml', - MATH: 'http://www.w3.org/1998/Math/MathML', - SE: 'http://svg-edit.googlecode.com', - SVG: 'http://www.w3.org/2000/svg', - XLINK: 'http://www.w3.org/1999/xlink', - XML: 'http://www.w3.org/XML/1998/namespace', - XMLNS: 'http://www.w3.org/2000/xmlns/' // see http://www.w3.org/TR/REC-xml-names/#xmlReserved - } + // common namepaces constants in alpha order + NS: { + HTML: 'http://www.w3.org/1999/xhtml', + MATH: 'http://www.w3.org/1998/Math/MathML', + SE: 'http://svg-edit.googlecode.com', + SVG: 'http://www.w3.org/2000/svg', + XLINK: 'http://www.w3.org/1999/xlink', + XML: 'http://www.w3.org/XML/1998/namespace', + XMLNS: 'http://www.w3.org/2000/xmlns/' // see http://www.w3.org/TR/REC-xml-names/#xmlReserved + } }; // return the svgedit.NS with key values switched and lowercase svgedit.getReverseNS = function () { - 'use strict'; - var reverseNS = {}; - $.each(this.NS, function (name, URI) { - reverseNS[URI] = name.toLowerCase(); - }); - return reverseNS; + 'use strict'; + var reverseNS = {}; + $.each(this.NS, function (name, URI) { + reverseNS[URI] = name.toLowerCase(); + }); + return reverseNS; }; diff --git a/editor/svgtransformlist.js b/editor/svgtransformlist.js index d9603a07..02aeb439 100644 --- a/editor/svgtransformlist.js +++ b/editor/svgtransformlist.js @@ -16,41 +16,41 @@ 'use strict'; if (!svgedit.transformlist) { - svgedit.transformlist = {}; + svgedit.transformlist = {}; } var svgroot = document.createElementNS(svgedit.NS.SVG, 'svg'); // Helper function. function transformToString (xform) { - var m = xform.matrix, - text = ''; - switch (xform.type) { - case 1: // MATRIX - text = 'matrix(' + [m.a, m.b, m.c, m.d, m.e, m.f].join(',') + ')'; - break; - case 2: // TRANSLATE - text = 'translate(' + m.e + ',' + m.f + ')'; - break; - case 3: // SCALE - if (m.a === m.d) { - text = 'scale(' + m.a + ')'; - } else { - text = 'scale(' + m.a + ',' + m.d + ')'; - } - break; - case 4: // ROTATE - var cx = 0, cy = 0; - // this prevents divide by zero - if (xform.angle !== 0) { - var K = 1 - m.a; - cy = (K * m.f + m.b * m.e) / (K * K + m.b * m.b); - cx = (m.e - m.b * cy) / K; - } - text = 'rotate(' + xform.angle + ' ' + cx + ',' + cy + ')'; - break; - } - return text; + var m = xform.matrix, + text = ''; + switch (xform.type) { + case 1: // MATRIX + text = 'matrix(' + [m.a, m.b, m.c, m.d, m.e, m.f].join(',') + ')'; + break; + case 2: // TRANSLATE + text = 'translate(' + m.e + ',' + m.f + ')'; + break; + case 3: // SCALE + if (m.a === m.d) { + text = 'scale(' + m.a + ')'; + } else { + text = 'scale(' + m.a + ',' + m.d + ')'; + } + break; + case 4: // ROTATE + var cx = 0, cy = 0; + // this prevents divide by zero + if (xform.angle !== 0) { + var K = 1 - m.a; + cy = (K * m.f + m.b * m.e) / (K * K + m.b * m.b); + cx = (m.e - m.b * cy) / K; + } + text = 'rotate(' + xform.angle + ' ' + cx + ',' + cy + ')'; + break; + } + return text; } /** @@ -78,176 +78,176 @@ var listMap_ = {}; // } // ************************************************************************************** svgedit.transformlist.SVGTransformList = function (elem) { - this._elem = elem || null; - this._xforms = []; - // TODO: how do we capture the undo-ability in the changed transform list? - this._update = function () { - var tstr = ''; - /* var concatMatrix = */ svgroot.createSVGMatrix(); - var i; - for (i = 0; i < this.numberOfItems; ++i) { - var xform = this._list.getItem(i); - tstr += transformToString(xform) + ' '; - } - this._elem.setAttribute('transform', tstr); - }; - this._list = this; - this._init = function () { - // Transform attribute parser - var str = this._elem.getAttribute('transform'); - if (!str) { return; } + this._elem = elem || null; + this._xforms = []; + // TODO: how do we capture the undo-ability in the changed transform list? + this._update = function () { + var tstr = ''; + /* var concatMatrix = */ svgroot.createSVGMatrix(); + var i; + for (i = 0; i < this.numberOfItems; ++i) { + var xform = this._list.getItem(i); + tstr += transformToString(xform) + ' '; + } + this._elem.setAttribute('transform', tstr); + }; + this._list = this; + this._init = function () { + // Transform attribute parser + var str = this._elem.getAttribute('transform'); + if (!str) { return; } - // TODO: Add skew support in future - var re = /\s*((scale|matrix|rotate|translate)\s*\(.*?\))\s*,?\s*/; - var m = true; - while (m) { - m = str.match(re); - str = str.replace(re, ''); - if (m && m[1]) { - var x = m[1]; - var bits = x.split(/\s*\(/); - var name = bits[0]; - var valBits = bits[1].match(/\s*(.*?)\s*\)/); - valBits[1] = valBits[1].replace(/(\d)-/g, '$1 -'); - var valArr = valBits[1].split(/[, ]+/); - var letters = 'abcdef'.split(''); - var mtx = svgroot.createSVGMatrix(); - $.each(valArr, function (i, item) { - valArr[i] = parseFloat(item); - if (name === 'matrix') { - mtx[letters[i]] = valArr[i]; - } - }); - var xform = svgroot.createSVGTransform(); - var fname = 'set' + name.charAt(0).toUpperCase() + name.slice(1); - var values = name === 'matrix' ? [mtx] : valArr; + // TODO: Add skew support in future + var re = /\s*((scale|matrix|rotate|translate)\s*\(.*?\))\s*,?\s*/; + var m = true; + while (m) { + m = str.match(re); + str = str.replace(re, ''); + if (m && m[1]) { + var x = m[1]; + var bits = x.split(/\s*\(/); + var name = bits[0]; + var valBits = bits[1].match(/\s*(.*?)\s*\)/); + valBits[1] = valBits[1].replace(/(\d)-/g, '$1 -'); + var valArr = valBits[1].split(/[, ]+/); + var letters = 'abcdef'.split(''); + var mtx = svgroot.createSVGMatrix(); + $.each(valArr, function (i, item) { + valArr[i] = parseFloat(item); + if (name === 'matrix') { + mtx[letters[i]] = valArr[i]; + } + }); + var xform = svgroot.createSVGTransform(); + var fname = 'set' + name.charAt(0).toUpperCase() + name.slice(1); + var values = name === 'matrix' ? [mtx] : valArr; - if (name === 'scale' && values.length === 1) { - values.push(values[0]); - } else if (name === 'translate' && values.length === 1) { - values.push(0); - } else if (name === 'rotate' && values.length === 1) { - values.push(0, 0); - } - xform[fname].apply(xform, values); - this._list.appendItem(xform); - } - } - }; - this._removeFromOtherLists = function (item) { - if (item) { - // Check if this transform is already in a transformlist, and - // remove it if so. - var found = false; - var id; - for (id in listMap_) { - var tl = listMap_[id]; - var i, len; - for (i = 0, len = tl._xforms.length; i < len; ++i) { - if (tl._xforms[i] === item) { - found = true; - tl.removeItem(i); - break; - } - } - if (found) { - break; - } - } - } - }; + if (name === 'scale' && values.length === 1) { + values.push(values[0]); + } else if (name === 'translate' && values.length === 1) { + values.push(0); + } else if (name === 'rotate' && values.length === 1) { + values.push(0, 0); + } + xform[fname].apply(xform, values); + this._list.appendItem(xform); + } + } + }; + this._removeFromOtherLists = function (item) { + if (item) { + // Check if this transform is already in a transformlist, and + // remove it if so. + var found = false; + var id; + for (id in listMap_) { + var tl = listMap_[id]; + var i, len; + for (i = 0, len = tl._xforms.length; i < len; ++i) { + if (tl._xforms[i] === item) { + found = true; + tl.removeItem(i); + break; + } + } + if (found) { + break; + } + } + } + }; - this.numberOfItems = 0; - this.clear = function () { - this.numberOfItems = 0; - this._xforms = []; - }; + this.numberOfItems = 0; + this.clear = function () { + this.numberOfItems = 0; + this._xforms = []; + }; - this.initialize = function (newItem) { - this.numberOfItems = 1; - this._removeFromOtherLists(newItem); - this._xforms = [newItem]; - }; + this.initialize = function (newItem) { + this.numberOfItems = 1; + this._removeFromOtherLists(newItem); + this._xforms = [newItem]; + }; - this.getItem = function (index) { - if (index < this.numberOfItems && index >= 0) { - return this._xforms[index]; - } - var err = new Error('DOMException with code=INDEX_SIZE_ERR'); - err.code = 1; - throw err; - }; + this.getItem = function (index) { + if (index < this.numberOfItems && index >= 0) { + return this._xforms[index]; + } + var err = new Error('DOMException with code=INDEX_SIZE_ERR'); + err.code = 1; + throw err; + }; - this.insertItemBefore = function (newItem, index) { - var retValue = null; - if (index >= 0) { - if (index < this.numberOfItems) { - this._removeFromOtherLists(newItem); - var newxforms = new Array(this.numberOfItems + 1); - // TODO: use array copying and slicing - var i; - for (i = 0; i < index; ++i) { - newxforms[i] = this._xforms[i]; - } - newxforms[i] = newItem; - var j; - for (j = i + 1; i < this.numberOfItems; ++j, ++i) { - newxforms[j] = this._xforms[i]; - } - this.numberOfItems++; - this._xforms = newxforms; - retValue = newItem; - this._list._update(); - } else { - retValue = this._list.appendItem(newItem); - } - } - return retValue; - }; + this.insertItemBefore = function (newItem, index) { + var retValue = null; + if (index >= 0) { + if (index < this.numberOfItems) { + this._removeFromOtherLists(newItem); + var newxforms = new Array(this.numberOfItems + 1); + // TODO: use array copying and slicing + var i; + for (i = 0; i < index; ++i) { + newxforms[i] = this._xforms[i]; + } + newxforms[i] = newItem; + var j; + for (j = i + 1; i < this.numberOfItems; ++j, ++i) { + newxforms[j] = this._xforms[i]; + } + this.numberOfItems++; + this._xforms = newxforms; + retValue = newItem; + this._list._update(); + } else { + retValue = this._list.appendItem(newItem); + } + } + return retValue; + }; - this.replaceItem = function (newItem, index) { - var retValue = null; - if (index < this.numberOfItems && index >= 0) { - this._removeFromOtherLists(newItem); - this._xforms[index] = newItem; - retValue = newItem; - this._list._update(); - } - return retValue; - }; + this.replaceItem = function (newItem, index) { + var retValue = null; + if (index < this.numberOfItems && index >= 0) { + this._removeFromOtherLists(newItem); + this._xforms[index] = newItem; + retValue = newItem; + this._list._update(); + } + return retValue; + }; - this.removeItem = function (index) { - if (index < this.numberOfItems && index >= 0) { - var retValue = this._xforms[index]; - var newxforms = new Array(this.numberOfItems - 1); - var i, j; - for (i = 0; i < index; ++i) { - newxforms[i] = this._xforms[i]; - } - for (j = i; j < this.numberOfItems - 1; ++j, ++i) { - newxforms[j] = this._xforms[i + 1]; - } - this.numberOfItems--; - this._xforms = newxforms; - this._list._update(); - return retValue; - } - var err = new Error('DOMException with code=INDEX_SIZE_ERR'); - err.code = 1; - throw err; - }; + this.removeItem = function (index) { + if (index < this.numberOfItems && index >= 0) { + var retValue = this._xforms[index]; + var newxforms = new Array(this.numberOfItems - 1); + var i, j; + for (i = 0; i < index; ++i) { + newxforms[i] = this._xforms[i]; + } + for (j = i; j < this.numberOfItems - 1; ++j, ++i) { + newxforms[j] = this._xforms[i + 1]; + } + this.numberOfItems--; + this._xforms = newxforms; + this._list._update(); + return retValue; + } + var err = new Error('DOMException with code=INDEX_SIZE_ERR'); + err.code = 1; + throw err; + }; - this.appendItem = function (newItem) { - this._removeFromOtherLists(newItem); - this._xforms.push(newItem); - this.numberOfItems++; - this._list._update(); - return newItem; - }; + this.appendItem = function (newItem) { + this._removeFromOtherLists(newItem); + this._xforms.push(newItem); + this.numberOfItems++; + this._list._update(); + return newItem; + }; }; svgedit.transformlist.resetListMap = function () { - listMap_ = {}; + listMap_ = {}; }; /** @@ -256,9 +256,9 @@ svgedit.transformlist.resetListMap = function () { * elem - a DOM Element */ svgedit.transformlist.removeElementFromListMap = function (elem) { - if (elem.id && listMap_[elem.id]) { - delete listMap_[elem.id]; - } + if (elem.id && listMap_[elem.id]) { + delete listMap_[elem.id]; + } }; // Function: getTransformList @@ -267,26 +267,26 @@ svgedit.transformlist.removeElementFromListMap = function (elem) { // Parameters: // elem - DOM element to get a transformlist from svgedit.transformlist.getTransformList = function (elem) { - if (!svgedit.browser.supportsNativeTransformLists()) { - var id = elem.id || 'temp'; - var t = listMap_[id]; - if (!t || id === 'temp') { - listMap_[id] = new svgedit.transformlist.SVGTransformList(elem); - listMap_[id]._init(); - t = listMap_[id]; - } - return t; - } - if (elem.transform) { - return elem.transform.baseVal; - } - if (elem.gradientTransform) { - return elem.gradientTransform.baseVal; - } - if (elem.patternTransform) { - return elem.patternTransform.baseVal; - } + if (!svgedit.browser.supportsNativeTransformLists()) { + var id = elem.id || 'temp'; + var t = listMap_[id]; + if (!t || id === 'temp') { + listMap_[id] = new svgedit.transformlist.SVGTransformList(elem); + listMap_[id]._init(); + t = listMap_[id]; + } + return t; + } + if (elem.transform) { + return elem.transform.baseVal; + } + if (elem.gradientTransform) { + return elem.gradientTransform.baseVal; + } + if (elem.patternTransform) { + return elem.patternTransform.baseVal; + } - return null; + return null; }; }()); diff --git a/editor/svgutils.js b/editor/svgutils.js index 48ea412b..8a9ef172 100644 --- a/editor/svgutils.js +++ b/editor/svgutils.js @@ -20,7 +20,7 @@ 'use strict'; if (!svgedit.utilities) { - svgedit.utilities = {}; + svgedit.utilities = {}; } // Constants @@ -40,10 +40,10 @@ var domcontainer_ = null; var svgroot_ = null; svgedit.utilities.init = function (editorContext) { - editorContext_ = editorContext; - domdoc_ = editorContext.getDOMDocument(); - domcontainer_ = editorContext.getDOMContainer(); - svgroot_ = editorContext.getSVGRoot(); + editorContext_ = editorContext; + domdoc_ = editorContext.getDOMDocument(); + domcontainer_ = editorContext.getDOMContainer(); + svgroot_ = editorContext.getSVGRoot(); }; // Function: svgedit.utilities.toXml @@ -57,9 +57,9 @@ svgedit.utilities.init = function (editorContext) { // Returns: // The converted string svgedit.utilities.toXml = function (str) { - // ' is ok in XML, but not HTML - // > does not normally need escaping, though it can if within a CDATA expression (and preceded by "]]") - return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/, '''); + // ' is ok in XML, but not HTML + // > does not normally need escaping, though it can if within a CDATA expression (and preceded by "]]") + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/, '''); }; // Function: svgedit.utilities.fromXml @@ -72,7 +72,7 @@ svgedit.utilities.toXml = function (str) { // Returns: // The converted string svgedit.utilities.fromXml = function (str) { - return $('

        ').html(str).text(); + return $('

        ').html(str).text(); }; // This code was written by Tyler Akins and has been placed in the @@ -85,89 +85,89 @@ svgedit.utilities.fromXml = function (str) { // Function: svgedit.utilities.encode64 // Converts a string to base64 svgedit.utilities.encode64 = function (input) { - // base64 strings are 4/3 larger than the original string - input = svgedit.utilities.encodeUTF8(input); // convert non-ASCII characters - // input = svgedit.utilities.convertToXMLReferences(input); - if (window.btoa) { - return window.btoa(input); // Use native if available - } - var output = []; - output.length = Math.floor((input.length + 2) / 3) * 4; - var chr1, chr2, chr3; - var enc1, enc2, enc3, enc4; - var i = 0, p = 0; + // base64 strings are 4/3 larger than the original string + input = svgedit.utilities.encodeUTF8(input); // convert non-ASCII characters + // input = svgedit.utilities.convertToXMLReferences(input); + if (window.btoa) { + return window.btoa(input); // Use native if available + } + var output = []; + output.length = Math.floor((input.length + 2) / 3) * 4; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0, p = 0; - do { - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); + do { + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } - output[p++] = KEYSTR.charAt(enc1); - output[p++] = KEYSTR.charAt(enc2); - output[p++] = KEYSTR.charAt(enc3); - output[p++] = KEYSTR.charAt(enc4); - } while (i < input.length); + output[p++] = KEYSTR.charAt(enc1); + output[p++] = KEYSTR.charAt(enc2); + output[p++] = KEYSTR.charAt(enc3); + output[p++] = KEYSTR.charAt(enc4); + } while (i < input.length); - return output.join(''); + return output.join(''); }; // Function: svgedit.utilities.decode64 // Converts a string from base64 svgedit.utilities.decode64 = function (input) { - if (window.atob) { - return svgedit.utilities.decodeUTF8(window.atob(input)); - } - var output = ''; - var chr1, chr2, chr3 = ''; - var enc1, enc2, enc3, enc4 = ''; - var i = 0; + if (window.atob) { + return svgedit.utilities.decodeUTF8(window.atob(input)); + } + var output = ''; + var chr1, chr2, chr3 = ''; + var enc1, enc2, enc3, enc4 = ''; + var i = 0; - // remove all characters that are not A-Z, a-z, 0-9, +, /, or = - input = input.replace(/[^A-Za-z0-9+/=]/g, ''); + // remove all characters that are not A-Z, a-z, 0-9, +, /, or = + input = input.replace(/[^A-Za-z0-9+/=]/g, ''); - do { - enc1 = KEYSTR.indexOf(input.charAt(i++)); - enc2 = KEYSTR.indexOf(input.charAt(i++)); - enc3 = KEYSTR.indexOf(input.charAt(i++)); - enc4 = KEYSTR.indexOf(input.charAt(i++)); + do { + enc1 = KEYSTR.indexOf(input.charAt(i++)); + enc2 = KEYSTR.indexOf(input.charAt(i++)); + enc3 = KEYSTR.indexOf(input.charAt(i++)); + enc4 = KEYSTR.indexOf(input.charAt(i++)); - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; - output = output + String.fromCharCode(chr1); + output = output + String.fromCharCode(chr1); - if (enc3 !== 64) { - output = output + String.fromCharCode(chr2); - } - if (enc4 !== 64) { - output = output + String.fromCharCode(chr3); - } + if (enc3 !== 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 !== 64) { + output = output + String.fromCharCode(chr3); + } - chr1 = chr2 = chr3 = ''; - enc1 = enc2 = enc3 = enc4 = ''; - } while (i < input.length); - return svgedit.utilities.decodeUTF8(output); + chr1 = chr2 = chr3 = ''; + enc1 = enc2 = enc3 = enc4 = ''; + } while (i < input.length); + return svgedit.utilities.decodeUTF8(output); }; svgedit.utilities.decodeUTF8 = function (argString) { - return decodeURIComponent(escape(argString)); + return decodeURIComponent(escape(argString)); }; // codedread:does not seem to work with webkit-based browsers on OSX // Brettz9: please test again as function upgraded svgedit.utilities.encodeUTF8 = function (argString) { - return unescape(encodeURIComponent(argString)); + return unescape(encodeURIComponent(argString)); }; /** @@ -176,16 +176,16 @@ svgedit.utilities.encodeUTF8 = function (argString) { * @return {string} object URL or empty string */ svgedit.utilities.dataURLToObjectURL = function (dataurl) { - if (typeof Uint8Array === 'undefined' || typeof Blob === 'undefined' || typeof URL === 'undefined' || !URL.createObjectURL) { - return ''; - } - var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], - bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); - while (n--) { - u8arr[n] = bstr.charCodeAt(n); - } - var blob = new Blob([u8arr], {type: mime}); - return URL.createObjectURL(blob); + if (typeof Uint8Array === 'undefined' || typeof Blob === 'undefined' || typeof URL === 'undefined' || !URL.createObjectURL) { + return ''; + } + var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], + bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + var blob = new Blob([u8arr], {type: mime}); + return URL.createObjectURL(blob); }; /** @@ -194,62 +194,62 @@ svgedit.utilities.dataURLToObjectURL = function (dataurl) { * @return {string} object URL or empty string */ svgedit.utilities.createObjectURL = function (blob) { - if (!blob || typeof URL === 'undefined' || !URL.createObjectURL) { - return ''; - } - return URL.createObjectURL(blob); + if (!blob || typeof URL === 'undefined' || !URL.createObjectURL) { + return ''; + } + return URL.createObjectURL(blob); }; /** * @property {string} blankPageObjectURL */ svgedit.utilities.blankPageObjectURL = (function () { - if (typeof Blob === 'undefined') { - return ''; - } - var blob = new Blob(['SVG-edit '], {type: 'text/html'}); - return svgedit.utilities.createObjectURL(blob); + if (typeof Blob === 'undefined') { + return ''; + } + var blob = new Blob(['SVG-edit '], {type: 'text/html'}); + return svgedit.utilities.createObjectURL(blob); })(); // Function: svgedit.utilities.convertToXMLReferences // Converts a string to use XML references svgedit.utilities.convertToXMLReferences = function (input) { - var n, - output = ''; - for (n = 0; n < input.length; n++) { - var c = input.charCodeAt(n); - if (c < 128) { - output += input[n]; - } else if (c > 127) { - output += ('&#' + c + ';'); - } - } - return output; + var n, + output = ''; + for (n = 0; n < input.length; n++) { + var c = input.charCodeAt(n); + if (c < 128) { + output += input[n]; + } else if (c > 127) { + output += ('&#' + c + ';'); + } + } + return output; }; // Function: svgedit.utilities.text2xml // Cross-browser compatible method of converting a string to an XML tree // found this function here: http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f svgedit.utilities.text2xml = function (sXML) { - if (sXML.indexOf('= 0) { - sXML = sXML.replace(/<(\/?)svg:/g, '<$1').replace('xmlns:svg', 'xmlns'); - } + if (sXML.indexOf('= 0) { + sXML = sXML.replace(/<(\/?)svg:/g, '<$1').replace('xmlns:svg', 'xmlns'); + } - var out, dXML; - try { - dXML = (window.DOMParser) ? new DOMParser() : new ActiveXObject('Microsoft.XMLDOM'); - dXML.async = false; - } catch (e) { - throw new Error('XML Parser could not be instantiated'); - } - try { - if (dXML.loadXML) { - out = (dXML.loadXML(sXML)) ? dXML : false; - } else { - out = dXML.parseFromString(sXML, 'text/xml'); - } - } catch (e2) { throw new Error('Error parsing XML string'); } - return out; + var out, dXML; + try { + dXML = (window.DOMParser) ? new DOMParser() : new ActiveXObject('Microsoft.XMLDOM'); + dXML.async = false; + } catch (e) { + throw new Error('XML Parser could not be instantiated'); + } + try { + if (dXML.loadXML) { + out = (dXML.loadXML(sXML)) ? dXML : false; + } else { + out = dXML.parseFromString(sXML, 'text/xml'); + } + } catch (e2) { throw new Error('Error parsing XML string'); } + return out; }; // Function: svgedit.utilities.bboxToObj @@ -261,12 +261,12 @@ svgedit.utilities.text2xml = function (sXML) { // Returns: // An object with properties names x, y, width, height. svgedit.utilities.bboxToObj = function (bbox) { - return { - x: bbox.x, - y: bbox.y, - width: bbox.width, - height: bbox.height - }; + return { + x: bbox.x, + y: bbox.y, + width: bbox.width, + height: bbox.height + }; }; // Function: svgedit.utilities.walkTree @@ -276,13 +276,13 @@ svgedit.utilities.bboxToObj = function (bbox) { // elem - DOM element to traverse // cbFn - Callback function to run on each element svgedit.utilities.walkTree = function (elem, cbFn) { - if (elem && elem.nodeType === 1) { - cbFn(elem); - var i = elem.childNodes.length; - while (i--) { - svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn); - } - } + if (elem && elem.nodeType === 1) { + cbFn(elem); + var i = elem.childNodes.length; + while (i--) { + svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn); + } + } }; // Function: svgedit.utilities.walkTreePost @@ -293,13 +293,13 @@ svgedit.utilities.walkTree = function (elem, cbFn) { // elem - DOM element to traverse // cbFn - Callback function to run on each element svgedit.utilities.walkTreePost = function (elem, cbFn) { - if (elem && elem.nodeType === 1) { - var i = elem.childNodes.length; - while (i--) { - svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn); - } - cbFn(elem); - } + if (elem && elem.nodeType === 1) { + var i = elem.childNodes.length; + while (i--) { + svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn); + } + cbFn(elem); + } }; // Function: svgedit.utilities.getUrlFromAttr @@ -315,32 +315,32 @@ svgedit.utilities.walkTreePost = function (elem, cbFn) { // Returns: // String with just the URL, like someFile.svg#foo svgedit.utilities.getUrlFromAttr = function (attrVal) { - if (attrVal) { - // url("#somegrad") - if (attrVal.indexOf('url("') === 0) { - return attrVal.substring(5, attrVal.indexOf('"', 6)); - } - // url('#somegrad') - if (attrVal.indexOf("url('") === 0) { - return attrVal.substring(5, attrVal.indexOf("'", 6)); - } - if (attrVal.indexOf('url(') === 0) { - return attrVal.substring(4, attrVal.indexOf(')')); - } - } - return null; + if (attrVal) { + // url("#somegrad") + if (attrVal.indexOf('url("') === 0) { + return attrVal.substring(5, attrVal.indexOf('"', 6)); + } + // url('#somegrad') + if (attrVal.indexOf("url('") === 0) { + return attrVal.substring(5, attrVal.indexOf("'", 6)); + } + if (attrVal.indexOf('url(') === 0) { + return attrVal.substring(4, attrVal.indexOf(')')); + } + } + return null; }; // Function: svgedit.utilities.getHref // Returns the given element's xlink:href value svgedit.utilities.getHref = function (elem) { - return elem.getAttributeNS(NS.XLINK, 'href'); + return elem.getAttributeNS(NS.XLINK, 'href'); }; // Function: svgedit.utilities.setHref // Sets the given element's xlink:href value svgedit.utilities.setHref = function (elem, val) { - elem.setAttributeNS(NS.XLINK, 'xlink:href', val); + elem.setAttributeNS(NS.XLINK, 'xlink:href', val); }; // Function: findDefs @@ -348,20 +348,20 @@ svgedit.utilities.setHref = function (elem, val) { // Returns: // The document's element, create it first if necessary svgedit.utilities.findDefs = function () { - var svgElement = editorContext_.getSVGContent(); - var defs = svgElement.getElementsByTagNameNS(NS.SVG, 'defs'); - if (defs.length > 0) { - defs = defs[0]; - } else { - defs = svgElement.ownerDocument.createElementNS(NS.SVG, 'defs'); - if (svgElement.firstChild) { - // first child is a comment, so call nextSibling - svgElement.insertBefore(defs, svgElement.firstChild.nextSibling); - } else { - svgElement.appendChild(defs); - } - } - return defs; + var svgElement = editorContext_.getSVGContent(); + var defs = svgElement.getElementsByTagNameNS(NS.SVG, 'defs'); + if (defs.length > 0) { + defs = defs[0]; + } else { + defs = svgElement.ownerDocument.createElementNS(NS.SVG, 'defs'); + if (svgElement.firstChild) { + // first child is a comment, so call nextSibling + svgElement.insertBefore(defs, svgElement.firstChild.nextSibling); + } else { + svgElement.appendChild(defs); + } + } + return defs; }; // TODO(codedread): Consider moving the next to functions to bbox.js @@ -377,75 +377,75 @@ svgedit.utilities.findDefs = function () { // Returns: // A BBox-like object svgedit.utilities.getPathBBox = function (path) { - var seglist = path.pathSegList; - var tot = seglist.numberOfItems; + var seglist = path.pathSegList; + var tot = seglist.numberOfItems; - var bounds = [[], []]; - var start = seglist.getItem(0); - var P0 = [start.x, start.y]; + var bounds = [[], []]; + var start = seglist.getItem(0); + var P0 = [start.x, start.y]; - var i; - for (i = 0; i < tot; i++) { - var seg = seglist.getItem(i); + var i; + for (i = 0; i < tot; i++) { + var seg = seglist.getItem(i); - if (seg.x === undef) { continue; } + if (seg.x === undef) { continue; } - // Add actual points to limits - bounds[0].push(P0[0]); - bounds[1].push(P0[1]); + // Add actual points to limits + bounds[0].push(P0[0]); + bounds[1].push(P0[1]); - if (seg.x1) { - var P1 = [seg.x1, seg.y1], - P2 = [seg.x2, seg.y2], - P3 = [seg.x, seg.y]; + if (seg.x1) { + var P1 = [seg.x1, seg.y1], + P2 = [seg.x2, seg.y2], + P3 = [seg.x, seg.y]; - var j; - for (j = 0; j < 2; j++) { - var calc = function (t) { - return Math.pow(1 - t, 3) * P0[j] + - 3 * Math.pow(1 - t, 2) * t * P1[j] + - 3 * (1 - t) * Math.pow(t, 2) * P2[j] + - Math.pow(t, 3) * P3[j]; - }; + var j; + for (j = 0; j < 2; j++) { + var calc = function (t) { + return Math.pow(1 - t, 3) * P0[j] + + 3 * Math.pow(1 - t, 2) * t * P1[j] + + 3 * (1 - t) * Math.pow(t, 2) * P2[j] + + Math.pow(t, 3) * P3[j]; + }; - var b = 6 * P0[j] - 12 * P1[j] + 6 * P2[j]; - var a = -3 * P0[j] + 9 * P1[j] - 9 * P2[j] + 3 * P3[j]; - var c = 3 * P1[j] - 3 * P0[j]; + var b = 6 * P0[j] - 12 * P1[j] + 6 * P2[j]; + var a = -3 * P0[j] + 9 * P1[j] - 9 * P2[j] + 3 * P3[j]; + var c = 3 * P1[j] - 3 * P0[j]; - if (a === 0) { - if (b === 0) { - continue; - } - var t = -c / b; - if (t > 0 && t < 1) { - bounds[j].push(calc(t)); - } - continue; - } - var b2ac = Math.pow(b, 2) - 4 * c * a; - if (b2ac < 0) { continue; } - var t1 = (-b + Math.sqrt(b2ac)) / (2 * a); - if (t1 > 0 && t1 < 1) { bounds[j].push(calc(t1)); } - var t2 = (-b - Math.sqrt(b2ac)) / (2 * a); - if (t2 > 0 && t2 < 1) { bounds[j].push(calc(t2)); } - } - P0 = P3; - } else { - bounds[0].push(seg.x); - bounds[1].push(seg.y); - } - } + if (a === 0) { + if (b === 0) { + continue; + } + var t = -c / b; + if (t > 0 && t < 1) { + bounds[j].push(calc(t)); + } + continue; + } + var b2ac = Math.pow(b, 2) - 4 * c * a; + if (b2ac < 0) { continue; } + var t1 = (-b + Math.sqrt(b2ac)) / (2 * a); + if (t1 > 0 && t1 < 1) { bounds[j].push(calc(t1)); } + var t2 = (-b - Math.sqrt(b2ac)) / (2 * a); + if (t2 > 0 && t2 < 1) { bounds[j].push(calc(t2)); } + } + P0 = P3; + } else { + bounds[0].push(seg.x); + bounds[1].push(seg.y); + } + } - var x = Math.min.apply(null, bounds[0]); - var w = Math.max.apply(null, bounds[0]) - x; - var y = Math.min.apply(null, bounds[1]); - var h = Math.max.apply(null, bounds[1]) - y; - return { - 'x': x, - 'y': y, - 'width': w, - 'height': h - }; + var x = Math.min.apply(null, bounds[0]); + var w = Math.max.apply(null, bounds[0]) - x; + var y = Math.min.apply(null, bounds[1]); + var h = Math.max.apply(null, bounds[1]) - y; + return { + 'x': x, + 'y': y, + 'width': w, + 'height': h + }; }; // Function: groupBBFix @@ -457,42 +457,42 @@ svgedit.utilities.getPathBBox = function (path) { // Parameters: // selected - Container or DOM element function groupBBFix (selected) { - if (svgedit.browser.supportsHVLineContainerBBox()) { - try { return selected.getBBox(); } catch (e) {} - } - var ref = $.data(selected, 'ref'); - var matched = null; - var ret, copy; + if (svgedit.browser.supportsHVLineContainerBBox()) { + try { return selected.getBBox(); } catch (e) {} + } + var ref = $.data(selected, 'ref'); + var matched = null; + var ret, copy; - if (ref) { - copy = $(ref).children().clone().attr('visibility', 'hidden'); - $(svgroot_).append(copy); - matched = copy.filter('line, path'); - } else { - matched = $(selected).find('line, path'); - } + if (ref) { + copy = $(ref).children().clone().attr('visibility', 'hidden'); + $(svgroot_).append(copy); + matched = copy.filter('line, path'); + } else { + matched = $(selected).find('line, path'); + } - var issue = false; - if (matched.length) { - matched.each(function () { - var bb = this.getBBox(); - if (!bb.width || !bb.height) { - issue = true; - } - }); - if (issue) { - var elems = ref ? copy : $(selected).children(); - ret = getStrokedBBox(elems); // getStrokedBBox defined in svgcanvas - } else { - ret = selected.getBBox(); - } - } else { - ret = selected.getBBox(); - } - if (ref) { - copy.remove(); - } - return ret; + var issue = false; + if (matched.length) { + matched.each(function () { + var bb = this.getBBox(); + if (!bb.width || !bb.height) { + issue = true; + } + }); + if (issue) { + var elems = ref ? copy : $(selected).children(); + ret = getStrokedBBox(elems); // getStrokedBBox defined in svgcanvas + } else { + ret = selected.getBBox(); + } + } else { + ret = selected.getBBox(); + } + if (ref) { + copy.remove(); + } + return ret; } // Function: svgedit.utilities.getBBox @@ -502,83 +502,83 @@ function groupBBFix (selected) { // Parameters: // elem - Optional DOM element to get the BBox for svgedit.utilities.getBBox = function (elem) { - var selected = elem || editorContext_.geSelectedElements()[0]; - if (elem.nodeType !== 1) { return null; } - var ret = null; - var elname = selected.nodeName; + var selected = elem || editorContext_.geSelectedElements()[0]; + if (elem.nodeType !== 1) { return null; } + var ret = null; + var elname = selected.nodeName; - switch (elname) { - case 'text': - if (selected.textContent === '') { - selected.textContent = 'a'; // Some character needed for the selector to use. - ret = selected.getBBox(); - selected.textContent = ''; - } else { - if (selected.getBBox) { ret = selected.getBBox(); } - } - break; - case 'path': - if (!svgedit.browser.supportsPathBBox()) { - ret = svgedit.utilities.getPathBBox(selected); - } else { - if (selected.getBBox) { ret = selected.getBBox(); } - } - break; - case 'g': - case 'a': - ret = groupBBFix(selected); - break; - default: + switch (elname) { + case 'text': + if (selected.textContent === '') { + selected.textContent = 'a'; // Some character needed for the selector to use. + ret = selected.getBBox(); + selected.textContent = ''; + } else { + if (selected.getBBox) { ret = selected.getBBox(); } + } + break; + case 'path': + if (!svgedit.browser.supportsPathBBox()) { + ret = svgedit.utilities.getPathBBox(selected); + } else { + if (selected.getBBox) { ret = selected.getBBox(); } + } + break; + case 'g': + case 'a': + ret = groupBBFix(selected); + break; + default: - if (elname === 'use') { - ret = groupBBFix(selected, true); - } - if (elname === 'use' || (elname === 'foreignObject' && svgedit.browser.isWebkit())) { - if (!ret) { ret = selected.getBBox(); } - // This is resolved in later versions of webkit, perhaps we should - // have a featured detection for correct 'use' behavior? - // —————————— - if (!svgedit.browser.isWebkit()) { - var bb = {}; - bb.width = ret.width; - bb.height = ret.height; - bb.x = ret.x + parseFloat(selected.getAttribute('x') || 0); - bb.y = ret.y + parseFloat(selected.getAttribute('y') || 0); - ret = bb; - } - } else if (~visElemsArr.indexOf(elname)) { - if (selected) { - try { - ret = selected.getBBox(); - } catch (err) { - // tspan (and textPath apparently) have no `getBBox` in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=937268 - // Re: Chrome returning bbox for containing text element, see: https://bugs.chromium.org/p/chromium/issues/detail?id=349835 - var extent = selected.getExtentOfChar(0); // pos+dimensions of the first glyph - var width = selected.getComputedTextLength(); // width of the tspan - ret = { - x: extent.x, - y: extent.y, - width: width, - height: extent.height - }; - } - } else { - // Check if element is child of a foreignObject - var fo = $(selected).closest('foreignObject'); - if (fo.length) { - if (fo[0].getBBox) { - ret = fo[0].getBBox(); - } - } - } - } - } - if (ret) { - ret = svgedit.utilities.bboxToObj(ret); - } + if (elname === 'use') { + ret = groupBBFix(selected, true); + } + if (elname === 'use' || (elname === 'foreignObject' && svgedit.browser.isWebkit())) { + if (!ret) { ret = selected.getBBox(); } + // This is resolved in later versions of webkit, perhaps we should + // have a featured detection for correct 'use' behavior? + // —————————— + if (!svgedit.browser.isWebkit()) { + var bb = {}; + bb.width = ret.width; + bb.height = ret.height; + bb.x = ret.x + parseFloat(selected.getAttribute('x') || 0); + bb.y = ret.y + parseFloat(selected.getAttribute('y') || 0); + ret = bb; + } + } else if (~visElemsArr.indexOf(elname)) { + if (selected) { + try { + ret = selected.getBBox(); + } catch (err) { + // tspan (and textPath apparently) have no `getBBox` in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=937268 + // Re: Chrome returning bbox for containing text element, see: https://bugs.chromium.org/p/chromium/issues/detail?id=349835 + var extent = selected.getExtentOfChar(0); // pos+dimensions of the first glyph + var width = selected.getComputedTextLength(); // width of the tspan + ret = { + x: extent.x, + y: extent.y, + width: width, + height: extent.height + }; + } + } else { + // Check if element is child of a foreignObject + var fo = $(selected).closest('foreignObject'); + if (fo.length) { + if (fo[0].getBBox) { + ret = fo[0].getBBox(); + } + } + } + } + } + if (ret) { + ret = svgedit.utilities.bboxToObj(ret); + } - // get the bounding box from the DOM (which is in that element's coordinate system) - return ret; + // get the bounding box from the DOM (which is in that element's coordinate system) + return ret; }; // Function: getPathDFromSegments @@ -591,18 +591,18 @@ svgedit.utilities.getBBox = function (elem) { // Returns: // The converted path d attribute. svgedit.utilities.getPathDFromSegments = function (pathSegments) { - var d = ''; + var d = ''; - $.each(pathSegments, function (j, seg) { - var i; - var pts = seg[1]; - d += seg[0]; - for (i = 0; i < pts.length; i += 2) { - d += (pts[i] + ',' + pts[i + 1]) + ' '; - } - }); + $.each(pathSegments, function (j, seg) { + var i; + var pts = seg[1]; + d += seg[0]; + for (i = 0; i < pts.length; i += 2) { + d += (pts[i] + ',' + pts[i + 1]) + ' '; + } + }); - return d; + return d; }; // Function: getPathDFromElement @@ -614,80 +614,80 @@ svgedit.utilities.getPathDFromSegments = function (pathSegments) { // Returns: // The path d attribute or undefined if the element type is unknown. svgedit.utilities.getPathDFromElement = function (elem) { - // Possibly the cubed root of 6, but 1.81 works best - var num = 1.81; - var d, a, rx, ry; - switch (elem.tagName) { - case 'ellipse': - case 'circle': - a = $(elem).attr(['rx', 'ry', 'cx', 'cy']); - var cx = a.cx, cy = a.cy; - rx = a.rx; - ry = a.ry; - if (elem.tagName === 'circle') { - rx = ry = $(elem).attr('r'); - } + // Possibly the cubed root of 6, but 1.81 works best + var num = 1.81; + var d, a, rx, ry; + switch (elem.tagName) { + case 'ellipse': + case 'circle': + a = $(elem).attr(['rx', 'ry', 'cx', 'cy']); + var cx = a.cx, cy = a.cy; + rx = a.rx; + ry = a.ry; + if (elem.tagName === 'circle') { + rx = ry = $(elem).attr('r'); + } - d = svgedit.utilities.getPathDFromSegments([ - ['M', [(cx - rx), (cy)]], - ['C', [(cx - rx), (cy - ry / num), (cx - rx / num), (cy - ry), (cx), (cy - ry)]], - ['C', [(cx + rx / num), (cy - ry), (cx + rx), (cy - ry / num), (cx + rx), (cy)]], - ['C', [(cx + rx), (cy + ry / num), (cx + rx / num), (cy + ry), (cx), (cy + ry)]], - ['C', [(cx - rx / num), (cy + ry), (cx - rx), (cy + ry / num), (cx - rx), (cy)]], - ['Z', []] - ]); - break; - case 'path': - d = elem.getAttribute('d'); - break; - case 'line': - a = $(elem).attr(['x1', 'y1', 'x2', 'y2']); - d = 'M' + a.x1 + ',' + a.y1 + 'L' + a.x2 + ',' + a.y2; - break; - case 'polyline': - d = 'M' + elem.getAttribute('points'); - break; - case 'polygon': - d = 'M' + elem.getAttribute('points') + ' Z'; - break; - case 'rect': - var r = $(elem).attr(['rx', 'ry']); - rx = r.rx; - ry = r.ry; - var b = elem.getBBox(); - var x = b.x, y = b.y, w = b.width, h = b.height; - num = 4 - num; // Why? Because! + d = svgedit.utilities.getPathDFromSegments([ + ['M', [(cx - rx), (cy)]], + ['C', [(cx - rx), (cy - ry / num), (cx - rx / num), (cy - ry), (cx), (cy - ry)]], + ['C', [(cx + rx / num), (cy - ry), (cx + rx), (cy - ry / num), (cx + rx), (cy)]], + ['C', [(cx + rx), (cy + ry / num), (cx + rx / num), (cy + ry), (cx), (cy + ry)]], + ['C', [(cx - rx / num), (cy + ry), (cx - rx), (cy + ry / num), (cx - rx), (cy)]], + ['Z', []] + ]); + break; + case 'path': + d = elem.getAttribute('d'); + break; + case 'line': + a = $(elem).attr(['x1', 'y1', 'x2', 'y2']); + d = 'M' + a.x1 + ',' + a.y1 + 'L' + a.x2 + ',' + a.y2; + break; + case 'polyline': + d = 'M' + elem.getAttribute('points'); + break; + case 'polygon': + d = 'M' + elem.getAttribute('points') + ' Z'; + break; + case 'rect': + var r = $(elem).attr(['rx', 'ry']); + rx = r.rx; + ry = r.ry; + var b = elem.getBBox(); + var x = b.x, y = b.y, w = b.width, h = b.height; + num = 4 - num; // Why? Because! - if (!rx && !ry) { - // Regular rect - d = svgedit.utilities.getPathDFromSegments([ - ['M', [x, y]], - ['L', [x + w, y]], - ['L', [x + w, y + h]], - ['L', [x, y + h]], - ['L', [x, y]], - ['Z', []] - ]); - } else { - d = svgedit.utilities.getPathDFromSegments([ - ['M', [x, y + ry]], - ['C', [x, y + ry / num, x + rx / num, y, x + rx, y]], - ['L', [x + w - rx, y]], - ['C', [x + w - rx / num, y, x + w, y + ry / num, x + w, y + ry]], - ['L', [x + w, y + h - ry]], - ['C', [x + w, y + h - ry / num, x + w - rx / num, y + h, x + w - rx, y + h]], - ['L', [x + rx, y + h]], - ['C', [x + rx / num, y + h, x, y + h - ry / num, x, y + h - ry]], - ['L', [x, y + ry]], - ['Z', []] - ]); - } - break; - default: - break; - } + if (!rx && !ry) { + // Regular rect + d = svgedit.utilities.getPathDFromSegments([ + ['M', [x, y]], + ['L', [x + w, y]], + ['L', [x + w, y + h]], + ['L', [x, y + h]], + ['L', [x, y]], + ['Z', []] + ]); + } else { + d = svgedit.utilities.getPathDFromSegments([ + ['M', [x, y + ry]], + ['C', [x, y + ry / num, x + rx / num, y, x + rx, y]], + ['L', [x + w - rx, y]], + ['C', [x + w - rx / num, y, x + w, y + ry / num, x + w, y + ry]], + ['L', [x + w, y + h - ry]], + ['C', [x + w, y + h - ry / num, x + w - rx / num, y + h, x + w - rx, y + h]], + ['L', [x + rx, y + h]], + ['C', [x + rx / num, y + h, x, y + h - ry / num, x, y + h - ry]], + ['L', [x, y + ry]], + ['Z', []] + ]); + } + break; + default: + break; + } - return d; + return d; }; // Function: getExtraAttributesForConvertToPath @@ -699,16 +699,16 @@ svgedit.utilities.getPathDFromElement = function (elem) { // Returns: // An object with attributes. svgedit.utilities.getExtraAttributesForConvertToPath = function (elem) { - var attrs = {}; - // TODO: make this list global so that we can properly maintain it - // TODO: what about @transform, @clip-rule, @fill-rule, etc? - $.each(['marker-start', 'marker-end', 'marker-mid', 'filter', 'clip-path'], function () { - var a = elem.getAttribute(this); - if (a) { - attrs[this] = a; - } - }); - return attrs; + var attrs = {}; + // TODO: make this list global so that we can properly maintain it + // TODO: what about @transform, @clip-rule, @fill-rule, etc? + $.each(['marker-start', 'marker-end', 'marker-mid', 'filter', 'clip-path'], function () { + var a = elem.getAttribute(this); + if (a) { + attrs[this] = a; + } + }); + return attrs; }; // Function: getBBoxOfElementAsPath @@ -722,37 +722,37 @@ svgedit.utilities.getExtraAttributesForConvertToPath = function (elem) { // Returns: // The resulting path's bounding box object. svgedit.utilities.getBBoxOfElementAsPath = function (elem, addSvgElementFromJson, pathActions) { - var path = addSvgElementFromJson({ - 'element': 'path', - 'attr': svgedit.utilities.getExtraAttributesForConvertToPath(elem) - }); + var path = addSvgElementFromJson({ + 'element': 'path', + 'attr': svgedit.utilities.getExtraAttributesForConvertToPath(elem) + }); - var eltrans = elem.getAttribute('transform'); - if (eltrans) { - path.setAttribute('transform', eltrans); - } + var eltrans = elem.getAttribute('transform'); + if (eltrans) { + path.setAttribute('transform', eltrans); + } - var parent = elem.parentNode; - if (elem.nextSibling) { - parent.insertBefore(path, elem); - } else { - parent.appendChild(path); - } + var parent = elem.parentNode; + if (elem.nextSibling) { + parent.insertBefore(path, elem); + } else { + parent.appendChild(path); + } - var d = svgedit.utilities.getPathDFromElement(elem); - if (d) path.setAttribute('d', d); - else path.parentNode.removeChild(path); + var d = svgedit.utilities.getPathDFromElement(elem); + if (d) path.setAttribute('d', d); + else path.parentNode.removeChild(path); - // Get the correct BBox of the new path, then discard it - pathActions.resetOrientation(path); - var bb = false; - try { - bb = path.getBBox(); - } catch (e) { - // Firefox fails - } - path.parentNode.removeChild(path); - return bb; + // Get the correct BBox of the new path, then discard it + pathActions.resetOrientation(path); + var bb = false; + try { + bb = path.getBBox(); + } catch (e) { + // Firefox fails + } + path.parentNode.removeChild(path); + return bb; }; // Function: convertToPath @@ -771,61 +771,61 @@ svgedit.utilities.getBBoxOfElementAsPath = function (elem, addSvgElementFromJson // Returns: // The converted path element or null if the DOM element was not recognized. svgedit.utilities.convertToPath = function (elem, attrs, addSvgElementFromJson, pathActions, clearSelection, addToSelection, history, addCommandToHistory) { - var batchCmd = new history.BatchCommand('Convert element to Path'); + var batchCmd = new history.BatchCommand('Convert element to Path'); - // Any attribute on the element not covered by the passed-in attributes - attrs = $.extend({}, attrs, svgedit.utilities.getExtraAttributesForConvertToPath(elem)); + // Any attribute on the element not covered by the passed-in attributes + attrs = $.extend({}, attrs, svgedit.utilities.getExtraAttributesForConvertToPath(elem)); - var path = addSvgElementFromJson({ - 'element': 'path', - 'attr': attrs - }); + var path = addSvgElementFromJson({ + 'element': 'path', + 'attr': attrs + }); - var eltrans = elem.getAttribute('transform'); - if (eltrans) { - path.setAttribute('transform', eltrans); - } + var eltrans = elem.getAttribute('transform'); + if (eltrans) { + path.setAttribute('transform', eltrans); + } - var id = elem.id; - var parent = elem.parentNode; - if (elem.nextSibling) { - parent.insertBefore(path, elem); - } else { - parent.appendChild(path); - } + var id = elem.id; + var parent = elem.parentNode; + if (elem.nextSibling) { + parent.insertBefore(path, elem); + } else { + parent.appendChild(path); + } - var d = svgedit.utilities.getPathDFromElement(elem); - if (d) { - path.setAttribute('d', d); + var d = svgedit.utilities.getPathDFromElement(elem); + if (d) { + path.setAttribute('d', d); - // Replace the current element with the converted one + // Replace the current element with the converted one - // Reorient if it has a matrix - if (eltrans) { - var tlist = svgedit.transformlist.getTransformList(path); - if (svgedit.math.hasMatrixTransform(tlist)) { - pathActions.resetOrientation(path); - } - } + // Reorient if it has a matrix + if (eltrans) { + var tlist = svgedit.transformlist.getTransformList(path); + if (svgedit.math.hasMatrixTransform(tlist)) { + pathActions.resetOrientation(path); + } + } - var nextSibling = elem.nextSibling; - batchCmd.addSubCommand(new history.RemoveElementCommand(elem, nextSibling, parent)); - batchCmd.addSubCommand(new history.InsertElementCommand(path)); + var nextSibling = elem.nextSibling; + batchCmd.addSubCommand(new history.RemoveElementCommand(elem, nextSibling, parent)); + batchCmd.addSubCommand(new history.InsertElementCommand(path)); - clearSelection(); - elem.parentNode.removeChild(elem); - path.setAttribute('id', id); - path.removeAttribute('visibility'); - addToSelection([path], true); + clearSelection(); + elem.parentNode.removeChild(elem); + path.setAttribute('id', id); + path.removeAttribute('visibility'); + addToSelection([path], true); - addCommandToHistory(batchCmd); + addCommandToHistory(batchCmd); - return path; - } else { - // the elem.tagName was not recognized, so no "d" attribute. Remove it, so we've haven't changed anything. - path.parentNode.removeChild(path); - return null; - } + return path; + } else { + // the elem.tagName was not recognized, so no "d" attribute. Remove it, so we've haven't changed anything. + path.parentNode.removeChild(path); + return null; + } }; // Function: bBoxCanBeOptimizedOverNativeGetBBox @@ -851,10 +851,10 @@ svgedit.utilities.convertToPath = function (elem, attrs, addSvgElementFromJson, // Returns: // True if the bbox can be optimized. function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasMatrixTransform) { - var angleModulo90 = angle % 90; - var closeTo90 = angleModulo90 < -89.99 || angleModulo90 > 89.99; - var closeTo0 = angleModulo90 > -0.001 && angleModulo90 < 0.001; - return hasMatrixTransform || !(closeTo0 || closeTo90); + var angleModulo90 = angle % 90; + var closeTo90 = angleModulo90 < -89.99 || angleModulo90 > 89.99; + var closeTo0 = angleModulo90 > -0.001 && angleModulo90 < 0.001; + return hasMatrixTransform || !(closeTo0 || closeTo90); } // Function: getBBoxWithTransform @@ -868,64 +868,64 @@ function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasMatrixTransform) { // Returns: // A single bounding box object svgedit.utilities.getBBoxWithTransform = function (elem, addSvgElementFromJson, pathActions) { - // 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). + // 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 bb = svgedit.utilities.getBBox(elem); - if (!bb) { - return null; - } + if (!bb) { + return null; + } - var tlist = svgedit.transformlist.getTransformList(elem); - var angle = svgedit.utilities.getRotationAngleFromTransformList(tlist); - var hasMatrixTransform = svgedit.math.hasMatrixTransform(tlist); + var tlist = svgedit.transformlist.getTransformList(elem); + var angle = svgedit.utilities.getRotationAngleFromTransformList(tlist); + var hasMatrixTransform = svgedit.math.hasMatrixTransform(tlist); - if (angle || hasMatrixTransform) { - var goodBb = false; - if (bBoxCanBeOptimizedOverNativeGetBBox(angle, hasMatrixTransform)) { - // Get the BBox from the raw path for these elements - // TODO: why ellipse and not circle - var elemNames = ['ellipse', 'path', 'line', 'polyline', 'polygon']; - if (elemNames.indexOf(elem.tagName) >= 0) { - bb = goodBb = svgedit.utilities.getBBoxOfElementAsPath(elem, addSvgElementFromJson, pathActions); - } else if (elem.tagName === 'rect') { - // Look for radius - var rx = elem.getAttribute('rx'); - var ry = elem.getAttribute('ry'); - if (rx || ry) { - bb = goodBb = svgedit.utilities.getBBoxOfElementAsPath(elem, addSvgElementFromJson, pathActions); - } - } - } + if (angle || hasMatrixTransform) { + var goodBb = false; + if (bBoxCanBeOptimizedOverNativeGetBBox(angle, hasMatrixTransform)) { + // Get the BBox from the raw path for these elements + // TODO: why ellipse and not circle + var elemNames = ['ellipse', 'path', 'line', 'polyline', 'polygon']; + if (elemNames.indexOf(elem.tagName) >= 0) { + bb = goodBb = svgedit.utilities.getBBoxOfElementAsPath(elem, addSvgElementFromJson, pathActions); + } else if (elem.tagName === 'rect') { + // Look for radius + var rx = elem.getAttribute('rx'); + var ry = elem.getAttribute('ry'); + if (rx || ry) { + bb = goodBb = svgedit.utilities.getBBoxOfElementAsPath(elem, addSvgElementFromJson, pathActions); + } + } + } - if (!goodBb) { - var matrix = svgedit.math.transformListToTransform(tlist).matrix; - bb = svgedit.math.transformBox(bb.x, bb.y, bb.width, bb.height, matrix).aabox; + if (!goodBb) { + var matrix = svgedit.math.transformListToTransform(tlist).matrix; + bb = svgedit.math.transformBox(bb.x, bb.y, bb.width, bb.height, matrix).aabox; - // Old technique that was exceedingly slow with large documents. - // - // Accurate way to get BBox of rotated element in Firefox: - // Put element in group and get its BBox - // - // Must use clone else FF freaks out - // var clone = elem.cloneNode(true); - // var g = document.createElementNS(NS.SVG, 'g'); - // var parent = elem.parentNode; - // parent.appendChild(g); - // g.appendChild(clone); - // var bb2 = svgedit.utilities.bboxToObj(g.getBBox()); - // parent.removeChild(g); - } - } - return bb; + // Old technique that was exceedingly slow with large documents. + // + // Accurate way to get BBox of rotated element in Firefox: + // Put element in group and get its BBox + // + // Must use clone else FF freaks out + // var clone = elem.cloneNode(true); + // var g = document.createElementNS(NS.SVG, 'g'); + // var parent = elem.parentNode; + // parent.appendChild(g); + // g.appendChild(clone); + // var bb2 = svgedit.utilities.bboxToObj(g.getBBox()); + // parent.removeChild(g); + } + } + return bb; }; // TODO: This is problematic with large stroke-width and, for example, a single horizontal line. The calculated BBox extends way beyond left and right sides. function getStrokeOffsetForBBox (elem) { - var sw = elem.getAttribute('stroke-width'); - return (!isNaN(sw) && elem.getAttribute('stroke') !== 'none') ? sw / 2 : 0; + var sw = elem.getAttribute('stroke-width'); + return (!isNaN(sw) && elem.getAttribute('stroke') !== 'none') ? sw / 2 : 0; } // Function: getStrokedBBox @@ -939,54 +939,54 @@ function getStrokeOffsetForBBox (elem) { // Returns: // A single bounding box object svgedit.utilities.getStrokedBBox = function (elems, addSvgElementFromJson, pathActions) { - if (!elems || !elems.length) { return false; } + if (!elems || !elems.length) { return false; } - var fullBb; - $.each(elems, function () { - if (fullBb) { return; } - if (!this.parentNode) { return; } - fullBb = svgedit.utilities.getBBoxWithTransform(this, addSvgElementFromJson, pathActions); - }); + var fullBb; + $.each(elems, function () { + if (fullBb) { return; } + if (!this.parentNode) { return; } + fullBb = svgedit.utilities.getBBoxWithTransform(this, addSvgElementFromJson, pathActions); + }); - // This shouldn't ever happen... - if (fullBb === undefined) { return null; } + // This shouldn't ever happen... + if (fullBb === undefined) { return null; } - // fullBb doesn't include the stoke, so this does no good! - // if (elems.length == 1) return fullBb; + // fullBb doesn't include the stoke, so this does no good! + // if (elems.length == 1) return fullBb; - var maxX = fullBb.x + fullBb.width; - var maxY = fullBb.y + fullBb.height; - var minX = fullBb.x; - var minY = fullBb.y; + var maxX = fullBb.x + fullBb.width; + var maxY = fullBb.y + fullBb.height; + var minX = fullBb.x; + var minY = fullBb.y; - // If only one elem, don't call the potentially slow getBBoxWithTransform method again. - if (elems.length === 1) { - var offset = getStrokeOffsetForBBox(elems[0]); - minX -= offset; - minY -= offset; - maxX += offset; - maxY += offset; - } else { - $.each(elems, function (i, elem) { - var curBb = svgedit.utilities.getBBoxWithTransform(elem, addSvgElementFromJson, pathActions); - if (curBb) { - var offset = getStrokeOffsetForBBox(elem); - minX = Math.min(minX, curBb.x - offset); - minY = Math.min(minY, curBb.y - offset); - // TODO: The old code had this test for max, but not min. I suspect this test should be for both min and max - if (elem.nodeType === 1) { - maxX = Math.max(maxX, curBb.x + curBb.width + offset); - maxY = Math.max(maxY, curBb.y + curBb.height + offset); - } - } - }); - } + // If only one elem, don't call the potentially slow getBBoxWithTransform method again. + if (elems.length === 1) { + var offset = getStrokeOffsetForBBox(elems[0]); + minX -= offset; + minY -= offset; + maxX += offset; + maxY += offset; + } else { + $.each(elems, function (i, elem) { + var curBb = svgedit.utilities.getBBoxWithTransform(elem, addSvgElementFromJson, pathActions); + if (curBb) { + var offset = getStrokeOffsetForBBox(elem); + minX = Math.min(minX, curBb.x - offset); + minY = Math.min(minY, curBb.y - offset); + // TODO: The old code had this test for max, but not min. I suspect this test should be for both min and max + if (elem.nodeType === 1) { + maxX = Math.max(maxX, curBb.x + curBb.width + offset); + maxY = Math.max(maxY, curBb.y + curBb.height + offset); + } + } + }); + } - fullBb.x = minX; - fullBb.y = minY; - fullBb.width = maxX - minX; - fullBb.height = maxY - minY; - return fullBb; + fullBb.x = minX; + fullBb.y = minY; + fullBb.width = maxX - minX; + fullBb.height = maxY - minY; + return fullBb; }; // Function: svgedit.utilities.getRotationAngleFromTransformList @@ -999,16 +999,16 @@ svgedit.utilities.getStrokedBBox = function (elems, addSvgElementFromJson, pathA // Returns: // Float with the angle in degrees or radians svgedit.utilities.getRotationAngleFromTransformList = function (tlist, toRad) { - if (!tlist) { return 0; } // elements have no tlist - var N = tlist.numberOfItems; - var i; - for (i = 0; i < N; ++i) { - var xform = tlist.getItem(i); - if (xform.type === 4) { - return toRad ? xform.angle * Math.PI / 180.0 : xform.angle; - } - } - return 0.0; + if (!tlist) { return 0; } // elements have no tlist + var N = tlist.numberOfItems; + var i; + for (i = 0; i < N; ++i) { + var xform = tlist.getItem(i); + if (xform.type === 4) { + return toRad ? xform.angle * Math.PI / 180.0 : xform.angle; + } + } + return 0.0; }; // Function: svgedit.utilities.getRotationAngle @@ -1021,10 +1021,10 @@ svgedit.utilities.getRotationAngleFromTransformList = function (tlist, toRad) { // Returns: // Float with the angle in degrees or radians svgedit.utilities.getRotationAngle = function (elem, toRad) { - var selected = elem || editorContext_.getSelectedElements()[0]; - // find the rotation transform (if any) and set it - var tlist = svgedit.transformlist.getTransformList(selected); - return svgedit.utilities.getRotationAngleFromTransformList(tlist, toRad); + var selected = elem || editorContext_.getSelectedElements()[0]; + // find the rotation transform (if any) and set it + var tlist = svgedit.transformlist.getTransformList(selected); + return svgedit.utilities.getRotationAngleFromTransformList(tlist, toRad); }; // Function getRefElem @@ -1033,7 +1033,7 @@ svgedit.utilities.getRotationAngle = function (elem, toRad) { // Parameters: // attrVal - The attribute value as a string svgedit.utilities.getRefElem = function (attrVal) { - return svgedit.utilities.getElem(svgedit.utilities.getUrlFromAttr(attrVal).substr(1)); + return svgedit.utilities.getElem(svgedit.utilities.getUrlFromAttr(attrVal).substr(1)); }; // Function: getElem @@ -1042,25 +1042,25 @@ svgedit.utilities.getRefElem = function (attrVal) { // Parameters: // id - String with the element's new ID if (svgedit.browser.supportsSelectors()) { - svgedit.utilities.getElem = function (id) { - // querySelector lookup - return svgroot_.querySelector('#' + id); - }; + svgedit.utilities.getElem = function (id) { + // querySelector lookup + return svgroot_.querySelector('#' + id); + }; } else if (svgedit.browser.supportsXpath()) { - svgedit.utilities.getElem = function (id) { - // xpath lookup - return domdoc_.evaluate( - 'svg:svg[@id="svgroot"]//svg:*[@id="' + id + '"]', - domcontainer_, - function () { return svgedit.NS.SVG; }, - 9, - null).singleNodeValue; - }; + svgedit.utilities.getElem = function (id) { + // xpath lookup + return domdoc_.evaluate( + 'svg:svg[@id="svgroot"]//svg:*[@id="' + id + '"]', + domcontainer_, + function () { return svgedit.NS.SVG; }, + 9, + null).singleNodeValue; + }; } else { - svgedit.utilities.getElem = function (id) { - // jQuery lookup: twice as slow as xpath in FF - return $(svgroot_).find('[id=' + id + ']')[0]; - }; + svgedit.utilities.getElem = function (id) { + // jQuery lookup: twice as slow as xpath in FF + return $(svgroot_).find('[id=' + id + ']')[0]; + }; } // Function: assignAttributes @@ -1072,20 +1072,20 @@ if (svgedit.browser.supportsSelectors()) { // suspendLength - Optional integer of milliseconds to suspend redraw // unitCheck - Boolean to indicate the need to use svgedit.units.setUnitAttr svgedit.utilities.assignAttributes = function (node, attrs, suspendLength, unitCheck) { - var i; - for (i in attrs) { - var ns = (i.substr(0, 4) === 'xml:' - ? NS.XML - : i.substr(0, 6) === 'xlink:' ? NS.XLINK : null); + var i; + for (i in attrs) { + var ns = (i.substr(0, 4) === 'xml:' + ? NS.XML + : i.substr(0, 6) === 'xlink:' ? NS.XLINK : null); - if (ns) { - node.setAttributeNS(ns, i, attrs[i]); - } else if (!unitCheck) { - node.setAttribute(i, attrs[i]); - } else { - svgedit.units.setUnitAttr(node, i, attrs[i]); - } - } + if (ns) { + node.setAttributeNS(ns, i, attrs[i]); + } else if (!unitCheck) { + node.setAttribute(i, attrs[i]); + } else { + svgedit.units.setUnitAttr(node, i, attrs[i]); + } + } }; // Function: cleanupElement @@ -1094,51 +1094,51 @@ svgedit.utilities.assignAttributes = function (node, attrs, suspendLength, unitC // Parameters: // element - DOM element to clean up svgedit.utilities.cleanupElement = function (element) { - var defaults = { - 'fill-opacity': 1, - 'stop-opacity': 1, - 'opacity': 1, - 'stroke': 'none', - 'stroke-dasharray': 'none', - 'stroke-linejoin': 'miter', - 'stroke-linecap': 'butt', - 'stroke-opacity': 1, - 'stroke-width': 1, - 'rx': 0, - 'ry': 0 - }; + var defaults = { + 'fill-opacity': 1, + 'stop-opacity': 1, + 'opacity': 1, + 'stroke': 'none', + 'stroke-dasharray': 'none', + 'stroke-linejoin': 'miter', + 'stroke-linecap': 'butt', + 'stroke-opacity': 1, + 'stroke-width': 1, + 'rx': 0, + 'ry': 0 + }; - if (element.nodeName === 'ellipse') { - // Ellipse elements requires rx and ry attributes - delete defaults.rx; - delete defaults.ry; - } + if (element.nodeName === 'ellipse') { + // Ellipse elements requires rx and ry attributes + delete defaults.rx; + delete defaults.ry; + } - var attr; - for (attr in defaults) { - var val = defaults[attr]; - if (element.getAttribute(attr) === String(val)) { - element.removeAttribute(attr); - } - } + var attr; + for (attr in defaults) { + var val = defaults[attr]; + if (element.getAttribute(attr) === String(val)) { + element.removeAttribute(attr); + } + } }; // Function: snapToGrid // round value to for snapping // NOTE: This function did not move to svgutils.js since it depends on curConfig. svgedit.utilities.snapToGrid = function (value) { - var stepSize = editorContext_.getSnappingStep(); - var unit = editorContext_.getBaseUnit(); - if (unit !== 'px') { - stepSize *= svgedit.units.getTypeMap()[unit]; - } - value = Math.round(value / stepSize) * stepSize; - return value; + var stepSize = editorContext_.getSnappingStep(); + var unit = editorContext_.getBaseUnit(); + if (unit !== 'px') { + stepSize *= svgedit.units.getTypeMap()[unit]; + } + value = Math.round(value / stepSize) * stepSize; + return value; }; svgedit.utilities.preg_quote = function (str, delimiter) { - // From: http://phpjs.org/functions - return String(str).replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&'); + // From: http://phpjs.org/functions + return String(str).replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&'); }; /** @@ -1147,35 +1147,35 @@ svgedit.utilities.preg_quote = function (str, delimiter) { * @param {function} cb The callback to execute upon load. */ svgedit.utilities.executeAfterLoads = function (globalCheck, scripts, cb) { - return function () { - var args = arguments; - function endCallback () { - cb.apply(null, args); - } - if (window[globalCheck]) { - endCallback(); - } else { - scripts.reduceRight(function (oldFunc, script) { - return function () { - $.getScript(script, oldFunc); - }; - }, endCallback)(); - } - }; + return function () { + var args = arguments; + function endCallback () { + cb.apply(null, args); + } + if (window[globalCheck]) { + endCallback(); + } else { + scripts.reduceRight(function (oldFunc, script) { + return function () { + $.getScript(script, oldFunc); + }; + }, endCallback)(); + } + }; }; svgedit.utilities.buildCanvgCallback = function (callCanvg) { - return svgedit.utilities.executeAfterLoads('canvg', ['canvg/rgbcolor.js', 'canvg/canvg.js'], callCanvg); + return svgedit.utilities.executeAfterLoads('canvg', ['canvg/rgbcolor.js', 'canvg/canvg.js'], callCanvg); }; svgedit.utilities.buildJSPDFCallback = function (callJSPDF) { - return svgedit.utilities.executeAfterLoads('RGBColor', ['canvg/rgbcolor.js'], function () { - var arr = []; - if (!RGBColor || RGBColor.ok === undef) { // It's not our RGBColor, so we'll need to load it - arr.push('canvg/rgbcolor.js'); - } - svgedit.utilities.executeAfterLoads('jsPDF', arr.concat('jspdf/underscore-min.js', 'jspdf/jspdf.min.js', 'jspdf/jspdf.plugin.svgToPdf.js'), callJSPDF)(); - }); + return svgedit.utilities.executeAfterLoads('RGBColor', ['canvg/rgbcolor.js'], function () { + var arr = []; + if (!RGBColor || RGBColor.ok === undef) { // It's not our RGBColor, so we'll need to load it + arr.push('canvg/rgbcolor.js'); + } + svgedit.utilities.executeAfterLoads('jsPDF', arr.concat('jspdf/underscore-min.js', 'jspdf/jspdf.min.js', 'jspdf/jspdf.plugin.svgToPdf.js'), callJSPDF)(); + }); }; /** @@ -1183,7 +1183,7 @@ svgedit.utilities.buildJSPDFCallback = function (callJSPDF) { * @param img - The DOM element to prevent the click on */ svgedit.utilities.preventClickDefault = function (img) { - $(img).click(function (e) { e.preventDefault(); }); + $(img).click(function (e) { e.preventDefault(); }); }; /** @@ -1193,48 +1193,48 @@ svgedit.utilities.preventClickDefault = function (img) { * @returns {Element} */ svgedit.utilities.copyElem = function (el, getNextId) { - // manually create a copy of the element - var newEl = document.createElementNS(el.namespaceURI, el.nodeName); - $.each(el.attributes, function (i, attr) { - if (attr.localName !== '-moz-math-font-style') { - newEl.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value); - } - }); - // set the copied element's new id - newEl.removeAttribute('id'); - newEl.id = getNextId(); + // manually create a copy of the element + var newEl = document.createElementNS(el.namespaceURI, el.nodeName); + $.each(el.attributes, function (i, attr) { + if (attr.localName !== '-moz-math-font-style') { + newEl.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value); + } + }); + // set the copied element's new id + newEl.removeAttribute('id'); + newEl.id = getNextId(); - // 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') { - var fixedD = svgedit.utilities.convertPath(el); - newEl.setAttribute('d', fixedD); - } + // 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') { + var fixedD = svgedit.utilities.convertPath(el); + newEl.setAttribute('d', fixedD); + } - // now create copies of all children - $.each(el.childNodes, function (i, child) { - switch (child.nodeType) { - case 1: // element node - newEl.appendChild(svgedit.utilities.copyElem(child, getNextId)); - break; - case 3: // text node - newEl.textContent = child.nodeValue; - break; - default: - break; - } - }); + // now create copies of all children + $.each(el.childNodes, function (i, child) { + switch (child.nodeType) { + case 1: // element node + newEl.appendChild(svgedit.utilities.copyElem(child, getNextId)); + break; + case 3: // text node + newEl.textContent = child.nodeValue; + break; + default: + break; + } + }); - if ($(el).data('gsvg')) { - $(newEl).data('gsvg', newEl.firstChild); - } else if ($(el).data('symbol')) { - var ref = $(el).data('symbol'); - $(newEl).data('ref', ref).data('symbol', ref); - } else if (newEl.tagName === 'image') { - svgedit.utilities.preventClickDefault(newEl); - } + if ($(el).data('gsvg')) { + $(newEl).data('gsvg', newEl.firstChild); + } else if ($(el).data('symbol')) { + var ref = $(el).data('symbol'); + $(newEl).data('ref', ref).data('symbol', ref); + } else if (newEl.tagName === 'image') { + svgedit.utilities.preventClickDefault(newEl); + } - return newEl; + return newEl; }; /** @@ -1247,22 +1247,22 @@ svgedit.utilities.copyElem = function (el, getNextId) { * @returns {string} */ function pathDSegment (letter, points, morePoints, lastPoint) { - $.each(points, function (i, pnt) { - points[i] = svgedit.units.shortFloat(pnt); - }); - var segment = letter + points.join(' '); - if (morePoints) { - segment += ' ' + morePoints.join(' '); - } - if (lastPoint) { - segment += ' ' + svgedit.units.shortFloat(lastPoint); - } - return segment; + $.each(points, function (i, pnt) { + points[i] = svgedit.units.shortFloat(pnt); + }); + var segment = letter + points.join(' '); + if (morePoints) { + segment += ' ' + morePoints.join(' '); + } + if (lastPoint) { + segment += ' ' + svgedit.units.shortFloat(lastPoint); + } + return segment; } // 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', - 'H', 'h', 'V', 'v', 'S', 's', 'T', 't']; + 'H', 'h', 'V', 'v', 'S', 's', 'T', 't']; /** * TODO: move to pathActions.js when migrating rest of pathActions out of svgcanvas.js @@ -1272,156 +1272,156 @@ var pathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', * @returns {string} */ svgedit.utilities.convertPath = function (path, toRel) { - var i; - var segList = path.pathSegList; - var len = segList.numberOfItems; - var curx = 0, cury = 0; - var d = ''; - var lastM = null; + var i; + var segList = path.pathSegList; + var len = segList.numberOfItems; + var curx = 0, cury = 0; + var d = ''; + var lastM = null; - for (i = 0; i < len; ++i) { - var seg = segList.getItem(i); - // if these properties are not in the segment, set them to zero - var x = seg.x || 0, - y = seg.y || 0, - x1 = seg.x1 || 0, - y1 = seg.y1 || 0, - x2 = seg.x2 || 0, - y2 = seg.y2 || 0; + for (i = 0; i < len; ++i) { + var seg = segList.getItem(i); + // if these properties are not in the segment, set them to zero + var x = seg.x || 0, + y = seg.y || 0, + x1 = seg.x1 || 0, + 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 type = seg.pathSegType; + var letter = pathMap[type]['to' + (toRel ? 'Lower' : 'Upper') + 'Case'](); - switch (type) { - case 1: // z,Z closepath (Z/z) - d += 'z'; - if (lastM && !toRel) { - curx = lastM[0]; - cury = lastM[1]; - } - break; - case 12: // absolute horizontal line (H) - x -= curx; - // Fallthrough - case 13: // relative horizontal line (h) - if (toRel) { - curx += x; - letter = 'l'; - } else { - x += curx; - curx = x; - letter = 'L'; - } - // Convert to "line" for easier editing - d += pathDSegment(letter, [[x, cury]]); - break; - case 14: // absolute vertical line (V) - y -= cury; - // Fallthrough - case 15: // relative vertical line (v) - if (toRel) { - cury += y; - letter = 'l'; - } else { - y += cury; - cury = y; - letter = 'L'; - } - // Convert to "line" for easier editing - d += pathDSegment(letter, [[curx, y]]); - break; - case 2: // absolute move (M) - case 4: // absolute line (L) - case 18: // absolute smooth quad (T) - x -= curx; - y -= cury; - // Fallthrough - case 5: // relative line (l) - case 3: // relative move (m) - case 19: // relative smooth quad (t) - if (toRel) { - curx += x; - cury += y; - } else { - x += curx; - y += cury; - curx = x; - cury = y; - } - if (type === 2 || type === 3) { lastM = [curx, cury]; } + switch (type) { + case 1: // z,Z closepath (Z/z) + d += 'z'; + if (lastM && !toRel) { + curx = lastM[0]; + cury = lastM[1]; + } + break; + case 12: // absolute horizontal line (H) + x -= curx; + // Fallthrough + case 13: // relative horizontal line (h) + if (toRel) { + curx += x; + letter = 'l'; + } else { + x += curx; + curx = x; + letter = 'L'; + } + // Convert to "line" for easier editing + d += pathDSegment(letter, [[x, cury]]); + break; + case 14: // absolute vertical line (V) + y -= cury; + // Fallthrough + case 15: // relative vertical line (v) + if (toRel) { + cury += y; + letter = 'l'; + } else { + y += cury; + cury = y; + letter = 'L'; + } + // Convert to "line" for easier editing + d += pathDSegment(letter, [[curx, y]]); + break; + case 2: // absolute move (M) + case 4: // absolute line (L) + case 18: // absolute smooth quad (T) + x -= curx; + y -= cury; + // Fallthrough + case 5: // relative line (l) + case 3: // relative move (m) + case 19: // relative smooth quad (t) + if (toRel) { + curx += x; + cury += y; + } else { + x += curx; + y += cury; + curx = x; + cury = y; + } + if (type === 2 || type === 3) { lastM = [curx, cury]; } - d += pathDSegment(letter, [[x, y]]); - break; - case 6: // absolute cubic (C) - x -= curx; x1 -= curx; x2 -= curx; - y -= cury; y1 -= cury; y2 -= cury; - // Fallthrough - case 7: // relative cubic (c) - if (toRel) { - curx += x; - cury += y; - } else { - x += curx; x1 += curx; x2 += curx; - y += cury; y1 += cury; y2 += cury; - curx = x; - cury = y; - } - d += pathDSegment(letter, [[x1, y1], [x2, y2], [x, y]]); - break; - case 8: // absolute quad (Q) - x -= curx; x1 -= curx; - y -= cury; y1 -= cury; - // Fallthrough - case 9: // relative quad (q) - if (toRel) { - curx += x; - cury += y; - } else { - x += curx; x1 += curx; - y += cury; y1 += cury; - curx = x; - cury = y; - } - d += pathDSegment(letter, [[x1, y1], [x, y]]); - break; - case 10: // absolute elliptical arc (A) - x -= curx; - y -= cury; - // Fallthrough - case 11: // relative elliptical arc (a) - if (toRel) { - curx += x; - cury += y; - } else { - x += curx; - y += cury; - curx = x; - cury = y; - } - d += pathDSegment(letter, [[seg.r1, seg.r2]], [ - seg.angle, - (seg.largeArcFlag ? 1 : 0), - (seg.sweepFlag ? 1 : 0) - ], [x, y]); - break; - case 16: // absolute smooth cubic (S) - x -= curx; x2 -= curx; - y -= cury; y2 -= cury; - // Fallthrough - case 17: // relative smooth cubic (s) - if (toRel) { - curx += x; - cury += y; - } else { - x += curx; x2 += curx; - y += cury; y2 += cury; - curx = x; - cury = y; - } - d += pathDSegment(letter, [[x2, y2], [x, y]]); - break; - } // switch on path segment type - } // for each segment - return d; + d += pathDSegment(letter, [[x, y]]); + break; + case 6: // absolute cubic (C) + x -= curx; x1 -= curx; x2 -= curx; + y -= cury; y1 -= cury; y2 -= cury; + // Fallthrough + case 7: // relative cubic (c) + if (toRel) { + curx += x; + cury += y; + } else { + x += curx; x1 += curx; x2 += curx; + y += cury; y1 += cury; y2 += cury; + curx = x; + cury = y; + } + d += pathDSegment(letter, [[x1, y1], [x2, y2], [x, y]]); + break; + case 8: // absolute quad (Q) + x -= curx; x1 -= curx; + y -= cury; y1 -= cury; + // Fallthrough + case 9: // relative quad (q) + if (toRel) { + curx += x; + cury += y; + } else { + x += curx; x1 += curx; + y += cury; y1 += cury; + curx = x; + cury = y; + } + d += pathDSegment(letter, [[x1, y1], [x, y]]); + break; + case 10: // absolute elliptical arc (A) + x -= curx; + y -= cury; + // Fallthrough + case 11: // relative elliptical arc (a) + if (toRel) { + curx += x; + cury += y; + } else { + x += curx; + y += cury; + curx = x; + cury = y; + } + d += pathDSegment(letter, [[seg.r1, seg.r2]], [ + seg.angle, + (seg.largeArcFlag ? 1 : 0), + (seg.sweepFlag ? 1 : 0) + ], [x, y]); + break; + case 16: // absolute smooth cubic (S) + x -= curx; x2 -= curx; + y -= cury; y2 -= cury; + // Fallthrough + case 17: // relative smooth cubic (s) + if (toRel) { + curx += x; + cury += y; + } else { + x += curx; x2 += curx; + y += cury; y2 += cury; + curx = x; + cury = y; + } + d += pathDSegment(letter, [[x2, y2], [x, y]]); + break; + } // switch on path segment type + } // for each segment + return d; }; }()); diff --git a/editor/touch.js b/editor/touch.js index 0f4c2423..82f11a4e 100644 --- a/editor/touch.js +++ b/editor/touch.js @@ -1,32 +1,32 @@ /* eslint-disable no-var */ // http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/ function touchHandler (event) { - 'use strict'; + 'use strict'; - var simulatedEvent, - touches = event.changedTouches, - first = touches[0], - type = ''; - switch (event.type) { - case 'touchstart': type = 'mousedown'; break; - case 'touchmove': type = 'mousemove'; break; - case 'touchend': type = 'mouseup'; break; - default: return; - } + var simulatedEvent, + touches = event.changedTouches, + first = touches[0], + type = ''; + switch (event.type) { + case 'touchstart': type = 'mousedown'; break; + case 'touchmove': type = 'mousemove'; break; + case 'touchend': type = 'mouseup'; break; + default: return; + } - // initMouseEvent(type, canBubble, cancelable, view, clickCount, - // screenX, screenY, clientX, clientY, ctrlKey, - // altKey, shiftKey, metaKey, button, relatedTarget); + // initMouseEvent(type, canBubble, cancelable, view, clickCount, + // screenX, screenY, clientX, clientY, ctrlKey, + // altKey, shiftKey, metaKey, button, relatedTarget); - simulatedEvent = document.createEvent('MouseEvent'); - simulatedEvent.initMouseEvent(type, true, true, window, 1, - first.screenX, first.screenY, - first.clientX, first.clientY, false, - false, false, false, 0/* left */, null); - if (touches.length < 2) { - first.target.dispatchEvent(simulatedEvent); - event.preventDefault(); - } + simulatedEvent = document.createEvent('MouseEvent'); + simulatedEvent.initMouseEvent(type, true, true, window, 1, + first.screenX, first.screenY, + first.clientX, first.clientY, false, + false, false, false, 0/* left */, null); + if (touches.length < 2) { + first.target.dispatchEvent(simulatedEvent); + event.preventDefault(); + } } document.addEventListener('touchstart', touchHandler, true); diff --git a/editor/units.js b/editor/units.js index f78de469..d0123082 100644 --- a/editor/units.js +++ b/editor/units.js @@ -16,7 +16,7 @@ 'use strict'; if (!svgedit.units) { - svgedit.units = {}; + svgedit.units = {}; } var NS = svgedit.NS; @@ -26,15 +26,15 @@ var unitAttrs = ['r', 'radius'].concat(wAttrs, hAttrs); // unused /* var unitNumMap = { - '%': 2, - 'em': 3, - 'ex': 4, - 'px': 5, - 'cm': 6, - 'mm': 7, - 'in': 8, - 'pt': 9, - 'pc': 10 + '%': 2, + 'em': 3, + 'ex': 4, + 'px': 5, + 'cm': 6, + 'mm': 7, + 'in': 8, + 'pt': 9, + 'pc': 10 }; */ // Container of elements. @@ -63,31 +63,31 @@ var typeMap_ = {}; * elementContainer - an object implementing the ElementContainer interface. */ svgedit.units.init = function (elementContainer) { - elementContainer_ = elementContainer; + elementContainer_ = elementContainer; - // Get correct em/ex values by creating a temporary SVG. - var svg = document.createElementNS(NS.SVG, 'svg'); - document.body.appendChild(svg); - var rect = document.createElementNS(NS.SVG, 'rect'); - rect.setAttribute('width', '1em'); - rect.setAttribute('height', '1ex'); - rect.setAttribute('x', '1in'); - svg.appendChild(rect); - var bb = rect.getBBox(); - document.body.removeChild(svg); + // Get correct em/ex values by creating a temporary SVG. + var svg = document.createElementNS(NS.SVG, 'svg'); + document.body.appendChild(svg); + var rect = document.createElementNS(NS.SVG, 'rect'); + rect.setAttribute('width', '1em'); + rect.setAttribute('height', '1ex'); + rect.setAttribute('x', '1in'); + svg.appendChild(rect); + var bb = rect.getBBox(); + document.body.removeChild(svg); - var inch = bb.x; - typeMap_ = { - 'em': bb.width, - 'ex': bb.height, - 'in': inch, - 'cm': inch / 2.54, - 'mm': inch / 25.4, - 'pt': inch / 72, - 'pc': inch / 6, - 'px': 1, - '%': 0 - }; + var inch = bb.x; + typeMap_ = { + 'em': bb.width, + 'ex': bb.height, + 'in': inch, + 'cm': inch / 2.54, + 'mm': inch / 25.4, + 'pt': inch / 72, + 'pc': inch / 6, + 'px': 1, + '%': 0 + }; }; // Group: Unit conversion functions @@ -95,7 +95,7 @@ svgedit.units.init = function (elementContainer) { // Function: svgedit.units.getTypeMap // Returns the unit object with values for each unit svgedit.units.getTypeMap = function () { - return typeMap_; + return typeMap_; }; // Function: svgedit.units.shortFloat @@ -108,25 +108,25 @@ svgedit.units.getTypeMap = function () { // If a string/number was given, returns a Float. If an array, return a string // with comma-separated floats svgedit.units.shortFloat = function (val) { - var digits = elementContainer_.getRoundDigits(); - if (!isNaN(val)) { - // Note that + converts to Number - return +((+val).toFixed(digits)); - } - if ($.isArray(val)) { - return svgedit.units.shortFloat(val[0]) + ',' + svgedit.units.shortFloat(val[1]); - } - return parseFloat(val).toFixed(digits) - 0; + var digits = elementContainer_.getRoundDigits(); + if (!isNaN(val)) { + // Note that + converts to Number + return +((+val).toFixed(digits)); + } + if ($.isArray(val)) { + return svgedit.units.shortFloat(val[0]) + ',' + svgedit.units.shortFloat(val[1]); + } + return parseFloat(val).toFixed(digits) - 0; }; // Function: svgedit.units.convertUnit // Converts the number to given unit or baseUnit svgedit.units.convertUnit = function (val, unit) { - unit = unit || elementContainer_.getBaseUnit(); - // baseVal.convertToSpecifiedUnits(unitNumMap[unit]); - // var val = baseVal.valueInSpecifiedUnits; - // baseVal.convertToSpecifiedUnits(1); - return svgedit.units.shortFloat(val / typeMap_[unit]); + unit = unit || elementContainer_.getBaseUnit(); + // baseVal.convertToSpecifiedUnits(unitNumMap[unit]); + // var val = baseVal.valueInSpecifiedUnits; + // baseVal.convertToSpecifiedUnits(1); + return svgedit.units.shortFloat(val / typeMap_[unit]); }; // Function: svgedit.units.setUnitAttr @@ -137,49 +137,49 @@ svgedit.units.convertUnit = function (val, unit) { // attr - String with the name of the attribute associated with the value // val - String with the attribute value to convert svgedit.units.setUnitAttr = function (elem, attr, val) { - // if (!isNaN(val)) { - // New value is a number, so check currently used unit - // var old_val = elem.getAttribute(attr); + // if (!isNaN(val)) { + // New value is a number, so check currently used unit + // var old_val = elem.getAttribute(attr); - // Enable this for alternate mode - // if (old_val !== null && (isNaN(old_val) || elementContainer_.getBaseUnit() !== 'px')) { - // // Old value was a number, so get unit, then convert - // var unit; - // if (old_val.substr(-1) === '%') { - // var res = getResolution(); - // unit = '%'; - // val *= 100; - // if (wAttrs.indexOf(attr) >= 0) { - // val = val / res.w; - // } else if (hAttrs.indexOf(attr) >= 0) { - // val = val / res.h; - // } else { - // return val / Math.sqrt((res.w*res.w) + (res.h*res.h))/Math.sqrt(2); - // } - // } else { - // if (elementContainer_.getBaseUnit() !== 'px') { - // unit = elementContainer_.getBaseUnit(); - // } else { - // unit = old_val.substr(-2); - // } - // val = val / typeMap_[unit]; - // } - // - // val += unit; - // } - // } - elem.setAttribute(attr, val); + // Enable this for alternate mode + // if (old_val !== null && (isNaN(old_val) || elementContainer_.getBaseUnit() !== 'px')) { + // // Old value was a number, so get unit, then convert + // var unit; + // if (old_val.substr(-1) === '%') { + // var res = getResolution(); + // unit = '%'; + // val *= 100; + // if (wAttrs.indexOf(attr) >= 0) { + // val = val / res.w; + // } else if (hAttrs.indexOf(attr) >= 0) { + // val = val / res.h; + // } else { + // return val / Math.sqrt((res.w*res.w) + (res.h*res.h))/Math.sqrt(2); + // } + // } else { + // if (elementContainer_.getBaseUnit() !== 'px') { + // unit = elementContainer_.getBaseUnit(); + // } else { + // unit = old_val.substr(-2); + // } + // val = val / typeMap_[unit]; + // } + // + // val += unit; + // } + // } + elem.setAttribute(attr, val); }; var attrsToConvert = { - 'line': ['x1', 'x2', 'y1', 'y2'], - 'circle': ['cx', 'cy', 'r'], - 'ellipse': ['cx', 'cy', 'rx', 'ry'], - 'foreignObject': ['x', 'y', 'width', 'height'], - 'rect': ['x', 'y', 'width', 'height'], - 'image': ['x', 'y', 'width', 'height'], - 'use': ['x', 'y', 'width', 'height'], - 'text': ['x', 'y'] + 'line': ['x1', 'x2', 'y1', 'y2'], + 'circle': ['cx', 'cy', 'r'], + 'ellipse': ['cx', 'cy', 'rx', 'ry'], + 'foreignObject': ['x', 'y', 'width', 'height'], + 'rect': ['x', 'y', 'width', 'height'], + 'image': ['x', 'y', 'width', 'height'], + 'use': ['x', 'y', 'width', 'height'], + 'text': ['x', 'y'] }; // Function: svgedit.units.convertAttrs @@ -188,25 +188,25 @@ var attrsToConvert = { // Parameters: // element - a DOM element whose attributes should be converted svgedit.units.convertAttrs = function (element) { - var elName = element.tagName; - var unit = elementContainer_.getBaseUnit(); - var attrs = attrsToConvert[elName]; - if (!attrs) { return; } + var elName = element.tagName; + var unit = elementContainer_.getBaseUnit(); + var attrs = attrsToConvert[elName]; + if (!attrs) { return; } - var len = attrs.length; - var i; - for (i = 0; i < len; i++) { - var attr = attrs[i]; - var cur = element.getAttribute(attr); - if (cur) { - if (!isNaN(cur)) { - element.setAttribute(attr, (cur / typeMap_[unit]) + unit); - } - // else { - // Convert existing? - // } - } - } + var len = attrs.length; + var i; + for (i = 0; i < len; i++) { + var attr = attrs[i]; + var cur = element.getAttribute(attr); + if (cur) { + if (!isNaN(cur)) { + element.setAttribute(attr, (cur / typeMap_[unit]) + unit); + } + // else { + // Convert existing? + // } + } + } }; // Function: svgedit.units.convertToNum @@ -217,27 +217,27 @@ svgedit.units.convertAttrs = function (element) { // attr - String with the name of the attribute associated with the value // val - String with the attribute value to convert svgedit.units.convertToNum = function (attr, val) { - // Return a number if that's what it already is - if (!isNaN(val)) { return val - 0; } - var num; - if (val.substr(-1) === '%') { - // Deal with percentage, depends on attribute - num = val.substr(0, val.length - 1) / 100; - var width = elementContainer_.getWidth(); - var height = elementContainer_.getHeight(); + // Return a number if that's what it already is + if (!isNaN(val)) { return val - 0; } + var num; + if (val.substr(-1) === '%') { + // Deal with percentage, depends on attribute + num = val.substr(0, val.length - 1) / 100; + var width = elementContainer_.getWidth(); + var height = elementContainer_.getHeight(); - if (wAttrs.indexOf(attr) >= 0) { - return num * width; - } - if (hAttrs.indexOf(attr) >= 0) { - return num * height; - } - return num * Math.sqrt((width * width) + (height * height)) / Math.sqrt(2); - } - var unit = val.substr(-2); - num = val.substr(0, val.length - 2); - // Note that this multiplication turns the string into a number - return num * typeMap_[unit]; + if (wAttrs.indexOf(attr) >= 0) { + return num * width; + } + if (hAttrs.indexOf(attr) >= 0) { + return num * height; + } + return num * Math.sqrt((width * width) + (height * height)) / Math.sqrt(2); + } + var unit = val.substr(-2); + num = val.substr(0, val.length - 2); + // Note that this multiplication turns the string into a number + return num * typeMap_[unit]; }; // Function: svgedit.units.isValidUnit @@ -247,37 +247,37 @@ svgedit.units.convertToNum = function (attr, val) { // attr - String with the name of the attribute associated with the value // val - String with the attribute value to check svgedit.units.isValidUnit = function (attr, val, selectedElement) { - var valid = false; - if (unitAttrs.indexOf(attr) >= 0) { - // True if it's just a number - if (!isNaN(val)) { - valid = true; - } else { - // Not a number, check if it has a valid unit - val = val.toLowerCase(); - $.each(typeMap_, function (unit) { - if (valid) { return; } - var re = new RegExp('^-?[\\d\\.]+' + unit + '$'); - if (re.test(val)) { valid = true; } - }); - } - } else if (attr === 'id') { - // if we're trying to change the id, make sure it's not already present in the doc - // and the id value is valid. + var valid = false; + if (unitAttrs.indexOf(attr) >= 0) { + // True if it's just a number + if (!isNaN(val)) { + valid = true; + } else { + // Not a number, check if it has a valid unit + val = val.toLowerCase(); + $.each(typeMap_, function (unit) { + if (valid) { return; } + var re = new RegExp('^-?[\\d\\.]+' + unit + '$'); + if (re.test(val)) { valid = true; } + }); + } + } else if (attr === 'id') { + // if we're trying to change the id, make sure it's not already present in the doc + // and the id value is valid. - var result = false; - // because getElem() can throw an exception in the case of an invalid id - // (according to http://www.w3.org/TR/xml-id/ IDs must be a NCName) - // we wrap it in an exception and only return true if the ID was valid and - // not already present - try { - var elem = elementContainer_.getElement(val); - result = (elem == null || elem === selectedElement); - } catch (e) {} - return result; - } - valid = true; + var result = false; + // because getElem() can throw an exception in the case of an invalid id + // (according to http://www.w3.org/TR/xml-id/ IDs must be a NCName) + // we wrap it in an exception and only return true if the ID was valid and + // not already present + try { + var elem = elementContainer_.getElement(val); + result = (elem == null || elem === selectedElement); + } catch (e) {} + return result; + } + valid = true; - return valid; + return valid; }; }());