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

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

View File

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

View File

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

View File

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

View File

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

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
$(function () {
'use strict';
'use strict';
var svgCanvas = null;
var frame;
var svgCanvas = null;
var frame;
initEmbed = function () {
var doc, mainButton;
svgCanvas = new EmbeddedSVGEdit(frame);
// Hide main button, as we will be controlling new, load, save, etc. from the host document
doc = frame.contentDocument || frame.contentWindow.document;
mainButton = doc.getElementById('main_button');
mainButton.style.display = 'none';
};
initEmbed = function () {
var doc, mainButton;
svgCanvas = new EmbeddedSVGEdit(frame);
// Hide main button, as we will be controlling new, load, save, etc. from the host document
doc = frame.contentDocument || frame.contentWindow.document;
mainButton = doc.getElementById('main_button');
mainButton.style.display = 'none';
};
function handleSvgData (data, error) {
if (error) {
alert('error ' + error);
} else {
alert('Congratulations. Your SVG string is back in the host page, do with it what you will\n\n' + data);
}
}
function handleSvgData (data, error) {
if (error) {
alert('error ' + error);
} else {
alert('Congratulations. Your SVG string is back in the host page, do with it what you will\n\n' + data);
}
}
function loadSvg () {
var svgexample = '<svg width="640" height="480" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><g><title>Layer 1</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);
}
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>';
svgCanvas.setSvgString(svgexample);
}
function saveSvg () {
svgCanvas.getSvgString()(handleSvgData);
}
function saveSvg () {
svgCanvas.getSvgString()(handleSvgData);
}
function exportPNG () {
var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage;
function exportPNG () {
var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage;
var exportWindow = window.open(
'data:text/html;charset=utf-8,' + encodeURIComponent('<title>' + str + '</title><h1>' + str + '</h1>'),
'svg-edit-exportWindow'
);
svgCanvas.rasterExport('PNG', null, exportWindow.name);
}
var exportWindow = window.open(
'data:text/html;charset=utf-8,' + encodeURIComponent('<title>' + str + '</title><h1>' + str + '</h1>'),
'svg-edit-exportWindow'
);
svgCanvas.rasterExport('PNG', null, exportWindow.name);
}
function exportPDF () {
var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage;
function exportPDF () {
var str = frame.contentWindow.svgEditor.uiStrings.notification.loadingImage;
/**
// If you want to handle the PDF blob yourself, do as follows
svgCanvas.bind('exportedPDF', function (win, data) {
alert(data.dataurlstring);
});
svgCanvas.exportPDF(); // Accepts two args: optionalWindowName supplied back to bound exportPDF handler and optionalOutputType (defaults to dataurlstring)
return;
*/
/**
// If you want to handle the PDF blob yourself, do as follows
svgCanvas.bind('exportedPDF', function (win, data) {
alert(data.dataurlstring);
});
svgCanvas.exportPDF(); // Accepts two args: optionalWindowName supplied back to bound exportPDF handler and optionalOutputType (defaults to dataurlstring)
return;
*/
var exportWindow = window.open(
'data:text/html;charset=utf-8,' + encodeURIComponent('<title>' + str + '</title><h1>' + str + '</h1>'),
'svg-edit-exportWindow'
);
svgCanvas.exportPDF(exportWindow.name);
}
var exportWindow = window.open(
'data:text/html;charset=utf-8,' + encodeURIComponent('<title>' + str + '</title><h1>' + str + '</h1>'),
'svg-edit-exportWindow'
);
svgCanvas.exportPDF(exportWindow.name);
}
// Add event handlers
$('#load').click(loadSvg);
$('#save').click(saveSvg);
$('#exportPNG').click(exportPNG);
$('#exportPDF').click(exportPDF);
$('body').append(
$('<iframe src="svg-editor.html?extensions=ext-xdomain-messaging.js' +
window.location.href.replace(/\?(.*)$/, '&$1') + // Append arguments to this file onto the iframe
'" width="900px" height="600px" id="svgedit" onload="initEmbed();"></iframe>'
)
);
frame = document.getElementById('svgedit');
// Add event handlers
$('#load').click(loadSvg);
$('#save').click(saveSvg);
$('#exportPNG').click(exportPNG);
$('#exportPDF').click(exportPDF);
$('body').append(
$('<iframe src="svg-editor.html?extensions=ext-xdomain-messaging.js' +
window.location.href.replace(/\?(.*)$/, '&$1') + // Append arguments to this file onto the iframe
'" width="900px" height="600px" id="svgedit" onload="initEmbed();"></iframe>'
)
);
frame = document.getElementById('svgedit');
});

View File

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

View File

@ -10,11 +10,11 @@ var svgCanvas = new EmbeddedSVGEdit(window.frames.svgedit);
svgCanvas.setSvgString('string')
- Or if a callback is needed:
svgCanvas.setSvgString('string')(function(data, error){
if (error){
// There was an error
} else{
// Handle data
}
if (error){
// There was an error
} else{
// Handle data
}
})
Everything is done with the same API as the real svg-edit,
@ -42,15 +42,15 @@ blah.clearSelection('woot', 'blah', 1337, [1, 2, 3, 4, 5, 'moo'], -42, {a: 'tree
var cbid = 0;
function getCallbackSetter (d) {
return function () {
var t = this, // New callback
args = [].slice.call(arguments),
cbid = t.send(d, args, function () {}); // The callback (currently it's nothing, but will be set later)
return function () {
var t = this, // New callback
args = [].slice.call(arguments),
cbid = t.send(d, args, function () {}); // The callback (currently it's nothing, but will be set later)
return function (newcallback) {
t.callbacks[cbid] = newcallback; // Set callback
};
};
return function (newcallback) {
t.callbacks[cbid] = newcallback; // Set callback
};
};
}
/*
@ -59,38 +59,38 @@ function getCallbackSetter (d) {
* of same domain control
*/
function addCallback (t, data) {
var result = data.result || data.error,
cbid = data.id;
if (t.callbacks[cbid]) {
if (data.result) {
t.callbacks[cbid](result);
} else {
t.callbacks[cbid](result, 'error');
}
}
var result = data.result || data.error,
cbid = data.id;
if (t.callbacks[cbid]) {
if (data.result) {
t.callbacks[cbid](result);
} else {
t.callbacks[cbid](result, 'error');
}
}
}
function messageListener (e) {
// We accept and post strings as opposed to objects for the sake of IE9 support; this
// will most likely be changed in the future
if (typeof e.data !== 'string') {
return;
}
var allowedOrigins = this.allowedOrigins,
data = e.data && JSON.parse(e.data);
if (!data || typeof data !== 'object' || data.namespace !== 'svg-edit' ||
e.source !== this.frame.contentWindow ||
(allowedOrigins.indexOf('*') === -1 && allowedOrigins.indexOf(e.origin) === -1)
) {
return;
}
addCallback(this, data);
// We accept and post strings as opposed to objects for the sake of IE9 support; this
// will most likely be changed in the future
if (typeof e.data !== 'string') {
return;
}
var allowedOrigins = this.allowedOrigins,
data = e.data && JSON.parse(e.data);
if (!data || typeof data !== 'object' || data.namespace !== 'svg-edit' ||
e.source !== this.frame.contentWindow ||
(allowedOrigins.indexOf('*') === -1 && allowedOrigins.indexOf(e.origin) === -1)
) {
return;
}
addCallback(this, data);
}
function getMessageListener (t) {
return function (e) {
messageListener.call(t, e);
};
return function (e) {
messageListener.call(t, e);
};
}
/**
@ -100,77 +100,77 @@ function getMessageListener (t) {
* If supplied, it should probably be the same as svgEditor's allowedOrigins
*/
function EmbeddedSVGEdit (frame, allowedOrigins) {
if (!(this instanceof EmbeddedSVGEdit)) { // Allow invocation without 'new' keyword
return new EmbeddedSVGEdit(frame);
}
this.allowedOrigins = allowedOrigins || [];
// Initialize communication
this.frame = frame;
this.callbacks = {};
// List of functions extracted with this:
// Run in firebug on http://svg-edit.googlecode.com/svn/trunk/docs/files/svgcanvas-js.html
if (!(this instanceof EmbeddedSVGEdit)) { // Allow invocation without 'new' keyword
return new EmbeddedSVGEdit(frame);
}
this.allowedOrigins = allowedOrigins || [];
// Initialize communication
this.frame = frame;
this.callbacks = {};
// List of functions extracted with this:
// Run in firebug on http://svg-edit.googlecode.com/svn/trunk/docs/files/svgcanvas-js.html
// for (var i=0,q=[],f = document.querySelectorAll('div.CFunction h3.CTitle a'); i < f.length; i++) { q.push(f[i].name); }; q
// var functions = ['clearSelection', 'addToSelection', 'removeFromSelection', 'open', 'save', 'getSvgString', 'setSvgString',
// 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility',
// 'moveSelectedToLayer', 'clear'];
// for (var i=0,q=[],f = document.querySelectorAll('div.CFunction h3.CTitle a'); i < f.length; i++) { q.push(f[i].name); }; q
// var functions = ['clearSelection', 'addToSelection', 'removeFromSelection', 'open', 'save', 'getSvgString', 'setSvgString',
// 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility',
// 'moveSelectedToLayer', 'clear'];
// Newer, well, it extracts things that aren't documented as well. All functions accessible through the normal thingy can now be accessed though the API
// var svgCanvas = frame.contentWindow.svgCanvas;
// var l = []; for (var i in svgCanvas){ if (typeof svgCanvas[i] == 'function') { l.push(i);} };
// alert("['" + l.join("', '") + "']");
// Run in svgedit itself
var i,
functions = [
'clearSvgContentElement', 'setIdPrefix', 'getCurrentDrawing', 'addSvgElementFromJson', 'getTransformList', 'matrixMultiply', 'hasMatrixTransform', 'transformListToTransform', 'convertToNum', 'findDefs', 'getUrlFromAttr', 'getHref', 'setHref', 'getBBox', 'getRotationAngle', 'getElem', 'getRefElem', 'assignAttributes', 'cleanupElement', 'remapElement', 'recalculateDimensions', 'sanitizeSvg', 'runExtensions', 'addExtension', 'round', 'getIntersectionList', 'getStrokedBBox', 'getVisibleElements', 'getVisibleElementsAndBBoxes', 'groupSvgElem', 'getId', 'getNextId', 'call', 'bind', 'prepareSvg', 'setRotationAngle', 'recalculateAllSelectedDimensions', 'clearSelection', 'addToSelection', 'selectOnly', 'removeFromSelection', 'selectAllInCurrentLayer', 'getMouseTarget', 'removeUnusedDefElems', 'svgCanvasToString', 'svgToString', 'embedImage', 'setGoodImage', 'open', 'save', 'rasterExport', 'exportPDF', 'getSvgString', 'randomizeIds', 'uniquifyElems', 'setUseData', 'convertGradients', 'convertToGroup', 'setSvgString', 'importSvgString', 'identifyLayers', 'createLayer', 'cloneLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility', 'moveSelectedToLayer', 'mergeLayer', 'mergeAllLayers', 'leaveContext', 'setContext', 'clear', 'linkControlPoints', 'getContentElem', 'getRootElem', 'getSelectedElems', 'getResolution', 'getZoom', 'getVersion', 'setUiStrings', 'setConfig', 'getTitle', 'setGroupTitle', 'getDocumentTitle', 'setDocumentTitle', 'getEditorNS', 'setResolution', 'getOffset', 'setBBoxZoom', 'setZoom', 'getMode', 'setMode', 'getColor', 'setColor', 'setGradient', 'setPaint', 'setStrokePaint', 'setFillPaint', 'getStrokeWidth', 'setStrokeWidth', 'setStrokeAttr', 'getStyle', 'getOpacity', 'setOpacity', 'getFillOpacity', 'getStrokeOpacity', 'setPaintOpacity', 'getPaintOpacity', 'getBlur', 'setBlurNoUndo', 'setBlurOffsets', 'setBlur', 'getBold', 'setBold', 'getItalic', 'setItalic', 'getFontFamily', 'setFontFamily', 'setFontColor', 'getFontColor', 'getFontSize', 'setFontSize', 'getText', 'setTextContent', 'setImageURL', 'setLinkURL', 'setRectRadius', 'makeHyperlink', 'removeHyperlink', 'setSegType', 'convertToPath', 'changeSelectedAttribute', 'deleteSelectedElements', 'cutSelectedElements', 'copySelectedElements', 'pasteElements', 'groupSelectedElements', 'pushGroupProperties', 'ungroupSelectedElement', 'moveToTopSelectedElement', 'moveToBottomSelectedElement', 'moveUpDownSelected', 'moveSelectedElements', 'cloneSelectedElements', 'alignSelectedElements', 'updateCanvas', 'setBackground', 'cycleElement', 'getPrivateMethods', 'zoomChanged', 'ready'
];
// Newer, well, it extracts things that aren't documented as well. All functions accessible through the normal thingy can now be accessed though the API
// var svgCanvas = frame.contentWindow.svgCanvas;
// var l = []; for (var i in svgCanvas){ if (typeof svgCanvas[i] == 'function') { l.push(i);} };
// alert("['" + l.join("', '") + "']");
// Run in svgedit itself
var i,
functions = [
'clearSvgContentElement', 'setIdPrefix', 'getCurrentDrawing', 'addSvgElementFromJson', 'getTransformList', 'matrixMultiply', 'hasMatrixTransform', 'transformListToTransform', 'convertToNum', 'findDefs', 'getUrlFromAttr', 'getHref', 'setHref', 'getBBox', 'getRotationAngle', 'getElem', 'getRefElem', 'assignAttributes', 'cleanupElement', 'remapElement', 'recalculateDimensions', 'sanitizeSvg', 'runExtensions', 'addExtension', 'round', 'getIntersectionList', 'getStrokedBBox', 'getVisibleElements', 'getVisibleElementsAndBBoxes', 'groupSvgElem', 'getId', 'getNextId', 'call', 'bind', 'prepareSvg', 'setRotationAngle', 'recalculateAllSelectedDimensions', 'clearSelection', 'addToSelection', 'selectOnly', 'removeFromSelection', 'selectAllInCurrentLayer', 'getMouseTarget', 'removeUnusedDefElems', 'svgCanvasToString', 'svgToString', 'embedImage', 'setGoodImage', 'open', 'save', 'rasterExport', 'exportPDF', 'getSvgString', 'randomizeIds', 'uniquifyElems', 'setUseData', 'convertGradients', 'convertToGroup', 'setSvgString', 'importSvgString', 'identifyLayers', 'createLayer', 'cloneLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility', 'moveSelectedToLayer', 'mergeLayer', 'mergeAllLayers', 'leaveContext', 'setContext', 'clear', 'linkControlPoints', 'getContentElem', 'getRootElem', 'getSelectedElems', 'getResolution', 'getZoom', 'getVersion', 'setUiStrings', 'setConfig', 'getTitle', 'setGroupTitle', 'getDocumentTitle', 'setDocumentTitle', 'getEditorNS', 'setResolution', 'getOffset', 'setBBoxZoom', 'setZoom', 'getMode', 'setMode', 'getColor', 'setColor', 'setGradient', 'setPaint', 'setStrokePaint', 'setFillPaint', 'getStrokeWidth', 'setStrokeWidth', 'setStrokeAttr', 'getStyle', 'getOpacity', 'setOpacity', 'getFillOpacity', 'getStrokeOpacity', 'setPaintOpacity', 'getPaintOpacity', 'getBlur', 'setBlurNoUndo', 'setBlurOffsets', 'setBlur', 'getBold', 'setBold', 'getItalic', 'setItalic', 'getFontFamily', 'setFontFamily', 'setFontColor', 'getFontColor', 'getFontSize', 'setFontSize', 'getText', 'setTextContent', 'setImageURL', 'setLinkURL', 'setRectRadius', 'makeHyperlink', 'removeHyperlink', 'setSegType', 'convertToPath', 'changeSelectedAttribute', 'deleteSelectedElements', 'cutSelectedElements', 'copySelectedElements', 'pasteElements', 'groupSelectedElements', 'pushGroupProperties', 'ungroupSelectedElement', 'moveToTopSelectedElement', 'moveToBottomSelectedElement', 'moveUpDownSelected', 'moveSelectedElements', 'cloneSelectedElements', 'alignSelectedElements', 'updateCanvas', 'setBackground', 'cycleElement', 'getPrivateMethods', 'zoomChanged', 'ready'
];
// TODO: rewrite the following, it's pretty scary.
for (i = 0; i < functions.length; i++) {
this[functions[i]] = getCallbackSetter(functions[i]);
}
// TODO: rewrite the following, it's pretty scary.
for (i = 0; i < functions.length; i++) {
this[functions[i]] = getCallbackSetter(functions[i]);
}
// Older IE may need a polyfill for addEventListener, but so it would for SVG
window.addEventListener('message', getMessageListener(this), false);
// Older IE may need a polyfill for addEventListener, but so it would for SVG
window.addEventListener('message', getMessageListener(this), false);
}
EmbeddedSVGEdit.prototype.send = function (name, args, callback) {
var t = this;
cbid++;
var t = this;
cbid++;
this.callbacks[cbid] = callback;
setTimeout((function (cbid) {
return function () { // Delay for the callback to be set in case its synchronous
/*
* Todo: Handle non-JSON arguments and return values (undefined,
* nonfinite numbers, functions, and built-in objects like Date,
* RegExp), etc.? Allow promises instead of callbacks? Review
* SVG-Edit functions for whether JSON-able parameters can be
* made compatile with all API functionality
*/
// We accept and post strings for the sake of IE9 support
if (window.location.origin === t.frame.contentWindow.location.origin) {
// Although we do not really need this API if we are working same
// domain, it could allow us to write in a way that would work
// cross-domain as well, assuming we stick to the argument limitations
// of the current JSON-based communication API (e.g., not passing
// callbacks). We might be able to address these shortcomings; see
// the todo elsewhere in this file.
var message = {id: cbid},
svgCanvas = t.frame.contentWindow.svgCanvas;
try {
message.result = svgCanvas[name].apply(svgCanvas, args);
} catch (err) {
message.error = err.message;
}
addCallback(t, message);
} else { // Requires the ext-xdomain-messaging.js extension
t.frame.contentWindow.postMessage(JSON.stringify({namespace: 'svgCanvas', id: cbid, name: name, args: args}), '*');
}
};
}(cbid)), 0);
this.callbacks[cbid] = callback;
setTimeout((function (cbid) {
return function () { // Delay for the callback to be set in case its synchronous
/*
* Todo: Handle non-JSON arguments and return values (undefined,
* nonfinite numbers, functions, and built-in objects like Date,
* RegExp), etc.? Allow promises instead of callbacks? Review
* SVG-Edit functions for whether JSON-able parameters can be
* made compatile with all API functionality
*/
// We accept and post strings for the sake of IE9 support
if (window.location.origin === t.frame.contentWindow.location.origin) {
// Although we do not really need this API if we are working same
// domain, it could allow us to write in a way that would work
// cross-domain as well, assuming we stick to the argument limitations
// of the current JSON-based communication API (e.g., not passing
// callbacks). We might be able to address these shortcomings; see
// the todo elsewhere in this file.
var message = {id: cbid},
svgCanvas = t.frame.contentWindow.svgCanvas;
try {
message.result = svgCanvas[name].apply(svgCanvas, args);
} catch (err) {
message.error = err.message;
}
addCallback(t, message);
} else { // Requires the ext-xdomain-messaging.js extension
t.frame.contentWindow.postMessage(JSON.stringify({namespace: 'svgCanvas', id: cbid, name: name, args: args}), '*');
}
};
}(cbid)), 0);
return cbid;
return cbid;
};
window.embedded_svg_edit = EmbeddedSVGEdit; // Export old, deprecated API

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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