'use strict';
|
|
|
|
/**
|
|
* Pathformer
|
|
* Beta version
|
|
*
|
|
* Take any SVG version 1.1 and transform
|
|
* child elements to 'path' elements
|
|
*
|
|
* This code is purely forked from
|
|
* https://github.com/Waest/SVGPathConverter
|
|
*/
|
|
|
|
/**
|
|
* Class constructor
|
|
*
|
|
* @param {DOM|String} element Dom element of the SVG or id of it
|
|
*/
|
|
function Pathformer(element) {
|
|
// Test params
|
|
if (typeof element === 'undefined') {
|
|
throw new Error('Pathformer [constructor]: "element" parameter is required');
|
|
}
|
|
|
|
// Set the element
|
|
if (element.constructor === String) {
|
|
element = document.getElementById(element);
|
|
if (!element) {
|
|
throw new Error('Pathformer [constructor]: "element" parameter is not related to an existing ID');
|
|
}
|
|
}
|
|
if (element instanceof window.SVGElement ||
|
|
element instanceof window.SVGGElement ||
|
|
/^svg$/i.test(element.nodeName)) {
|
|
this.el = element;
|
|
} else {
|
|
throw new Error('Pathformer [constructor]: "element" parameter must be a string or a SVGelement');
|
|
}
|
|
|
|
// Start
|
|
this.scan(element);
|
|
}
|
|
|
|
/**
|
|
* List of tags which can be transformed
|
|
* to path elements
|
|
*
|
|
* @type {Array}
|
|
*/
|
|
Pathformer.prototype.TYPES = ['line', 'ellipse', 'circle', 'polygon', 'polyline', 'rect'];
|
|
|
|
/**
|
|
* List of attribute names which contain
|
|
* data. This array list them to check if
|
|
* they contain bad values, like percentage.
|
|
*
|
|
* @type {Array}
|
|
*/
|
|
Pathformer.prototype.ATTR_WATCH = ['cx', 'cy', 'points', 'r', 'rx', 'ry', 'x', 'x1', 'x2', 'y', 'y1', 'y2'];
|
|
|
|
/**
|
|
* Finds the elements compatible for transform
|
|
* and apply the liked method
|
|
*
|
|
* @param {object} options Object from the constructor
|
|
*/
|
|
Pathformer.prototype.scan = function (svg) {
|
|
var fn, element, pathData, pathDom,
|
|
elements = svg.querySelectorAll(this.TYPES.join(','));
|
|
|
|
for (var i = 0; i < elements.length; i++) {
|
|
element = elements[i];
|
|
fn = this[element.tagName.toLowerCase() + 'ToPath'];
|
|
pathData = fn(this.parseAttr(element.attributes));
|
|
pathDom = this.pathMaker(element, pathData);
|
|
element.parentNode.replaceChild(pathDom, element);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Read `line` element to extract and transform
|
|
* data, to make it ready for a `path` object.
|
|
*
|
|
* @param {DOMelement} element Line element to transform
|
|
* @return {object} Data for a `path` element
|
|
*/
|
|
Pathformer.prototype.lineToPath = function (element) {
|
|
var newElement = {},
|
|
x1 = element.x1 || 0,
|
|
y1 = element.y1 || 0,
|
|
x2 = element.x2 || 0,
|
|
y2 = element.y2 || 0;
|
|
|
|
newElement.d = 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2;
|
|
return newElement;
|
|
};
|
|
|
|
/**
|
|
* Read `rect` element to extract and transform
|
|
* data, to make it ready for a `path` object.
|
|
* The radius-border is not taken in charge yet.
|
|
* (your help is more than welcomed)
|
|
*
|
|
* @param {DOMelement} element Rect element to transform
|
|
* @return {object} Data for a `path` element
|
|
*/
|
|
Pathformer.prototype.rectToPath = function (element) {
|
|
var newElement = {},
|
|
x = parseFloat(element.x) || 0,
|
|
y = parseFloat(element.y) || 0,
|
|
width = parseFloat(element.width) || 0,
|
|
height = parseFloat(element.height) || 0;
|
|
|
|
if (element.rx || element.ry) {
|
|
var rx = parseInt(element.rx, 10) || -1,
|
|
ry = parseInt(element.ry, 10) || -1;
|
|
rx = Math.min(Math.max(rx < 0 ? ry : rx, 0), width/2);
|
|
ry = Math.min(Math.max(ry < 0 ? rx : ry, 0), height/2);
|
|
|
|
newElement.d = 'M ' + (x + rx) + ',' + y + ' ' +
|
|
'L ' + (x + width - rx) + ',' + y + ' ' +
|
|
'A ' + rx + ',' + ry + ',0,0,1,' + (x + width) + ',' + (y + ry) + ' ' +
|
|
'L ' + (x + width) + ',' + (y + height - ry) + ' ' +
|
|
'A ' + rx + ',' + ry + ',0,0,1,' + (x + width - rx) + ',' + (y + height) + ' ' +
|
|
'L ' + (x + rx) + ',' + (y + height) + ' ' +
|
|
'A ' + rx + ',' + ry + ',0,0,1,' + x + ',' + (y + height - ry) + ' ' +
|
|
'L ' + x + ',' + (y + ry) + ' ' +
|
|
'A ' + rx + ',' + ry + ',0,0,1,' + (x + rx) + ',' + y;
|
|
}
|
|
else {
|
|
newElement.d = 'M' + x + ' ' + y + ' ' +
|
|
'L' + (x + width) + ' ' + y + ' ' +
|
|
'L' + (x + width) + ' ' + (y + height) + ' ' +
|
|
'L' + x + ' ' + (y + height) + ' Z';
|
|
}
|
|
return newElement;
|
|
};
|
|
|
|
/**
|
|
* Read `polyline` element to extract and transform
|
|
* data, to make it ready for a `path` object.
|
|
*
|
|
* @param {DOMelement} element Polyline element to transform
|
|
* @return {object} Data for a `path` element
|
|
*/
|
|
Pathformer.prototype.polylineToPath = function (element) {
|
|
var newElement = {},
|
|
points = element.points.trim().split(' '),
|
|
i, path;
|
|
|
|
// Reformatting if points are defined without commas
|
|
if (element.points.indexOf(',') === -1) {
|
|
var formattedPoints = [];
|
|
for (i = 0; i < points.length; i+=2) {
|
|
formattedPoints.push(points[i] + ',' + points[i+1]);
|
|
}
|
|
points = formattedPoints;
|
|
}
|
|
|
|
// Generate the path.d value
|
|
path = 'M' + points[0];
|
|
for(i = 1; i < points.length; i++) {
|
|
if (points[i].indexOf(',') !== -1) {
|
|
path += 'L' + points[i];
|
|
}
|
|
}
|
|
newElement.d = path;
|
|
return newElement;
|
|
};
|
|
|
|
/**
|
|
* Read `polygon` element to extract and transform
|
|
* data, to make it ready for a `path` object.
|
|
* This method rely on polylineToPath, because the
|
|
* logic is similar. The path created is just closed,
|
|
* so it needs an 'Z' at the end.
|
|
*
|
|
* @param {DOMelement} element Polygon element to transform
|
|
* @return {object} Data for a `path` element
|
|
*/
|
|
Pathformer.prototype.polygonToPath = function (element) {
|
|
var newElement = Pathformer.prototype.polylineToPath(element);
|
|
|
|
newElement.d += 'Z';
|
|
return newElement;
|
|
};
|
|
|
|
/**
|
|
* Read `ellipse` element to extract and transform
|
|
* data, to make it ready for a `path` object.
|
|
*
|
|
* @param {DOMelement} element ellipse element to transform
|
|
* @return {object} Data for a `path` element
|
|
*/
|
|
Pathformer.prototype.ellipseToPath = function (element) {
|
|
var newElement = {},
|
|
rx = parseFloat(element.rx) || 0,
|
|
ry = parseFloat(element.ry) || 0,
|
|
cx = parseFloat(element.cx) || 0,
|
|
cy = parseFloat(element.cy) || 0,
|
|
startX = cx - rx,
|
|
startY = cy,
|
|
endX = parseFloat(cx) + parseFloat(rx),
|
|
endY = cy;
|
|
|
|
newElement.d = 'M' + startX + ',' + startY +
|
|
'A' + rx + ',' + ry + ' 0,1,1 ' + endX + ',' + endY +
|
|
'A' + rx + ',' + ry + ' 0,1,1 ' + startX + ',' + endY;
|
|
return newElement;
|
|
};
|
|
|
|
/**
|
|
* Read `circle` element to extract and transform
|
|
* data, to make it ready for a `path` object.
|
|
*
|
|
* @param {DOMelement} element Circle element to transform
|
|
* @return {object} Data for a `path` element
|
|
*/
|
|
Pathformer.prototype.circleToPath = function (element) {
|
|
var newElement = {},
|
|
r = parseFloat(element.r) || 0,
|
|
cx = parseFloat(element.cx) || 0,
|
|
cy = parseFloat(element.cy) || 0,
|
|
startX = cx - r,
|
|
startY = cy,
|
|
endX = parseFloat(cx) + parseFloat(r),
|
|
endY = cy;
|
|
|
|
newElement.d = 'M' + startX + ',' + startY +
|
|
'A' + r + ',' + r + ' 0,1,1 ' + endX + ',' + endY +
|
|
'A' + r + ',' + r + ' 0,1,1 ' + startX + ',' + endY;
|
|
return newElement;
|
|
};
|
|
|
|
/**
|
|
* Create `path` elements form original element
|
|
* and prepared objects
|
|
*
|
|
* @param {DOMelement} element Original element to transform
|
|
* @param {object} pathData Path data (from `toPath` methods)
|
|
* @return {DOMelement} Path element
|
|
*/
|
|
Pathformer.prototype.pathMaker = function (element, pathData) {
|
|
var i, attr, pathTag = document.createElementNS('http://www.w3.org/2000/svg','path');
|
|
for(i = 0; i < element.attributes.length; i++) {
|
|
attr = element.attributes[i];
|
|
if (this.ATTR_WATCH.indexOf(attr.name) === -1) {
|
|
pathTag.setAttribute(attr.name, attr.value);
|
|
}
|
|
}
|
|
for(i in pathData) {
|
|
pathTag.setAttribute(i, pathData[i]);
|
|
}
|
|
return pathTag;
|
|
};
|
|
|
|
/**
|
|
* Parse attributes of a DOM element to
|
|
* get an object of attribute => value
|
|
*
|
|
* @param {NamedNodeMap} attributes Attributes object from DOM element to parse
|
|
* @return {object} Object of attributes
|
|
*/
|
|
Pathformer.prototype.parseAttr = function (element) {
|
|
var attr, output = {};
|
|
for (var i = 0; i < element.length; i++) {
|
|
attr = element[i];
|
|
// Check if no data attribute contains '%', or the transformation is impossible
|
|
if (this.ATTR_WATCH.indexOf(attr.name) !== -1 && attr.value.indexOf('%') !== -1) {
|
|
throw new Error('Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into \'path\' tags. Please use \'viewBox\'.');
|
|
}
|
|
output[attr.name] = attr.value;
|
|
}
|
|
return output;
|
|
};
|