Refactor canvas in multiple pieces to increase maintainability (#446)
* #refactor-canvas getJsonFromSvgElement and svgroot code moved to separate file * #refactor-canvas build files changes * #refactor-canvas addSVGElementsFromJson move to json file * #refactor-canvas selected element option function move separate file * #refactor-canvas moveUpDownSelected move to select-elem * ##refactor-canvas build file updated * #refactor-canvas moveSelectedElements move to selected-elem * #refactor-canvas cloneSelectedElements move to slected-elem * #refactor-canvas alignSelectedElements move to selected-elem * #refactor-canvas deleteSelectedElements move to selected-elem * #refactor-canvas copySelectedElements and groupSelectedElements move to selected-elem * #refactor-canvas pushGroupProperty, ungroupSelectedElement move to selected-elem * #refactor-canvas comment changes * #refactor-canvas UndoManager move to separate file * #refactor-canvas event file move to mouseMove, mouseUpEvent and dblClickEvent * #refactor-canvas mouseDown move to event * #refactor-canvas move to undo file * #refactor alignment changes and set function revert return * #refactor-canvas textaction move to separate file * #refactor-canvas paste-element function move to separate file * #refactor-canvas set and get method move to separate file * #refactor-canvas set and get function moved changes * #refactor clear file import and regaring function moved changes changes * #refactor-canvas svg related function move to separate file * #refactor-canvas setBackground methos move to elem-get-set file * #refactor-canvas getBsplinePoint method move to event file * #refactor-canvas export functions move to svg-exec * #refactor-canvas svg related function moved separate file * #refactor-canvas updateCanvas, cycleElement move to selected-elem file * #refactor-canvas removeUnusedDefElems move to svg-exec file * #refactor-canvas blur method move to separate file blur-event.js * #refactor-canvas selection related function move to separate file slection.js * #refactor-canvas convertGradients, mousewheelmove event bind function move to other files * #refactor-canvas extension function move to selection file * #refactor-canvas svg tag long string changes to es6 format * eslint fixes * eslint and test fixes * add netlify logo per their requirements * #refactor-canvas path file separate to path-method.js and path-actions.js * #refactor-canvas lint issue fixed * #refactor-canvas path.js file class and const move to path-method.js * #refactor-canvas eslint issue fixed. 'jsdoc/check-examples': 'off' already so removed eslint-disable jsdoc/check-examples * #refactor-canvas path class moved changes and copy-elem.js file cypress test issue fixed * #refactor-canvas UI - Clipboard paste element cypress issue fixed * #refactor-canvas cypress test cases issue fixed changes * #refactor-canvas cypress test cases issue fixed changes * #refactor-canvas cypress test case issue fixed * npm update and fix a few eslint new errors * fix snapshot and run tests * add star tool to cypress * #refactor-canvas shapelibrary, star, polygon and panning tool issue fixed * build * Update layer.js * revert proper declarations Authored-by OptimistikSAS
This commit is contained in:
886
src/svgcanvas/elem-get-set.js
Normal file
886
src/svgcanvas/elem-get-set.js
Normal file
@@ -0,0 +1,886 @@
|
||||
/* globals jQuery */
|
||||
/**
|
||||
* @module elem-get-set get and set methods.
|
||||
* @license MIT
|
||||
* @copyright 2011 Jeff Schiller
|
||||
*/
|
||||
|
||||
import * as hstry from './history.js';
|
||||
import jQueryPluginSVG from '../common/jQuery.attr.js';
|
||||
import {NS} from '../common/namespaces.js';
|
||||
import {
|
||||
getVisibleElements, getStrokedBBoxDefaultVisible, findDefs,
|
||||
walkTree, isNullish, getHref, setHref, getElem
|
||||
} from '../common/utilities.js';
|
||||
import {
|
||||
convertToNum
|
||||
} from '../common/units.js';
|
||||
|
||||
const $ = jQueryPluginSVG(jQuery);
|
||||
|
||||
const {
|
||||
InsertElementCommand, RemoveElementCommand,
|
||||
ChangeElementCommand, BatchCommand
|
||||
} = hstry;
|
||||
|
||||
let elemContext_ = null;
|
||||
|
||||
/**
|
||||
* @function module:elem-get-set.init
|
||||
* @param {module:elem-get-set.elemContext} elemContext
|
||||
* @returns {void}
|
||||
*/
|
||||
export const init = function (elemContext) {
|
||||
elemContext_ = elemContext;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function module:elem-get-set.SvgCanvas#getResolution
|
||||
* @returns {DimensionsAndZoom} The current dimensions and zoom level in an object
|
||||
*/
|
||||
export const getResolutionMethod = function () {
|
||||
const currentZoom = elemContext_.getCurrentZoom();
|
||||
const w = elemContext_.getSVGContent().getAttribute('width') / currentZoom;
|
||||
const h = elemContext_.getSVGContent().getAttribute('height') / currentZoom;
|
||||
|
||||
return {
|
||||
w,
|
||||
h,
|
||||
zoom: currentZoom
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @function module:elem-get-set.SvgCanvas#getTitle
|
||||
* @param {Element} [elem]
|
||||
* @returns {string|void} the current group/SVG's title contents or
|
||||
* `undefined` if no element is passed nd there are no selected elements.
|
||||
*/
|
||||
export const getTitleMethod = function (elem) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
elem = elem || selectedElements[0];
|
||||
if (!elem) { return undefined; }
|
||||
elem = $(elem).data('gsvg') || $(elem).data('symbol') || elem;
|
||||
const childs = elem.childNodes;
|
||||
for (const child of childs) {
|
||||
if (child.nodeName === 'title') {
|
||||
return child.textContent;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the group/SVG's title content.
|
||||
* @function module:elem-get-set.SvgCanvas#setGroupTitle
|
||||
* @param {string} val
|
||||
* @todo Combine this with `setDocumentTitle`
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setGroupTitleMethod = function (val) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
let elem = selectedElements[0];
|
||||
elem = $(elem).data('gsvg') || elem;
|
||||
|
||||
const ts = $(elem).children('title');
|
||||
|
||||
const batchCmd = new BatchCommand('Set Label');
|
||||
|
||||
let title;
|
||||
if (!val.length) {
|
||||
// Remove title element
|
||||
const tsNextSibling = ts.nextSibling;
|
||||
batchCmd.addSubCommand(new RemoveElementCommand(ts[0], tsNextSibling, elem));
|
||||
ts.remove();
|
||||
} else if (ts.length) {
|
||||
// Change title contents
|
||||
title = ts[0];
|
||||
batchCmd.addSubCommand(new ChangeElementCommand(title, {'#text': title.textContent}));
|
||||
title.textContent = val;
|
||||
} else {
|
||||
// Add title element
|
||||
title = elemContext_.getDOMDocument().createElementNS(NS.SVG, 'title');
|
||||
title.textContent = val;
|
||||
$(elem).prepend(title);
|
||||
batchCmd.addSubCommand(new InsertElementCommand(title));
|
||||
}
|
||||
|
||||
elemContext_.addCommandToHistory(batchCmd);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds/updates a title element for the document with the given name.
|
||||
* This is an undoable action.
|
||||
* @function module:elem-get-set.SvgCanvas#setDocumentTitle
|
||||
* @param {string} newTitle - String with the new title
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setDocumentTitleMethod = function (newTitle) {
|
||||
const childs = elemContext_.getSVGContent().childNodes;
|
||||
let docTitle = false, oldTitle = '';
|
||||
|
||||
const batchCmd = new BatchCommand('Change Image Title');
|
||||
|
||||
for (const child of childs) {
|
||||
if (child.nodeName === 'title') {
|
||||
docTitle = child;
|
||||
oldTitle = docTitle.textContent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!docTitle) {
|
||||
docTitle = elemContext_.getDOMDocument().createElementNS(NS.SVG, 'title');
|
||||
elemContext_.getSVGContent().insertBefore(docTitle, elemContext_.getSVGContent().firstChild);
|
||||
// svgcontent.firstChild.before(docTitle); // Ok to replace above with this?
|
||||
}
|
||||
|
||||
if (newTitle.length) {
|
||||
docTitle.textContent = newTitle;
|
||||
} else {
|
||||
// No title given, so element is not necessary
|
||||
docTitle.remove();
|
||||
}
|
||||
batchCmd.addSubCommand(new ChangeElementCommand(docTitle, {'#text': oldTitle}));
|
||||
elemContext_.addCommandToHistory(batchCmd);
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the document's dimensions to the given size.
|
||||
* @function module:elem-get-set.SvgCanvas#setResolution
|
||||
* @param {Float|"fit"} x - Number with the width of the new dimensions in user units.
|
||||
* Can also be the string "fit" to indicate "fit to content".
|
||||
* @param {Float} y - Number with the height of the new dimensions in user units.
|
||||
* @fires module:elem-get-set.SvgCanvas#event:changed
|
||||
* @returns {boolean} Indicates if resolution change was successful.
|
||||
* It will fail on "fit to content" option with no content to fit to.
|
||||
*/
|
||||
export const setResolutionMethod = function (x, y) {
|
||||
const currentZoom = elemContext_.getCurrentZoom();
|
||||
const res = elemContext_.getCanvas().getResolution();
|
||||
const {w, h} = res;
|
||||
let batchCmd;
|
||||
|
||||
if (x === 'fit') {
|
||||
// Get bounding box
|
||||
const bbox = getStrokedBBoxDefaultVisible();
|
||||
|
||||
if (bbox) {
|
||||
batchCmd = new BatchCommand('Fit Canvas to Content');
|
||||
const visEls = getVisibleElements();
|
||||
elemContext_.getCanvas().addToSelection(visEls);
|
||||
const dx = [], dy = [];
|
||||
$.each(visEls, function (i, item) {
|
||||
dx.push(bbox.x * -1);
|
||||
dy.push(bbox.y * -1);
|
||||
});
|
||||
|
||||
const cmd = elemContext_.getCanvas().moveSelectedElements(dx, dy, true);
|
||||
batchCmd.addSubCommand(cmd);
|
||||
elemContext_.getCanvas().clearSelection();
|
||||
|
||||
x = Math.round(bbox.width);
|
||||
y = Math.round(bbox.height);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (x !== w || y !== h) {
|
||||
if (!batchCmd) {
|
||||
batchCmd = new BatchCommand('Change Image Dimensions');
|
||||
}
|
||||
|
||||
x = convertToNum('width', x);
|
||||
y = convertToNum('height', y);
|
||||
|
||||
elemContext_.getSVGContent().setAttribute('width', x);
|
||||
elemContext_.getSVGContent().setAttribute('height', y);
|
||||
|
||||
this.contentW = x;
|
||||
this.contentH = y;
|
||||
batchCmd.addSubCommand(new ChangeElementCommand(elemContext_.getSVGContent(), {width: w, height: h}));
|
||||
|
||||
elemContext_.getSVGContent().setAttribute('viewBox', [0, 0, x / currentZoom, y / currentZoom].join(' '));
|
||||
batchCmd.addSubCommand(new ChangeElementCommand(elemContext_.getSVGContent(), {viewBox: ['0 0', w, h].join(' ')}));
|
||||
|
||||
elemContext_.addCommandToHistory(batchCmd);
|
||||
elemContext_.call('changed', [elemContext_.getSVGContent()]);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the editor's namespace URL, optionally adding it to the root element.
|
||||
* @function module:elem-get-set.SvgCanvas#getEditorNS
|
||||
* @param {boolean} [add] - Indicates whether or not to add the namespace value
|
||||
* @returns {string} The editor's namespace URL
|
||||
*/
|
||||
export const getEditorNSMethod = function (add) {
|
||||
if (add) {
|
||||
elemContext_.getSVGContent().setAttribute('xmlns:se', NS.SE);
|
||||
}
|
||||
return NS.SE;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {PlainObject} module:elem-get-set.ZoomAndBBox
|
||||
* @property {Float} zoom
|
||||
* @property {module:utilities.BBoxObject} bbox
|
||||
*/
|
||||
/**
|
||||
* Sets the zoom level on the canvas-side based on the given value.
|
||||
* @function module:elem-get-set.SvgCanvas#setBBoxZoom
|
||||
* @param {"selection"|"canvas"|"content"|"layer"|module:SVGEditor.BBoxObjectWithFactor} val - Bounding box object to zoom to or string indicating zoom option. Note: the object value type is defined in `svg-editor.js`
|
||||
* @param {Integer} editorW - The editor's workarea box's width
|
||||
* @param {Integer} editorH - The editor's workarea box's height
|
||||
* @returns {module:elem-get-set.ZoomAndBBox|void}
|
||||
*/
|
||||
export const setBBoxZoomMethod = function (val, editorW, editorH) {
|
||||
const currentZoom = elemContext_.getCurrentZoom();
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
let spacer = 0.85;
|
||||
let bb;
|
||||
const calcZoom = function (bb) { // eslint-disable-line no-shadow
|
||||
if (!bb) { return false; }
|
||||
const wZoom = Math.round((editorW / bb.width) * 100 * spacer) / 100;
|
||||
const hZoom = Math.round((editorH / bb.height) * 100 * spacer) / 100;
|
||||
const zoom = Math.min(wZoom, hZoom);
|
||||
elemContext_.getCanvas().setZoom(zoom);
|
||||
return {zoom, bbox: bb};
|
||||
};
|
||||
|
||||
if (typeof val === 'object') {
|
||||
bb = val;
|
||||
if (bb.width === 0 || bb.height === 0) {
|
||||
const newzoom = bb.zoom ? bb.zoom : currentZoom * bb.factor;
|
||||
elemContext_.getCanvas().setZoom(newzoom);
|
||||
return {zoom: currentZoom, bbox: bb};
|
||||
}
|
||||
return calcZoom(bb);
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case 'selection': {
|
||||
if (!selectedElements[0]) { return undefined; }
|
||||
const selectedElems = $.map(selectedElements, function (n) {
|
||||
if (n) {
|
||||
return n;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
bb = getStrokedBBoxDefaultVisible(selectedElems);
|
||||
break;
|
||||
} case 'canvas': {
|
||||
const res = elemContext_.getCanvas().getResolution();
|
||||
spacer = 0.95;
|
||||
bb = {width: res.w, height: res.h, x: 0, y: 0};
|
||||
break;
|
||||
} case 'content':
|
||||
bb = getStrokedBBoxDefaultVisible();
|
||||
break;
|
||||
case 'layer':
|
||||
bb = getStrokedBBoxDefaultVisible(getVisibleElements(elemContext_.getCanvas().getCurrentDrawing().getCurrentLayer()));
|
||||
break;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
return calcZoom(bb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the zoom to the given level.
|
||||
* @function module:elem-get-set.SvgCanvas#setZoom
|
||||
* @param {Float} zoomLevel - Float indicating the zoom level to change to
|
||||
* @fires module:elem-get-set.SvgCanvas#event:ext_zoomChanged
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setZoomMethod = function (zoomLevel) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
const res = elemContext_.getCanvas().getResolution();
|
||||
elemContext_.getSVGContent().setAttribute('viewBox', '0 0 ' + res.w / zoomLevel + ' ' + res.h / zoomLevel);
|
||||
elemContext_.setCurrentZoom(zoomLevel);
|
||||
$.each(selectedElements, function (i, elem) {
|
||||
if (!elem) { return; }
|
||||
elemContext_.getCanvas().selectorManager.requestSelector(elem).resize();
|
||||
});
|
||||
elemContext_.getCanvas().pathActions.zoomChange();
|
||||
elemContext_.getCanvas().runExtensions('zoomChanged', zoomLevel);
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the current stroke/fill color/gradient value.
|
||||
* @function module:elem-get-set.SvgCanvas#setColor
|
||||
* @param {string} type - String indicating fill or stroke
|
||||
* @param {string} val - The value to set the stroke attribute to
|
||||
* @param {boolean} preventUndo - Boolean indicating whether or not this should be an undoable option
|
||||
* @fires module:elem-get-set.SvgCanvas#event:changed
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setColorMethod = function (type, val, preventUndo) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
elemContext_.setCurShape(type, val);
|
||||
elemContext_.setCurProperties(type + '_paint', {type: 'solidColor'});
|
||||
const elems = [];
|
||||
/**
|
||||
*
|
||||
* @param {Element} e
|
||||
* @returns {void}
|
||||
*/
|
||||
function addNonG (e) {
|
||||
if (e.nodeName !== 'g') {
|
||||
elems.push(e);
|
||||
}
|
||||
}
|
||||
let i = selectedElements.length;
|
||||
while (i--) {
|
||||
const elem = selectedElements[i];
|
||||
if (elem) {
|
||||
if (elem.tagName === 'g') {
|
||||
walkTree(elem, addNonG);
|
||||
} else if (type === 'fill') {
|
||||
if (elem.tagName !== 'polyline' && elem.tagName !== 'line') {
|
||||
elems.push(elem);
|
||||
}
|
||||
} else {
|
||||
elems.push(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (elems.length > 0) {
|
||||
if (!preventUndo) {
|
||||
elemContext_.getCanvas().changeSelectedAttribute(type, val, elems);
|
||||
elemContext_.call('changed', elems);
|
||||
} else {
|
||||
elemContext_.changeSelectedAttributeNoUndoMethod(type, val, elems);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply the current gradient to selected element's fill or stroke.
|
||||
* @function module:elem-get-set.SvgCanvas#setGradient
|
||||
* @param {"fill"|"stroke"} type - String indicating "fill" or "stroke" to apply to an element
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setGradientMethod = function (type) {
|
||||
if (!elemContext_.getCurProperties(type + '_paint') ||
|
||||
elemContext_.getCurProperties(type + '_paint').type === 'solidColor') { return; }
|
||||
const canvas = elemContext_.getCanvas();
|
||||
let grad = canvas[type + 'Grad'];
|
||||
// find out if there is a duplicate gradient already in the defs
|
||||
const duplicateGrad = findDuplicateGradient(grad);
|
||||
const defs = findDefs();
|
||||
// no duplicate found, so import gradient into defs
|
||||
if (!duplicateGrad) {
|
||||
// const origGrad = grad;
|
||||
grad = defs.appendChild(elemContext_.getDOMDocument().importNode(grad, true));
|
||||
// get next id and set it on the grad
|
||||
grad.id = elemContext_.getCanvas().getNextId();
|
||||
} else { // use existing gradient
|
||||
grad = duplicateGrad;
|
||||
}
|
||||
elemContext_.getCanvas().setColor(type, 'url(#' + grad.id + ')');
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if exact gradient already exists.
|
||||
* @function module:svgcanvas~findDuplicateGradient
|
||||
* @param {SVGGradientElement} grad - The gradient DOM element to compare to others
|
||||
* @returns {SVGGradientElement} The existing gradient if found, `null` if not
|
||||
*/
|
||||
export const findDuplicateGradient = function (grad) {
|
||||
const defs = findDefs();
|
||||
const existingGrads = $(defs).find('linearGradient, radialGradient');
|
||||
let i = existingGrads.length;
|
||||
const radAttrs = ['r', 'cx', 'cy', 'fx', 'fy'];
|
||||
while (i--) {
|
||||
const og = existingGrads[i];
|
||||
if (grad.tagName === 'linearGradient') {
|
||||
if (grad.getAttribute('x1') !== og.getAttribute('x1') ||
|
||||
grad.getAttribute('y1') !== og.getAttribute('y1') ||
|
||||
grad.getAttribute('x2') !== og.getAttribute('x2') ||
|
||||
grad.getAttribute('y2') !== og.getAttribute('y2')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const gradAttrs = $(grad).attr(radAttrs);
|
||||
const ogAttrs = $(og).attr(radAttrs);
|
||||
|
||||
let diff = false;
|
||||
$.each(radAttrs, function (j, attr) {
|
||||
if (gradAttrs[attr] !== ogAttrs[attr]) { diff = true; }
|
||||
});
|
||||
|
||||
if (diff) { continue; }
|
||||
}
|
||||
|
||||
// else could be a duplicate, iterate through stops
|
||||
const stops = grad.getElementsByTagNameNS(NS.SVG, 'stop');
|
||||
const ostops = og.getElementsByTagNameNS(NS.SVG, 'stop');
|
||||
|
||||
if (stops.length !== ostops.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let j = stops.length;
|
||||
while (j--) {
|
||||
const stop = stops[j];
|
||||
const ostop = ostops[j];
|
||||
|
||||
if (stop.getAttribute('offset') !== ostop.getAttribute('offset') ||
|
||||
stop.getAttribute('stop-opacity') !== ostop.getAttribute('stop-opacity') ||
|
||||
stop.getAttribute('stop-color') !== ostop.getAttribute('stop-color')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j === -1) {
|
||||
return og;
|
||||
}
|
||||
} // for each gradient in defs
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a color/gradient to a fill/stroke.
|
||||
* @function module:elem-get-set.SvgCanvas#setPaint
|
||||
* @param {"fill"|"stroke"} type - String with "fill" or "stroke"
|
||||
* @param {module:jGraduate.jGraduatePaintOptions} paint - The jGraduate paint object to apply
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setPaintMethod = function (type, paint) {
|
||||
// make a copy
|
||||
const p = new $.jGraduate.Paint(paint);
|
||||
this.setPaintOpacity(type, p.alpha / 100, true);
|
||||
|
||||
// now set the current paint object
|
||||
elemContext_.setCurProperties(type + '_paint', p);
|
||||
switch (p.type) {
|
||||
case 'solidColor':
|
||||
this.setColor(type, p.solidColor !== 'none' ? '#' + p.solidColor : 'none');
|
||||
break;
|
||||
case 'linearGradient':
|
||||
case 'radialGradient':
|
||||
elemContext_.setCanvas(type + 'Grad', p[p.type]);
|
||||
elemContext_.getCanvas().setGradient(type);
|
||||
break;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Sets the stroke width for the current selected elements.
|
||||
* When attempting to set a line's width to 0, this changes it to 1 instead.
|
||||
* @function module:elem-get-set.SvgCanvas#setStrokeWidth
|
||||
* @param {Float} val - A Float indicating the new stroke width value
|
||||
* @fires module:elem-get-set.SvgCanvas#event:changed
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setStrokeWidthMethod = function (val) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
if (val === 0 && ['line', 'path'].includes(elemContext_.getCanvas().getMode())) {
|
||||
elemContext_.getCanvas().setStrokeWidth(1);
|
||||
return;
|
||||
}
|
||||
elemContext_.setCurProperties('stroke_width', val);
|
||||
|
||||
const elems = [];
|
||||
/**
|
||||
*
|
||||
* @param {Element} e
|
||||
* @returns {void}
|
||||
*/
|
||||
function addNonG (e) {
|
||||
if (e.nodeName !== 'g') {
|
||||
elems.push(e);
|
||||
}
|
||||
}
|
||||
let i = selectedElements.length;
|
||||
while (i--) {
|
||||
const elem = selectedElements[i];
|
||||
if (elem) {
|
||||
if (elem.tagName === 'g') {
|
||||
walkTree(elem, addNonG);
|
||||
} else {
|
||||
elems.push(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (elems.length > 0) {
|
||||
elemContext_.getCanvas().changeSelectedAttribute('stroke-width', val, elems);
|
||||
elemContext_.call('changed', selectedElements);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the given stroke-related attribute the given value for selected elements.
|
||||
* @function module:elem-get-set.SvgCanvas#setStrokeAttr
|
||||
* @param {string} attr - String with the attribute name
|
||||
* @param {string|Float} val - String or number with the attribute value
|
||||
* @fires module:elem-get-set.SvgCanvas#event:changed
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setStrokeAttrMethod = function (attr, val) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
elemContext_.setCurShape(attr.replace('-', '_'), val);
|
||||
const elems = [];
|
||||
|
||||
let i = selectedElements.length;
|
||||
while (i--) {
|
||||
const elem = selectedElements[i];
|
||||
if (elem) {
|
||||
if (elem.tagName === 'g') {
|
||||
walkTree(elem, function (e) { if (e.nodeName !== 'g') { elems.push(e); } });
|
||||
} else {
|
||||
elems.push(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (elems.length > 0) {
|
||||
elemContext_.getCanvas().changeSelectedAttribute(attr, val, elems);
|
||||
elemContext_.call('changed', selectedElements);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Check whether selected element is bold or not.
|
||||
* @function module:svgcanvas.SvgCanvas#getBold
|
||||
* @returns {boolean} Indicates whether or not element is bold
|
||||
*/
|
||||
export const getBoldMethod = function () {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
// should only have one element selected
|
||||
const selected = selectedElements[0];
|
||||
if (!isNullish(selected) && selected.tagName === 'text' &&
|
||||
isNullish(selectedElements[1])) {
|
||||
return (selected.getAttribute('font-weight') === 'bold');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make the selected element bold or normal.
|
||||
* @function module:svgcanvas.SvgCanvas#setBold
|
||||
* @param {boolean} b - Indicates bold (`true`) or normal (`false`)
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setBoldMethod = function (b) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
const selected = selectedElements[0];
|
||||
if (!isNullish(selected) && selected.tagName === 'text' &&
|
||||
isNullish(selectedElements[1])) {
|
||||
elemContext_.getCanvas().changeSelectedAttribute('font-weight', b ? 'bold' : 'normal');
|
||||
}
|
||||
if (!selectedElements[0].textContent) {
|
||||
elemContext_.getCanvas().textActions.setCursor();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether selected element is in italics or not.
|
||||
* @function module:svgcanvas.SvgCanvas#getItalic
|
||||
* @returns {boolean} Indicates whether or not element is italic
|
||||
*/
|
||||
export const getItalicMethod = function () {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
const selected = selectedElements[0];
|
||||
if (!isNullish(selected) && selected.tagName === 'text' &&
|
||||
isNullish(selectedElements[1])) {
|
||||
return (selected.getAttribute('font-style') === 'italic');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make the selected element italic or normal.
|
||||
* @function module:svgcanvas.SvgCanvas#setItalic
|
||||
* @param {boolean} i - Indicates italic (`true`) or normal (`false`)
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setItalicMethod = function (i) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
const selected = selectedElements[0];
|
||||
if (!isNullish(selected) && selected.tagName === 'text' &&
|
||||
isNullish(selectedElements[1])) {
|
||||
elemContext_.getCanvas().changeSelectedAttribute('font-style', i ? 'italic' : 'normal');
|
||||
}
|
||||
if (!selectedElements[0].textContent) {
|
||||
elemContext_.getCanvas().textActions.setCursor();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @function module:svgcanvas.SvgCanvas#getFontFamily
|
||||
* @returns {string} The current font family
|
||||
*/
|
||||
export const getFontFamilyMethod = function () {
|
||||
return elemContext_.getCurText('font_family');
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the new font family.
|
||||
* @function module:svgcanvas.SvgCanvas#setFontFamily
|
||||
* @param {string} val - String with the new font family
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setFontFamilyMethod = function (val) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
elemContext_.setCurText('font_family', val);
|
||||
elemContext_.getCanvas().changeSelectedAttribute('font-family', val);
|
||||
if (selectedElements[0] && !selectedElements[0].textContent) {
|
||||
elemContext_.getCanvas().textActions.setCursor();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the new font color.
|
||||
* @function module:svgcanvas.SvgCanvas#setFontColor
|
||||
* @param {string} val - String with the new font color
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setFontColorMethod = function (val) {
|
||||
elemContext_.setCurText('fill', val);
|
||||
elemContext_.getCanvas().changeSelectedAttribute('fill', val);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function module:svgcanvas.SvgCanvas#getFontColor
|
||||
* @returns {string} The current font color
|
||||
*/
|
||||
export const getFontColorMethod = function () {
|
||||
return elemContext_.getCurText('fill');
|
||||
};
|
||||
|
||||
/**
|
||||
* @function module:svgcanvas.SvgCanvas#getFontSize
|
||||
* @returns {Float} The current font size
|
||||
*/
|
||||
export const getFontSizeMethod = function () {
|
||||
return elemContext_.getCurText('font_size');
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies the given font size to the selected element.
|
||||
* @function module:svgcanvas.SvgCanvas#setFontSize
|
||||
* @param {Float} val - Float with the new font size
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setFontSizeMethod = function (val) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
elemContext_.setCurText('font_size', val);
|
||||
elemContext_.getCanvas().changeSelectedAttribute('font-size', val);
|
||||
if (!selectedElements[0].textContent) {
|
||||
elemContext_.getCanvas().textActions.setCursor();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @function module:svgcanvas.SvgCanvas#getText
|
||||
* @returns {string} The current text (`textContent`) of the selected element
|
||||
*/
|
||||
export const getTextMethod = function () {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
const selected = selectedElements[0];
|
||||
if (isNullish(selected)) { return ''; }
|
||||
return selected.textContent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the text element with the given string.
|
||||
* @function module:svgcanvas.SvgCanvas#setTextContent
|
||||
* @param {string} val - String with the new text
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setTextContentMethod = function (val) {
|
||||
elemContext_.getCanvas().changeSelectedAttribute('#text', val);
|
||||
elemContext_.getCanvas().textActions.init(val);
|
||||
elemContext_.getCanvas().textActions.setCursor();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the new image URL for the selected image element. Updates its size if
|
||||
* a new URL is given.
|
||||
* @function module:svgcanvas.SvgCanvas#setImageURL
|
||||
* @param {string} val - String with the image URL/path
|
||||
* @fires module:svgcanvas.SvgCanvas#event:changed
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setImageURLMethod = function (val) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
const elem = selectedElements[0];
|
||||
if (!elem) { return; }
|
||||
|
||||
const attrs = $(elem).attr(['width', 'height']);
|
||||
const setsize = (!attrs.width || !attrs.height);
|
||||
|
||||
const curHref = getHref(elem);
|
||||
|
||||
// Do nothing if no URL change or size change
|
||||
if (curHref === val && !setsize) {
|
||||
return;
|
||||
}
|
||||
|
||||
const batchCmd = new BatchCommand('Change Image URL');
|
||||
|
||||
setHref(elem, val);
|
||||
batchCmd.addSubCommand(new ChangeElementCommand(elem, {
|
||||
'#href': curHref
|
||||
}));
|
||||
|
||||
$(new Image()).load(function () {
|
||||
const changes = $(elem).attr(['width', 'height']);
|
||||
|
||||
$(elem).attr({
|
||||
width: this.width,
|
||||
height: this.height
|
||||
});
|
||||
|
||||
elemContext_.getCanvas().selectorManager.requestSelector(elem).resize();
|
||||
|
||||
batchCmd.addSubCommand(new ChangeElementCommand(elem, changes));
|
||||
elemContext_.addCommandToHistory(batchCmd);
|
||||
elemContext_.call('changed', [elem]);
|
||||
}).attr('src', val);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the new link URL for the selected anchor element.
|
||||
* @function module:svgcanvas.SvgCanvas#setLinkURL
|
||||
* @param {string} val - String with the link URL/path
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setLinkURLMethod = function (val) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
let elem = selectedElements[0];
|
||||
if (!elem) { return; }
|
||||
if (elem.tagName !== 'a') {
|
||||
// See if parent is an anchor
|
||||
const parentsA = $(elem).parents('a');
|
||||
if (parentsA.length) {
|
||||
elem = parentsA[0];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const curHref = getHref(elem);
|
||||
|
||||
if (curHref === val) { return; }
|
||||
|
||||
const batchCmd = new BatchCommand('Change Link URL');
|
||||
|
||||
setHref(elem, val);
|
||||
batchCmd.addSubCommand(new ChangeElementCommand(elem, {
|
||||
'#href': curHref
|
||||
}));
|
||||
|
||||
elemContext_.addCommandToHistory(batchCmd);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the `rx` and `ry` values to the selected `rect` element
|
||||
* to change its corner radius.
|
||||
* @function module:svgcanvas.SvgCanvas#setRectRadius
|
||||
* @param {string|Float} val - The new radius
|
||||
* @fires module:svgcanvas.SvgCanvas#event:changed
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setRectRadiusMethod = function (val) {
|
||||
const selectedElements = elemContext_.getSelectedElements();
|
||||
const selected = selectedElements[0];
|
||||
if (!isNullish(selected) && selected.tagName === 'rect') {
|
||||
const r = selected.getAttribute('rx');
|
||||
if (r !== String(val)) {
|
||||
selected.setAttribute('rx', val);
|
||||
selected.setAttribute('ry', val);
|
||||
elemContext_.addCommandToHistory(new ChangeElementCommand(selected, {rx: r, ry: r}, 'Radius'));
|
||||
elemContext_.call('changed', [selected]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps the selected element(s) in an anchor element or converts group to one.
|
||||
* @function module:svgcanvas.SvgCanvas#makeHyperlink
|
||||
* @param {string} url
|
||||
* @returns {void}
|
||||
*/
|
||||
export const makeHyperlinkMethod = function (url) {
|
||||
elemContext_.getCanvas().groupSelectedElements('a', url);
|
||||
|
||||
// TODO: If element is a single "g", convert to "a"
|
||||
// if (selectedElements.length > 1 && selectedElements[1]) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @function module:svgcanvas.SvgCanvas#removeHyperlink
|
||||
* @returns {void}
|
||||
*/
|
||||
export const removeHyperlinkMethod = function () {
|
||||
elemContext_.getCanvas().ungroupSelectedElement();
|
||||
};
|
||||
|
||||
/**
|
||||
* Group: Element manipulation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets the new segment type to the selected segment(s).
|
||||
* @function module:svgcanvas.SvgCanvas#setSegType
|
||||
* @param {Integer} newType - New segment type. See {@link https://www.w3.org/TR/SVG/paths.html#InterfaceSVGPathSeg} for list
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setSegTypeMethod = function (newType) {
|
||||
elemContext_.getCanvas().pathActions.setSegType(newType);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the background of the editor (NOT the actual document).
|
||||
* @function module:svgcanvas.SvgCanvas#setBackground
|
||||
* @param {string} color - String with fill color to apply
|
||||
* @param {string} url - URL or path to image to use
|
||||
* @returns {void}
|
||||
*/
|
||||
export const setBackgroundMethod = function (color, url) {
|
||||
const bg = getElem('canvasBackground');
|
||||
const border = $(bg).find('rect')[0];
|
||||
let bgImg = getElem('background_image');
|
||||
let bgPattern = getElem('background_pattern');
|
||||
border.setAttribute('fill', color === 'chessboard' ? '#fff' : color);
|
||||
if (color === 'chessboard') {
|
||||
if (!bgPattern) {
|
||||
bgPattern = elemContext_.getDOMDocument().createElementNS(NS.SVG, 'foreignObject');
|
||||
elemContext_.getCanvas().assignAttributes(bgPattern, {
|
||||
id: 'background_pattern',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
preserveAspectRatio: 'xMinYMin',
|
||||
style: 'pointer-events:none'
|
||||
});
|
||||
const div = document.createElement('div');
|
||||
elemContext_.getCanvas().assignAttributes(div, {
|
||||
style: 'pointer-events:none;width:100%;height:100%;' +
|
||||
'background-image:url(data:image/gif;base64,' +
|
||||
'R0lGODlhEAAQAIAAAP///9bW1iH5BAAAAAAALAAAAAAQABAAAAIfjG+' +
|
||||
'gq4jM3IFLJgpswNly/XkcBpIiVaInlLJr9FZWAQA7);'
|
||||
});
|
||||
bgPattern.appendChild(div);
|
||||
bg.append(bgPattern);
|
||||
}
|
||||
} else if (bgPattern) {
|
||||
bgPattern.remove();
|
||||
}
|
||||
if (url) {
|
||||
if (!bgImg) {
|
||||
bgImg = elemContext_.getDOMDocument().createElementNS(NS.SVG, 'image');
|
||||
elemContext_.getCanvas().assignAttributes(bgImg, {
|
||||
id: 'background_image',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
preserveAspectRatio: 'xMinYMin',
|
||||
style: 'pointer-events:none'
|
||||
});
|
||||
}
|
||||
setHref(bgImg, url);
|
||||
bg.append(bgImg);
|
||||
} else if (bgImg) {
|
||||
bgImg.remove();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user