Change all tab indentations to 2sp indentation. Addresses issue #37

This commit is contained in:
codedread
2018-05-17 21:02:30 -07:00
parent 89cbab7217
commit 4043c6e537
26 changed files with 17191 additions and 17191 deletions

View File

@@ -16,20 +16,20 @@
'use strict'; 'use strict';
if (!svgedit.browser) { if (!svgedit.browser) {
svgedit.browser = {}; svgedit.browser = {};
} }
// alias // alias
var NS = svgedit.NS; var NS = svgedit.NS;
var supportsSvg_ = (function () { 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_; }; svgedit.browser.supportsSvg = function () { return supportsSvg_; };
if (!svgedit.browser.supportsSvg()) { if (!svgedit.browser.supportsSvg()) {
window.location = 'browser-not-supported.html'; window.location = 'browser-not-supported.html';
return; return;
} }
var userAgent = navigator.userAgent; var userAgent = navigator.userAgent;
@@ -46,120 +46,120 @@ var isMac_ = userAgent.indexOf('Macintosh') >= 0;
var isTouch_ = 'ontouchstart' in window; var isTouch_ = 'ontouchstart' in window;
var supportsSelectors_ = (function () { var supportsSelectors_ = (function () {
return !!svg.querySelector; return !!svg.querySelector;
}()); }());
var supportsXpath_ = (function () { var supportsXpath_ = (function () {
return !!document.evaluate; return !!document.evaluate;
}()); }());
// segList functions (for FF1.5 and 2.0) // segList functions (for FF1.5 and 2.0)
var supportsPathReplaceItem_ = (function () { var supportsPathReplaceItem_ = (function () {
var path = document.createElementNS(NS.SVG, 'path'); var path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 10,10'); path.setAttribute('d', 'M0,0 10,10');
var seglist = path.pathSegList; var seglist = path.pathSegList;
var seg = path.createSVGPathSegLinetoAbs(5, 5); var seg = path.createSVGPathSegLinetoAbs(5, 5);
try { try {
seglist.replaceItem(seg, 1); seglist.replaceItem(seg, 1);
return true; return true;
} catch (err) {} } catch (err) {}
return false; return false;
}()); }());
var supportsPathInsertItemBefore_ = (function () { var supportsPathInsertItemBefore_ = (function () {
var path = document.createElementNS(NS.SVG, 'path'); var path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 10,10'); path.setAttribute('d', 'M0,0 10,10');
var seglist = path.pathSegList; var seglist = path.pathSegList;
var seg = path.createSVGPathSegLinetoAbs(5, 5); var seg = path.createSVGPathSegLinetoAbs(5, 5);
try { try {
seglist.insertItemBefore(seg, 1); seglist.insertItemBefore(seg, 1);
return true; return true;
} catch (err) {} } catch (err) {}
return false; return false;
}()); }());
// text character positioning (for IE9) // text character positioning (for IE9)
var supportsGoodTextCharPos_ = (function () { var supportsGoodTextCharPos_ = (function () {
var svgroot = document.createElementNS(NS.SVG, 'svg'); var svgroot = document.createElementNS(NS.SVG, 'svg');
var svgcontent = document.createElementNS(NS.SVG, 'svg'); var svgcontent = document.createElementNS(NS.SVG, 'svg');
document.documentElement.appendChild(svgroot); document.documentElement.appendChild(svgroot);
svgcontent.setAttribute('x', 5); svgcontent.setAttribute('x', 5);
svgroot.appendChild(svgcontent); svgroot.appendChild(svgcontent);
var text = document.createElementNS(NS.SVG, 'text'); var text = document.createElementNS(NS.SVG, 'text');
text.textContent = 'a'; text.textContent = 'a';
svgcontent.appendChild(text); svgcontent.appendChild(text);
var pos = text.getStartPositionOfChar(0).x; var pos = text.getStartPositionOfChar(0).x;
document.documentElement.removeChild(svgroot); document.documentElement.removeChild(svgroot);
return (pos === 0); return (pos === 0);
}()); }());
var supportsPathBBox_ = (function () { var supportsPathBBox_ = (function () {
var svgcontent = document.createElementNS(NS.SVG, 'svg'); var svgcontent = document.createElementNS(NS.SVG, 'svg');
document.documentElement.appendChild(svgcontent); document.documentElement.appendChild(svgcontent);
var path = document.createElementNS(NS.SVG, 'path'); var path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 C0,0 10,10 10,0'); path.setAttribute('d', 'M0,0 C0,0 10,10 10,0');
svgcontent.appendChild(path); svgcontent.appendChild(path);
var bbox = path.getBBox(); var bbox = path.getBBox();
document.documentElement.removeChild(svgcontent); document.documentElement.removeChild(svgcontent);
return (bbox.height > 4 && bbox.height < 5); return (bbox.height > 4 && bbox.height < 5);
}()); }());
// Support for correct bbox sizing on groups with horizontal/vertical lines // Support for correct bbox sizing on groups with horizontal/vertical lines
var supportsHVLineContainerBBox_ = (function () { var supportsHVLineContainerBBox_ = (function () {
var svgcontent = document.createElementNS(NS.SVG, 'svg'); var svgcontent = document.createElementNS(NS.SVG, 'svg');
document.documentElement.appendChild(svgcontent); document.documentElement.appendChild(svgcontent);
var path = document.createElementNS(NS.SVG, 'path'); var path = document.createElementNS(NS.SVG, 'path');
path.setAttribute('d', 'M0,0 10,0'); path.setAttribute('d', 'M0,0 10,0');
var path2 = document.createElementNS(NS.SVG, 'path'); var path2 = document.createElementNS(NS.SVG, 'path');
path2.setAttribute('d', 'M5,0 15,0'); path2.setAttribute('d', 'M5,0 15,0');
var g = document.createElementNS(NS.SVG, 'g'); var g = document.createElementNS(NS.SVG, 'g');
g.appendChild(path); g.appendChild(path);
g.appendChild(path2); g.appendChild(path2);
svgcontent.appendChild(g); svgcontent.appendChild(g);
var bbox = g.getBBox(); var bbox = g.getBBox();
document.documentElement.removeChild(svgcontent); document.documentElement.removeChild(svgcontent);
// Webkit gives 0, FF gives 10, Opera (correctly) gives 15 // Webkit gives 0, FF gives 10, Opera (correctly) gives 15
return (bbox.width === 15); return (bbox.width === 15);
}()); }());
var supportsEditableText_ = (function () { var supportsEditableText_ = (function () {
// TODO: Find better way to check support for this // TODO: Find better way to check support for this
return isOpera_; return isOpera_;
}()); }());
var supportsGoodDecimals_ = (function () { var supportsGoodDecimals_ = (function () {
// Correct decimals on clone attributes (Opera < 10.5/win/non-en) // Correct decimals on clone attributes (Opera < 10.5/win/non-en)
var rect = document.createElementNS(NS.SVG, 'rect'); var rect = document.createElementNS(NS.SVG, 'rect');
rect.setAttribute('x', 0.1); rect.setAttribute('x', 0.1);
var crect = rect.cloneNode(false); var crect = rect.cloneNode(false);
var retValue = (crect.getAttribute('x').indexOf(',') === -1); var retValue = (crect.getAttribute('x').indexOf(',') === -1);
if (!retValue) { if (!retValue) {
$.alert('NOTE: This version of Opera is known to contain bugs in SVG-edit.\n' + $.alert('NOTE: This version of Opera is known to contain bugs in SVG-edit.\n' +
'Please upgrade to the <a href="http://opera.com">latest version</a> in which the problems have been fixed.'); 'Please upgrade to the <a href="http://opera.com">latest version</a> in which the problems have been fixed.');
} }
return retValue; return retValue;
}()); }());
var supportsNonScalingStroke_ = (function () { var supportsNonScalingStroke_ = (function () {
var rect = document.createElementNS(NS.SVG, 'rect'); var rect = document.createElementNS(NS.SVG, 'rect');
rect.setAttribute('style', 'vector-effect:non-scaling-stroke'); rect.setAttribute('style', 'vector-effect:non-scaling-stroke');
return rect.style.vectorEffect === 'non-scaling-stroke'; return rect.style.vectorEffect === 'non-scaling-stroke';
}()); }());
var supportsNativeSVGTransformLists_ = (function () { var supportsNativeSVGTransformLists_ = (function () {
var rect = document.createElementNS(NS.SVG, 'rect'); var rect = document.createElementNS(NS.SVG, 'rect');
var rxform = rect.transform.baseVal; var rxform = rect.transform.baseVal;
var t1 = svg.createSVGTransform(); var t1 = svg.createSVGTransform();
rxform.appendItem(t1); rxform.appendItem(t1);
var r1 = rxform.getItem(0); var r1 = rxform.getItem(0);
return r1 instanceof SVGTransform && t1 instanceof SVGTransform && return r1 instanceof SVGTransform && t1 instanceof SVGTransform &&
r1.type === t1.type && r1.angle === t1.angle && r1.type === t1.type && r1.angle === t1.angle &&
r1.matrix.a === t1.matrix.a && r1.matrix.a === t1.matrix.a &&
r1.matrix.b === t1.matrix.b && r1.matrix.b === t1.matrix.b &&
r1.matrix.c === t1.matrix.c && r1.matrix.c === t1.matrix.c &&
r1.matrix.d === t1.matrix.d && r1.matrix.d === t1.matrix.d &&
r1.matrix.e === t1.matrix.e && r1.matrix.e === t1.matrix.e &&
r1.matrix.f === t1.matrix.f; r1.matrix.f === t1.matrix.f;
}()); }());
// Public API // Public API

View File

@@ -21,86 +21,86 @@ See svg-editor.js for documentation on using setConfig().
// URL OVERRIDE CONFIG // URL OVERRIDE CONFIG
svgEditor.setConfig({ svgEditor.setConfig({
/** /**
To override the ability for URLs to set URL-based SVG content, To override the ability for URLs to set URL-based SVG content,
uncomment the following: uncomment the following:
*/ */
// preventURLContentLoading: true, // preventURLContentLoading: true,
/** /**
To override the ability for URLs to set other configuration (including To override the ability for URLs to set other configuration (including
extension config), uncomment the following: extension config), uncomment the following:
*/ */
// preventAllURLConfig: true, // preventAllURLConfig: true,
/** /**
To override the ability for URLs to set their own extensions, To override the ability for URLs to set their own extensions,
uncomment the following (note that if setConfig() is used in uncomment the following (note that if setConfig() is used in
extension code, it will still be additive to extensions, extension code, it will still be additive to extensions,
however): however):
*/ */
// lockExtensions: true, // lockExtensions: true,
}); });
svgEditor.setConfig({ svgEditor.setConfig({
/* /*
Provide default values here which differ from that of the editor but Provide default values here which differ from that of the editor but
which the URL can override which the URL can override
*/ */
}, {allowInitialUserOverride: true}); }, {allowInitialUserOverride: true});
// EXTENSION CONFIG // EXTENSION CONFIG
svgEditor.setConfig({ svgEditor.setConfig({
extensions: [ 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' // '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 // , noDefaultExtensions: false, // noDefaultExtensions can only be meaningfully used in config.js or in the URL
}); });
// OTHER CONFIG // OTHER CONFIG
svgEditor.setConfig({ svgEditor.setConfig({
// canvasName: 'default', // canvasName: 'default',
// canvas_expansion: 3, // canvas_expansion: 3,
// initFill: { // initFill: {
// color: 'FF0000', // solid red // color: 'FF0000', // solid red
// opacity: 1 // opacity: 1
// }, // },
// initStroke: { // initStroke: {
// width: 5, // width: 5,
// color: '000000', // solid black // color: '000000', // solid black
// opacity: 1 // opacity: 1
// }, // },
// initOpacity: 1, // initOpacity: 1,
// colorPickerCSS: null, // colorPickerCSS: null,
// initTool: 'select', // initTool: 'select',
// exportWindowType: 'new', // 'same' // exportWindowType: 'new', // 'same'
// wireframe: false, // wireframe: false,
// showlayers: false, // showlayers: false,
// no_save_warning: false, // no_save_warning: false,
// PATH CONFIGURATION // PATH CONFIGURATION
// imgPath: 'images/', // imgPath: 'images/',
// langPath: 'locale/', // langPath: 'locale/',
// extPath: 'extensions/', // extPath: 'extensions/',
// jGraduatePath: 'jgraduate/images/', // jGraduatePath: 'jgraduate/images/',
/* /*
Uncomment the following to allow at least same domain (embedded) access, Uncomment the following to allow at least same domain (embedded) access,
including file:// access. including file:// access.
Setting as `['*']` would allow any domain to access but would be unsafe to Setting as `['*']` would allow any domain to access but would be unsafe to
data privacy and integrity. data privacy and integrity.
*/ */
// allowedOrigins: [window.location.origin || 'null'], // May be 'null' (as a string) when used as a file:// URL // allowedOrigins: [window.location.origin || 'null'], // May be 'null' (as a string) when used as a file:// URL
// DOCUMENT PROPERTIES // DOCUMENT PROPERTIES
// dimensions: [640, 480], // dimensions: [640, 480],
// EDITOR OPTIONS // EDITOR OPTIONS
// gridSnapping: false, // gridSnapping: false,
// gridColor: '#000', // gridColor: '#000',
// baseUnit: 'px', // baseUnit: 'px',
// snappingStep: 10, // snappingStep: 10,
// showRulers: true, // showRulers: true,
// EXTENSION-RELATED (GRID) // EXTENSION-RELATED (GRID)
// showGrid: false, // Set by ext-grid.js // showGrid: false, // Set by ext-grid.js
// EXTENSION-RELATED (STORAGE) // EXTENSION-RELATED (STORAGE)
// noStorageOnLoad: false, // Some interaction with ext-storage.js; prevent even the loading of previously saved local 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 // 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 // emptyStorageOnDecline: true, // Used by ext-storage.js; empty any prior storage if the user declines to store
}); });
// PREF CHANGES // 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. are hard-coded here regardless of URL or prior user storage setting.
*/ */
svgEditor.setConfig( svgEditor.setConfig(
{ {
// lang: '', // Set dynamically within locale.js if not previously set // 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 // 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 * When showing the preferences dialog, svg-editor.js currently relies
* on curPrefs instead of $.pref, so allowing an override for bkgd_color * 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 * means that this value won't have priority over block auto-detection as
* far as determining which color shows initially in the preferences * far as determining which color shows initially in the preferences
* dialog (though it can be changed and saved). * dialog (though it can be changed and saved).
*/ */
// bkgd_color: '#FFF', // bkgd_color: '#FFF',
// bkgd_url: '', // bkgd_url: '',
// img_save: 'embed', // img_save: 'embed',
// Only shows in UI as far as alert notices // Only shows in UI as far as alert notices
// save_notice_done: false, // save_notice_done: false,
// export_notice_done: false // export_notice_done: false
} }
); );
svgEditor.setConfig( svgEditor.setConfig(
{ {
// Indicate pref settings here if you wish to allow user storage or URL settings // 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 // to be able to override your default preferences (unless other config options
// have already explicitly prevented one or the other) // have already explicitly prevented one or the other)
}, },
{allowInitialUserOverride: true} {allowInitialUserOverride: true}
); );

View File

@@ -13,51 +13,51 @@ var svgedit = svgedit || {}; // eslint-disable-line no-use-before-define
(function () { (function () {
var self = this; var self = this;
if (!svgedit.contextmenu) { if (!svgedit.contextmenu) {
svgedit.contextmenu = {}; svgedit.contextmenu = {};
} }
self.contextMenuExtensions = {}; self.contextMenuExtensions = {};
var menuItemIsValid = function (menuItem) { 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) { var addContextMenuItem = function (menuItem) {
// menuItem: {id, label, shortcut, action} // menuItem: {id, label, shortcut, action}
if (!menuItemIsValid(menuItem)) { if (!menuItemIsValid(menuItem)) {
console.error('Menu items must be defined and have at least properties: id, label, action, where action must be a function'); console.error('Menu items must be defined and have at least properties: id, label, action, where action must be a function');
return; return;
} }
if (menuItem.id in self.contextMenuExtensions) { if (menuItem.id in self.contextMenuExtensions) {
console.error('Cannot add extension "' + menuItem.id + '", an extension by that name already exists"'); console.error('Cannot add extension "' + menuItem.id + '", an extension by that name already exists"');
return; return;
} }
// Register menuItem action, see below for deferred menu dom injection // Register menuItem action, see below for deferred menu dom injection
console.log('Registed contextmenu item: {id:' + menuItem.id + ', label:' + menuItem.label + '}'); console.log('Registed contextmenu item: {id:' + menuItem.id + ', label:' + menuItem.label + '}');
self.contextMenuExtensions[menuItem.id] = menuItem; self.contextMenuExtensions[menuItem.id] = menuItem;
// TODO: Need to consider how to handle custom enable/disable behavior // TODO: Need to consider how to handle custom enable/disable behavior
}; };
var hasCustomHandler = function (handlerKey) { var hasCustomHandler = function (handlerKey) {
return self.contextMenuExtensions[handlerKey] && true; return self.contextMenuExtensions[handlerKey] && true;
}; };
var getCustomHandler = function (handlerKey) { var getCustomHandler = function (handlerKey) {
return self.contextMenuExtensions[handlerKey].action; return self.contextMenuExtensions[handlerKey].action;
}; };
var injectExtendedContextMenuItemIntoDom = function (menuItem) { var injectExtendedContextMenuItemIntoDom = function (menuItem) {
if (Object.keys(self.contextMenuExtensions).length === 0) { if (Object.keys(self.contextMenuExtensions).length === 0) {
// all menuItems appear at the bottom of the menu in their own container. // 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. // if this is the first extension menu we need to add the separator.
$('#cmenu_canvas').append("<li class='separator'>"); $('#cmenu_canvas').append("<li class='separator'>");
} }
var shortcut = menuItem.shortcut || ''; var shortcut = menuItem.shortcut || '';
$('#cmenu_canvas').append("<li class='disabled'><a href='#" + menuItem.id + "'>" + $('#cmenu_canvas').append("<li class='disabled'><a href='#" + menuItem.id + "'>" +
menuItem.label + "<span class='shortcut'>" + menuItem.label + "<span class='shortcut'>" +
shortcut + '</span></a></li>'); shortcut + '</span></a></li>');
}; };
// Defer injection to wait out initial menu processing. This probably goes away once all context // Defer injection to wait out initial menu processing. This probably goes away once all context
// menu behavior is brought here. // menu behavior is brought here.
svgEditor.ready(function () { svgEditor.ready(function () {
var menuItem; var menuItem;
for (menuItem in self.contextMenuExtensions) { for (menuItem in self.contextMenuExtensions) {
injectExtendedContextMenuItemIntoDom(self.contextMenuExtensions[menuItem]); injectExtendedContextMenuItemIntoDom(self.contextMenuExtensions[menuItem]);
} }
}); });
svgedit.contextmenu.resetCustomMenus = function () { self.contextMenuExtensions = {}; }; svgedit.contextmenu.resetCustomMenus = function () { self.contextMenuExtensions = {}; };
svgedit.contextmenu.add = addContextMenuItem; svgedit.contextmenu.add = addContextMenuItem;

View File

@@ -22,12 +22,12 @@ var svgedit = svgedit || {}; // eslint-disable-line no-use-before-define
'use strict'; 'use strict';
if (!svgedit.coords) { if (!svgedit.coords) {
svgedit.coords = {}; svgedit.coords = {};
} }
// this is how we map paths to our preferred relative segment types // this is how we map paths to our preferred relative segment types
var pathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', var pathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a',
'H', 'h', 'V', 'v', 'S', 's', 'T', 't']; 'H', 'h', 'V', 'v', 'S', 's', 'T', 't'];
/** /**
* @typedef editorContext * @typedef editorContext
@@ -41,7 +41,7 @@ var editorContext_ = null;
* @param {editorContext} editorContext * @param {editorContext} editorContext
*/ */
svgedit.coords.init = function (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 * @param {SVGMatrix} m - Matrix object to use for remapping coordinates
*/ */
svgedit.coords.remapElement = function (selected, changes, m) { svgedit.coords.remapElement = function (selected, changes, m) {
var i, type, var i, type,
remap = function (x, y) { return svgedit.math.transformPoint(x, y, m); }, remap = function (x, y) { return svgedit.math.transformPoint(x, y, m); },
scalew = function (w) { return m.a * w; }, scalew = function (w) { return m.a * w; },
scaleh = function (h) { return m.d * h; }, scaleh = function (h) { return m.d * h; },
doSnapping = editorContext_.getGridSnapping() && selected.parentNode.parentNode.localName === 'svg', doSnapping = editorContext_.getGridSnapping() && selected.parentNode.parentNode.localName === 'svg',
finishUp = function () { finishUp = function () {
var o; var o;
if (doSnapping) { if (doSnapping) {
for (o in changes) { for (o in changes) {
changes[o] = svgedit.utilities.snapToGrid(changes[o]); changes[o] = svgedit.utilities.snapToGrid(changes[o]);
} }
} }
svgedit.utilities.assignAttributes(selected, changes, 1000, true); svgedit.utilities.assignAttributes(selected, changes, 1000, true);
}, },
box = svgedit.utilities.getBBox(selected); box = svgedit.utilities.getBBox(selected);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
type = i === 0 ? 'fill' : 'stroke'; type = i === 0 ? 'fill' : 'stroke';
var attrVal = selected.getAttribute(type); var attrVal = selected.getAttribute(type);
if (attrVal && attrVal.indexOf('url(') === 0) { if (attrVal && attrVal.indexOf('url(') === 0) {
if (m.a < 0 || m.d < 0) { if (m.a < 0 || m.d < 0) {
var grad = svgedit.utilities.getRefElem(attrVal); var grad = svgedit.utilities.getRefElem(attrVal);
var newgrad = grad.cloneNode(true); var newgrad = grad.cloneNode(true);
if (m.a < 0) { if (m.a < 0) {
// flip x // flip x
var x1 = newgrad.getAttribute('x1'); var x1 = newgrad.getAttribute('x1');
var x2 = newgrad.getAttribute('x2'); var x2 = newgrad.getAttribute('x2');
newgrad.setAttribute('x1', -(x1 - 1)); newgrad.setAttribute('x1', -(x1 - 1));
newgrad.setAttribute('x2', -(x2 - 1)); newgrad.setAttribute('x2', -(x2 - 1));
} }
if (m.d < 0) { if (m.d < 0) {
// flip y // flip y
var y1 = newgrad.getAttribute('y1'); var y1 = newgrad.getAttribute('y1');
var y2 = newgrad.getAttribute('y2'); var y2 = newgrad.getAttribute('y2');
newgrad.setAttribute('y1', -(y1 - 1)); newgrad.setAttribute('y1', -(y1 - 1));
newgrad.setAttribute('y2', -(y2 - 1)); newgrad.setAttribute('y2', -(y2 - 1));
} }
newgrad.id = editorContext_.getDrawing().getNextId(); newgrad.id = editorContext_.getDrawing().getNextId();
svgedit.utilities.findDefs().appendChild(newgrad); svgedit.utilities.findDefs().appendChild(newgrad);
selected.setAttribute(type, 'url(#' + newgrad.id + ')'); selected.setAttribute(type, 'url(#' + newgrad.id + ')');
} }
// Not really working :( // Not really working :(
// if (selected.tagName === 'path') { // if (selected.tagName === 'path') {
// reorientGrads(selected, m); // reorientGrads(selected, m);
// } // }
} }
} }
var elName = selected.tagName; var elName = selected.tagName;
var chlist, mt; var chlist, mt;
if (elName === 'g' || elName === 'text' || elName === 'tspan' || elName === 'use') { if (elName === 'g' || elName === 'text' || elName === 'tspan' || elName === 'use') {
// if it was a translate, then just update x,y // if it was a translate, then just update x,y
if (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && (m.e !== 0 || m.f !== 0)) { if (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && (m.e !== 0 || m.f !== 0)) {
// [T][M] = [M][T'] // [T][M] = [M][T']
// therefore [T'] = [M_inv][T][M] // therefore [T'] = [M_inv][T][M]
var existing = svgedit.math.transformListToTransform(selected).matrix, var existing = svgedit.math.transformListToTransform(selected).matrix,
tNew = svgedit.math.matrixMultiply(existing.inverse(), m, existing); tNew = svgedit.math.matrixMultiply(existing.inverse(), m, existing);
changes.x = parseFloat(changes.x) + tNew.e; changes.x = parseFloat(changes.x) + tNew.e;
changes.y = parseFloat(changes.y) + tNew.f; changes.y = parseFloat(changes.y) + tNew.f;
} else { } else {
// we just absorb all matrices into the element and don't do any remapping // we just absorb all matrices into the element and don't do any remapping
chlist = svgedit.transformlist.getTransformList(selected); chlist = svgedit.transformlist.getTransformList(selected);
mt = svgroot.createSVGTransform(); mt = svgroot.createSVGTransform();
mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m)); mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m));
chlist.clear(); chlist.clear();
chlist.appendItem(mt); chlist.appendItem(mt);
} }
} }
var c, pt, pt1, pt2, len; var c, pt, pt1, pt2, len;
// now we have a set of changes and an applied reduced transform list // now we have a set of changes and an applied reduced transform list
// we apply the changes directly to the DOM // we apply the changes directly to the DOM
switch (elName) { switch (elName) {
case 'foreignObject': case 'foreignObject':
case 'rect': case 'rect':
case 'image': case 'image':
// Allow images to be inverted (give them matrix when flipped) // Allow images to be inverted (give them matrix when flipped)
if (elName === 'image' && (m.a < 0 || m.d < 0)) { if (elName === 'image' && (m.a < 0 || m.d < 0)) {
// Convert to matrix // Convert to matrix
chlist = svgedit.transformlist.getTransformList(selected); chlist = svgedit.transformlist.getTransformList(selected);
mt = svgroot.createSVGTransform(); mt = svgroot.createSVGTransform();
mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m)); mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m));
chlist.clear(); chlist.clear();
chlist.appendItem(mt); chlist.appendItem(mt);
} else { } else {
pt1 = remap(changes.x, changes.y); pt1 = remap(changes.x, changes.y);
changes.width = scalew(changes.width); changes.width = scalew(changes.width);
changes.height = scaleh(changes.height); changes.height = scaleh(changes.height);
changes.x = pt1.x + Math.min(0, changes.width); changes.x = pt1.x + Math.min(0, changes.width);
changes.y = pt1.y + Math.min(0, changes.height); changes.y = pt1.y + Math.min(0, changes.height);
changes.width = Math.abs(changes.width); changes.width = Math.abs(changes.width);
changes.height = Math.abs(changes.height); changes.height = Math.abs(changes.height);
} }
finishUp(); finishUp();
break; break;
case 'ellipse': case 'ellipse':
c = remap(changes.cx, changes.cy); c = remap(changes.cx, changes.cy);
changes.cx = c.x; changes.cx = c.x;
changes.cy = c.y; changes.cy = c.y;
changes.rx = scalew(changes.rx); changes.rx = scalew(changes.rx);
changes.ry = scaleh(changes.ry); changes.ry = scaleh(changes.ry);
changes.rx = Math.abs(changes.rx); changes.rx = Math.abs(changes.rx);
changes.ry = Math.abs(changes.ry); changes.ry = Math.abs(changes.ry);
finishUp(); finishUp();
break; break;
case 'circle': case 'circle':
c = remap(changes.cx, changes.cy); c = remap(changes.cx, changes.cy);
changes.cx = c.x; changes.cx = c.x;
changes.cy = c.y; changes.cy = c.y;
// take the minimum of the new selected box's dimensions for the new circle radius // 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 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; var w = tbox.tr.x - tbox.tl.x, h = tbox.bl.y - tbox.tl.y;
changes.r = Math.min(w / 2, h / 2); changes.r = Math.min(w / 2, h / 2);
if (changes.r) { changes.r = Math.abs(changes.r); } if (changes.r) { changes.r = Math.abs(changes.r); }
finishUp(); finishUp();
break; break;
case 'line': case 'line':
pt1 = remap(changes.x1, changes.y1); pt1 = remap(changes.x1, changes.y1);
pt2 = remap(changes.x2, changes.y2); pt2 = remap(changes.x2, changes.y2);
changes.x1 = pt1.x; changes.x1 = pt1.x;
changes.y1 = pt1.y; changes.y1 = pt1.y;
changes.x2 = pt2.x; changes.x2 = pt2.x;
changes.y2 = pt2.y; changes.y2 = pt2.y;
// deliberately fall through here // deliberately fall through here
case 'text': case 'text':
case 'tspan': case 'tspan':
case 'use': case 'use':
finishUp(); finishUp();
break; break;
case 'g': case 'g':
var gsvg = $(selected).data('gsvg'); var gsvg = $(selected).data('gsvg');
if (gsvg) { if (gsvg) {
svgedit.utilities.assignAttributes(gsvg, changes, 1000, true); svgedit.utilities.assignAttributes(gsvg, changes, 1000, true);
} }
break; break;
case 'polyline': case 'polyline':
case 'polygon': case 'polygon':
len = changes.points.length; len = changes.points.length;
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
pt = changes.points[i]; pt = changes.points[i];
pt = remap(pt.x, pt.y); pt = remap(pt.x, pt.y);
changes.points[i].x = pt.x; changes.points[i].x = pt.x;
changes.points[i].y = pt.y; changes.points[i].y = pt.y;
} }
len = changes.points.length; len = changes.points.length;
var pstr = ''; var pstr = '';
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
pt = changes.points[i]; pt = changes.points[i];
pstr += pt.x + ',' + pt.y + ' '; pstr += pt.x + ',' + pt.y + ' ';
} }
selected.setAttribute('points', pstr); selected.setAttribute('points', pstr);
break; break;
case 'path': case 'path':
var seg; var seg;
var segList = selected.pathSegList; var segList = selected.pathSegList;
len = segList.numberOfItems; len = segList.numberOfItems;
changes.d = []; changes.d = [];
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
seg = segList.getItem(i); seg = segList.getItem(i);
changes.d[i] = { changes.d[i] = {
type: seg.pathSegType, type: seg.pathSegType,
x: seg.x, x: seg.x,
y: seg.y, y: seg.y,
x1: seg.x1, x1: seg.x1,
y1: seg.y1, y1: seg.y1,
x2: seg.x2, x2: seg.x2,
y2: seg.y2, y2: seg.y2,
r1: seg.r1, r1: seg.r1,
r2: seg.r2, r2: seg.r2,
angle: seg.angle, angle: seg.angle,
largeArcFlag: seg.largeArcFlag, largeArcFlag: seg.largeArcFlag,
sweepFlag: seg.sweepFlag sweepFlag: seg.sweepFlag
}; };
} }
len = changes.d.length; len = changes.d.length;
var firstseg = changes.d[0], var firstseg = changes.d[0],
currentpt = remap(firstseg.x, firstseg.y); currentpt = remap(firstseg.x, firstseg.y);
changes.d[0].x = currentpt.x; changes.d[0].x = currentpt.x;
changes.d[0].y = currentpt.y; changes.d[0].y = currentpt.y;
for (i = 1; i < len; ++i) { for (i = 1; i < len; ++i) {
seg = changes.d[i]; seg = changes.d[i];
type = seg.type; type = seg.type;
// if absolute or first segment, we want to remap x, y, x1, y1, x2, y2 // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2
// if relative, we want to scalew, scaleh // if relative, we want to scalew, scaleh
if (type % 2 === 0) { // absolute if (type % 2 === 0) { // absolute
var thisx = (seg.x !== undefined) ? seg.x : currentpt.x, // for V commands var thisx = (seg.x !== undefined) ? seg.x : currentpt.x, // for V commands
thisy = (seg.y !== undefined) ? seg.y : currentpt.y; // for H commands thisy = (seg.y !== undefined) ? seg.y : currentpt.y; // for H commands
pt = remap(thisx, thisy); pt = remap(thisx, thisy);
pt1 = remap(seg.x1, seg.y1); pt1 = remap(seg.x1, seg.y1);
pt2 = remap(seg.x2, seg.y2); pt2 = remap(seg.x2, seg.y2);
seg.x = pt.x; seg.x = pt.x;
seg.y = pt.y; seg.y = pt.y;
seg.x1 = pt1.x; seg.x1 = pt1.x;
seg.y1 = pt1.y; seg.y1 = pt1.y;
seg.x2 = pt2.x; seg.x2 = pt2.x;
seg.y2 = pt2.y; seg.y2 = pt2.y;
seg.r1 = scalew(seg.r1); seg.r1 = scalew(seg.r1);
seg.r2 = scaleh(seg.r2); seg.r2 = scaleh(seg.r2);
} else { // relative } else { // relative
seg.x = scalew(seg.x); seg.x = scalew(seg.x);
seg.y = scaleh(seg.y); seg.y = scaleh(seg.y);
seg.x1 = scalew(seg.x1); seg.x1 = scalew(seg.x1);
seg.y1 = scaleh(seg.y1); seg.y1 = scaleh(seg.y1);
seg.x2 = scalew(seg.x2); seg.x2 = scalew(seg.x2);
seg.y2 = scaleh(seg.y2); seg.y2 = scaleh(seg.y2);
seg.r1 = scalew(seg.r1); seg.r1 = scalew(seg.r1);
seg.r2 = scaleh(seg.r2); seg.r2 = scaleh(seg.r2);
} }
} // for each segment } // for each segment
var dstr = ''; var dstr = '';
len = changes.d.length; len = changes.d.length;
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
seg = changes.d[i]; seg = changes.d[i];
type = seg.type; type = seg.type;
dstr += pathMap[type]; dstr += pathMap[type];
switch (type) { switch (type) {
case 13: // relative horizontal line (h) case 13: // relative horizontal line (h)
case 12: // absolute horizontal line (H) case 12: // absolute horizontal line (H)
dstr += seg.x + ' '; dstr += seg.x + ' ';
break; break;
case 15: // relative vertical line (v) case 15: // relative vertical line (v)
case 14: // absolute vertical line (V) case 14: // absolute vertical line (V)
dstr += seg.y + ' '; dstr += seg.y + ' ';
break; break;
case 3: // relative move (m) case 3: // relative move (m)
case 5: // relative line (l) case 5: // relative line (l)
case 19: // relative smooth quad (t) case 19: // relative smooth quad (t)
case 2: // absolute move (M) case 2: // absolute move (M)
case 4: // absolute line (L) case 4: // absolute line (L)
case 18: // absolute smooth quad (T) case 18: // absolute smooth quad (T)
dstr += seg.x + ',' + seg.y + ' '; dstr += seg.x + ',' + seg.y + ' ';
break; break;
case 7: // relative cubic (c) case 7: // relative cubic (c)
case 6: // absolute cubic (C) case 6: // absolute cubic (C)
dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x2 + ',' + seg.y2 + ' ' + dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x2 + ',' + seg.y2 + ' ' +
seg.x + ',' + seg.y + ' '; seg.x + ',' + seg.y + ' ';
break; break;
case 9: // relative quad (q) case 9: // relative quad (q)
case 8: // absolute quad (Q) case 8: // absolute quad (Q)
dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x + ',' + seg.y + ' '; dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x + ',' + seg.y + ' ';
break; break;
case 11: // relative elliptical arc (a) case 11: // relative elliptical arc (a)
case 10: // absolute elliptical arc (A) case 10: // absolute elliptical arc (A)
dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + (+seg.largeArcFlag) + dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + (+seg.largeArcFlag) +
' ' + (+seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' '; ' ' + (+seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' ';
break; break;
case 17: // relative smooth cubic (s) case 17: // relative smooth cubic (s)
case 16: // absolute smooth cubic (S) case 16: // absolute smooth cubic (S)
dstr += seg.x2 + ',' + seg.y2 + ' ' + seg.x + ',' + seg.y + ' '; dstr += seg.x2 + ',' + seg.y2 + ' ' + seg.x + ',' + seg.y + ' ';
break; break;
} }
} }
selected.setAttribute('d', dstr); selected.setAttribute('d', dstr);
break; break;
} }
}; };
}()); }());

View File

@@ -17,7 +17,7 @@
'use strict'; 'use strict';
if (!svgedit.draw) { if (!svgedit.draw) {
svgedit.draw = {}; svgedit.draw = {};
} }
// alias // alias
var NS = svgedit.NS; 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 visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use'.split(',');
var RandomizeModes = { var RandomizeModes = {
LET_DOCUMENT_DECIDE: 0, LET_DOCUMENT_DECIDE: 0,
ALWAYS_RANDOMIZE: 1, ALWAYS_RANDOMIZE: 1,
NEVER_RANDOMIZE: 2 NEVER_RANDOMIZE: 2
}; };
var randomizeIds = RandomizeModes.LET_DOCUMENT_DECIDE; var randomizeIds = RandomizeModes.LET_DOCUMENT_DECIDE;
@@ -38,15 +38,15 @@ var randomizeIds = RandomizeModes.LET_DOCUMENT_DECIDE;
* @param {svgedit.draw.Drawing} currentDrawing * @param {svgedit.draw.Drawing} currentDrawing
*/ */
svgedit.draw.randomizeIds = function (enableRandomization, currentDrawing) { svgedit.draw.randomizeIds = function (enableRandomization, currentDrawing) {
randomizeIds = enableRandomization === false randomizeIds = enableRandomization === false
? RandomizeModes.NEVER_RANDOMIZE ? RandomizeModes.NEVER_RANDOMIZE
: RandomizeModes.ALWAYS_RANDOMIZE; : RandomizeModes.ALWAYS_RANDOMIZE;
if (randomizeIds === RandomizeModes.ALWAYS_RANDOMIZE && !currentDrawing.getNonce()) { if (randomizeIds === RandomizeModes.ALWAYS_RANDOMIZE && !currentDrawing.getNonce()) {
currentDrawing.setNonce(Math.floor(Math.random() * 100001)); currentDrawing.setNonce(Math.floor(Math.random() * 100001));
} else if (randomizeIds === RandomizeModes.NEVER_RANDOMIZE && currentDrawing.getNonce()) { } else if (randomizeIds === RandomizeModes.NEVER_RANDOMIZE && currentDrawing.getNonce()) {
currentDrawing.clearNonce(); currentDrawing.clearNonce();
} }
}; };
/** /**
@@ -57,72 +57,72 @@ svgedit.draw.randomizeIds = function (enableRandomization, currentDrawing) {
* @param {String=svg_} [optIdPrefix] - The ID prefix to use. * @param {String=svg_} [optIdPrefix] - The ID prefix to use.
*/ */
svgedit.draw.Drawing = function (svgElem, optIdPrefix) { svgedit.draw.Drawing = function (svgElem, optIdPrefix) {
if (!svgElem || !svgElem.tagName || !svgElem.namespaceURI || if (!svgElem || !svgElem.tagName || !svgElem.namespaceURI ||
svgElem.tagName !== 'svg' || svgElem.namespaceURI !== NS.SVG) { svgElem.tagName !== 'svg' || svgElem.namespaceURI !== NS.SVG) {
throw new Error('Error: svgedit.draw.Drawing instance initialized without a <svg> element'); throw new Error('Error: svgedit.draw.Drawing instance initialized without a <svg> element');
} }
/** /**
* The SVG DOM Element that represents this drawing. * The SVG DOM Element that represents this drawing.
* @type {SVGSVGElement} * @type {SVGSVGElement}
*/ */
this.svgElem_ = svgElem; this.svgElem_ = svgElem;
/** /**
* The latest object number used in this drawing. * The latest object number used in this drawing.
* @type {number} * @type {number}
*/ */
this.obj_num = 0; this.obj_num = 0;
/** /**
* The prefix to prepend to each element id in the drawing. * The prefix to prepend to each element id in the drawing.
* @type {String} * @type {String}
*/ */
this.idPrefix = optIdPrefix || 'svg_'; this.idPrefix = optIdPrefix || 'svg_';
/** /**
* An array of released element ids to immediately reuse. * An array of released element ids to immediately reuse.
* @type {Array.<number>} * @type {Array.<number>}
*/ */
this.releasedNums = []; this.releasedNums = [];
/** /**
* The z-ordered array of Layer objects. Each layer has a name * The z-ordered array of Layer objects. Each layer has a name
* and group element. * and group element.
* The first layer is the one at the bottom of the rendering. * The first layer is the one at the bottom of the rendering.
* @type {Array.<Layer>} * @type {Array.<Layer>}
*/ */
this.all_layers = []; this.all_layers = [];
/** /**
* Map of all_layers by name. * Map of all_layers by name.
* *
* Note: Layers are ordered, but referenced externally by name; so, we need both container * 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). * types depending on which function is called (i.e. all_layers and layer_map).
* *
* @type {Object.<string, Layer>} * @type {Object.<string, Layer>}
*/ */
this.layer_map = {}; this.layer_map = {};
/** /**
* The current layer being used. * The current layer being used.
* @type {Layer} * @type {Layer}
*/ */
this.current_layer = null; this.current_layer = null;
/** /**
* The nonce to use to uniquely identify elements across drawings. * The nonce to use to uniquely identify elements across drawings.
* @type {!String} * @type {!String}
*/ */
this.nonce_ = ''; this.nonce_ = '';
var n = this.svgElem_.getAttributeNS(NS.SE, 'nonce'); var n = this.svgElem_.getAttributeNS(NS.SE, 'nonce');
// If already set in the DOM, use the nonce throughout the document // If already set in the DOM, use the nonce throughout the document
// else, if randomizeIds(true) has been called, create and set the nonce. // else, if randomizeIds(true) has been called, create and set the nonce.
if (!!n && randomizeIds !== RandomizeModes.NEVER_RANDOMIZE) { if (!!n && randomizeIds !== RandomizeModes.NEVER_RANDOMIZE) {
this.nonce_ = n; this.nonce_ = n;
} else if (randomizeIds === RandomizeModes.ALWAYS_RANDOMIZE) { } else if (randomizeIds === RandomizeModes.ALWAYS_RANDOMIZE) {
this.setNonce(Math.floor(Math.random() * 100001)); 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 * @returns {Element} SVG element within the root SVGSVGElement
*/ */
svgedit.draw.Drawing.prototype.getElem_ = function (id) { svgedit.draw.Drawing.prototype.getElem_ = function (id) {
if (this.svgElem_.querySelector) { if (this.svgElem_.querySelector) {
// querySelector lookup // querySelector lookup
return this.svgElem_.querySelector('#' + id); return this.svgElem_.querySelector('#' + id);
} }
// jQuery lookup: twice as slow as xpath in FF // jQuery lookup: twice as slow as xpath in FF
return $(this.svgElem_).find('[id=' + id + ']')[0]; return $(this.svgElem_).find('[id=' + id + ']')[0];
}; };
/** /**
* @returns {SVGSVGElement} * @returns {SVGSVGElement}
*/ */
svgedit.draw.Drawing.prototype.getSvgElem = function () { svgedit.draw.Drawing.prototype.getSvgElem = function () {
return this.svgElem_; return this.svgElem_;
}; };
/** /**
* @returns {!string|number} The previously set nonce * @returns {!string|number} The previously set nonce
*/ */
svgedit.draw.Drawing.prototype.getNonce = function () { svgedit.draw.Drawing.prototype.getNonce = function () {
return this.nonce_; return this.nonce_;
}; };
/** /**
* @param {!string|number} n The nonce to set * @param {!string|number} n The nonce to set
*/ */
svgedit.draw.Drawing.prototype.setNonce = function (n) { svgedit.draw.Drawing.prototype.setNonce = function (n) {
this.svgElem_.setAttributeNS(NS.XMLNS, 'xmlns:se', NS.SE); this.svgElem_.setAttributeNS(NS.XMLNS, 'xmlns:se', NS.SE);
this.svgElem_.setAttributeNS(NS.SE, 'se:nonce', n); this.svgElem_.setAttributeNS(NS.SE, 'se:nonce', n);
this.nonce_ = n; this.nonce_ = n;
}; };
/** /**
* Clears any previously set nonce * Clears any previously set nonce
*/ */
svgedit.draw.Drawing.prototype.clearNonce = function () { svgedit.draw.Drawing.prototype.clearNonce = function () {
// We deliberately leave any se:nonce attributes alone, // We deliberately leave any se:nonce attributes alone,
// we just don't use it to randomize ids. // we just don't use it to randomize ids.
this.nonce_ = ''; this.nonce_ = '';
}; };
/** /**
@@ -175,9 +175,9 @@ svgedit.draw.Drawing.prototype.clearNonce = function () {
* @return {String} The latest object Id. * @return {String} The latest object Id.
*/ */
svgedit.draw.Drawing.prototype.getId = function () { svgedit.draw.Drawing.prototype.getId = function () {
return this.nonce_ return this.nonce_
? this.idPrefix + this.nonce_ + '_' + this.obj_num ? this.idPrefix + this.nonce_ + '_' + this.obj_num
: this.idPrefix + 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. * @return {String} The next object Id to use.
*/ */
svgedit.draw.Drawing.prototype.getNextId = function () { svgedit.draw.Drawing.prototype.getNextId = function () {
var oldObjNum = this.obj_num; var oldObjNum = this.obj_num;
var restoreOldObjNum = false; var restoreOldObjNum = false;
// If there are any released numbers in the release stack, // If there are any released numbers in the release stack,
// use the last one instead of the next obj_num. // use the last one instead of the next obj_num.
// We need to temporarily use obj_num as that is what getId() depends on. // We need to temporarily use obj_num as that is what getId() depends on.
if (this.releasedNums.length > 0) { if (this.releasedNums.length > 0) {
this.obj_num = this.releasedNums.pop(); this.obj_num = this.releasedNums.pop();
restoreOldObjNum = true; restoreOldObjNum = true;
} else { } else {
// If we are not using a released id, then increment the obj_num. // If we are not using a released id, then increment the obj_num.
this.obj_num++; this.obj_num++;
} }
// Ensure the ID does not exist. // Ensure the ID does not exist.
var id = this.getId(); var id = this.getId();
while (this.getElem_(id)) { while (this.getElem_(id)) {
if (restoreOldObjNum) { if (restoreOldObjNum) {
this.obj_num = oldObjNum; this.obj_num = oldObjNum;
restoreOldObjNum = false; restoreOldObjNum = false;
} }
this.obj_num++; this.obj_num++;
id = this.getId(); id = this.getId();
} }
// Restore the old object number if required. // Restore the old object number if required.
if (restoreOldObjNum) { if (restoreOldObjNum) {
this.obj_num = oldObjNum; this.obj_num = oldObjNum;
} }
return id; 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. * @returns {boolean} True if the id was valid to be released, false otherwise.
*/ */
svgedit.draw.Drawing.prototype.releaseId = function (id) { svgedit.draw.Drawing.prototype.releaseId = function (id) {
// confirm if this is a valid id for this Document, else return false // confirm if this is a valid id for this Document, else return false
var front = this.idPrefix + (this.nonce_ ? this.nonce_ + '_' : ''); var front = this.idPrefix + (this.nonce_ ? this.nonce_ + '_' : '');
if (typeof id !== 'string' || id.indexOf(front) !== 0) { if (typeof id !== 'string' || id.indexOf(front) !== 0) {
return false; return false;
} }
// extract the obj_num of this id // extract the obj_num of this id
var num = parseInt(id.substr(front.length), 10); var num = parseInt(id.substr(front.length), 10);
// if we didn't get a positive number or we already released this number // if we didn't get a positive number or we already released this number
// then return false. // then return false.
if (typeof num !== 'number' || num <= 0 || this.releasedNums.indexOf(num) !== -1) { if (typeof num !== 'number' || num <= 0 || this.releasedNums.indexOf(num) !== -1) {
return false; return false;
} }
// push the released number into the released queue // push the released number into the released queue
this.releasedNums.push(num); 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. * @returns {integer} The number of layers in the current drawing.
*/ */
svgedit.draw.Drawing.prototype.getNumLayers = function () { 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 * @param {string} name - The layer name to check
*/ */
svgedit.draw.Drawing.prototype.hasLayer = function (name) { 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) * @returns {string} The name of the ith layer (or the empty string if none found)
*/ */
svgedit.draw.Drawing.prototype.getLayerName = function (i) { 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. * @returns {SVGGElement} The SVGGElement representing the current layer.
*/ */
svgedit.draw.Drawing.prototype.getCurrentLayer = function () { 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. * @returns {SVGGElement} The SVGGElement representing the named layer or null.
*/ */
svgedit.draw.Drawing.prototype.getLayerByName = function (name) { svgedit.draw.Drawing.prototype.getLayerByName = function (name) {
var layer = this.layer_map[name]; var layer = this.layer_map[name];
return layer ? layer.getGroup() : null; 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). * @returns {string} The name of the currently active layer (or the empty string if none found).
*/ */
svgedit.draw.Drawing.prototype.getCurrentLayerName = function () { 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. * @returns {string|null} The new name if changed; otherwise, null.
*/ */
svgedit.draw.Drawing.prototype.setCurrentLayerName = function (name, hrService) { svgedit.draw.Drawing.prototype.setCurrentLayerName = function (name, hrService) {
var finalName = null; var finalName = null;
if (this.current_layer) { if (this.current_layer) {
var oldName = this.current_layer.getName(); var oldName = this.current_layer.getName();
finalName = this.current_layer.setName(name, hrService); finalName = this.current_layer.setName(name, hrService);
if (finalName) { if (finalName) {
delete this.layer_map[oldName]; delete this.layer_map[oldName];
this.layer_map[finalName] = this.current_layer; this.layer_map[finalName] = this.current_layer;
} }
} }
return finalName; 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. * @returns {Object} If the name was changed, returns {title:SVGGElement, previousName:string}; otherwise null.
*/ */
svgedit.draw.Drawing.prototype.setCurrentLayerPosition = function (newpos) { svgedit.draw.Drawing.prototype.setCurrentLayerPosition = function (newpos) {
var layerCount = this.getNumLayers(); var layerCount = this.getNumLayers();
if (!this.current_layer || newpos < 0 || newpos >= layerCount) { if (!this.current_layer || newpos < 0 || newpos >= layerCount) {
return null; return null;
} }
var oldpos; var oldpos;
for (oldpos = 0; oldpos < layerCount; ++oldpos) { for (oldpos = 0; oldpos < layerCount; ++oldpos) {
if (this.all_layers[oldpos] === this.current_layer) { break; } if (this.all_layers[oldpos] === this.current_layer) { break; }
} }
// some unknown error condition (current_layer not in all_layers) // some unknown error condition (current_layer not in all_layers)
if (oldpos === layerCount) { return null; } if (oldpos === layerCount) { return null; }
if (oldpos !== newpos) { if (oldpos !== newpos) {
// if our new position is below us, we need to insert before the node after newpos // if our new position is below us, we need to insert before the node after newpos
var refGroup = null; var refGroup = null;
var currentGroup = this.current_layer.getGroup(); var currentGroup = this.current_layer.getGroup();
var oldNextSibling = currentGroup.nextSibling; var oldNextSibling = currentGroup.nextSibling;
if (newpos > oldpos) { if (newpos > oldpos) {
if (newpos < layerCount - 1) { if (newpos < layerCount - 1) {
refGroup = this.all_layers[newpos + 1].getGroup(); refGroup = this.all_layers[newpos + 1].getGroup();
} }
// if our new position is above us, we need to insert before the node at newpos // if our new position is above us, we need to insert before the node at newpos
} else { } else {
refGroup = this.all_layers[newpos].getGroup(); refGroup = this.all_layers[newpos].getGroup();
} }
this.svgElem_.insertBefore(currentGroup, refGroup); this.svgElem_.insertBefore(currentGroup, refGroup);
this.identifyLayers(); this.identifyLayers();
this.setCurrentLayer(this.getLayerName(newpos)); this.setCurrentLayer(this.getLayerName(newpos));
return { return {
currentGroup: currentGroup, currentGroup: currentGroup,
oldNextSibling: oldNextSibling oldNextSibling: oldNextSibling
}; };
} }
return null; return null;
}; };
svgedit.draw.Drawing.prototype.mergeLayer = function (hrService) { svgedit.draw.Drawing.prototype.mergeLayer = function (hrService) {
var currentGroup = this.current_layer.getGroup(); var currentGroup = this.current_layer.getGroup();
var prevGroup = $(currentGroup).prev()[0]; var prevGroup = $(currentGroup).prev()[0];
if (!prevGroup) { return; } if (!prevGroup) { return; }
hrService.startBatchCommand('Merge Layer'); hrService.startBatchCommand('Merge Layer');
var layerNextSibling = currentGroup.nextSibling; var layerNextSibling = currentGroup.nextSibling;
hrService.removeElement(currentGroup, layerNextSibling, this.svgElem_); hrService.removeElement(currentGroup, layerNextSibling, this.svgElem_);
while (currentGroup.firstChild) { while (currentGroup.firstChild) {
var child = currentGroup.firstChild; var child = currentGroup.firstChild;
if (child.localName === 'title') { if (child.localName === 'title') {
hrService.removeElement(child, child.nextSibling, currentGroup); hrService.removeElement(child, child.nextSibling, currentGroup);
currentGroup.removeChild(child); currentGroup.removeChild(child);
continue; continue;
} }
var oldNextSibling = child.nextSibling; var oldNextSibling = child.nextSibling;
prevGroup.appendChild(child); prevGroup.appendChild(child);
hrService.moveElement(child, oldNextSibling, currentGroup); hrService.moveElement(child, oldNextSibling, currentGroup);
} }
// Remove current layer's group // Remove current layer's group
this.current_layer.removeGroup(); this.current_layer.removeGroup();
// Remove the current layer and set the previous layer as the new current layer // Remove the current layer and set the previous layer as the new current layer
var index = this.all_layers.indexOf(this.current_layer); var index = this.all_layers.indexOf(this.current_layer);
if (index > 0) { if (index > 0) {
var name = this.current_layer.getName(); var name = this.current_layer.getName();
this.current_layer = this.all_layers[index - 1]; this.current_layer = this.all_layers[index - 1];
this.all_layers.splice(index, 1); this.all_layers.splice(index, 1);
delete this.layer_map[name]; delete this.layer_map[name];
} }
hrService.endBatchCommand(); hrService.endBatchCommand();
}; };
svgedit.draw.Drawing.prototype.mergeAllLayers = function (hrService) { svgedit.draw.Drawing.prototype.mergeAllLayers = function (hrService) {
// Set the current layer to the last layer. // Set the current layer to the last layer.
this.current_layer = this.all_layers[this.all_layers.length - 1]; this.current_layer = this.all_layers[this.all_layers.length - 1];
hrService.startBatchCommand('Merge all Layers'); hrService.startBatchCommand('Merge all Layers');
while (this.all_layers.length > 1) { while (this.all_layers.length > 1) {
this.mergeLayer(hrService); this.mergeLayer(hrService);
} }
hrService.endBatchCommand(); hrService.endBatchCommand();
}; };
/** /**
@@ -412,16 +412,16 @@ svgedit.draw.Drawing.prototype.mergeAllLayers = function (hrService) {
* @returns {boolean} true if the current layer was switched, otherwise false * @returns {boolean} true if the current layer was switched, otherwise false
*/ */
svgedit.draw.Drawing.prototype.setCurrentLayer = function (name) { svgedit.draw.Drawing.prototype.setCurrentLayer = function (name) {
var layer = this.layer_map[name]; var layer = this.layer_map[name];
if (layer) { if (layer) {
if (this.current_layer) { if (this.current_layer) {
this.current_layer.deactivate(); this.current_layer.deactivate();
} }
this.current_layer = layer; this.current_layer = layer;
this.current_layer.activate(); this.current_layer.activate();
return true; return true;
} }
return false; return false;
}; };
/** /**
@@ -430,12 +430,12 @@ svgedit.draw.Drawing.prototype.setCurrentLayer = function (name) {
* @returns {SVGGElement} The SVGGElement of the layer removed or null. * @returns {SVGGElement} The SVGGElement of the layer removed or null.
*/ */
svgedit.draw.Drawing.prototype.deleteCurrentLayer = function () { svgedit.draw.Drawing.prototype.deleteCurrentLayer = function () {
if (this.current_layer && this.getNumLayers() > 1) { if (this.current_layer && this.getNumLayers() > 1) {
var oldLayerGroup = this.current_layer.removeGroup(); var oldLayerGroup = this.current_layer.removeGroup();
this.identifyLayers(); this.identifyLayers();
return oldLayerGroup; return oldLayerGroup;
} }
return null; return null;
}; };
/** /**
@@ -444,13 +444,13 @@ svgedit.draw.Drawing.prototype.deleteCurrentLayer = function () {
* @returns {string} The layer name or empty string. * @returns {string} The layer name or empty string.
*/ */
function findLayerNameInGroup (group) { function findLayerNameInGroup (group) {
var name = $('title', group).text(); var name = $('title', group).text();
// Hack for Opera 10.60 // Hack for Opera 10.60
if (!name && svgedit.browser.isOpera() && group.querySelectorAll) { if (!name && svgedit.browser.isOpera() && group.querySelectorAll) {
name = $(group.querySelectorAll('title')).text(); name = $(group.querySelectorAll('title')).text();
} }
return name; return name;
} }
/** /**
@@ -459,10 +459,10 @@ function findLayerNameInGroup (group) {
* @returns {string} - The new name. * @returns {string} - The new name.
*/ */
function getNewLayerName (existingLayerNames) { function getNewLayerName (existingLayerNames) {
var i = 1; var i = 1;
// TODO(codedread): What about internationalization of "Layer"? // TODO(codedread): What about internationalization of "Layer"?
while (existingLayerNames.indexOf(('Layer ' + i)) >= 0) { i++; } while (existingLayerNames.indexOf(('Layer ' + i)) >= 0) { i++; }
return 'Layer ' + i; return 'Layer ' + i;
} }
/** /**
@@ -470,46 +470,46 @@ function getNewLayerName (existingLayerNames) {
* top-most layer (last <g> child of this drawing). * top-most layer (last <g> child of this drawing).
*/ */
svgedit.draw.Drawing.prototype.identifyLayers = function () { svgedit.draw.Drawing.prototype.identifyLayers = function () {
this.all_layers = []; this.all_layers = [];
this.layer_map = {}; this.layer_map = {};
var numchildren = this.svgElem_.childNodes.length; var numchildren = this.svgElem_.childNodes.length;
// loop through all children of SVG element // loop through all children of SVG element
var orphans = [], layernames = []; var orphans = [], layernames = [];
var layer = null; var layer = null;
var childgroups = false; var childgroups = false;
for (var i = 0; i < numchildren; ++i) { for (var i = 0; i < numchildren; ++i) {
var child = this.svgElem_.childNodes.item(i); var child = this.svgElem_.childNodes.item(i);
// for each g, find its layer name // for each g, find its layer name
if (child && child.nodeType === 1) { if (child && child.nodeType === 1) {
if (child.tagName === 'g') { if (child.tagName === 'g') {
childgroups = true; childgroups = true;
var name = findLayerNameInGroup(child); var name = findLayerNameInGroup(child);
if (name) { if (name) {
layernames.push(name); layernames.push(name);
layer = new svgedit.draw.Layer(name, child); layer = new svgedit.draw.Layer(name, child);
this.all_layers.push(layer); this.all_layers.push(layer);
this.layer_map[name] = layer; this.layer_map[name] = layer;
} else { } else {
// if group did not have a name, it is an orphan // if group did not have a name, it is an orphan
orphans.push(child); orphans.push(child);
} }
} else if (~visElems.indexOf(child.nodeName)) { } else if (~visElems.indexOf(child.nodeName)) {
// Child is "visible" (i.e. not a <title> or <defs> element), so it is an orphan // Child is "visible" (i.e. not a <title> or <defs> element), so it is an orphan
orphans.push(child); orphans.push(child);
} }
} }
} }
// If orphans or no layers found, create a new layer and add all the orphans to it // If orphans or no layers found, create a new layer and add all the orphans to it
if (orphans.length > 0 || !childgroups) { if (orphans.length > 0 || !childgroups) {
layer = new svgedit.draw.Layer(getNewLayerName(layernames), null, this.svgElem_); layer = new svgedit.draw.Layer(getNewLayerName(layernames), null, this.svgElem_);
layer.appendChildren(orphans); layer.appendChildren(orphans);
this.all_layers.push(layer); this.all_layers.push(layer);
this.layer_map[name] = layer; this.layer_map[name] = layer;
} else { } else {
layer.activate(); layer.activate();
} }
this.current_layer = layer; this.current_layer = layer;
}; };
/** /**
@@ -521,27 +521,27 @@ svgedit.draw.Drawing.prototype.identifyLayers = function () {
* also the current layer of this drawing. * also the current layer of this drawing.
*/ */
svgedit.draw.Drawing.prototype.createLayer = function (name, hrService) { svgedit.draw.Drawing.prototype.createLayer = function (name, hrService) {
if (this.current_layer) { if (this.current_layer) {
this.current_layer.deactivate(); this.current_layer.deactivate();
} }
// Check for duplicate name. // Check for duplicate name.
if (name === undefined || name === null || name === '' || this.layer_map[name]) { if (name === undefined || name === null || name === '' || this.layer_map[name]) {
name = getNewLayerName(Object.keys(this.layer_map)); name = getNewLayerName(Object.keys(this.layer_map));
} }
// Crate new layer and add to DOM as last layer // Crate new layer and add to DOM as last layer
var layer = new svgedit.draw.Layer(name, null, this.svgElem_); 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. // Like to assume hrService exists, but this is backwards compatible with old version of createLayer.
if (hrService) { if (hrService) {
hrService.startBatchCommand('Create Layer'); hrService.startBatchCommand('Create Layer');
hrService.insertElement(layer.getGroup()); hrService.insertElement(layer.getGroup());
hrService.endBatchCommand(); hrService.endBatchCommand();
} }
this.all_layers.push(layer); this.all_layers.push(layer);
this.layer_map[name] = layer; this.layer_map[name] = layer;
this.current_layer = layer; this.current_layer = layer;
return layer.getGroup(); return layer.getGroup();
}; };
/** /**
@@ -552,43 +552,43 @@ svgedit.draw.Drawing.prototype.createLayer = function (name, hrService) {
* also the current layer of this drawing. * also the current layer of this drawing.
*/ */
svgedit.draw.Drawing.prototype.cloneLayer = function (name, hrService) { svgedit.draw.Drawing.prototype.cloneLayer = function (name, hrService) {
if (!this.current_layer) { return null; } if (!this.current_layer) { return null; }
this.current_layer.deactivate(); this.current_layer.deactivate();
// Check for duplicate name. // Check for duplicate name.
if (name === undefined || name === null || name === '' || this.layer_map[name]) { if (name === undefined || name === null || name === '' || this.layer_map[name]) {
name = getNewLayerName(Object.keys(this.layer_map)); name = getNewLayerName(Object.keys(this.layer_map));
} }
// Create new group and add to DOM just after current_layer // Create new group and add to DOM just after current_layer
var currentGroup = this.current_layer.getGroup(); var currentGroup = this.current_layer.getGroup();
var layer = new svgedit.draw.Layer(name, currentGroup, this.svgElem_); var layer = new svgedit.draw.Layer(name, currentGroup, this.svgElem_);
var group = layer.getGroup(); var group = layer.getGroup();
// Clone children // Clone children
var children = currentGroup.childNodes; var children = currentGroup.childNodes;
var index; var index;
for (index = 0; index < children.length; index++) { for (index = 0; index < children.length; index++) {
var ch = children[index]; var ch = children[index];
if (ch.localName === 'title') { continue; } if (ch.localName === 'title') { continue; }
group.appendChild(this.copyElem(ch)); group.appendChild(this.copyElem(ch));
} }
if (hrService) { if (hrService) {
hrService.startBatchCommand('Duplicate Layer'); hrService.startBatchCommand('Duplicate Layer');
hrService.insertElement(group); hrService.insertElement(group);
hrService.endBatchCommand(); hrService.endBatchCommand();
} }
// Update layer containers and current_layer. // Update layer containers and current_layer.
index = this.all_layers.indexOf(this.current_layer); index = this.all_layers.indexOf(this.current_layer);
if (index >= 0) { if (index >= 0) {
this.all_layers.splice(index + 1, 0, layer); this.all_layers.splice(index + 1, 0, layer);
} else { } else {
this.all_layers.push(layer); this.all_layers.push(layer);
} }
this.layer_map[name] = layer; this.layer_map[name] = layer;
this.current_layer = layer; this.current_layer = layer;
return group; 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. * @returns {boolean} The visibility state of the layer, or false if the layer name was invalid.
*/ */
svgedit.draw.Drawing.prototype.getLayerVisibility = function (layername) { svgedit.draw.Drawing.prototype.getLayerVisibility = function (layername) {
var layer = this.layer_map[layername]; var layer = this.layer_map[layername];
return layer ? layer.isVisible() : false; return layer ? layer.isVisible() : false;
}; };
/** /**
@@ -612,13 +612,13 @@ svgedit.draw.Drawing.prototype.getLayerVisibility = function (layername) {
* layername was valid, otherwise null. * layername was valid, otherwise null.
*/ */
svgedit.draw.Drawing.prototype.setLayerVisibility = function (layername, bVisible) { svgedit.draw.Drawing.prototype.setLayerVisibility = function (layername, bVisible) {
if (typeof bVisible !== 'boolean') { if (typeof bVisible !== 'boolean') {
return null; return null;
} }
var layer = this.layer_map[layername]; var layer = this.layer_map[layername];
if (!layer) { return null; } if (!layer) { return null; }
layer.setVisible(bVisible); layer.setVisible(bVisible);
return layer.getGroup(); return layer.getGroup();
}; };
/** /**
@@ -628,9 +628,9 @@ svgedit.draw.Drawing.prototype.setLayerVisibility = function (layername, bVisibl
* if layername is not a valid layer * if layername is not a valid layer
*/ */
svgedit.draw.Drawing.prototype.getLayerOpacity = function (layername) { svgedit.draw.Drawing.prototype.getLayerOpacity = function (layername) {
var layer = this.layer_map[layername]; var layer = this.layer_map[layername];
if (!layer) { return null; } if (!layer) { return null; }
return layer.getOpacity(); 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 * @param {number} opacity - A float value in the range 0.0-1.0
*/ */
svgedit.draw.Drawing.prototype.setLayerOpacity = function (layername, opacity) { svgedit.draw.Drawing.prototype.setLayerOpacity = function (layername, opacity) {
if (typeof opacity !== 'number' || opacity < 0.0 || opacity > 1.0) { if (typeof opacity !== 'number' || opacity < 0.0 || opacity > 1.0) {
return; return;
} }
var layer = this.layer_map[layername]; var layer = this.layer_map[layername];
if (layer) { if (layer) {
layer.setOpacity(opacity); layer.setOpacity(opacity);
} }
}; };
/** /**
@@ -656,8 +656,8 @@ svgedit.draw.Drawing.prototype.setLayerOpacity = function (layername, opacity) {
* @returns {Element} * @returns {Element}
*/ */
svgedit.draw.Drawing.prototype.copyElem = function (el) { svgedit.draw.Drawing.prototype.copyElem = function (el) {
var self = this; var self = this;
var getNextIdClosure = function () { return self.getNextId(); }; var getNextIdClosure = function () { return self.getNextId(); };
return svgedit.utilities.copyElem(el, getNextIdClosure); return svgedit.utilities.copyElem(el, getNextIdClosure);
}; };
}()); }());

View File

@@ -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 // Todo: Get rid of frame.contentWindow dependencies so can be more easily adjusted to work cross-domain
$(function () { $(function () {
'use strict'; 'use strict';
var svgCanvas = null; var svgCanvas = null;
var frame; var frame;
initEmbed = function () { initEmbed = function () {
var doc, mainButton; var doc, mainButton;
svgCanvas = new EmbeddedSVGEdit(frame); svgCanvas = new EmbeddedSVGEdit(frame);
// Hide main button, as we will be controlling new, load, save, etc. from the host document // Hide main button, as we will be controlling new, load, save, etc. from the host document
doc = frame.contentDocument || frame.contentWindow.document; doc = frame.contentDocument || frame.contentWindow.document;
mainButton = doc.getElementById('main_button'); mainButton = doc.getElementById('main_button');
mainButton.style.display = 'none'; mainButton.style.display = 'none';
}; };
function handleSvgData (data, error) { function handleSvgData (data, error) {
if (error) { if (error) {
alert('error ' + error); alert('error ' + error);
} else { } else {
alert('Congratulations. Your SVG string is back in the host page, do with it what you will\n\n' + data); alert('Congratulations. Your SVG string is back in the host page, do with it what you will\n\n' + data);
} }
} }
function loadSvg () { 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</title><rect stroke-width="5" stroke="#000000" fill="#FF0000" id="svg_1" height="35" width="51" y="35" x="32"/><ellipse ry="15" rx="24" stroke-width="5" stroke="#000000" fill="#0000ff" id="svg_2" cy="60" cx="66"/></g></svg>'; 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</title><rect stroke-width="5" stroke="#000000" fill="#FF0000" id="svg_1" height="35" width="51" y="35" x="32"/><ellipse ry="15" rx="24" stroke-width="5" stroke="#000000" fill="#0000ff" id="svg_2" cy="60" cx="66"/></g></svg>';
svgCanvas.setSvgString(svgexample); svgCanvas.setSvgString(svgexample);
} }
function saveSvg () { function saveSvg () {
svgCanvas.getSvgString()(handleSvgData); svgCanvas.getSvgString()(handleSvgData);
} }
function exportPNG () { function exportPNG () {
var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage; var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage;
var exportWindow = window.open( var exportWindow = window.open(
'data:text/html;charset=utf-8,' + encodeURIComponent('<title>' + str + '</title><h1>' + str + '</h1>'), 'data:text/html;charset=utf-8,' + encodeURIComponent('<title>' + str + '</title><h1>' + str + '</h1>'),
'svg-edit-exportWindow' 'svg-edit-exportWindow'
); );
svgCanvas.rasterExport('PNG', null, exportWindow.name); svgCanvas.rasterExport('PNG', null, exportWindow.name);
} }
function exportPDF () { function exportPDF () {
var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage; var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage;
/** /**
// If you want to handle the PDF blob yourself, do as follows // If you want to handle the PDF blob yourself, do as follows
svgCanvas.bind('exportedPDF', function (win, data) { svgCanvas.bind('exportedPDF', function (win, data) {
alert(data.dataurlstring); alert(data.dataurlstring);
}); });
svgCanvas.exportPDF(); // Accepts two args: optionalWindowName supplied back to bound exportPDF handler and optionalOutputType (defaults to dataurlstring) svgCanvas.exportPDF(); // Accepts two args: optionalWindowName supplied back to bound exportPDF handler and optionalOutputType (defaults to dataurlstring)
return; return;
*/ */
var exportWindow = window.open( var exportWindow = window.open(
'data:text/html;charset=utf-8,' + encodeURIComponent('<title>' + str + '</title><h1>' + str + '</h1>'), 'data:text/html;charset=utf-8,' + encodeURIComponent('<title>' + str + '</title><h1>' + str + '</h1>'),
'svg-edit-exportWindow' 'svg-edit-exportWindow'
); );
svgCanvas.exportPDF(exportWindow.name); svgCanvas.exportPDF(exportWindow.name);
} }
// Add event handlers // Add event handlers
$('#load').click(loadSvg); $('#load').click(loadSvg);
$('#save').click(saveSvg); $('#save').click(saveSvg);
$('#exportPNG').click(exportPNG); $('#exportPNG').click(exportPNG);
$('#exportPDF').click(exportPDF); $('#exportPDF').click(exportPDF);
$('body').append( $('body').append(
$('<iframe src="svg-editor.html?extensions=ext-xdomain-messaging.js' + $('<iframe src="svg-editor.html?extensions=ext-xdomain-messaging.js' +
window.location.href.replace(/\?(.*)$/, '&$1') + // Append arguments to this file onto the iframe window.location.href.replace(/\?(.*)$/, '&$1') + // Append arguments to this file onto the iframe
'" width="900px" height="600px" id="svgedit" onload="initEmbed();"></iframe>' '" width="900px" height="600px" id="svgedit" onload="initEmbed();"></iframe>'
) )
); );
frame = document.getElementById('svgedit'); frame = document.getElementById('svgedit');
}); });

View File

@@ -1,17 +1,17 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Embed API</title> <title>Embed API</title>
<script src="jquery.js"></script> <script src="jquery.js"></script>
<script src="embedapi.js"></script> <script src="embedapi.js"></script>
<script src="embedapi-dom.js"></script> <script src="embedapi-dom.js"></script>
</head> </head>
<body> <body>
<button id="load">Load example</button> <button id="load">Load example</button>
<button id="save">Save data</button> <button id="save">Save data</button>
<button id="exportPNG">Export data to PNG</button> <button id="exportPNG">Export data to PNG</button>
<button id="exportPDF">Export data to PDF</button> <button id="exportPDF">Export data to PDF</button>
<br/> <br/>
</body> </body>
</html> </html>

View File

@@ -10,11 +10,11 @@ var svgCanvas = new EmbeddedSVGEdit(window.frames.svgedit);
svgCanvas.setSvgString('string') svgCanvas.setSvgString('string')
- Or if a callback is needed: - Or if a callback is needed:
svgCanvas.setSvgString('string')(function(data, error){ svgCanvas.setSvgString('string')(function(data, error){
if (error){ if (error){
// There was an error // There was an error
} else{ } else{
// Handle data // Handle data
} }
}) })
Everything is done with the same API as the real svg-edit, 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; var cbid = 0;
function getCallbackSetter (d) { function getCallbackSetter (d) {
return function () { return function () {
var t = this, // New callback var t = this, // New callback
args = [].slice.call(arguments), args = [].slice.call(arguments),
cbid = t.send(d, args, function () {}); // The callback (currently it's nothing, but will be set later) cbid = t.send(d, args, function () {}); // The callback (currently it's nothing, but will be set later)
return function (newcallback) { return function (newcallback) {
t.callbacks[cbid] = newcallback; // Set callback t.callbacks[cbid] = newcallback; // Set callback
}; };
}; };
} }
/* /*
@@ -59,38 +59,38 @@ function getCallbackSetter (d) {
* of same domain control * of same domain control
*/ */
function addCallback (t, data) { function addCallback (t, data) {
var result = data.result || data.error, var result = data.result || data.error,
cbid = data.id; cbid = data.id;
if (t.callbacks[cbid]) { if (t.callbacks[cbid]) {
if (data.result) { if (data.result) {
t.callbacks[cbid](result); t.callbacks[cbid](result);
} else { } else {
t.callbacks[cbid](result, 'error'); t.callbacks[cbid](result, 'error');
} }
} }
} }
function messageListener (e) { function messageListener (e) {
// We accept and post strings as opposed to objects for the sake of IE9 support; this // We accept and post strings as opposed to objects for the sake of IE9 support; this
// will most likely be changed in the future // will most likely be changed in the future
if (typeof e.data !== 'string') { if (typeof e.data !== 'string') {
return; return;
} }
var allowedOrigins = this.allowedOrigins, var allowedOrigins = this.allowedOrigins,
data = e.data && JSON.parse(e.data); data = e.data && JSON.parse(e.data);
if (!data || typeof data !== 'object' || data.namespace !== 'svg-edit' || if (!data || typeof data !== 'object' || data.namespace !== 'svg-edit' ||
e.source !== this.frame.contentWindow || e.source !== this.frame.contentWindow ||
(allowedOrigins.indexOf('*') === -1 && allowedOrigins.indexOf(e.origin) === -1) (allowedOrigins.indexOf('*') === -1 && allowedOrigins.indexOf(e.origin) === -1)
) { ) {
return; return;
} }
addCallback(this, data); addCallback(this, data);
} }
function getMessageListener (t) { function getMessageListener (t) {
return function (e) { return function (e) {
messageListener.call(t, e); messageListener.call(t, e);
}; };
} }
/** /**
@@ -100,77 +100,77 @@ function getMessageListener (t) {
* If supplied, it should probably be the same as svgEditor's allowedOrigins * If supplied, it should probably be the same as svgEditor's allowedOrigins
*/ */
function EmbeddedSVGEdit (frame, allowedOrigins) { function EmbeddedSVGEdit (frame, allowedOrigins) {
if (!(this instanceof EmbeddedSVGEdit)) { // Allow invocation without 'new' keyword if (!(this instanceof EmbeddedSVGEdit)) { // Allow invocation without 'new' keyword
return new EmbeddedSVGEdit(frame); return new EmbeddedSVGEdit(frame);
} }
this.allowedOrigins = allowedOrigins || []; this.allowedOrigins = allowedOrigins || [];
// Initialize communication // Initialize communication
this.frame = frame; this.frame = frame;
this.callbacks = {}; this.callbacks = {};
// List of functions extracted with this: // List of functions extracted with this:
// Run in firebug on http://svg-edit.googlecode.com/svn/trunk/docs/files/svgcanvas-js.html // 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 // 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', // var functions = ['clearSelection', 'addToSelection', 'removeFromSelection', 'open', 'save', 'getSvgString', 'setSvgString',
// 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility', // 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility',
// 'moveSelectedToLayer', 'clear']; // '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 // 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 svgCanvas = frame.contentWindow.svgCanvas;
// var l = []; for (var i in svgCanvas){ if (typeof svgCanvas[i] == 'function') { l.push(i);} }; // var l = []; for (var i in svgCanvas){ if (typeof svgCanvas[i] == 'function') { l.push(i);} };
// alert("['" + l.join("', '") + "']"); // alert("['" + l.join("', '") + "']");
// Run in svgedit itself // Run in svgedit itself
var i, var i,
functions = [ 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' '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. // TODO: rewrite the following, it's pretty scary.
for (i = 0; i < functions.length; i++) { for (i = 0; i < functions.length; i++) {
this[functions[i]] = getCallbackSetter(functions[i]); this[functions[i]] = getCallbackSetter(functions[i]);
} }
// Older IE may need a polyfill for addEventListener, but so it would for SVG // Older IE may need a polyfill for addEventListener, but so it would for SVG
window.addEventListener('message', getMessageListener(this), false); window.addEventListener('message', getMessageListener(this), false);
} }
EmbeddedSVGEdit.prototype.send = function (name, args, callback) { EmbeddedSVGEdit.prototype.send = function (name, args, callback) {
var t = this; var t = this;
cbid++; cbid++;
this.callbacks[cbid] = callback; this.callbacks[cbid] = callback;
setTimeout((function (cbid) { setTimeout((function (cbid) {
return function () { // Delay for the callback to be set in case its synchronous return function () { // Delay for the callback to be set in case its synchronous
/* /*
* Todo: Handle non-JSON arguments and return values (undefined, * Todo: Handle non-JSON arguments and return values (undefined,
* nonfinite numbers, functions, and built-in objects like Date, * nonfinite numbers, functions, and built-in objects like Date,
* RegExp), etc.? Allow promises instead of callbacks? Review * RegExp), etc.? Allow promises instead of callbacks? Review
* SVG-Edit functions for whether JSON-able parameters can be * SVG-Edit functions for whether JSON-able parameters can be
* made compatile with all API functionality * made compatile with all API functionality
*/ */
// We accept and post strings for the sake of IE9 support // We accept and post strings for the sake of IE9 support
if (window.location.origin === t.frame.contentWindow.location.origin) { if (window.location.origin === t.frame.contentWindow.location.origin) {
// Although we do not really need this API if we are working same // 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 // domain, it could allow us to write in a way that would work
// cross-domain as well, assuming we stick to the argument limitations // cross-domain as well, assuming we stick to the argument limitations
// of the current JSON-based communication API (e.g., not passing // of the current JSON-based communication API (e.g., not passing
// callbacks). We might be able to address these shortcomings; see // callbacks). We might be able to address these shortcomings; see
// the todo elsewhere in this file. // the todo elsewhere in this file.
var message = {id: cbid}, var message = {id: cbid},
svgCanvas = t.frame.contentWindow.svgCanvas; svgCanvas = t.frame.contentWindow.svgCanvas;
try { try {
message.result = svgCanvas[name].apply(svgCanvas, args); message.result = svgCanvas[name].apply(svgCanvas, args);
} catch (err) { } catch (err) {
message.error = err.message; message.error = err.message;
} }
addCallback(t, message); addCallback(t, message);
} else { // Requires the ext-xdomain-messaging.js extension } else { // Requires the ext-xdomain-messaging.js extension
t.frame.contentWindow.postMessage(JSON.stringify({namespace: 'svgCanvas', id: cbid, name: name, args: args}), '*'); t.frame.contentWindow.postMessage(JSON.stringify({namespace: 'svgCanvas', id: cbid, name: name, args: args}), '*');
} }
}; };
}(cbid)), 0); }(cbid)), 0);
return cbid; return cbid;
}; };
window.embedded_svg_edit = EmbeddedSVGEdit; // Export old, deprecated API window.embedded_svg_edit = EmbeddedSVGEdit; // Export old, deprecated API

View File

@@ -17,15 +17,15 @@
'use strict'; 'use strict';
if (!svgedit.history) { if (!svgedit.history) {
svgedit.history = {}; svgedit.history = {};
} }
// Group: Undo/Redo history management // Group: Undo/Redo history management
svgedit.history.HistoryEventTypes = { svgedit.history.HistoryEventTypes = {
BEFORE_APPLY: 'before_apply', BEFORE_APPLY: 'before_apply',
AFTER_APPLY: 'after_apply', AFTER_APPLY: 'after_apply',
BEFORE_UNAPPLY: 'before_unapply', BEFORE_UNAPPLY: 'before_unapply',
AFTER_UNAPPLY: 'after_unapply' AFTER_UNAPPLY: 'after_unapply'
}; };
// var removedElements = {}; // var removedElements = {};
@@ -63,18 +63,18 @@ svgedit.history.HistoryEventTypes = {
* @param {string} [text] - An optional string visible to user related to this change * @param {string} [text] - An optional string visible to user related to this change
*/ */
svgedit.history.MoveElementCommand = function (elem, oldNextSibling, oldParent, text) { svgedit.history.MoveElementCommand = function (elem, oldNextSibling, oldParent, text) {
this.elem = elem; this.elem = elem;
this.text = text ? ('Move ' + elem.tagName + ' to ' + text) : ('Move ' + elem.tagName); this.text = text ? ('Move ' + elem.tagName + ' to ' + text) : ('Move ' + elem.tagName);
this.oldNextSibling = oldNextSibling; this.oldNextSibling = oldNextSibling;
this.oldParent = oldParent; this.oldParent = oldParent;
this.newNextSibling = elem.nextSibling; this.newNextSibling = elem.nextSibling;
this.newParent = elem.parentNode; this.newParent = elem.parentNode;
}; };
svgedit.history.MoveElementCommand.type = function () { return 'svgedit.history.MoveElementCommand'; }; svgedit.history.MoveElementCommand.type = function () { return 'svgedit.history.MoveElementCommand'; };
svgedit.history.MoveElementCommand.prototype.type = svgedit.history.MoveElementCommand.type; svgedit.history.MoveElementCommand.prototype.type = svgedit.history.MoveElementCommand.type;
svgedit.history.MoveElementCommand.prototype.getText = function () { 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} * @param {handleHistoryEvent: function}
*/ */
svgedit.history.MoveElementCommand.prototype.apply = function (handler) { svgedit.history.MoveElementCommand.prototype.apply = function (handler) {
// TODO(codedread): Refactor this common event code into a base HistoryCommand class. // TODO(codedread): Refactor this common event code into a base HistoryCommand class.
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); 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) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
} }
}; };
/** /**
@@ -99,21 +99,21 @@ svgedit.history.MoveElementCommand.prototype.apply = function (handler) {
* @param {handleHistoryEvent: function} * @param {handleHistoryEvent: function}
*/ */
svgedit.history.MoveElementCommand.prototype.unapply = function (handler) { svgedit.history.MoveElementCommand.prototype.unapply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); 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) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
} }
}; };
// Function: svgedit.history.MoveElementCommand.elements // Function: svgedit.history.MoveElementCommand.elements
// Returns array with element associated with this command // Returns array with element associated with this command
svgedit.history.MoveElementCommand.prototype.elements = function () { svgedit.history.MoveElementCommand.prototype.elements = function () {
return [this.elem]; return [this.elem];
}; };
// Class: svgedit.history.InsertElementCommand // Class: svgedit.history.InsertElementCommand
@@ -124,52 +124,52 @@ svgedit.history.MoveElementCommand.prototype.elements = function () {
// elem - The newly added DOM element // elem - The newly added DOM element
// text - An optional string visible to user related to this change // text - An optional string visible to user related to this change
svgedit.history.InsertElementCommand = function (elem, text) { svgedit.history.InsertElementCommand = function (elem, text) {
this.elem = elem; this.elem = elem;
this.text = text || ('Create ' + elem.tagName); this.text = text || ('Create ' + elem.tagName);
this.parent = elem.parentNode; this.parent = elem.parentNode;
this.nextSibling = this.elem.nextSibling; this.nextSibling = this.elem.nextSibling;
}; };
svgedit.history.InsertElementCommand.type = function () { return 'svgedit.history.InsertElementCommand'; }; svgedit.history.InsertElementCommand.type = function () { return 'svgedit.history.InsertElementCommand'; };
svgedit.history.InsertElementCommand.prototype.type = svgedit.history.InsertElementCommand.type; svgedit.history.InsertElementCommand.prototype.type = svgedit.history.InsertElementCommand.type;
// Function: svgedit.history.InsertElementCommand.getText // Function: svgedit.history.InsertElementCommand.getText
svgedit.history.InsertElementCommand.prototype.getText = function () { svgedit.history.InsertElementCommand.prototype.getText = function () {
return this.text; return this.text;
}; };
// Function: svgedit.history.InsertElementCommand.apply // Function: svgedit.history.InsertElementCommand.apply
// Re-Inserts the new element // Re-Inserts the new element
svgedit.history.InsertElementCommand.prototype.apply = function (handler) { svgedit.history.InsertElementCommand.prototype.apply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); 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) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
} }
}; };
// Function: svgedit.history.InsertElementCommand.unapply // Function: svgedit.history.InsertElementCommand.unapply
// Removes the element // Removes the element
svgedit.history.InsertElementCommand.prototype.unapply = function (handler) { svgedit.history.InsertElementCommand.prototype.unapply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
} }
this.parent = this.elem.parentNode; this.parent = this.elem.parentNode;
this.elem = this.elem.parentNode.removeChild(this.elem); this.elem = this.elem.parentNode.removeChild(this.elem);
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
} }
}; };
// Function: svgedit.history.InsertElementCommand.elements // Function: svgedit.history.InsertElementCommand.elements
// Returns array with element associated with this command // Returns array with element associated with this command
svgedit.history.InsertElementCommand.prototype.elements = function () { svgedit.history.InsertElementCommand.prototype.elements = function () {
return [this.elem]; return [this.elem];
}; };
// Class: svgedit.history.RemoveElementCommand // Class: svgedit.history.RemoveElementCommand
@@ -182,62 +182,62 @@ svgedit.history.InsertElementCommand.prototype.elements = function () {
// oldParent - The DOM element's parent // oldParent - The DOM element's parent
// text - An optional string visible to user related to this change // text - An optional string visible to user related to this change
svgedit.history.RemoveElementCommand = function (elem, oldNextSibling, oldParent, text) { svgedit.history.RemoveElementCommand = function (elem, oldNextSibling, oldParent, text) {
this.elem = elem; this.elem = elem;
this.text = text || ('Delete ' + elem.tagName); this.text = text || ('Delete ' + elem.tagName);
this.nextSibling = oldNextSibling; this.nextSibling = oldNextSibling;
this.parent = oldParent; this.parent = oldParent;
// special hack for webkit: remove this element's entry in the svgTransformLists map // special hack for webkit: remove this element's entry in the svgTransformLists map
svgedit.transformlist.removeElementFromListMap(elem); svgedit.transformlist.removeElementFromListMap(elem);
}; };
svgedit.history.RemoveElementCommand.type = function () { return 'svgedit.history.RemoveElementCommand'; }; svgedit.history.RemoveElementCommand.type = function () { return 'svgedit.history.RemoveElementCommand'; };
svgedit.history.RemoveElementCommand.prototype.type = svgedit.history.RemoveElementCommand.type; svgedit.history.RemoveElementCommand.prototype.type = svgedit.history.RemoveElementCommand.type;
// Function: svgedit.history.RemoveElementCommand.getText // Function: svgedit.history.RemoveElementCommand.getText
svgedit.history.RemoveElementCommand.prototype.getText = function () { svgedit.history.RemoveElementCommand.prototype.getText = function () {
return this.text; return this.text;
}; };
// Function: RemoveElementCommand.apply // Function: RemoveElementCommand.apply
// Re-removes the new element // Re-removes the new element
svgedit.history.RemoveElementCommand.prototype.apply = function (handler) { svgedit.history.RemoveElementCommand.prototype.apply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
} }
svgedit.transformlist.removeElementFromListMap(this.elem); svgedit.transformlist.removeElementFromListMap(this.elem);
this.parent = this.elem.parentNode; this.parent = this.elem.parentNode;
this.elem = this.parent.removeChild(this.elem); this.elem = this.parent.removeChild(this.elem);
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
} }
}; };
// Function: RemoveElementCommand.unapply // Function: RemoveElementCommand.unapply
// Re-adds the new element // Re-adds the new element
svgedit.history.RemoveElementCommand.prototype.unapply = function (handler) { svgedit.history.RemoveElementCommand.prototype.unapply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
} }
svgedit.transformlist.removeElementFromListMap(this.elem); svgedit.transformlist.removeElementFromListMap(this.elem);
if (this.nextSibling == null) { if (this.nextSibling == null) {
if (window.console) { if (window.console) {
console.log('Error: reference element was lost'); console.log('Error: reference element was lost');
} }
} }
this.parent.insertBefore(this.elem, this.nextSibling); this.parent.insertBefore(this.elem, this.nextSibling);
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
} }
}; };
// Function: RemoveElementCommand.elements // Function: RemoveElementCommand.elements
// Returns array with element associated with this command // Returns array with element associated with this command
svgedit.history.RemoveElementCommand.prototype.elements = function () { svgedit.history.RemoveElementCommand.prototype.elements = function () {
return [this.elem]; return [this.elem];
}; };
// Class: svgedit.history.ChangeElementCommand // 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 // 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 // text - An optional string visible to user related to this change
svgedit.history.ChangeElementCommand = function (elem, attrs, text) { svgedit.history.ChangeElementCommand = function (elem, attrs, text) {
this.elem = elem; this.elem = elem;
this.text = text ? ('Change ' + elem.tagName + ' ' + text) : ('Change ' + elem.tagName); this.text = text ? ('Change ' + elem.tagName + ' ' + text) : ('Change ' + elem.tagName);
this.newValues = {}; this.newValues = {};
this.oldValues = attrs; this.oldValues = attrs;
var attr; var attr;
for (attr in attrs) { for (attr in attrs) {
if (attr === '#text') { if (attr === '#text') {
this.newValues[attr] = elem.textContent; this.newValues[attr] = elem.textContent;
} else if (attr === '#href') { } else if (attr === '#href') {
this.newValues[attr] = svgedit.utilities.getHref(elem); this.newValues[attr] = svgedit.utilities.getHref(elem);
} else { } else {
this.newValues[attr] = elem.getAttribute(attr); this.newValues[attr] = elem.getAttribute(attr);
} }
} }
}; };
svgedit.history.ChangeElementCommand.type = function () { return 'svgedit.history.ChangeElementCommand'; }; svgedit.history.ChangeElementCommand.type = function () { return 'svgedit.history.ChangeElementCommand'; };
svgedit.history.ChangeElementCommand.prototype.type = svgedit.history.ChangeElementCommand.type; svgedit.history.ChangeElementCommand.prototype.type = svgedit.history.ChangeElementCommand.type;
// Function: svgedit.history.ChangeElementCommand.getText // Function: svgedit.history.ChangeElementCommand.getText
svgedit.history.ChangeElementCommand.prototype.getText = function () { svgedit.history.ChangeElementCommand.prototype.getText = function () {
return this.text; return this.text;
}; };
// Function: svgedit.history.ChangeElementCommand.apply // Function: svgedit.history.ChangeElementCommand.apply
// Performs the stored change action // Performs the stored change action
svgedit.history.ChangeElementCommand.prototype.apply = function (handler) { svgedit.history.ChangeElementCommand.prototype.apply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
} }
var bChangedTransform = false; var bChangedTransform = false;
var attr; var attr;
for (attr in this.newValues) { for (attr in this.newValues) {
if (this.newValues[attr]) { if (this.newValues[attr]) {
if (attr === '#text') { if (attr === '#text') {
this.elem.textContent = this.newValues[attr]; this.elem.textContent = this.newValues[attr];
} else if (attr === '#href') { } else if (attr === '#href') {
svgedit.utilities.setHref(this.elem, this.newValues[attr]); svgedit.utilities.setHref(this.elem, this.newValues[attr]);
} else { } else {
this.elem.setAttribute(attr, this.newValues[attr]); this.elem.setAttribute(attr, this.newValues[attr]);
} }
} else { } else {
if (attr === '#text') { if (attr === '#text') {
this.elem.textContent = ''; this.elem.textContent = '';
} else { } else {
this.elem.setAttribute(attr, ''); this.elem.setAttribute(attr, '');
this.elem.removeAttribute(attr); this.elem.removeAttribute(attr);
} }
} }
if (attr === 'transform') { bChangedTransform = true; } if (attr === 'transform') { bChangedTransform = true; }
} }
// relocate rotational transform, if necessary // relocate rotational transform, if necessary
if (!bChangedTransform) { if (!bChangedTransform) {
var angle = svgedit.utilities.getRotationAngle(this.elem); var angle = svgedit.utilities.getRotationAngle(this.elem);
if (angle) { if (angle) {
var bbox = this.elem.getBBox(); var bbox = this.elem.getBBox();
var cx = bbox.x + bbox.width / 2, var cx = bbox.x + bbox.width / 2,
cy = bbox.y + bbox.height / 2; cy = bbox.y + bbox.height / 2;
var rotate = ['rotate(', angle, ' ', cx, ',', cy, ')'].join(''); var rotate = ['rotate(', angle, ' ', cx, ',', cy, ')'].join('');
if (rotate !== this.elem.getAttribute('transform')) { if (rotate !== this.elem.getAttribute('transform')) {
this.elem.setAttribute('transform', rotate); this.elem.setAttribute('transform', rotate);
} }
} }
} }
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
} }
return true; return true;
}; };
// Function: svgedit.history.ChangeElementCommand.unapply // Function: svgedit.history.ChangeElementCommand.unapply
// Reverses the stored change action // Reverses the stored change action
svgedit.history.ChangeElementCommand.prototype.unapply = function (handler) { svgedit.history.ChangeElementCommand.prototype.unapply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
} }
var bChangedTransform = false; var bChangedTransform = false;
var attr; var attr;
for (attr in this.oldValues) { for (attr in this.oldValues) {
if (this.oldValues[attr]) { if (this.oldValues[attr]) {
if (attr === '#text') { if (attr === '#text') {
this.elem.textContent = this.oldValues[attr]; this.elem.textContent = this.oldValues[attr];
} else if (attr === '#href') { } else if (attr === '#href') {
svgedit.utilities.setHref(this.elem, this.oldValues[attr]); svgedit.utilities.setHref(this.elem, this.oldValues[attr]);
} else { } else {
this.elem.setAttribute(attr, this.oldValues[attr]); this.elem.setAttribute(attr, this.oldValues[attr]);
} }
} else { } else {
if (attr === '#text') { if (attr === '#text') {
this.elem.textContent = ''; this.elem.textContent = '';
} else { } else {
this.elem.removeAttribute(attr); this.elem.removeAttribute(attr);
} }
} }
if (attr === 'transform') { bChangedTransform = true; } if (attr === 'transform') { bChangedTransform = true; }
} }
// relocate rotational transform, if necessary // relocate rotational transform, if necessary
if (!bChangedTransform) { if (!bChangedTransform) {
var angle = svgedit.utilities.getRotationAngle(this.elem); var angle = svgedit.utilities.getRotationAngle(this.elem);
if (angle) { if (angle) {
var bbox = this.elem.getBBox(); var bbox = this.elem.getBBox();
var cx = bbox.x + bbox.width / 2, var cx = bbox.x + bbox.width / 2,
cy = bbox.y + bbox.height / 2; cy = bbox.y + bbox.height / 2;
var rotate = ['rotate(', angle, ' ', cx, ',', cy, ')'].join(''); var rotate = ['rotate(', angle, ' ', cx, ',', cy, ')'].join('');
if (rotate !== this.elem.getAttribute('transform')) { if (rotate !== this.elem.getAttribute('transform')) {
this.elem.setAttribute('transform', rotate); this.elem.setAttribute('transform', rotate);
} }
} }
} }
// Remove transformlist to prevent confusion that causes bugs like 575. // Remove transformlist to prevent confusion that causes bugs like 575.
svgedit.transformlist.removeElementFromListMap(this.elem); svgedit.transformlist.removeElementFromListMap(this.elem);
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
} }
return true; return true;
}; };
// Function: ChangeElementCommand.elements // Function: ChangeElementCommand.elements
// Returns array with element associated with this command // Returns array with element associated with this command
svgedit.history.ChangeElementCommand.prototype.elements = function () { svgedit.history.ChangeElementCommand.prototype.elements = function () {
return [this.elem]; return [this.elem];
}; };
// TODO: create a 'typing' command object that tracks changes in text // TODO: create a 'typing' command object that tracks changes in text
@@ -392,65 +392,65 @@ svgedit.history.ChangeElementCommand.prototype.elements = function () {
// Parameters: // Parameters:
// text - An optional string visible to user related to this change // text - An optional string visible to user related to this change
svgedit.history.BatchCommand = function (text) { svgedit.history.BatchCommand = function (text) {
this.text = text || 'Batch Command'; this.text = text || 'Batch Command';
this.stack = []; this.stack = [];
}; };
svgedit.history.BatchCommand.type = function () { return 'svgedit.history.BatchCommand'; }; svgedit.history.BatchCommand.type = function () { return 'svgedit.history.BatchCommand'; };
svgedit.history.BatchCommand.prototype.type = svgedit.history.BatchCommand.type; svgedit.history.BatchCommand.prototype.type = svgedit.history.BatchCommand.type;
// Function: svgedit.history.BatchCommand.getText // Function: svgedit.history.BatchCommand.getText
svgedit.history.BatchCommand.prototype.getText = function () { svgedit.history.BatchCommand.prototype.getText = function () {
return this.text; return this.text;
}; };
// Function: svgedit.history.BatchCommand.apply // Function: svgedit.history.BatchCommand.apply
// Runs "apply" on all subcommands // Runs "apply" on all subcommands
svgedit.history.BatchCommand.prototype.apply = function (handler) { svgedit.history.BatchCommand.prototype.apply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
} }
var i, var i,
len = this.stack.length; len = this.stack.length;
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
this.stack[i].apply(handler); this.stack[i].apply(handler);
} }
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
} }
}; };
// Function: svgedit.history.BatchCommand.unapply // Function: svgedit.history.BatchCommand.unapply
// Runs "unapply" on all subcommands // Runs "unapply" on all subcommands
svgedit.history.BatchCommand.prototype.unapply = function (handler) { svgedit.history.BatchCommand.prototype.unapply = function (handler) {
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
} }
var i; var i;
for (i = this.stack.length - 1; i >= 0; i--) { for (i = this.stack.length - 1; i >= 0; i--) {
this.stack[i].unapply(handler); this.stack[i].unapply(handler);
} }
if (handler) { if (handler) {
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this); handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
} }
}; };
// Function: svgedit.history.BatchCommand.elements // Function: svgedit.history.BatchCommand.elements
// Iterate through all our subcommands and returns all the elements we are changing // Iterate through all our subcommands and returns all the elements we are changing
svgedit.history.BatchCommand.prototype.elements = function () { svgedit.history.BatchCommand.prototype.elements = function () {
var elems = []; var elems = [];
var cmd = this.stack.length; var cmd = this.stack.length;
while (cmd--) { while (cmd--) {
var thisElems = this.stack[cmd].elements(); var thisElems = this.stack[cmd].elements();
var elem = thisElems.length; var elem = thisElems.length;
while (elem--) { while (elem--) {
if (elems.indexOf(thisElems[elem]) === -1) { elems.push(thisElems[elem]); } if (elems.indexOf(thisElems[elem]) === -1) { elems.push(thisElems[elem]); }
} }
} }
return elems; return elems;
}; };
// Function: svgedit.history.BatchCommand.addSubCommand // Function: svgedit.history.BatchCommand.addSubCommand
@@ -459,13 +459,13 @@ svgedit.history.BatchCommand.prototype.elements = function () {
// Parameters: // Parameters:
// cmd - The undo command object to add // cmd - The undo command object to add
svgedit.history.BatchCommand.prototype.addSubCommand = function (cmd) { svgedit.history.BatchCommand.prototype.addSubCommand = function (cmd) {
this.stack.push(cmd); this.stack.push(cmd);
}; };
// Function: svgedit.history.BatchCommand.isEmpty // Function: svgedit.history.BatchCommand.isEmpty
// Returns a boolean indicating whether or not the batch command is empty // Returns a boolean indicating whether or not the batch command is empty
svgedit.history.BatchCommand.prototype.isEmpty = function () { svgedit.history.BatchCommand.prototype.isEmpty = function () {
return this.stack.length === 0; return this.stack.length === 0;
}; };
// Class: svgedit.history.UndoManager // Class: svgedit.history.UndoManager
@@ -473,67 +473,67 @@ svgedit.history.BatchCommand.prototype.isEmpty = function () {
// historyEventHandler - an object that conforms to the HistoryEventHandler interface // historyEventHandler - an object that conforms to the HistoryEventHandler interface
// (see above) // (see above)
svgedit.history.UndoManager = function (historyEventHandler) { svgedit.history.UndoManager = function (historyEventHandler) {
this.handler_ = historyEventHandler || null; this.handler_ = historyEventHandler || null;
this.undoStackPointer = 0; this.undoStackPointer = 0;
this.undoStack = []; this.undoStack = [];
// this is the stack that stores the original values, the elements and // this is the stack that stores the original values, the elements and
// the attribute name for begin/finish // the attribute name for begin/finish
this.undoChangeStackPointer = -1; this.undoChangeStackPointer = -1;
this.undoableChangeStack = []; this.undoableChangeStack = [];
}; };
// Function: svgedit.history.UndoManager.resetUndoStack // Function: svgedit.history.UndoManager.resetUndoStack
// Resets the undo stack, effectively clearing the undo/redo history // Resets the undo stack, effectively clearing the undo/redo history
svgedit.history.UndoManager.prototype.resetUndoStack = function () { svgedit.history.UndoManager.prototype.resetUndoStack = function () {
this.undoStack = []; this.undoStack = [];
this.undoStackPointer = 0; this.undoStackPointer = 0;
}; };
// Function: svgedit.history.UndoManager.getUndoStackSize // Function: svgedit.history.UndoManager.getUndoStackSize
// Returns: // Returns:
// Integer with the current size of the undo history stack // Integer with the current size of the undo history stack
svgedit.history.UndoManager.prototype.getUndoStackSize = function () { svgedit.history.UndoManager.prototype.getUndoStackSize = function () {
return this.undoStackPointer; return this.undoStackPointer;
}; };
// Function: svgedit.history.UndoManager.getRedoStackSize // Function: svgedit.history.UndoManager.getRedoStackSize
// Returns: // Returns:
// Integer with the current size of the redo history stack // Integer with the current size of the redo history stack
svgedit.history.UndoManager.prototype.getRedoStackSize = function () { svgedit.history.UndoManager.prototype.getRedoStackSize = function () {
return this.undoStack.length - this.undoStackPointer; return this.undoStack.length - this.undoStackPointer;
}; };
// Function: svgedit.history.UndoManager.getNextUndoCommandText // Function: svgedit.history.UndoManager.getNextUndoCommandText
// Returns: // Returns:
// String associated with the next undo command // String associated with the next undo command
svgedit.history.UndoManager.prototype.getNextUndoCommandText = function () { 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 // Function: svgedit.history.UndoManager.getNextRedoCommandText
// Returns: // Returns:
// String associated with the next redo command // String associated with the next redo command
svgedit.history.UndoManager.prototype.getNextRedoCommandText = function () { 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 // Function: svgedit.history.UndoManager.undo
// Performs an undo step // Performs an undo step
svgedit.history.UndoManager.prototype.undo = function () { svgedit.history.UndoManager.prototype.undo = function () {
if (this.undoStackPointer > 0) { if (this.undoStackPointer > 0) {
var cmd = this.undoStack[--this.undoStackPointer]; var cmd = this.undoStack[--this.undoStackPointer];
cmd.unapply(this.handler_); cmd.unapply(this.handler_);
} }
}; };
// Function: svgedit.history.UndoManager.redo // Function: svgedit.history.UndoManager.redo
// Performs a redo step // Performs a redo step
svgedit.history.UndoManager.prototype.redo = function () { svgedit.history.UndoManager.prototype.redo = function () {
if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) { if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) {
var cmd = this.undoStack[this.undoStackPointer++]; var cmd = this.undoStack[this.undoStackPointer++];
cmd.apply(this.handler_); cmd.apply(this.handler_);
} }
}; };
// Function: svgedit.history.UndoManager.addCommandToHistory // Function: svgedit.history.UndoManager.addCommandToHistory
@@ -542,18 +542,18 @@ svgedit.history.UndoManager.prototype.redo = function () {
// Parameters: // Parameters:
// cmd - The command object to add // cmd - The command object to add
svgedit.history.UndoManager.prototype.addCommandToHistory = function (cmd) { svgedit.history.UndoManager.prototype.addCommandToHistory = function (cmd) {
// FIXME: we MUST compress consecutive text changes to the same element // FIXME: we MUST compress consecutive text changes to the same element
// (right now each keystroke is saved as a separate command that includes the // (right now each keystroke is saved as a separate command that includes the
// entire text contents of the text element) // entire text contents of the text element)
// TODO: consider limiting the history that we store here (need to do some slicing) // 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 // if our stack pointer is not at the end, then we have to remove
// all commands after the pointer and insert the new command // all commands after the pointer and insert the new command
if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) { if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) {
this.undoStack = this.undoStack.splice(0, this.undoStackPointer); this.undoStack = this.undoStack.splice(0, this.undoStackPointer);
} }
this.undoStack.push(cmd); this.undoStack.push(cmd);
this.undoStackPointer = this.undoStack.length; this.undoStackPointer = this.undoStack.length;
}; };
// Function: svgedit.history.UndoManager.beginUndoableChange // Function: svgedit.history.UndoManager.beginUndoableChange
@@ -567,20 +567,20 @@ svgedit.history.UndoManager.prototype.addCommandToHistory = function (cmd) {
// attrName - The name of the attribute being changed // attrName - The name of the attribute being changed
// elems - Array of DOM elements being changed // elems - Array of DOM elements being changed
svgedit.history.UndoManager.prototype.beginUndoableChange = function (attrName, elems) { svgedit.history.UndoManager.prototype.beginUndoableChange = function (attrName, elems) {
var p = ++this.undoChangeStackPointer; var p = ++this.undoChangeStackPointer;
var i = elems.length; var i = elems.length;
var oldValues = new Array(i), elements = new Array(i); var oldValues = new Array(i), elements = new Array(i);
while (i--) { while (i--) {
var elem = elems[i]; var elem = elems[i];
if (elem == null) { continue; } if (elem == null) { continue; }
elements[i] = elem; elements[i] = elem;
oldValues[i] = elem.getAttribute(attrName); oldValues[i] = elem.getAttribute(attrName);
} }
this.undoableChangeStack[p] = { this.undoableChangeStack[p] = {
'attrName': attrName, 'attrName': attrName,
'oldValues': oldValues, 'oldValues': oldValues,
'elements': elements 'elements': elements
}; };
}; };
// Function: svgedit.history.UndoManager.finishUndoableChange // Function: svgedit.history.UndoManager.finishUndoableChange
@@ -591,21 +591,21 @@ svgedit.history.UndoManager.prototype.beginUndoableChange = function (attrName,
// Returns: // Returns:
// Batch command object with resulting changes // Batch command object with resulting changes
svgedit.history.UndoManager.prototype.finishUndoableChange = function () { svgedit.history.UndoManager.prototype.finishUndoableChange = function () {
var p = this.undoChangeStackPointer--; var p = this.undoChangeStackPointer--;
var changeset = this.undoableChangeStack[p]; var changeset = this.undoableChangeStack[p];
var i = changeset.elements.length; var i = changeset.elements.length;
var attrName = changeset.attrName; var attrName = changeset.attrName;
var batchCmd = new svgedit.history.BatchCommand('Change ' + attrName); var batchCmd = new svgedit.history.BatchCommand('Change ' + attrName);
while (i--) { while (i--) {
var elem = changeset.elements[i]; var elem = changeset.elements[i];
if (elem == null) { continue; } if (elem == null) { continue; }
var changes = {}; var changes = {};
changes[attrName] = changeset.oldValues[i]; changes[attrName] = changeset.oldValues[i];
if (changes[attrName] !== elem.getAttribute(attrName)) { if (changes[attrName] !== elem.getAttribute(attrName)) {
batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes, attrName)); batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes, attrName));
} }
} }
this.undoableChangeStack[p] = null; this.undoableChangeStack[p] = null;
return batchCmd; return batchCmd;
}; };
}()); }());

View File

@@ -15,7 +15,7 @@
'use strict'; 'use strict';
if (!svgedit.history) { if (!svgedit.history) {
svgedit.history = {}; svgedit.history = {};
} }
var history = svgedit.history; var history = svgedit.history;
@@ -58,9 +58,9 @@ var history = svgedit.history;
* See singleton: HistoryRecordingService.NO_HISTORY * See singleton: HistoryRecordingService.NO_HISTORY
*/ */
var HistoryRecordingService = history.HistoryRecordingService = function (undoManager) { var HistoryRecordingService = history.HistoryRecordingService = function (undoManager) {
this.undoManager_ = undoManager; this.undoManager_ = undoManager;
this.currentBatchCommand_ = null; this.currentBatchCommand_ = null;
this.batchCommandStack_ = []; this.batchCommandStack_ = [];
}; };
/** /**
@@ -77,10 +77,10 @@ HistoryRecordingService.NO_HISTORY = new HistoryRecordingService();
* @returns {svgedit.history.HistoryRecordingService} * @returns {svgedit.history.HistoryRecordingService}
*/ */
HistoryRecordingService.prototype.startBatchCommand = function (text) { HistoryRecordingService.prototype.startBatchCommand = function (text) {
if (!this.undoManager_) { return this; } if (!this.undoManager_) { return this; }
this.currentBatchCommand_ = new history.BatchCommand(text); this.currentBatchCommand_ = new history.BatchCommand(text);
this.batchCommandStack_.push(this.currentBatchCommand_); this.batchCommandStack_.push(this.currentBatchCommand_);
return this; return this;
}; };
/** /**
@@ -88,15 +88,15 @@ HistoryRecordingService.prototype.startBatchCommand = function (text) {
* @returns {svgedit.history.HistoryRecordingService} * @returns {svgedit.history.HistoryRecordingService}
*/ */
HistoryRecordingService.prototype.endBatchCommand = function () { HistoryRecordingService.prototype.endBatchCommand = function () {
if (!this.undoManager_) { return this; } if (!this.undoManager_) { return this; }
if (this.currentBatchCommand_) { if (this.currentBatchCommand_) {
var batchCommand = this.currentBatchCommand_; var batchCommand = this.currentBatchCommand_;
this.batchCommandStack_.pop(); this.batchCommandStack_.pop();
var length = this.batchCommandStack_.length; var length = this.batchCommandStack_.length;
this.currentBatchCommand_ = length ? this.batchCommandStack_[length - 1] : null; this.currentBatchCommand_ = length ? this.batchCommandStack_[length - 1] : null;
this.addCommand_(batchCommand); this.addCommand_(batchCommand);
} }
return this; return this;
}; };
/** /**
@@ -108,9 +108,9 @@ HistoryRecordingService.prototype.endBatchCommand = function () {
* @returns {svgedit.history.HistoryRecordingService} * @returns {svgedit.history.HistoryRecordingService}
*/ */
HistoryRecordingService.prototype.moveElement = function (elem, oldNextSibling, oldParent, text) { HistoryRecordingService.prototype.moveElement = function (elem, oldNextSibling, oldParent, text) {
if (!this.undoManager_) { return this; } if (!this.undoManager_) { return this; }
this.addCommand_(new history.MoveElementCommand(elem, oldNextSibling, oldParent, text)); this.addCommand_(new history.MoveElementCommand(elem, oldNextSibling, oldParent, text));
return this; return this;
}; };
/** /**
@@ -120,9 +120,9 @@ HistoryRecordingService.prototype.moveElement = function (elem, oldNextSibling,
* @returns {svgedit.history.HistoryRecordingService} * @returns {svgedit.history.HistoryRecordingService}
*/ */
HistoryRecordingService.prototype.insertElement = function (elem, text) { HistoryRecordingService.prototype.insertElement = function (elem, text) {
if (!this.undoManager_) { return this; } if (!this.undoManager_) { return this; }
this.addCommand_(new history.InsertElementCommand(elem, text)); this.addCommand_(new history.InsertElementCommand(elem, text));
return this; return this;
}; };
/** /**
@@ -134,9 +134,9 @@ HistoryRecordingService.prototype.insertElement = function (elem, text) {
* @returns {svgedit.history.HistoryRecordingService} * @returns {svgedit.history.HistoryRecordingService}
*/ */
HistoryRecordingService.prototype.removeElement = function (elem, oldNextSibling, oldParent, text) { HistoryRecordingService.prototype.removeElement = function (elem, oldNextSibling, oldParent, text) {
if (!this.undoManager_) { return this; } if (!this.undoManager_) { return this; }
this.addCommand_(new history.RemoveElementCommand(elem, oldNextSibling, oldParent, text)); this.addCommand_(new history.RemoveElementCommand(elem, oldNextSibling, oldParent, text));
return this; return this;
}; };
/** /**
@@ -147,9 +147,9 @@ HistoryRecordingService.prototype.removeElement = function (elem, oldNextSibling
* @returns {svgedit.history.HistoryRecordingService} * @returns {svgedit.history.HistoryRecordingService}
*/ */
HistoryRecordingService.prototype.changeElement = function (elem, attrs, text) { HistoryRecordingService.prototype.changeElement = function (elem, attrs, text) {
if (!this.undoManager_) { return this; } if (!this.undoManager_) { return this; }
this.addCommand_(new history.ChangeElementCommand(elem, attrs, text)); this.addCommand_(new history.ChangeElementCommand(elem, attrs, text));
return this; return this;
}; };
/** /**
@@ -159,11 +159,11 @@ HistoryRecordingService.prototype.changeElement = function (elem, attrs, text) {
* @private * @private
*/ */
HistoryRecordingService.prototype.addCommand_ = function (cmd) { HistoryRecordingService.prototype.addCommand_ = function (cmd) {
if (!this.undoManager_) { return this; } if (!this.undoManager_) { return this; }
if (this.currentBatchCommand_) { if (this.currentBatchCommand_) {
this.currentBatchCommand_.addSubCommand(cmd); this.currentBatchCommand_.addSubCommand(cmd);
} else { } else {
this.undoManager_.addCommandToHistory(cmd); this.undoManager_.addCommandToHistory(cmd);
} }
}; };
}()); }());

View File

@@ -17,7 +17,7 @@
'use strict'; 'use strict';
if (!svgedit.draw) { if (!svgedit.draw) {
svgedit.draw = {}; svgedit.draw = {};
} }
var NS = svgedit.NS; var NS = svgedit.NS;
@@ -39,29 +39,29 @@ var NS = svgedit.NS;
* a new layer to the document. * a new layer to the document.
*/ */
var Layer = svgedit.draw.Layer = function (name, group, svgElem) { var Layer = svgedit.draw.Layer = function (name, group, svgElem) {
this.name_ = name; this.name_ = name;
this.group_ = svgElem ? null : group; this.group_ = svgElem ? null : group;
if (svgElem) { if (svgElem) {
// Create a group element with title and add it to the DOM. // Create a group element with title and add it to the DOM.
var svgdoc = svgElem.ownerDocument; var svgdoc = svgElem.ownerDocument;
this.group_ = svgdoc.createElementNS(NS.SVG, 'g'); this.group_ = svgdoc.createElementNS(NS.SVG, 'g');
var layerTitle = svgdoc.createElementNS(NS.SVG, 'title'); var layerTitle = svgdoc.createElementNS(NS.SVG, 'title');
layerTitle.textContent = name; layerTitle.textContent = name;
this.group_.appendChild(layerTitle); this.group_.appendChild(layerTitle);
if (group) { if (group) {
$(group).after(this.group_); $(group).after(this.group_);
} else { } else {
svgElem.appendChild(this.group_); svgElem.appendChild(this.group_);
} }
} }
addLayerClass(this.group_); addLayerClass(this.group_);
svgedit.utilities.walkTree(this.group_, function (e) { svgedit.utilities.walkTree(this.group_, function (e) {
e.setAttribute('style', 'pointer-events:inherit'); 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 * @returns {string} The layer name
*/ */
Layer.prototype.getName = function () { Layer.prototype.getName = function () {
return this.name_; return this.name_;
}; };
/** /**
@@ -87,21 +87,21 @@ Layer.prototype.getName = function () {
* @returns {SVGGElement} The layer SVG group * @returns {SVGGElement} The layer SVG group
*/ */
Layer.prototype.getGroup = function () { Layer.prototype.getGroup = function () {
return this.group_; return this.group_;
}; };
/** /**
* Active this layer so it takes pointer events. * Active this layer so it takes pointer events.
*/ */
Layer.prototype.activate = function () { 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. * Deactive this layer so it does NOT take pointer events.
*/ */
Layer.prototype.deactivate = function () { 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. * @param {boolean} visible - If true, make visible; otherwise, hide it.
*/ */
Layer.prototype.setVisible = function (visible) { Layer.prototype.setVisible = function (visible) {
var expected = visible === undefined || visible ? 'inline' : 'none'; var expected = visible === undefined || visible ? 'inline' : 'none';
var oldDisplay = this.group_.getAttribute('display'); var oldDisplay = this.group_.getAttribute('display');
if (oldDisplay !== expected) { if (oldDisplay !== expected) {
this.group_.setAttribute('display', expected); this.group_.setAttribute('display', expected);
} }
}; };
/** /**
@@ -121,7 +121,7 @@ Layer.prototype.setVisible = function (visible) {
* @returns {boolean} True if visible. * @returns {boolean} True if visible.
*/ */
Layer.prototype.isVisible = function () { 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. * @returns {number} Opacity value.
*/ */
Layer.prototype.getOpacity = function () { Layer.prototype.getOpacity = function () {
var opacity = this.group_.getAttribute('opacity'); var opacity = this.group_.getAttribute('opacity');
if (opacity === null || opacity === undefined) { if (opacity === null || opacity === undefined) {
return 1; return 1;
} }
return parseFloat(opacity); return parseFloat(opacity);
}; };
/** /**
@@ -142,9 +142,9 @@ Layer.prototype.getOpacity = function () {
* @param {number} opacity - A float value in the range 0.0-1.0 * @param {number} opacity - A float value in the range 0.0-1.0
*/ */
Layer.prototype.setOpacity = function (opacity) { Layer.prototype.setOpacity = function (opacity) {
if (typeof opacity === 'number' && opacity >= 0.0 && opacity <= 1.0) { if (typeof opacity === 'number' && opacity >= 0.0 && opacity <= 1.0) {
this.group_.setAttribute('opacity', opacity); this.group_.setAttribute('opacity', opacity);
} }
}; };
/** /**
@@ -152,20 +152,20 @@ Layer.prototype.setOpacity = function (opacity) {
* @param {SVGGElement} children - The children to append to this layer. * @param {SVGGElement} children - The children to append to this layer.
*/ */
Layer.prototype.appendChildren = function (children) { Layer.prototype.appendChildren = function (children) {
for (var i = 0; i < children.length; ++i) { for (var i = 0; i < children.length; ++i) {
this.group_.appendChild(children[i]); this.group_.appendChild(children[i]);
} }
}; };
Layer.prototype.getTitleElement = function () { Layer.prototype.getTitleElement = function () {
var len = this.group_.childNodes.length; var len = this.group_.childNodes.length;
for (var i = 0; i < len; ++i) { for (var i = 0; i < len; ++i) {
var child = this.group_.childNodes.item(i); var child = this.group_.childNodes.item(i);
if (child && child.tagName === 'title') { if (child && child.tagName === 'title') {
return child; return child;
} }
} }
return null; return null;
}; };
/** /**
@@ -175,20 +175,20 @@ Layer.prototype.getTitleElement = function () {
* @returns {string|null} The new name if changed; otherwise, null. * @returns {string|null} The new name if changed; otherwise, null.
*/ */
Layer.prototype.setName = function (name, hrService) { Layer.prototype.setName = function (name, hrService) {
var previousName = this.name_; var previousName = this.name_;
name = svgedit.utilities.toXml(name); name = svgedit.utilities.toXml(name);
// now change the underlying title element contents // now change the underlying title element contents
var title = this.getTitleElement(); var title = this.getTitleElement();
if (title) { if (title) {
$(title).empty(); $(title).empty();
title.textContent = name; title.textContent = name;
this.name_ = name; this.name_ = name;
if (hrService) { if (hrService) {
hrService.changeElement(title, {'#text': previousName}); hrService.changeElement(title, {'#text': previousName});
} }
return this.name_; return this.name_;
} }
return null; return null;
}; };
/** /**
@@ -197,10 +197,10 @@ Layer.prototype.setName = function (name, hrService) {
* @returns {SVGGElement} The layer SVG group that was just removed. * @returns {SVGGElement} The layer SVG group that was just removed.
*/ */
Layer.prototype.removeGroup = function () { Layer.prototype.removeGroup = function () {
var parent = this.group_.parentNode; var parent = this.group_.parentNode;
var group = parent.removeChild(this.group_); var group = parent.removeChild(this.group_);
this.group_ = undefined; this.group_ = undefined;
return group; return group;
}; };
/** /**
@@ -210,11 +210,11 @@ Layer.prototype.removeGroup = function () {
* @param {SVGGElement} elem - The SVG element to update * @param {SVGGElement} elem - The SVG element to update
*/ */
function addLayerClass (elem) { function addLayerClass (elem) {
var classes = elem.getAttribute('class'); var classes = elem.getAttribute('class');
if (classes === null || classes === undefined || classes.length === 0) { if (classes === null || classes === undefined || classes.length === 0) {
elem.setAttribute('class', Layer.CLASS_NAME); elem.setAttribute('class', Layer.CLASS_NAME);
} else if (!Layer.CLASS_REGEX.test(classes)) { } else if (!Layer.CLASS_REGEX.test(classes)) {
elem.setAttribute('class', classes + ' ' + Layer.CLASS_NAME); elem.setAttribute('class', classes + ' ' + Layer.CLASS_NAME);
} }
} }
}()); }());

View File

@@ -24,7 +24,7 @@
'use strict'; 'use strict';
if (!svgedit.math) { if (!svgedit.math) {
svgedit.math = {}; svgedit.math = {};
} }
// Constants // Constants
@@ -42,7 +42,7 @@ var svg = document.createElementNS(svgedit.NS.SVG, 'svg');
* @returns {Object} An x, y object representing the transformed point * @returns {Object} An x, y object representing the transformed point
*/ */
svgedit.math.transformPoint = function (x, y, m) { 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 * @returns {boolean} Indicates whether or not the matrix is 1,0,0,1,0,0
*/ */
svgedit.math.isIdentity = function (m) { 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 * @returns {SVGMatrix} The matrix object resulting from the calculation
*/ */
svgedit.math.matrixMultiply = function (matr) { 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) { while (i-- > 1) {
var m1 = args[i - 1]; var m1 = args[i - 1];
m = m1.multiply(m); m = m1.multiply(m);
} }
if (Math.abs(m.a) < NEAR_ZERO) { m.a = 0; } if (Math.abs(m.a) < NEAR_ZERO) { m.a = 0; }
if (Math.abs(m.b) < NEAR_ZERO) { m.b = 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.c) < NEAR_ZERO) { m.c = 0; }
if (Math.abs(m.d) < NEAR_ZERO) { m.d = 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.e) < NEAR_ZERO) { m.e = 0; }
if (Math.abs(m.f) < NEAR_ZERO) { m.f = 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 * @returns {boolean} Whether or not a matrix transform was found
*/ */
svgedit.math.hasMatrixTransform = function (tlist) { svgedit.math.hasMatrixTransform = function (tlist) {
if (!tlist) { return false; } if (!tlist) { return false; }
var num = tlist.numberOfItems; var num = tlist.numberOfItems;
while (num--) { while (num--) {
var xform = tlist.getItem(num); var xform = tlist.getItem(num);
if (xform.type === 1 && !svgedit.math.isIdentity(xform.matrix)) { return true; } if (xform.type === 1 && !svgedit.math.isIdentity(xform.matrix)) { return true; }
} }
return false; return false;
}; };
/** /**
@@ -112,30 +112,30 @@ svgedit.math.hasMatrixTransform = function (tlist) {
* height - Float with the axis-aligned height coordinate * height - Float with the axis-aligned height coordinate
*/ */
svgedit.math.transformBox = function (l, t, w, h, m) { svgedit.math.transformBox = function (l, t, w, h, m) {
var transformPoint = svgedit.math.transformPoint, var transformPoint = svgedit.math.transformPoint,
tl = transformPoint(l, t, m), tl = transformPoint(l, t, m),
tr = transformPoint((l + w), t, m), tr = transformPoint((l + w), t, m),
bl = transformPoint(l, (t + h), m), bl = transformPoint(l, (t + h), m),
br = transformPoint((l + w), (t + h), m), br = transformPoint((l + w), (t + h), m),
minx = Math.min(tl.x, tr.x, bl.x, br.x), minx = Math.min(tl.x, tr.x, bl.x, br.x),
maxx = Math.max(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), miny = Math.min(tl.y, tr.y, bl.y, br.y),
maxy = Math.max(tl.y, tr.y, bl.y, br.y); maxy = Math.max(tl.y, tr.y, bl.y, br.y);
return { return {
tl: tl, tl: tl,
tr: tr, tr: tr,
bl: bl, bl: bl,
br: br, br: br,
aabox: { aabox: {
x: minx, x: minx,
y: miny, y: miny,
width: (maxx - minx), width: (maxx - minx),
height: (maxy - miny) height: (maxy - miny)
} }
}; };
}; };
/** /**
@@ -150,25 +150,25 @@ svgedit.math.transformBox = function (l, t, w, h, m) {
* @returns {Object} A single matrix transform object * @returns {Object} A single matrix transform object
*/ */
svgedit.math.transformListToTransform = function (tlist, min, max) { svgedit.math.transformListToTransform = function (tlist, min, max) {
if (tlist == null) { if (tlist == null) {
// Or should tlist = null have been prevented before this? // Or should tlist = null have been prevented before this?
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix()); return svg.createSVGTransformFromMatrix(svg.createSVGMatrix());
} }
min = min || 0; min = min || 0;
max = max || (tlist.numberOfItems - 1); max = max || (tlist.numberOfItems - 1);
min = parseInt(min, 10); min = parseInt(min, 10);
max = parseInt(max, 10); max = parseInt(max, 10);
if (min > max) { var temp = max; max = min; min = temp; } if (min > max) { var temp = max; max = min; min = temp; }
var m = svg.createSVGMatrix(); var m = svg.createSVGMatrix();
var i; var i;
for (i = min; i <= max; ++i) { for (i = min; i <= max; ++i) {
// if our indices are out of range, just use a harmless identity matrix // if our indices are out of range, just use a harmless identity matrix
var mtom = (i >= 0 && i < tlist.numberOfItems var mtom = (i >= 0 && i < tlist.numberOfItems
? tlist.getItem(i).matrix ? tlist.getItem(i).matrix
: svg.createSVGMatrix()); : svg.createSVGMatrix());
m = svgedit.math.matrixMultiply(m, mtom); m = svgedit.math.matrixMultiply(m, mtom);
} }
return svg.createSVGTransformFromMatrix(m); 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 * @returns {SVGMatrix} The matrix object associated with the element's transformlist
*/ */
svgedit.math.getMatrix = function (elem) { svgedit.math.getMatrix = function (elem) {
var tlist = svgedit.transformlist.getTransformList(elem); var tlist = svgedit.transformlist.getTransformList(elem);
return svgedit.math.transformListToTransform(tlist).matrix; return svgedit.math.transformListToTransform(tlist).matrix;
}; };
/** /**
@@ -191,18 +191,18 @@ svgedit.math.getMatrix = function (elem) {
* @returns {AngleCoord45} * @returns {AngleCoord45}
*/ */
svgedit.math.snapToAngle = function (x1, y1, x2, y2) { svgedit.math.snapToAngle = function (x1, y1, x2, y2) {
var snap = Math.PI / 4; // 45 degrees var snap = Math.PI / 4; // 45 degrees
var dx = x2 - x1; var dx = x2 - x1;
var dy = y2 - y1; var dy = y2 - y1;
var angle = Math.atan2(dy, dx); var angle = Math.atan2(dy, dx);
var dist = Math.sqrt(dx * dx + dy * dy); var dist = Math.sqrt(dx * dx + dy * dy);
var snapangle = Math.round(angle / snap) * snap; var snapangle = Math.round(angle / snap) * snap;
return { return {
x: x1 + dist * Math.cos(snapangle), x: x1 + dist * Math.cos(snapangle),
y: y1 + dist * Math.sin(snapangle), y: y1 + dist * Math.sin(snapangle),
a: snapangle a: snapangle
}; };
}; };
/** /**
@@ -212,9 +212,9 @@ svgedit.math.snapToAngle = function (x1, y1, x2, y2) {
* @returns {boolean} True if rectangles intersect * @returns {boolean} True if rectangles intersect
*/ */
svgedit.math.rectsIntersect = function (r1, r2) { svgedit.math.rectsIntersect = function (r1, r2) {
return r2.x < (r1.x + r1.width) && return r2.x < (r1.x + r1.width) &&
(r2.x + r2.width) > r1.x && (r2.x + r2.width) > r1.x &&
r2.y < (r1.y + r1.height) && r2.y < (r1.y + r1.height) &&
(r2.y + r2.height) > r1.y; (r2.y + r2.height) > r1.y;
}; };
}()); }());

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -19,95 +19,95 @@
'use strict'; 'use strict';
if (!svgedit.sanitize) { if (!svgedit.sanitize) {
svgedit.sanitize = {}; svgedit.sanitize = {};
} }
var NS = svgedit.NS, var NS = svgedit.NS,
REVERSE_NS = svgedit.getReverseNS(); REVERSE_NS = svgedit.getReverseNS();
// this defines which elements and attributes that we support // this defines which elements and attributes that we support
var svgWhiteList_ = { var svgWhiteList_ = {
// SVG Elements // 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'], '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'], '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'], 'clipPath': ['class', 'clipPathUnits', 'id'],
'defs': [], 'defs': [],
'style': ['type'], 'style': ['type'],
'desc': [], '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'], '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'], 'feGaussianBlur': ['class', 'color-interpolation-filters', 'id', 'requiredFeatures', 'stdDeviation'],
'filter': ['class', 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'id', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y'], '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'], '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'], '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'], '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'], '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'], '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'], 'marker': ['id', 'class', 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox'],
'mask': ['class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y'], 'mask': ['class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y'],
'metadata': ['class', 'id'], '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'], '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'], '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'], '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'], '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'], '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'], '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'], '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'], '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'], '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'], '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'], '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'], 'textPath': ['class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href'],
'title': [], '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'], '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'], '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 // MathML Elements
'annotation': ['encoding'], 'annotation': ['encoding'],
'annotation-xml': ['encoding'], 'annotation-xml': ['encoding'],
'maction': ['actiontype', 'other', 'selection'], 'maction': ['actiontype', 'other', 'selection'],
'math': ['class', 'id', 'display', 'xmlns'], 'math': ['class', 'id', 'display', 'xmlns'],
'menclose': ['notation'], 'menclose': ['notation'],
'merror': [], 'merror': [],
'mfrac': ['linethickness'], 'mfrac': ['linethickness'],
'mi': ['mathvariant'], 'mi': ['mathvariant'],
'mmultiscripts': [], 'mmultiscripts': [],
'mn': [], 'mn': [],
'mo': ['fence', 'lspace', 'maxsize', 'minsize', 'rspace', 'stretchy'], 'mo': ['fence', 'lspace', 'maxsize', 'minsize', 'rspace', 'stretchy'],
'mover': [], 'mover': [],
'mpadded': ['lspace', 'width', 'height', 'depth', 'voffset'], 'mpadded': ['lspace', 'width', 'height', 'depth', 'voffset'],
'mphantom': [], 'mphantom': [],
'mprescripts': [], 'mprescripts': [],
'mroot': [], 'mroot': [],
'mrow': ['xlink:href', 'xlink:type', 'xmlns:xlink'], 'mrow': ['xlink:href', 'xlink:type', 'xmlns:xlink'],
'mspace': ['depth', 'height', 'width'], 'mspace': ['depth', 'height', 'width'],
'msqrt': [], 'msqrt': [],
'mstyle': ['displaystyle', 'mathbackground', 'mathcolor', 'mathvariant', 'scriptlevel'], 'mstyle': ['displaystyle', 'mathbackground', 'mathcolor', 'mathvariant', 'scriptlevel'],
'msub': [], 'msub': [],
'msubsup': [], 'msubsup': [],
'msup': [], 'msup': [],
'mtable': ['align', 'columnalign', 'columnlines', 'columnspacing', 'displaystyle', 'equalcolumns', 'equalrows', 'frame', 'rowalign', 'rowlines', 'rowspacing', 'width'], 'mtable': ['align', 'columnalign', 'columnlines', 'columnspacing', 'displaystyle', 'equalcolumns', 'equalrows', 'frame', 'rowalign', 'rowlines', 'rowspacing', 'width'],
'mtd': ['columnalign', 'columnspan', 'rowalign', 'rowspan'], 'mtd': ['columnalign', 'columnspan', 'rowalign', 'rowspan'],
'mtext': [], 'mtext': [],
'mtr': ['columnalign', 'rowalign'], 'mtr': ['columnalign', 'rowalign'],
'munder': [], 'munder': [],
'munderover': [], 'munderover': [],
'none': [], 'none': [],
'semantics': [] 'semantics': []
}; };
// Produce a Namespace-aware version of svgWhitelist // Produce a Namespace-aware version of svgWhitelist
var svgWhiteListNS_ = {}; var svgWhiteListNS_ = {};
$.each(svgWhiteList_, function (elt, atts) { $.each(svgWhiteList_, function (elt, atts) {
var attNS = {}; var attNS = {};
$.each(atts, function (i, att) { $.each(atts, function (i, att) {
if (att.indexOf(':') >= 0) { if (att.indexOf(':') >= 0) {
var v = att.split(':'); var v = att.split(':');
attNS[v[1]] = NS[(v[0]).toUpperCase()]; attNS[v[1]] = NS[(v[0]).toUpperCase()];
} else { } else {
attNS[att] = att === 'xmlns' ? NS.XMLNS : null; attNS[att] = att === 'xmlns' ? NS.XMLNS : null;
} }
}); });
svgWhiteListNS_[elt] = attNS; svgWhiteListNS_[elt] = attNS;
}); });
// Function: svgedit.sanitize.sanitizeSvg // Function: svgedit.sanitize.sanitizeSvg
@@ -117,140 +117,140 @@ $.each(svgWhiteList_, function (elt, atts) {
// Parameters: // Parameters:
// node - The DOM element to be checked (we'll also check its children) // node - The DOM element to be checked (we'll also check its children)
svgedit.sanitize.sanitizeSvg = function (node) { svgedit.sanitize.sanitizeSvg = function (node) {
// Cleanup text nodes // Cleanup text nodes
if (node.nodeType === 3) { // 3 == TEXT_NODE if (node.nodeType === 3) { // 3 == TEXT_NODE
// Trim whitespace // Trim whitespace
node.nodeValue = node.nodeValue.replace(/^\s+|\s+$/g, ''); node.nodeValue = node.nodeValue.replace(/^\s+|\s+$/g, '');
// Remove if empty // Remove if empty
if (node.nodeValue.length === 0) { if (node.nodeValue.length === 0) {
node.parentNode.removeChild(node); node.parentNode.removeChild(node);
} }
} }
// We only care about element nodes. // We only care about element nodes.
// Automatically return for all non-element nodes, such as comments, etc. // Automatically return for all non-element nodes, such as comments, etc.
if (node.nodeType !== 1) { // 1 == ELEMENT_NODE if (node.nodeType !== 1) { // 1 == ELEMENT_NODE
return; return;
} }
var doc = node.ownerDocument; var doc = node.ownerDocument;
var parent = node.parentNode; var parent = node.parentNode;
// can parent ever be null here? I think the root node's parent is the document... // can parent ever be null here? I think the root node's parent is the document...
if (!doc || !parent) { if (!doc || !parent) {
return; return;
} }
var allowedAttrs = svgWhiteList_[node.nodeName]; var allowedAttrs = svgWhiteList_[node.nodeName];
var allowedAttrsNS = svgWhiteListNS_[node.nodeName]; var allowedAttrsNS = svgWhiteListNS_[node.nodeName];
var i; var i;
// if this element is supported, sanitize it // if this element is supported, sanitize it
if (typeof allowedAttrs !== 'undefined') { if (typeof allowedAttrs !== 'undefined') {
var seAttrs = []; var seAttrs = [];
i = node.attributes.length; i = node.attributes.length;
while (i--) { while (i--) {
// if the attribute is not in our whitelist, then remove it // 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 // could use jQuery's inArray(), but I don't know if that's any better
var attr = node.attributes.item(i); var attr = node.attributes.item(i);
var attrName = attr.nodeName; var attrName = attr.nodeName;
var attrLocalName = attr.localName; var attrLocalName = attr.localName;
var attrNsURI = attr.namespaceURI; var attrNsURI = attr.namespaceURI;
// Check that an attribute with the correct localName in the correct namespace is on // 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 // our whitelist or is a namespace declaration for one of our allowed namespaces
if (!(allowedAttrsNS.hasOwnProperty(attrLocalName) && attrNsURI === allowedAttrsNS[attrLocalName] && attrNsURI !== NS.XMLNS) && if (!(allowedAttrsNS.hasOwnProperty(attrLocalName) && attrNsURI === allowedAttrsNS[attrLocalName] && attrNsURI !== NS.XMLNS) &&
!(attrNsURI === NS.XMLNS && REVERSE_NS[attr.value])) { !(attrNsURI === NS.XMLNS && REVERSE_NS[attr.value])) {
// TODO(codedread): Programmatically add the se: attributes to the NS-aware whitelist. // TODO(codedread): Programmatically add the se: attributes to the NS-aware whitelist.
// Bypassing the whitelist to allow se: prefixes. // Bypassing the whitelist to allow se: prefixes.
// Is there a more appropriate way to do this? // Is there a more appropriate way to do this?
if (attrName.indexOf('se:') === 0 || attrName.indexOf('data-') === 0) { if (attrName.indexOf('se:') === 0 || attrName.indexOf('data-') === 0) {
seAttrs.push([attrName, attr.value]); seAttrs.push([attrName, attr.value]);
} }
node.removeAttributeNS(attrNsURI, attrLocalName); node.removeAttributeNS(attrNsURI, attrLocalName);
} }
// Add spaces before negative signs where necessary // Add spaces before negative signs where necessary
if (svgedit.browser.isGecko()) { if (svgedit.browser.isGecko()) {
switch (attrName) { switch (attrName) {
case 'transform': case 'transform':
case 'gradientTransform': case 'gradientTransform':
case 'patternTransform': case 'patternTransform':
var val = attr.value.replace(/(\d)-/g, '$1 -'); var val = attr.value.replace(/(\d)-/g, '$1 -');
node.setAttribute(attrName, val); node.setAttribute(attrName, val);
break; break;
} }
} }
// For the style attribute, rewrite it in terms of XML presentational attributes // For the style attribute, rewrite it in terms of XML presentational attributes
if (attrName === 'style') { if (attrName === 'style') {
var props = attr.value.split(';'), var props = attr.value.split(';'),
p = props.length; p = props.length;
while (p--) { while (p--) {
var nv = props[p].split(':'); var nv = props[p].split(':');
var styleAttrName = $.trim(nv[0]); var styleAttrName = $.trim(nv[0]);
var styleAttrVal = $.trim(nv[1]); var styleAttrVal = $.trim(nv[1]);
// Now check that this attribute is supported // Now check that this attribute is supported
if (allowedAttrs.indexOf(styleAttrName) >= 0) { if (allowedAttrs.indexOf(styleAttrName) >= 0) {
node.setAttribute(styleAttrName, styleAttrVal); node.setAttribute(styleAttrName, styleAttrVal);
} }
} }
node.removeAttribute('style'); node.removeAttribute('style');
} }
} }
$.each(seAttrs, function (i, attr) { $.each(seAttrs, function (i, attr) {
node.setAttributeNS(NS.SE, attr[0], attr[1]); node.setAttributeNS(NS.SE, attr[0], attr[1]);
}); });
// for some elements that have a xlink:href, ensure the URI refers to a local element // for some elements that have a xlink:href, ensure the URI refers to a local element
// (but not for links) // (but not for links)
var href = svgedit.utilities.getHref(node); var href = svgedit.utilities.getHref(node);
if (href && if (href &&
['filter', 'linearGradient', 'pattern', ['filter', 'linearGradient', 'pattern',
'radialGradient', 'textPath', 'use'].indexOf(node.nodeName) >= 0) { 'radialGradient', 'textPath', 'use'].indexOf(node.nodeName) >= 0) {
// TODO: we simply check if the first character is a #, is this bullet-proof? // TODO: we simply check if the first character is a #, is this bullet-proof?
if (href[0] !== '#') { if (href[0] !== '#') {
// remove the attribute (but keep the element) // remove the attribute (but keep the element)
svgedit.utilities.setHref(node, ''); svgedit.utilities.setHref(node, '');
node.removeAttributeNS(NS.XLINK, 'href'); node.removeAttributeNS(NS.XLINK, 'href');
} }
} }
// Safari crashes on a <use> without a xlink:href, so we just remove the node here // Safari crashes on a <use> without a xlink:href, so we just remove the node here
if (node.nodeName === 'use' && !svgedit.utilities.getHref(node)) { if (node.nodeName === 'use' && !svgedit.utilities.getHref(node)) {
parent.removeChild(node); parent.removeChild(node);
return; return;
} }
// if the element has attributes pointing to a non-local reference, // if the element has attributes pointing to a non-local reference,
// need to remove the attribute // need to remove the attribute
$.each(['clip-path', 'fill', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke'], function (i, attr) { $.each(['clip-path', 'fill', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke'], function (i, attr) {
var val = node.getAttribute(attr); var val = node.getAttribute(attr);
if (val) { if (val) {
val = svgedit.utilities.getUrlFromAttr(val); val = svgedit.utilities.getUrlFromAttr(val);
// simply check for first character being a '#' // simply check for first character being a '#'
if (val && val[0] !== '#') { if (val && val[0] !== '#') {
node.setAttribute(attr, ''); node.setAttribute(attr, '');
node.removeAttribute(attr); node.removeAttribute(attr);
} }
} }
}); });
// recurse to children // recurse to children
i = node.childNodes.length; i = node.childNodes.length;
while (i--) { svgedit.sanitize.sanitizeSvg(node.childNodes.item(i)); } while (i--) { svgedit.sanitize.sanitizeSvg(node.childNodes.item(i)); }
// else (element not supported), remove it // else (element not supported), remove it
} else { } else {
// remove all children from this node and insert them before this node // 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 // FIXME: in the case of animation elements this will hardly ever be correct
var children = []; var children = [];
while (node.hasChildNodes()) { while (node.hasChildNodes()) {
children.push(parent.insertBefore(node.firstChild, node)); children.push(parent.insertBefore(node.firstChild, node));
} }
// remove this node from the document altogether // remove this node from the document altogether
parent.removeChild(node); parent.removeChild(node);
// call sanitizeSvg on each of those children // call sanitizeSvg on each of those children
i = children.length; i = children.length;
while (i--) { svgedit.sanitize.sanitizeSvg(children[i]); } while (i--) { svgedit.sanitize.sanitizeSvg(children[i]); }
} }
}; };
}()); }());

View File

@@ -20,7 +20,7 @@
'use strict'; 'use strict';
if (!svgedit.select) { if (!svgedit.select) {
svgedit.select = {}; svgedit.select = {};
} }
var svgFactory_; var svgFactory_;
@@ -36,50 +36,50 @@ var gripRadius = svgedit.browser.isTouch() ? 10 : 4;
// elem - DOM element associated with this selector // elem - DOM element associated with this selector
// bbox - Optional bbox to use for initialization (prevents duplicate getBBox call). // bbox - Optional bbox to use for initialization (prevents duplicate getBBox call).
svgedit.select.Selector = function (id, elem, bbox) { svgedit.select.Selector = function (id, elem, bbox) {
// this is the selector's unique number // this is the selector's unique number
this.id = id; this.id = id;
// this holds a reference to the element for which this selector is being used // this holds a reference to the element for which this selector is being used
this.selectedElement = elem; this.selectedElement = elem;
// this is a flag used internally to track whether the selector is being used or not // this is a flag used internally to track whether the selector is being used or not
this.locked = true; this.locked = true;
// this holds a reference to the <g> element that holds all visual elements of the selector // this holds a reference to the <g> element that holds all visual elements of the selector
this.selectorGroup = svgFactory_.createSVGElement({ this.selectorGroup = svgFactory_.createSVGElement({
'element': 'g', 'element': 'g',
'attr': {'id': ('selectorGroup' + this.id)} 'attr': {'id': ('selectorGroup' + this.id)}
}); });
// this holds a reference to the path rect // this holds a reference to the path rect
this.selectorRect = this.selectorGroup.appendChild( this.selectorRect = this.selectorGroup.appendChild(
svgFactory_.createSVGElement({ svgFactory_.createSVGElement({
'element': 'path', 'element': 'path',
'attr': { 'attr': {
'id': ('selectedBox' + this.id), 'id': ('selectedBox' + this.id),
'fill': 'none', 'fill': 'none',
'stroke': '#22C', 'stroke': '#22C',
'stroke-width': '1', 'stroke-width': '1',
'stroke-dasharray': '5,5', 'stroke-dasharray': '5,5',
// need to specify this so that the rect is not selectable // need to specify this so that the rect is not selectable
'style': 'pointer-events:none' 'style': 'pointer-events:none'
} }
}) })
); );
// this holds a reference to the grip coordinates for this selector // this holds a reference to the grip coordinates for this selector
this.gripCoords = { this.gripCoords = {
'nw': null, 'nw': null,
'n': null, 'n': null,
'ne': null, 'ne': null,
'e': null, 'e': null,
'se': null, 'se': null,
's': null, 's': null,
'sw': null, 'sw': null,
'w': null 'w': null
}; };
this.reset(this.selectedElement, bbox); this.reset(this.selectedElement, bbox);
}; };
// Function: svgedit.select.Selector.reset // Function: svgedit.select.Selector.reset
@@ -89,10 +89,10 @@ svgedit.select.Selector = function (id, elem, bbox) {
// e - DOM element associated with this selector // e - DOM element associated with this selector
// bbox - Optional bbox to use for reset (prevents duplicate getBBox call). // bbox - Optional bbox to use for reset (prevents duplicate getBBox call).
svgedit.select.Selector.prototype.reset = function (e, bbox) { svgedit.select.Selector.prototype.reset = function (e, bbox) {
this.locked = true; this.locked = true;
this.selectedElement = e; this.selectedElement = e;
this.resize(bbox); this.resize(bbox);
this.selectorGroup.setAttribute('display', 'inline'); this.selectorGroup.setAttribute('display', 'inline');
}; };
// Function: svgedit.select.Selector.updateGripCursors // Function: svgedit.select.Selector.updateGripCursors
@@ -101,22 +101,22 @@ svgedit.select.Selector.prototype.reset = function (e, bbox) {
// Parameters: // Parameters:
// angle - Float indicating current rotation angle in degrees // angle - Float indicating current rotation angle in degrees
svgedit.select.Selector.prototype.updateGripCursors = function (angle) { svgedit.select.Selector.prototype.updateGripCursors = function (angle) {
var dir, var dir,
dirArr = [], dirArr = [],
steps = Math.round(angle / 45); steps = Math.round(angle / 45);
if (steps < 0) { steps += 8; } if (steps < 0) { steps += 8; }
for (dir in selectorManager_.selectorGrips) { for (dir in selectorManager_.selectorGrips) {
dirArr.push(dir); dirArr.push(dir);
} }
while (steps > 0) { while (steps > 0) {
dirArr.push(dirArr.shift()); dirArr.push(dirArr.shift());
steps--; steps--;
} }
var i = 0; var i = 0;
for (dir in selectorManager_.selectorGrips) { for (dir in selectorManager_.selectorGrips) {
selectorManager_.selectorGrips[dir].setAttribute('style', ('cursor:' + dirArr[i] + '-resize')); selectorManager_.selectorGrips[dir].setAttribute('style', ('cursor:' + dirArr[i] + '-resize'));
i++; i++;
} }
}; };
// Function: svgedit.select.Selector.showGrips // Function: svgedit.select.Selector.showGrips
@@ -125,292 +125,292 @@ svgedit.select.Selector.prototype.updateGripCursors = function (angle) {
// Parameters: // Parameters:
// show - boolean indicating whether grips should be shown or not // show - boolean indicating whether grips should be shown or not
svgedit.select.Selector.prototype.showGrips = function (show) { svgedit.select.Selector.prototype.showGrips = function (show) {
var bShow = show ? 'inline' : 'none'; var bShow = show ? 'inline' : 'none';
selectorManager_.selectorGripsGroup.setAttribute('display', bShow); selectorManager_.selectorGripsGroup.setAttribute('display', bShow);
var elem = this.selectedElement; var elem = this.selectedElement;
this.hasGrips = show; this.hasGrips = show;
if (elem && show) { if (elem && show) {
this.selectorGroup.appendChild(selectorManager_.selectorGripsGroup); this.selectorGroup.appendChild(selectorManager_.selectorGripsGroup);
this.updateGripCursors(svgedit.utilities.getRotationAngle(elem)); this.updateGripCursors(svgedit.utilities.getRotationAngle(elem));
} }
}; };
// Function: svgedit.select.Selector.resize // Function: svgedit.select.Selector.resize
// Updates the selector to match the element's size // Updates the selector to match the element's size
// bbox - Optional bbox to use for resize (prevents duplicate getBBox call). // bbox - Optional bbox to use for resize (prevents duplicate getBBox call).
svgedit.select.Selector.prototype.resize = function (bbox) { svgedit.select.Selector.prototype.resize = function (bbox) {
var selectedBox = this.selectorRect, var selectedBox = this.selectorRect,
mgr = selectorManager_, mgr = selectorManager_,
selectedGrips = mgr.selectorGrips, selectedGrips = mgr.selectorGrips,
selected = this.selectedElement, selected = this.selectedElement,
sw = selected.getAttribute('stroke-width'), sw = selected.getAttribute('stroke-width'),
currentZoom = svgFactory_.currentZoom(); currentZoom = svgFactory_.currentZoom();
var offset = 1 / currentZoom; var offset = 1 / currentZoom;
if (selected.getAttribute('stroke') !== 'none' && !isNaN(sw)) { if (selected.getAttribute('stroke') !== 'none' && !isNaN(sw)) {
offset += (sw / 2); offset += (sw / 2);
} }
var tagName = selected.tagName; var tagName = selected.tagName;
if (tagName === 'text') { if (tagName === 'text') {
offset += 2 / currentZoom; offset += 2 / currentZoom;
} }
// loop and transform our bounding box until we reach our first rotation // loop and transform our bounding box until we reach our first rotation
var tlist = svgedit.transformlist.getTransformList(selected); var tlist = svgedit.transformlist.getTransformList(selected);
var m = svgedit.math.transformListToTransform(tlist).matrix; var m = svgedit.math.transformListToTransform(tlist).matrix;
// This should probably be handled somewhere else, but for now // This should probably be handled somewhere else, but for now
// it keeps the selection box correctly positioned when zoomed // it keeps the selection box correctly positioned when zoomed
m.e *= currentZoom; m.e *= currentZoom;
m.f *= currentZoom; m.f *= currentZoom;
if (!bbox) { if (!bbox) {
bbox = svgedit.utilities.getBBox(selected); 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 (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? // TODO: svgedit.utilities.getBBox doesn't exclude 'gsvg' and calls getStrokedBBox for any 'g'. Should getBBox be updated?
if (tagName === 'g' && !$.data(selected, 'gsvg')) { if (tagName === 'g' && !$.data(selected, 'gsvg')) {
// The bbox for a group does not include stroke vals, so we // The bbox for a group does not include stroke vals, so we
// get the bbox based on its children. // get the bbox based on its children.
var strokedBbox = svgFactory_.getStrokedBBox(selected.childNodes); var strokedBbox = svgFactory_.getStrokedBBox(selected.childNodes);
if (strokedBbox) { if (strokedBbox) {
bbox = strokedBbox; bbox = strokedBbox;
} }
} }
// apply the transforms // apply the transforms
var l = bbox.x, t = bbox.y, w = bbox.width, h = bbox.height; var l = bbox.x, t = bbox.y, w = bbox.width, h = bbox.height;
bbox = {x: l, y: t, width: w, height: h}; bbox = {x: l, y: t, width: w, height: h};
// we need to handle temporary transforms too // we need to handle temporary transforms too
// if skewed, get its transformed box, then find its axis-aligned bbox // 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), var nbox = svgedit.math.transformBox(l * currentZoom, t * currentZoom, w * currentZoom, h * currentZoom, m),
aabox = nbox.aabox, aabox = nbox.aabox,
nbax = aabox.x - offset, nbax = aabox.x - offset,
nbay = aabox.y - offset, nbay = aabox.y - offset,
nbaw = aabox.width + (offset * 2), nbaw = aabox.width + (offset * 2),
nbah = aabox.height + (offset * 2); nbah = aabox.height + (offset * 2);
// now if the shape is rotated, un-rotate it // now if the shape is rotated, un-rotate it
var cx = nbax + nbaw / 2, var cx = nbax + nbaw / 2,
cy = nbay + nbah / 2; cy = nbay + nbah / 2;
var angle = svgedit.utilities.getRotationAngle(selected); var angle = svgedit.utilities.getRotationAngle(selected);
if (angle) { if (angle) {
var rot = svgFactory_.svgRoot().createSVGTransform(); var rot = svgFactory_.svgRoot().createSVGTransform();
rot.setRotate(-angle, cx, cy); rot.setRotate(-angle, cx, cy);
var rotm = rot.matrix; var rotm = rot.matrix;
nbox.tl = svgedit.math.transformPoint(nbox.tl.x, nbox.tl.y, rotm); 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.tr = svgedit.math.transformPoint(nbox.tr.x, nbox.tr.y, rotm);
nbox.bl = svgedit.math.transformPoint(nbox.bl.x, nbox.bl.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); nbox.br = svgedit.math.transformPoint(nbox.br.x, nbox.br.y, rotm);
// calculate the axis-aligned bbox // calculate the axis-aligned bbox
var tl = nbox.tl; var tl = nbox.tl;
var minx = tl.x, var minx = tl.x,
miny = tl.y, miny = tl.y,
maxx = tl.x, maxx = tl.x,
maxy = tl.y; 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; 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; 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; 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; maxy = max(maxy, max(nbox.tr.y, max(nbox.bl.y, nbox.br.y))) + offset;
nbax = minx; nbax = minx;
nbay = miny; nbay = miny;
nbaw = (maxx - minx); nbaw = (maxx - minx);
nbah = (maxy - miny); nbah = (maxy - miny);
} }
var dstr = 'M' + nbax + ',' + nbay + var dstr = 'M' + nbax + ',' + nbay +
' L' + (nbax + nbaw) + ',' + nbay + ' L' + (nbax + nbaw) + ',' + nbay +
' ' + (nbax + nbaw) + ',' + (nbay + nbah) + ' ' + (nbax + nbaw) + ',' + (nbay + nbah) +
' ' + nbax + ',' + (nbay + nbah) + 'z'; ' ' + nbax + ',' + (nbay + nbah) + 'z';
selectedBox.setAttribute('d', dstr); selectedBox.setAttribute('d', dstr);
var xform = angle ? 'rotate(' + [angle, cx, cy].join(',') + ')' : ''; var xform = angle ? 'rotate(' + [angle, cx, cy].join(',') + ')' : '';
this.selectorGroup.setAttribute('transform', xform); this.selectorGroup.setAttribute('transform', xform);
// TODO(codedread): Is this if needed? // TODO(codedread): Is this if needed?
// if (selected === selectedElements[0]) { // if (selected === selectedElements[0]) {
this.gripCoords = { this.gripCoords = {
'nw': [nbax, nbay], 'nw': [nbax, nbay],
'ne': [nbax + nbaw, nbay], 'ne': [nbax + nbaw, nbay],
'sw': [nbax, nbay + nbah], 'sw': [nbax, nbay + nbah],
'se': [nbax + nbaw, nbay + nbah], 'se': [nbax + nbaw, nbay + nbah],
'n': [nbax + (nbaw) / 2, nbay], 'n': [nbax + (nbaw) / 2, nbay],
'w': [nbax, nbay + (nbah) / 2], 'w': [nbax, nbay + (nbah) / 2],
'e': [nbax + nbaw, nbay + (nbah) / 2], 'e': [nbax + nbaw, nbay + (nbah) / 2],
's': [nbax + (nbaw) / 2, nbay + nbah] 's': [nbax + (nbaw) / 2, nbay + nbah]
}; };
var dir; var dir;
for (dir in this.gripCoords) { for (dir in this.gripCoords) {
var coords = this.gripCoords[dir]; var coords = this.gripCoords[dir];
selectedGrips[dir].setAttribute('cx', coords[0]); selectedGrips[dir].setAttribute('cx', coords[0]);
selectedGrips[dir].setAttribute('cy', coords[1]); selectedGrips[dir].setAttribute('cy', coords[1]);
} }
// we want to go 20 pixels in the negative transformed y direction, ignoring scale // we want to go 20 pixels in the negative transformed y direction, ignoring scale
mgr.rotateGripConnector.setAttribute('x1', nbax + (nbaw) / 2); mgr.rotateGripConnector.setAttribute('x1', nbax + (nbaw) / 2);
mgr.rotateGripConnector.setAttribute('y1', nbay); mgr.rotateGripConnector.setAttribute('y1', nbay);
mgr.rotateGripConnector.setAttribute('x2', nbax + (nbaw) / 2); mgr.rotateGripConnector.setAttribute('x2', nbax + (nbaw) / 2);
mgr.rotateGripConnector.setAttribute('y2', nbay - (gripRadius * 5)); mgr.rotateGripConnector.setAttribute('y2', nbay - (gripRadius * 5));
mgr.rotateGrip.setAttribute('cx', nbax + (nbaw) / 2); mgr.rotateGrip.setAttribute('cx', nbax + (nbaw) / 2);
mgr.rotateGrip.setAttribute('cy', nbay - (gripRadius * 5)); mgr.rotateGrip.setAttribute('cy', nbay - (gripRadius * 5));
// } // }
}; };
// Class: svgedit.select.SelectorManager // Class: svgedit.select.SelectorManager
svgedit.select.SelectorManager = function () { svgedit.select.SelectorManager = function () {
// this will hold the <g> element that contains all selector rects/grips // this will hold the <g> element that contains all selector rects/grips
this.selectorParentGroup = null; this.selectorParentGroup = null;
// this is a special rect that is used for multi-select // this is a special rect that is used for multi-select
this.rubberBandBox = null; this.rubberBandBox = null;
// this will hold objects of type svgedit.select.Selector (see above) // this will hold objects of type svgedit.select.Selector (see above)
this.selectors = []; this.selectors = [];
// this holds a map of SVG elements to their Selector object // this holds a map of SVG elements to their Selector object
this.selectorMap = {}; this.selectorMap = {};
// this holds a reference to the grip elements // this holds a reference to the grip elements
this.selectorGrips = { this.selectorGrips = {
'nw': null, 'nw': null,
'n': null, 'n': null,
'ne': null, 'ne': null,
'e': null, 'e': null,
'se': null, 'se': null,
's': null, 's': null,
'sw': null, 'sw': null,
'w': null 'w': null
}; };
this.selectorGripsGroup = null; this.selectorGripsGroup = null;
this.rotateGripConnector = null; this.rotateGripConnector = null;
this.rotateGrip = null; this.rotateGrip = null;
this.initGroup(); this.initGroup();
}; };
// Function: svgedit.select.SelectorManager.initGroup // Function: svgedit.select.SelectorManager.initGroup
// Resets the parent selector group element // Resets the parent selector group element
svgedit.select.SelectorManager.prototype.initGroup = function () { svgedit.select.SelectorManager.prototype.initGroup = function () {
// remove old selector parent group if it existed // remove old selector parent group if it existed
if (this.selectorParentGroup && this.selectorParentGroup.parentNode) { if (this.selectorParentGroup && this.selectorParentGroup.parentNode) {
this.selectorParentGroup.parentNode.removeChild(this.selectorParentGroup); this.selectorParentGroup.parentNode.removeChild(this.selectorParentGroup);
} }
// create parent selector group and add it to svgroot // create parent selector group and add it to svgroot
this.selectorParentGroup = svgFactory_.createSVGElement({ this.selectorParentGroup = svgFactory_.createSVGElement({
'element': 'g', 'element': 'g',
'attr': {'id': 'selectorParentGroup'} 'attr': {'id': 'selectorParentGroup'}
}); });
this.selectorGripsGroup = svgFactory_.createSVGElement({ this.selectorGripsGroup = svgFactory_.createSVGElement({
'element': 'g', 'element': 'g',
'attr': {'display': 'none'} 'attr': {'display': 'none'}
}); });
this.selectorParentGroup.appendChild(this.selectorGripsGroup); this.selectorParentGroup.appendChild(this.selectorGripsGroup);
svgFactory_.svgRoot().appendChild(this.selectorParentGroup); svgFactory_.svgRoot().appendChild(this.selectorParentGroup);
this.selectorMap = {}; this.selectorMap = {};
this.selectors = []; this.selectors = [];
this.rubberBandBox = null; this.rubberBandBox = null;
// add the corner grips // add the corner grips
var dir; var dir;
for (dir in this.selectorGrips) { for (dir in this.selectorGrips) {
var grip = svgFactory_.createSVGElement({ var grip = svgFactory_.createSVGElement({
'element': 'circle', 'element': 'circle',
'attr': { 'attr': {
'id': ('selectorGrip_resize_' + dir), 'id': ('selectorGrip_resize_' + dir),
'fill': '#22C', 'fill': '#22C',
'r': gripRadius, 'r': gripRadius,
'style': ('cursor:' + dir + '-resize'), 'style': ('cursor:' + dir + '-resize'),
// This expands the mouse-able area of the grips making them // This expands the mouse-able area of the grips making them
// easier to grab with the mouse. // easier to grab with the mouse.
// This works in Opera and WebKit, but does not work in Firefox // This works in Opera and WebKit, but does not work in Firefox
// see https://bugzilla.mozilla.org/show_bug.cgi?id=500174 // see https://bugzilla.mozilla.org/show_bug.cgi?id=500174
'stroke-width': 2, 'stroke-width': 2,
'pointer-events': 'all' 'pointer-events': 'all'
} }
}); });
$.data(grip, 'dir', dir); $.data(grip, 'dir', dir);
$.data(grip, 'type', 'resize'); $.data(grip, 'type', 'resize');
this.selectorGrips[dir] = this.selectorGripsGroup.appendChild(grip); this.selectorGrips[dir] = this.selectorGripsGroup.appendChild(grip);
} }
// add rotator elems // add rotator elems
this.rotateGripConnector = this.selectorGripsGroup.appendChild( this.rotateGripConnector = this.selectorGripsGroup.appendChild(
svgFactory_.createSVGElement({ svgFactory_.createSVGElement({
'element': 'line', 'element': 'line',
'attr': { 'attr': {
'id': ('selectorGrip_rotateconnector'), 'id': ('selectorGrip_rotateconnector'),
'stroke': '#22C', 'stroke': '#22C',
'stroke-width': '1' 'stroke-width': '1'
} }
}) })
); );
this.rotateGrip = this.selectorGripsGroup.appendChild( this.rotateGrip = this.selectorGripsGroup.appendChild(
svgFactory_.createSVGElement({ svgFactory_.createSVGElement({
'element': 'circle', 'element': 'circle',
'attr': { 'attr': {
'id': 'selectorGrip_rotate', 'id': 'selectorGrip_rotate',
'fill': 'lime', 'fill': 'lime',
'r': gripRadius, 'r': gripRadius,
'stroke': '#22C', 'stroke': '#22C',
'stroke-width': 2, 'stroke-width': 2,
'style': 'cursor:url(' + config_.imgPath + 'rotate.png) 12 12, auto;' 'style': 'cursor:url(' + config_.imgPath + 'rotate.png) 12 12, auto;'
} }
}) })
); );
$.data(this.rotateGrip, 'type', 'rotate'); $.data(this.rotateGrip, 'type', 'rotate');
if ($('#canvasBackground').length) { return; } if ($('#canvasBackground').length) { return; }
var dims = config_.dimensions; var dims = config_.dimensions;
var canvasbg = svgFactory_.createSVGElement({ var canvasbg = svgFactory_.createSVGElement({
'element': 'svg', 'element': 'svg',
'attr': { 'attr': {
'id': 'canvasBackground', 'id': 'canvasBackground',
'width': dims[0], 'width': dims[0],
'height': dims[1], 'height': dims[1],
'x': 0, 'x': 0,
'y': 0, 'y': 0,
'overflow': (svgedit.browser.isWebkit() ? 'none' : 'visible'), // Chrome 7 has a problem with this when zooming out 'overflow': (svgedit.browser.isWebkit() ? 'none' : 'visible'), // Chrome 7 has a problem with this when zooming out
'style': 'pointer-events:none' 'style': 'pointer-events:none'
} }
}); });
var rect = svgFactory_.createSVGElement({ var rect = svgFactory_.createSVGElement({
'element': 'rect', 'element': 'rect',
'attr': { 'attr': {
'width': '100%', 'width': '100%',
'height': '100%', 'height': '100%',
'x': 0, 'x': 0,
'y': 0, 'y': 0,
'stroke-width': 1, 'stroke-width': 1,
'stroke': '#000', 'stroke': '#000',
'fill': '#FFF', 'fill': '#FFF',
'style': 'pointer-events:none' 'style': 'pointer-events:none'
} }
}); });
// Both Firefox and WebKit are too slow with this filter region (especially at higher // Both Firefox and WebKit are too slow with this filter region (especially at higher
// zoom levels) and Opera has at least one bug // zoom levels) and Opera has at least one bug
// if (!svgedit.browser.isOpera()) rect.setAttribute('filter', 'url(#canvashadow)'); // if (!svgedit.browser.isOpera()) rect.setAttribute('filter', 'url(#canvashadow)');
canvasbg.appendChild(rect); canvasbg.appendChild(rect);
svgFactory_.svgRoot().insertBefore(canvasbg, svgFactory_.svgContent()); svgFactory_.svgRoot().insertBefore(canvasbg, svgFactory_.svgContent());
}; };
// Function: svgedit.select.SelectorManager.requestSelector // Function: svgedit.select.SelectorManager.requestSelector
@@ -420,27 +420,27 @@ svgedit.select.SelectorManager.prototype.initGroup = function () {
// elem - DOM element to get the selector for // elem - DOM element to get the selector for
// bbox - Optional bbox to use for reset (prevents duplicate getBBox call). // bbox - Optional bbox to use for reset (prevents duplicate getBBox call).
svgedit.select.SelectorManager.prototype.requestSelector = function (elem, bbox) { svgedit.select.SelectorManager.prototype.requestSelector = function (elem, bbox) {
if (elem == null) { return null; } if (elem == null) { return null; }
var i, var i,
N = this.selectors.length; N = this.selectors.length;
// If we've already acquired one for this element, return it. // If we've already acquired one for this element, return it.
if (typeof this.selectorMap[elem.id] === 'object') { if (typeof this.selectorMap[elem.id] === 'object') {
this.selectorMap[elem.id].locked = true; this.selectorMap[elem.id].locked = true;
return this.selectorMap[elem.id]; return this.selectorMap[elem.id];
} }
for (i = 0; i < N; ++i) { for (i = 0; i < N; ++i) {
if (this.selectors[i] && !this.selectors[i].locked) { if (this.selectors[i] && !this.selectors[i].locked) {
this.selectors[i].locked = true; this.selectors[i].locked = true;
this.selectors[i].reset(elem, bbox); this.selectors[i].reset(elem, bbox);
this.selectorMap[elem.id] = this.selectors[i]; this.selectorMap[elem.id] = this.selectors[i];
return this.selectors[i]; return this.selectors[i];
} }
} }
// if we reached here, no available selectors were found, we create one // if we reached here, no available selectors were found, we create one
this.selectors[N] = new svgedit.select.Selector(N, elem, bbox); this.selectors[N] = new svgedit.select.Selector(N, elem, bbox);
this.selectorParentGroup.appendChild(this.selectors[N].selectorGroup); this.selectorParentGroup.appendChild(this.selectors[N].selectorGroup);
this.selectorMap[elem.id] = this.selectors[N]; this.selectorMap[elem.id] = this.selectors[N];
return this.selectors[N]; return this.selectors[N];
}; };
// Function: svgedit.select.SelectorManager.releaseSelector // Function: svgedit.select.SelectorManager.releaseSelector
@@ -449,51 +449,51 @@ svgedit.select.SelectorManager.prototype.requestSelector = function (elem, bbox)
// Parameters: // Parameters:
// elem - DOM element to remove the selector for // elem - DOM element to remove the selector for
svgedit.select.SelectorManager.prototype.releaseSelector = function (elem) { svgedit.select.SelectorManager.prototype.releaseSelector = function (elem) {
if (elem == null) { return; } if (elem == null) { return; }
var i, var i,
N = this.selectors.length, N = this.selectors.length,
sel = this.selectorMap[elem.id]; sel = this.selectorMap[elem.id];
if (!sel.locked) { if (!sel.locked) {
// TODO(codedread): Ensure this exists in this module. // TODO(codedread): Ensure this exists in this module.
console.log('WARNING! selector was released but was already unlocked'); console.log('WARNING! selector was released but was already unlocked');
} }
for (i = 0; i < N; ++i) { for (i = 0; i < N; ++i) {
if (this.selectors[i] && this.selectors[i] === sel) { if (this.selectors[i] && this.selectors[i] === sel) {
delete this.selectorMap[elem.id]; delete this.selectorMap[elem.id];
sel.locked = false; sel.locked = false;
sel.selectedElement = null; sel.selectedElement = null;
sel.showGrips(false); sel.showGrips(false);
// remove from DOM and store reference in JS but only if it exists in the DOM // remove from DOM and store reference in JS but only if it exists in the DOM
try { try {
sel.selectorGroup.setAttribute('display', 'none'); sel.selectorGroup.setAttribute('display', 'none');
} catch (e) {} } catch (e) {}
break; break;
} }
} }
}; };
// Function: svgedit.select.SelectorManager.getRubberBandBox // Function: svgedit.select.SelectorManager.getRubberBandBox
// Returns the rubberBandBox DOM element. This is the rectangle drawn by the user for selecting/zooming // Returns the rubberBandBox DOM element. This is the rectangle drawn by the user for selecting/zooming
svgedit.select.SelectorManager.prototype.getRubberBandBox = function () { svgedit.select.SelectorManager.prototype.getRubberBandBox = function () {
if (!this.rubberBandBox) { if (!this.rubberBandBox) {
this.rubberBandBox = this.selectorParentGroup.appendChild( this.rubberBandBox = this.selectorParentGroup.appendChild(
svgFactory_.createSVGElement({ svgFactory_.createSVGElement({
'element': 'rect', 'element': 'rect',
'attr': { 'attr': {
'id': 'selectorRubberBand', 'id': 'selectorRubberBand',
'fill': '#22C', 'fill': '#22C',
'fill-opacity': 0.15, 'fill-opacity': 0.15,
'stroke': '#22C', 'stroke': '#22C',
'stroke-width': 0.5, 'stroke-width': 0.5,
'display': 'none', 'display': 'none',
'style': 'pointer-events:none' 'style': 'pointer-events:none'
} }
}) })
); );
} }
return this.rubberBandBox; return this.rubberBandBox;
}; };
/** /**
@@ -519,9 +519,9 @@ svgedit.select.SelectorManager.prototype.getRubberBandBox = function () {
* svgFactory - an object implementing the SVGFactory interface (see above). * svgFactory - an object implementing the SVGFactory interface (see above).
*/ */
svgedit.select.init = function (config, svgFactory) { svgedit.select.init = function (config, svgFactory) {
config_ = config; config_ = config;
svgFactory_ = svgFactory; svgFactory_ = svgFactory;
selectorManager_ = new svgedit.select.SelectorManager(); selectorManager_ = new svgedit.select.SelectorManager();
}; };
/** /**
@@ -531,6 +531,6 @@ svgedit.select.init = function (config, svgFactory) {
* The SelectorManager instance. * The SelectorManager instance.
*/ */
svgedit.select.getSelectorManager = function () { svgedit.select.getSelectorManager = function () {
return selectorManager_; return selectorManager_;
}; };
}()); }());

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,24 +7,24 @@
*/ */
svgedit = { svgedit = {
// common namepaces constants in alpha order // common namepaces constants in alpha order
NS: { NS: {
HTML: 'http://www.w3.org/1999/xhtml', HTML: 'http://www.w3.org/1999/xhtml',
MATH: 'http://www.w3.org/1998/Math/MathML', MATH: 'http://www.w3.org/1998/Math/MathML',
SE: 'http://svg-edit.googlecode.com', SE: 'http://svg-edit.googlecode.com',
SVG: 'http://www.w3.org/2000/svg', SVG: 'http://www.w3.org/2000/svg',
XLINK: 'http://www.w3.org/1999/xlink', XLINK: 'http://www.w3.org/1999/xlink',
XML: 'http://www.w3.org/XML/1998/namespace', 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 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 // return the svgedit.NS with key values switched and lowercase
svgedit.getReverseNS = function () { svgedit.getReverseNS = function () {
'use strict'; 'use strict';
var reverseNS = {}; var reverseNS = {};
$.each(this.NS, function (name, URI) { $.each(this.NS, function (name, URI) {
reverseNS[URI] = name.toLowerCase(); reverseNS[URI] = name.toLowerCase();
}); });
return reverseNS; return reverseNS;
}; };

View File

@@ -16,41 +16,41 @@
'use strict'; 'use strict';
if (!svgedit.transformlist) { if (!svgedit.transformlist) {
svgedit.transformlist = {}; svgedit.transformlist = {};
} }
var svgroot = document.createElementNS(svgedit.NS.SVG, 'svg'); var svgroot = document.createElementNS(svgedit.NS.SVG, 'svg');
// Helper function. // Helper function.
function transformToString (xform) { function transformToString (xform) {
var m = xform.matrix, var m = xform.matrix,
text = ''; text = '';
switch (xform.type) { switch (xform.type) {
case 1: // MATRIX case 1: // MATRIX
text = 'matrix(' + [m.a, m.b, m.c, m.d, m.e, m.f].join(',') + ')'; text = 'matrix(' + [m.a, m.b, m.c, m.d, m.e, m.f].join(',') + ')';
break; break;
case 2: // TRANSLATE case 2: // TRANSLATE
text = 'translate(' + m.e + ',' + m.f + ')'; text = 'translate(' + m.e + ',' + m.f + ')';
break; break;
case 3: // SCALE case 3: // SCALE
if (m.a === m.d) { if (m.a === m.d) {
text = 'scale(' + m.a + ')'; text = 'scale(' + m.a + ')';
} else { } else {
text = 'scale(' + m.a + ',' + m.d + ')'; text = 'scale(' + m.a + ',' + m.d + ')';
} }
break; break;
case 4: // ROTATE case 4: // ROTATE
var cx = 0, cy = 0; var cx = 0, cy = 0;
// this prevents divide by zero // this prevents divide by zero
if (xform.angle !== 0) { if (xform.angle !== 0) {
var K = 1 - m.a; var K = 1 - m.a;
cy = (K * m.f + m.b * m.e) / (K * K + m.b * m.b); cy = (K * m.f + m.b * m.e) / (K * K + m.b * m.b);
cx = (m.e - m.b * cy) / K; cx = (m.e - m.b * cy) / K;
} }
text = 'rotate(' + xform.angle + ' ' + cx + ',' + cy + ')'; text = 'rotate(' + xform.angle + ' ' + cx + ',' + cy + ')';
break; break;
} }
return text; return text;
} }
/** /**
@@ -78,176 +78,176 @@ var listMap_ = {};
// } // }
// ************************************************************************************** // **************************************************************************************
svgedit.transformlist.SVGTransformList = function (elem) { svgedit.transformlist.SVGTransformList = function (elem) {
this._elem = elem || null; this._elem = elem || null;
this._xforms = []; this._xforms = [];
// TODO: how do we capture the undo-ability in the changed transform list? // TODO: how do we capture the undo-ability in the changed transform list?
this._update = function () { this._update = function () {
var tstr = ''; var tstr = '';
/* var concatMatrix = */ svgroot.createSVGMatrix(); /* var concatMatrix = */ svgroot.createSVGMatrix();
var i; var i;
for (i = 0; i < this.numberOfItems; ++i) { for (i = 0; i < this.numberOfItems; ++i) {
var xform = this._list.getItem(i); var xform = this._list.getItem(i);
tstr += transformToString(xform) + ' '; tstr += transformToString(xform) + ' ';
} }
this._elem.setAttribute('transform', tstr); this._elem.setAttribute('transform', tstr);
}; };
this._list = this; this._list = this;
this._init = function () { this._init = function () {
// Transform attribute parser // Transform attribute parser
var str = this._elem.getAttribute('transform'); var str = this._elem.getAttribute('transform');
if (!str) { return; } if (!str) { return; }
// TODO: Add skew support in future // TODO: Add skew support in future
var re = /\s*((scale|matrix|rotate|translate)\s*\(.*?\))\s*,?\s*/; var re = /\s*((scale|matrix|rotate|translate)\s*\(.*?\))\s*,?\s*/;
var m = true; var m = true;
while (m) { while (m) {
m = str.match(re); m = str.match(re);
str = str.replace(re, ''); str = str.replace(re, '');
if (m && m[1]) { if (m && m[1]) {
var x = m[1]; var x = m[1];
var bits = x.split(/\s*\(/); var bits = x.split(/\s*\(/);
var name = bits[0]; var name = bits[0];
var valBits = bits[1].match(/\s*(.*?)\s*\)/); var valBits = bits[1].match(/\s*(.*?)\s*\)/);
valBits[1] = valBits[1].replace(/(\d)-/g, '$1 -'); valBits[1] = valBits[1].replace(/(\d)-/g, '$1 -');
var valArr = valBits[1].split(/[, ]+/); var valArr = valBits[1].split(/[, ]+/);
var letters = 'abcdef'.split(''); var letters = 'abcdef'.split('');
var mtx = svgroot.createSVGMatrix(); var mtx = svgroot.createSVGMatrix();
$.each(valArr, function (i, item) { $.each(valArr, function (i, item) {
valArr[i] = parseFloat(item); valArr[i] = parseFloat(item);
if (name === 'matrix') { if (name === 'matrix') {
mtx[letters[i]] = valArr[i]; mtx[letters[i]] = valArr[i];
} }
}); });
var xform = svgroot.createSVGTransform(); var xform = svgroot.createSVGTransform();
var fname = 'set' + name.charAt(0).toUpperCase() + name.slice(1); var fname = 'set' + name.charAt(0).toUpperCase() + name.slice(1);
var values = name === 'matrix' ? [mtx] : valArr; var values = name === 'matrix' ? [mtx] : valArr;
if (name === 'scale' && values.length === 1) { if (name === 'scale' && values.length === 1) {
values.push(values[0]); values.push(values[0]);
} else if (name === 'translate' && values.length === 1) { } else if (name === 'translate' && values.length === 1) {
values.push(0); values.push(0);
} else if (name === 'rotate' && values.length === 1) { } else if (name === 'rotate' && values.length === 1) {
values.push(0, 0); values.push(0, 0);
} }
xform[fname].apply(xform, values); xform[fname].apply(xform, values);
this._list.appendItem(xform); this._list.appendItem(xform);
} }
} }
}; };
this._removeFromOtherLists = function (item) { this._removeFromOtherLists = function (item) {
if (item) { if (item) {
// Check if this transform is already in a transformlist, and // Check if this transform is already in a transformlist, and
// remove it if so. // remove it if so.
var found = false; var found = false;
var id; var id;
for (id in listMap_) { for (id in listMap_) {
var tl = listMap_[id]; var tl = listMap_[id];
var i, len; var i, len;
for (i = 0, len = tl._xforms.length; i < len; ++i) { for (i = 0, len = tl._xforms.length; i < len; ++i) {
if (tl._xforms[i] === item) { if (tl._xforms[i] === item) {
found = true; found = true;
tl.removeItem(i); tl.removeItem(i);
break; break;
} }
} }
if (found) { if (found) {
break; break;
} }
} }
} }
}; };
this.numberOfItems = 0; this.numberOfItems = 0;
this.clear = function () { this.clear = function () {
this.numberOfItems = 0; this.numberOfItems = 0;
this._xforms = []; this._xforms = [];
}; };
this.initialize = function (newItem) { this.initialize = function (newItem) {
this.numberOfItems = 1; this.numberOfItems = 1;
this._removeFromOtherLists(newItem); this._removeFromOtherLists(newItem);
this._xforms = [newItem]; this._xforms = [newItem];
}; };
this.getItem = function (index) { this.getItem = function (index) {
if (index < this.numberOfItems && index >= 0) { if (index < this.numberOfItems && index >= 0) {
return this._xforms[index]; return this._xforms[index];
} }
var err = new Error('DOMException with code=INDEX_SIZE_ERR'); var err = new Error('DOMException with code=INDEX_SIZE_ERR');
err.code = 1; err.code = 1;
throw err; throw err;
}; };
this.insertItemBefore = function (newItem, index) { this.insertItemBefore = function (newItem, index) {
var retValue = null; var retValue = null;
if (index >= 0) { if (index >= 0) {
if (index < this.numberOfItems) { if (index < this.numberOfItems) {
this._removeFromOtherLists(newItem); this._removeFromOtherLists(newItem);
var newxforms = new Array(this.numberOfItems + 1); var newxforms = new Array(this.numberOfItems + 1);
// TODO: use array copying and slicing // TODO: use array copying and slicing
var i; var i;
for (i = 0; i < index; ++i) { for (i = 0; i < index; ++i) {
newxforms[i] = this._xforms[i]; newxforms[i] = this._xforms[i];
} }
newxforms[i] = newItem; newxforms[i] = newItem;
var j; var j;
for (j = i + 1; i < this.numberOfItems; ++j, ++i) { for (j = i + 1; i < this.numberOfItems; ++j, ++i) {
newxforms[j] = this._xforms[i]; newxforms[j] = this._xforms[i];
} }
this.numberOfItems++; this.numberOfItems++;
this._xforms = newxforms; this._xforms = newxforms;
retValue = newItem; retValue = newItem;
this._list._update(); this._list._update();
} else { } else {
retValue = this._list.appendItem(newItem); retValue = this._list.appendItem(newItem);
} }
} }
return retValue; return retValue;
}; };
this.replaceItem = function (newItem, index) { this.replaceItem = function (newItem, index) {
var retValue = null; var retValue = null;
if (index < this.numberOfItems && index >= 0) { if (index < this.numberOfItems && index >= 0) {
this._removeFromOtherLists(newItem); this._removeFromOtherLists(newItem);
this._xforms[index] = newItem; this._xforms[index] = newItem;
retValue = newItem; retValue = newItem;
this._list._update(); this._list._update();
} }
return retValue; return retValue;
}; };
this.removeItem = function (index) { this.removeItem = function (index) {
if (index < this.numberOfItems && index >= 0) { if (index < this.numberOfItems && index >= 0) {
var retValue = this._xforms[index]; var retValue = this._xforms[index];
var newxforms = new Array(this.numberOfItems - 1); var newxforms = new Array(this.numberOfItems - 1);
var i, j; var i, j;
for (i = 0; i < index; ++i) { for (i = 0; i < index; ++i) {
newxforms[i] = this._xforms[i]; newxforms[i] = this._xforms[i];
} }
for (j = i; j < this.numberOfItems - 1; ++j, ++i) { for (j = i; j < this.numberOfItems - 1; ++j, ++i) {
newxforms[j] = this._xforms[i + 1]; newxforms[j] = this._xforms[i + 1];
} }
this.numberOfItems--; this.numberOfItems--;
this._xforms = newxforms; this._xforms = newxforms;
this._list._update(); this._list._update();
return retValue; return retValue;
} }
var err = new Error('DOMException with code=INDEX_SIZE_ERR'); var err = new Error('DOMException with code=INDEX_SIZE_ERR');
err.code = 1; err.code = 1;
throw err; throw err;
}; };
this.appendItem = function (newItem) { this.appendItem = function (newItem) {
this._removeFromOtherLists(newItem); this._removeFromOtherLists(newItem);
this._xforms.push(newItem); this._xforms.push(newItem);
this.numberOfItems++; this.numberOfItems++;
this._list._update(); this._list._update();
return newItem; return newItem;
}; };
}; };
svgedit.transformlist.resetListMap = function () { svgedit.transformlist.resetListMap = function () {
listMap_ = {}; listMap_ = {};
}; };
/** /**
@@ -256,9 +256,9 @@ svgedit.transformlist.resetListMap = function () {
* elem - a DOM Element * elem - a DOM Element
*/ */
svgedit.transformlist.removeElementFromListMap = function (elem) { svgedit.transformlist.removeElementFromListMap = function (elem) {
if (elem.id && listMap_[elem.id]) { if (elem.id && listMap_[elem.id]) {
delete listMap_[elem.id]; delete listMap_[elem.id];
} }
}; };
// Function: getTransformList // Function: getTransformList
@@ -267,26 +267,26 @@ svgedit.transformlist.removeElementFromListMap = function (elem) {
// Parameters: // Parameters:
// elem - DOM element to get a transformlist from // elem - DOM element to get a transformlist from
svgedit.transformlist.getTransformList = function (elem) { svgedit.transformlist.getTransformList = function (elem) {
if (!svgedit.browser.supportsNativeTransformLists()) { if (!svgedit.browser.supportsNativeTransformLists()) {
var id = elem.id || 'temp'; var id = elem.id || 'temp';
var t = listMap_[id]; var t = listMap_[id];
if (!t || id === 'temp') { if (!t || id === 'temp') {
listMap_[id] = new svgedit.transformlist.SVGTransformList(elem); listMap_[id] = new svgedit.transformlist.SVGTransformList(elem);
listMap_[id]._init(); listMap_[id]._init();
t = listMap_[id]; t = listMap_[id];
} }
return t; return t;
} }
if (elem.transform) { if (elem.transform) {
return elem.transform.baseVal; return elem.transform.baseVal;
} }
if (elem.gradientTransform) { if (elem.gradientTransform) {
return elem.gradientTransform.baseVal; return elem.gradientTransform.baseVal;
} }
if (elem.patternTransform) { if (elem.patternTransform) {
return elem.patternTransform.baseVal; return elem.patternTransform.baseVal;
} }
return null; return null;
}; };
}()); }());

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,32 @@
/* eslint-disable no-var */ /* eslint-disable no-var */
// http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/ // http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/
function touchHandler (event) { function touchHandler (event) {
'use strict'; 'use strict';
var simulatedEvent, var simulatedEvent,
touches = event.changedTouches, touches = event.changedTouches,
first = touches[0], first = touches[0],
type = ''; type = '';
switch (event.type) { switch (event.type) {
case 'touchstart': type = 'mousedown'; break; case 'touchstart': type = 'mousedown'; break;
case 'touchmove': type = 'mousemove'; break; case 'touchmove': type = 'mousemove'; break;
case 'touchend': type = 'mouseup'; break; case 'touchend': type = 'mouseup'; break;
default: return; default: return;
} }
// initMouseEvent(type, canBubble, cancelable, view, clickCount, // initMouseEvent(type, canBubble, cancelable, view, clickCount,
// screenX, screenY, clientX, clientY, ctrlKey, // screenX, screenY, clientX, clientY, ctrlKey,
// altKey, shiftKey, metaKey, button, relatedTarget); // altKey, shiftKey, metaKey, button, relatedTarget);
simulatedEvent = document.createEvent('MouseEvent'); simulatedEvent = document.createEvent('MouseEvent');
simulatedEvent.initMouseEvent(type, true, true, window, 1, simulatedEvent.initMouseEvent(type, true, true, window, 1,
first.screenX, first.screenY, first.screenX, first.screenY,
first.clientX, first.clientY, false, first.clientX, first.clientY, false,
false, false, false, 0/* left */, null); false, false, false, 0/* left */, null);
if (touches.length < 2) { if (touches.length < 2) {
first.target.dispatchEvent(simulatedEvent); first.target.dispatchEvent(simulatedEvent);
event.preventDefault(); event.preventDefault();
} }
} }
document.addEventListener('touchstart', touchHandler, true); document.addEventListener('touchstart', touchHandler, true);

View File

@@ -16,7 +16,7 @@
'use strict'; 'use strict';
if (!svgedit.units) { if (!svgedit.units) {
svgedit.units = {}; svgedit.units = {};
} }
var NS = svgedit.NS; var NS = svgedit.NS;
@@ -26,15 +26,15 @@ var unitAttrs = ['r', 'radius'].concat(wAttrs, hAttrs);
// unused // unused
/* /*
var unitNumMap = { var unitNumMap = {
'%': 2, '%': 2,
'em': 3, 'em': 3,
'ex': 4, 'ex': 4,
'px': 5, 'px': 5,
'cm': 6, 'cm': 6,
'mm': 7, 'mm': 7,
'in': 8, 'in': 8,
'pt': 9, 'pt': 9,
'pc': 10 'pc': 10
}; };
*/ */
// Container of elements. // Container of elements.
@@ -63,31 +63,31 @@ var typeMap_ = {};
* elementContainer - an object implementing the ElementContainer interface. * elementContainer - an object implementing the ElementContainer interface.
*/ */
svgedit.units.init = function (elementContainer) { svgedit.units.init = function (elementContainer) {
elementContainer_ = elementContainer; elementContainer_ = elementContainer;
// Get correct em/ex values by creating a temporary SVG. // Get correct em/ex values by creating a temporary SVG.
var svg = document.createElementNS(NS.SVG, 'svg'); var svg = document.createElementNS(NS.SVG, 'svg');
document.body.appendChild(svg); document.body.appendChild(svg);
var rect = document.createElementNS(NS.SVG, 'rect'); var rect = document.createElementNS(NS.SVG, 'rect');
rect.setAttribute('width', '1em'); rect.setAttribute('width', '1em');
rect.setAttribute('height', '1ex'); rect.setAttribute('height', '1ex');
rect.setAttribute('x', '1in'); rect.setAttribute('x', '1in');
svg.appendChild(rect); svg.appendChild(rect);
var bb = rect.getBBox(); var bb = rect.getBBox();
document.body.removeChild(svg); document.body.removeChild(svg);
var inch = bb.x; var inch = bb.x;
typeMap_ = { typeMap_ = {
'em': bb.width, 'em': bb.width,
'ex': bb.height, 'ex': bb.height,
'in': inch, 'in': inch,
'cm': inch / 2.54, 'cm': inch / 2.54,
'mm': inch / 25.4, 'mm': inch / 25.4,
'pt': inch / 72, 'pt': inch / 72,
'pc': inch / 6, 'pc': inch / 6,
'px': 1, 'px': 1,
'%': 0 '%': 0
}; };
}; };
// Group: Unit conversion functions // Group: Unit conversion functions
@@ -95,7 +95,7 @@ svgedit.units.init = function (elementContainer) {
// Function: svgedit.units.getTypeMap // Function: svgedit.units.getTypeMap
// Returns the unit object with values for each unit // Returns the unit object with values for each unit
svgedit.units.getTypeMap = function () { svgedit.units.getTypeMap = function () {
return typeMap_; return typeMap_;
}; };
// Function: svgedit.units.shortFloat // 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 // If a string/number was given, returns a Float. If an array, return a string
// with comma-separated floats // with comma-separated floats
svgedit.units.shortFloat = function (val) { svgedit.units.shortFloat = function (val) {
var digits = elementContainer_.getRoundDigits(); var digits = elementContainer_.getRoundDigits();
if (!isNaN(val)) { if (!isNaN(val)) {
// Note that + converts to Number // Note that + converts to Number
return +((+val).toFixed(digits)); return +((+val).toFixed(digits));
} }
if ($.isArray(val)) { if ($.isArray(val)) {
return svgedit.units.shortFloat(val[0]) + ',' + svgedit.units.shortFloat(val[1]); return svgedit.units.shortFloat(val[0]) + ',' + svgedit.units.shortFloat(val[1]);
} }
return parseFloat(val).toFixed(digits) - 0; return parseFloat(val).toFixed(digits) - 0;
}; };
// Function: svgedit.units.convertUnit // Function: svgedit.units.convertUnit
// Converts the number to given unit or baseUnit // Converts the number to given unit or baseUnit
svgedit.units.convertUnit = function (val, unit) { svgedit.units.convertUnit = function (val, unit) {
unit = unit || elementContainer_.getBaseUnit(); unit = unit || elementContainer_.getBaseUnit();
// baseVal.convertToSpecifiedUnits(unitNumMap[unit]); // baseVal.convertToSpecifiedUnits(unitNumMap[unit]);
// var val = baseVal.valueInSpecifiedUnits; // var val = baseVal.valueInSpecifiedUnits;
// baseVal.convertToSpecifiedUnits(1); // baseVal.convertToSpecifiedUnits(1);
return svgedit.units.shortFloat(val / typeMap_[unit]); return svgedit.units.shortFloat(val / typeMap_[unit]);
}; };
// Function: svgedit.units.setUnitAttr // 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 // attr - String with the name of the attribute associated with the value
// val - String with the attribute value to convert // val - String with the attribute value to convert
svgedit.units.setUnitAttr = function (elem, attr, val) { svgedit.units.setUnitAttr = function (elem, attr, val) {
// if (!isNaN(val)) { // if (!isNaN(val)) {
// New value is a number, so check currently used unit // New value is a number, so check currently used unit
// var old_val = elem.getAttribute(attr); // var old_val = elem.getAttribute(attr);
// Enable this for alternate mode // Enable this for alternate mode
// if (old_val !== null && (isNaN(old_val) || elementContainer_.getBaseUnit() !== 'px')) { // if (old_val !== null && (isNaN(old_val) || elementContainer_.getBaseUnit() !== 'px')) {
// // Old value was a number, so get unit, then convert // // Old value was a number, so get unit, then convert
// var unit; // var unit;
// if (old_val.substr(-1) === '%') { // if (old_val.substr(-1) === '%') {
// var res = getResolution(); // var res = getResolution();
// unit = '%'; // unit = '%';
// val *= 100; // val *= 100;
// if (wAttrs.indexOf(attr) >= 0) { // if (wAttrs.indexOf(attr) >= 0) {
// val = val / res.w; // val = val / res.w;
// } else if (hAttrs.indexOf(attr) >= 0) { // } else if (hAttrs.indexOf(attr) >= 0) {
// val = val / res.h; // val = val / res.h;
// } else { // } else {
// return val / Math.sqrt((res.w*res.w) + (res.h*res.h))/Math.sqrt(2); // return val / Math.sqrt((res.w*res.w) + (res.h*res.h))/Math.sqrt(2);
// } // }
// } else { // } else {
// if (elementContainer_.getBaseUnit() !== 'px') { // if (elementContainer_.getBaseUnit() !== 'px') {
// unit = elementContainer_.getBaseUnit(); // unit = elementContainer_.getBaseUnit();
// } else { // } else {
// unit = old_val.substr(-2); // unit = old_val.substr(-2);
// } // }
// val = val / typeMap_[unit]; // val = val / typeMap_[unit];
// } // }
// //
// val += unit; // val += unit;
// } // }
// } // }
elem.setAttribute(attr, val); elem.setAttribute(attr, val);
}; };
var attrsToConvert = { var attrsToConvert = {
'line': ['x1', 'x2', 'y1', 'y2'], 'line': ['x1', 'x2', 'y1', 'y2'],
'circle': ['cx', 'cy', 'r'], 'circle': ['cx', 'cy', 'r'],
'ellipse': ['cx', 'cy', 'rx', 'ry'], 'ellipse': ['cx', 'cy', 'rx', 'ry'],
'foreignObject': ['x', 'y', 'width', 'height'], 'foreignObject': ['x', 'y', 'width', 'height'],
'rect': ['x', 'y', 'width', 'height'], 'rect': ['x', 'y', 'width', 'height'],
'image': ['x', 'y', 'width', 'height'], 'image': ['x', 'y', 'width', 'height'],
'use': ['x', 'y', 'width', 'height'], 'use': ['x', 'y', 'width', 'height'],
'text': ['x', 'y'] 'text': ['x', 'y']
}; };
// Function: svgedit.units.convertAttrs // Function: svgedit.units.convertAttrs
@@ -188,25 +188,25 @@ var attrsToConvert = {
// Parameters: // Parameters:
// element - a DOM element whose attributes should be converted // element - a DOM element whose attributes should be converted
svgedit.units.convertAttrs = function (element) { svgedit.units.convertAttrs = function (element) {
var elName = element.tagName; var elName = element.tagName;
var unit = elementContainer_.getBaseUnit(); var unit = elementContainer_.getBaseUnit();
var attrs = attrsToConvert[elName]; var attrs = attrsToConvert[elName];
if (!attrs) { return; } if (!attrs) { return; }
var len = attrs.length; var len = attrs.length;
var i; var i;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
var attr = attrs[i]; var attr = attrs[i];
var cur = element.getAttribute(attr); var cur = element.getAttribute(attr);
if (cur) { if (cur) {
if (!isNaN(cur)) { if (!isNaN(cur)) {
element.setAttribute(attr, (cur / typeMap_[unit]) + unit); element.setAttribute(attr, (cur / typeMap_[unit]) + unit);
} }
// else { // else {
// Convert existing? // Convert existing?
// } // }
} }
} }
}; };
// Function: svgedit.units.convertToNum // 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 // attr - String with the name of the attribute associated with the value
// val - String with the attribute value to convert // val - String with the attribute value to convert
svgedit.units.convertToNum = function (attr, val) { svgedit.units.convertToNum = function (attr, val) {
// Return a number if that's what it already is // Return a number if that's what it already is
if (!isNaN(val)) { return val - 0; } if (!isNaN(val)) { return val - 0; }
var num; var num;
if (val.substr(-1) === '%') { if (val.substr(-1) === '%') {
// Deal with percentage, depends on attribute // Deal with percentage, depends on attribute
num = val.substr(0, val.length - 1) / 100; num = val.substr(0, val.length - 1) / 100;
var width = elementContainer_.getWidth(); var width = elementContainer_.getWidth();
var height = elementContainer_.getHeight(); var height = elementContainer_.getHeight();
if (wAttrs.indexOf(attr) >= 0) { if (wAttrs.indexOf(attr) >= 0) {
return num * width; return num * width;
} }
if (hAttrs.indexOf(attr) >= 0) { if (hAttrs.indexOf(attr) >= 0) {
return num * height; return num * height;
} }
return num * Math.sqrt((width * width) + (height * height)) / Math.sqrt(2); return num * Math.sqrt((width * width) + (height * height)) / Math.sqrt(2);
} }
var unit = val.substr(-2); var unit = val.substr(-2);
num = val.substr(0, val.length - 2); num = val.substr(0, val.length - 2);
// Note that this multiplication turns the string into a number // Note that this multiplication turns the string into a number
return num * typeMap_[unit]; return num * typeMap_[unit];
}; };
// Function: svgedit.units.isValidUnit // 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 // attr - String with the name of the attribute associated with the value
// val - String with the attribute value to check // val - String with the attribute value to check
svgedit.units.isValidUnit = function (attr, val, selectedElement) { svgedit.units.isValidUnit = function (attr, val, selectedElement) {
var valid = false; var valid = false;
if (unitAttrs.indexOf(attr) >= 0) { if (unitAttrs.indexOf(attr) >= 0) {
// True if it's just a number // True if it's just a number
if (!isNaN(val)) { if (!isNaN(val)) {
valid = true; valid = true;
} else { } else {
// Not a number, check if it has a valid unit // Not a number, check if it has a valid unit
val = val.toLowerCase(); val = val.toLowerCase();
$.each(typeMap_, function (unit) { $.each(typeMap_, function (unit) {
if (valid) { return; } if (valid) { return; }
var re = new RegExp('^-?[\\d\\.]+' + unit + '$'); var re = new RegExp('^-?[\\d\\.]+' + unit + '$');
if (re.test(val)) { valid = true; } if (re.test(val)) { valid = true; }
}); });
} }
} else if (attr === 'id') { } else if (attr === 'id') {
// if we're trying to change the id, make sure it's not already present in the doc // if we're trying to change the id, make sure it's not already present in the doc
// and the id value is valid. // and the id value is valid.
var result = false; var result = false;
// because getElem() can throw an exception in the case of an invalid id // 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) // (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 // we wrap it in an exception and only return true if the ID was valid and
// not already present // not already present
try { try {
var elem = elementContainer_.getElement(val); var elem = elementContainer_.getElement(val);
result = (elem == null || elem === selectedElement); result = (elem == null || elem === selectedElement);
} catch (e) {} } catch (e) {}
return result; return result;
} }
valid = true; valid = true;
return valid; return valid;
}; };
}()); }());