restore markers
parent
28e8601efd
commit
e723c71823
|
@ -175,8 +175,7 @@ export default class ConfigObj {
|
||||||
'ext-eyedropper',
|
'ext-eyedropper',
|
||||||
'ext-grid',
|
'ext-grid',
|
||||||
'ext-imagelib',
|
'ext-imagelib',
|
||||||
// 'ext-arrows',
|
'ext-markers',
|
||||||
// 'ext-markers',
|
|
||||||
'ext-overview_window',
|
'ext-overview_window',
|
||||||
'ext-panning',
|
'ext-panning',
|
||||||
'ext-shapes',
|
'ext-shapes',
|
||||||
|
|
|
@ -0,0 +1,340 @@
|
||||||
|
/**
|
||||||
|
* @file ext-markers.js
|
||||||
|
*
|
||||||
|
* @license Apache-2.0
|
||||||
|
*
|
||||||
|
* @copyright 2010 Will Schleter based on ext-arrows.js by Copyright(c) 2010 Alexis Deveria
|
||||||
|
* @copyright 2021 OptimistikSAS
|
||||||
|
*
|
||||||
|
* This extension provides for the addition of markers to the either end
|
||||||
|
* or the middle of a line, polyline, path, polygon.
|
||||||
|
*
|
||||||
|
* Markers are graphics
|
||||||
|
*
|
||||||
|
* to simplify the coding and make the implementation as robust as possible,
|
||||||
|
* markers are not shared - every object has its own set of markers.
|
||||||
|
* this relationship is maintained by a naming convention between the
|
||||||
|
* ids of the markers and the ids of the object
|
||||||
|
*
|
||||||
|
* The following restrictions exist for simplicty of use and programming
|
||||||
|
* objects and their markers to have the same color
|
||||||
|
* marker size is fixed
|
||||||
|
* an application specific attribute - se_type - is added to each marker element
|
||||||
|
* to store the type of marker
|
||||||
|
*
|
||||||
|
* @todo
|
||||||
|
* remove some of the restrictions above
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const loadExtensionTranslation = async function (lang) {
|
||||||
|
let translationModule;
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-unsanitized/method
|
||||||
|
translationModule = await import(`./locale/${encodeURIComponent(lang)}.js`);
|
||||||
|
} catch (_error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(`Missing translation (${lang}) - using 'en'`);
|
||||||
|
translationModule = await import(`./locale/en.js`);
|
||||||
|
}
|
||||||
|
return translationModule.default;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'markers',
|
||||||
|
async init (S) {
|
||||||
|
const svgEditor = this;
|
||||||
|
const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
|
||||||
|
const { svgCanvas } = svgEditor;
|
||||||
|
const { $id, addSVGElementFromJson: addElem } = svgCanvas;
|
||||||
|
const mtypes = [ 'start', 'mid', 'end' ];
|
||||||
|
|
||||||
|
// note - to add additional marker types add them below with a unique id
|
||||||
|
// and add the associated icon(s) to marker-icons.svg
|
||||||
|
// the geometry is normalized to a 100x100 box with the origin at lower left
|
||||||
|
// Safari did not like negative values for low left of viewBox
|
||||||
|
// remember that the coordinate system has +y downward
|
||||||
|
const markerTypes = {
|
||||||
|
nomarker: {},
|
||||||
|
leftarrow:
|
||||||
|
{ element: 'path', attr: { d: 'M0,50 L100,90 L70,50 L100,10 Z' } },
|
||||||
|
rightarrow:
|
||||||
|
{ element: 'path', attr: { d: 'M100,50 L0,90 L30,50 L0,10 Z' } },
|
||||||
|
box:
|
||||||
|
{ element: 'path', attr: { d: 'M20,20 L20,80 L80,80 L80,20 Z' } },
|
||||||
|
mcircle:
|
||||||
|
{ element: 'circle', attr: { r: 30, cx: 50, cy: 50 } }
|
||||||
|
};
|
||||||
|
|
||||||
|
// duplicate shapes to support unfilled (open) marker types with an _o suffix
|
||||||
|
[ 'leftarrow', 'rightarrow', 'box', 'mcircle' ].forEach((v) => {
|
||||||
|
markerTypes[v + '_o'] = markerTypes[v];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Element} elem - A graphic element will have an attribute like marker-start
|
||||||
|
* @param {"marker-start"|"marker-mid"|"marker-end"} attr
|
||||||
|
* @returns {Element} The marker element that is linked to the graphic element
|
||||||
|
*/
|
||||||
|
const getLinked = (elem, attr) => {
|
||||||
|
const str = elem.getAttribute(attr);
|
||||||
|
if (!str) { return null; }
|
||||||
|
const m = str.match(/\(#(.*)\)/);
|
||||||
|
if (!m || m.length !== 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return svgCanvas.getElem(m[1]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let selElems;
|
||||||
|
/**
|
||||||
|
* Toggles context tool panel off/on.
|
||||||
|
* @param {boolean} on
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const showPanel = (on) => {
|
||||||
|
$id('marker_panel').style.display = (on) ? 'block' : 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @param {""|"\\nomarker"|"nomarker"|"leftarrow"|"rightarrow"|"textmarker"|"forwardslash"|"reverseslash"|"verticalslash"|"box"|"star"|"xmark"|"triangle"|"mcircle"} seType
|
||||||
|
* @returns {SVGMarkerElement}
|
||||||
|
*/
|
||||||
|
const addMarker = (id, seType) => {
|
||||||
|
let marker = svgCanvas.getElem(id);
|
||||||
|
if (marker) { return undefined; }
|
||||||
|
if (seType === '' || seType === '\\nomarker') { return undefined; }
|
||||||
|
|
||||||
|
const el = selElems[0];
|
||||||
|
const color = el.getAttribute('stroke');
|
||||||
|
const strokeWidth = 10;
|
||||||
|
const refX = 50;
|
||||||
|
const refY = 50;
|
||||||
|
const viewBox = '0 0 100 100';
|
||||||
|
const markerWidth = 5;
|
||||||
|
const markerHeight = 5;
|
||||||
|
|
||||||
|
if (!markerTypes[seType]) {
|
||||||
|
console.error(`unknown marker type: ${seType}`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a generic marker
|
||||||
|
marker = addElem({
|
||||||
|
element: 'marker',
|
||||||
|
attr: {
|
||||||
|
id,
|
||||||
|
markerUnits: 'strokeWidth',
|
||||||
|
orient: 'auto',
|
||||||
|
style: 'pointer-events:none',
|
||||||
|
se_type: seType
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const mel = addElem(markerTypes[seType]);
|
||||||
|
const fillcolor = (seType.substr(-2) === '_o')
|
||||||
|
? 'none'
|
||||||
|
: color;
|
||||||
|
|
||||||
|
mel.setAttribute('fill', fillcolor);
|
||||||
|
mel.setAttribute('stroke', color);
|
||||||
|
mel.setAttribute('stroke-width', strokeWidth);
|
||||||
|
marker.append(mel);
|
||||||
|
|
||||||
|
marker.setAttribute('viewBox', viewBox);
|
||||||
|
marker.setAttribute('markerWidth', markerWidth);
|
||||||
|
marker.setAttribute('markerHeight', markerHeight);
|
||||||
|
marker.setAttribute('refX', refX);
|
||||||
|
marker.setAttribute('refY', refY);
|
||||||
|
svgCanvas.findDefs().append(marker);
|
||||||
|
|
||||||
|
return marker;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Element} elem
|
||||||
|
* @returns {SVGPolylineElement}
|
||||||
|
*/
|
||||||
|
const convertline = (elem) => {
|
||||||
|
// this routine came from the connectors extension
|
||||||
|
// it is needed because midpoint markers don't work with line elements
|
||||||
|
if (elem.tagName !== 'line') { return elem; }
|
||||||
|
|
||||||
|
// Convert to polyline to accept mid-arrow
|
||||||
|
|
||||||
|
const x1 = Number(elem.getAttribute('x1'));
|
||||||
|
const x2 = Number(elem.getAttribute('x2'));
|
||||||
|
const y1 = Number(elem.getAttribute('y1'));
|
||||||
|
const y2 = Number(elem.getAttribute('y2'));
|
||||||
|
const { id } = elem;
|
||||||
|
|
||||||
|
const midPt = (' ' + ((x1 + x2) / 2) + ',' + ((y1 + y2) / 2) + ' ');
|
||||||
|
const pline = addElem({
|
||||||
|
element: 'polyline',
|
||||||
|
attr: {
|
||||||
|
points: (x1 + ',' + y1 + midPt + x2 + ',' + y2),
|
||||||
|
stroke: elem.getAttribute('stroke'),
|
||||||
|
'stroke-width': elem.getAttribute('stroke-width'),
|
||||||
|
fill: 'none',
|
||||||
|
opacity: elem.getAttribute('opacity') || 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mtypes.forEach((pos) => { // get any existing marker definitions
|
||||||
|
const nam = 'marker-' + pos;
|
||||||
|
const m = elem.getAttribute(nam);
|
||||||
|
if (m) { pline.setAttribute(nam, elem.getAttribute(nam)); }
|
||||||
|
});
|
||||||
|
|
||||||
|
const batchCmd = new S.BatchCommand();
|
||||||
|
batchCmd.addSubCommand(new S.RemoveElementCommand(elem, elem.parentNode));
|
||||||
|
batchCmd.addSubCommand(new S.InsertElementCommand(pline));
|
||||||
|
|
||||||
|
elem.insertAdjacentElement('afterend', pline);
|
||||||
|
elem.remove();
|
||||||
|
svgCanvas.clearSelection();
|
||||||
|
pline.id = id;
|
||||||
|
svgCanvas.addToSelection([ pline ]);
|
||||||
|
S.addCommandToHistory(batchCmd);
|
||||||
|
return pline;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const setMarker = (pos, markerType) => {
|
||||||
|
if (!selElems) return;
|
||||||
|
const markerName = 'marker-' + pos;
|
||||||
|
const el = selElems[0];
|
||||||
|
const marker = getLinked(el, markerName);
|
||||||
|
if (marker) { marker.remove(); }
|
||||||
|
el.removeAttribute(markerName);
|
||||||
|
let val = markerType;
|
||||||
|
if (val === '') { val = '\\nomarker'; }
|
||||||
|
if (val === '\\nomarker') {
|
||||||
|
svgCanvas.call('changed', selElems);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Set marker on element
|
||||||
|
const id = 'mkr_' + pos + '_' + el.id;
|
||||||
|
addMarker(id, val);
|
||||||
|
svgCanvas.changeSelectedAttribute(markerName, 'url(#' + id + ')');
|
||||||
|
if (el.tagName === 'line' && pos === 'mid') {
|
||||||
|
convertline(el);
|
||||||
|
}
|
||||||
|
svgCanvas.call('changed', selElems);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the main system modifies an object. This routine changes
|
||||||
|
* the associated markers to be the same color.
|
||||||
|
* @param {Element} elem
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const colorChanged = (elem) => {
|
||||||
|
const color = elem.getAttribute('stroke');
|
||||||
|
|
||||||
|
mtypes.forEach((pos) => {
|
||||||
|
const marker = getLinked(elem, 'marker-' + pos);
|
||||||
|
if (!marker) { return; }
|
||||||
|
if (!marker.attributes.se_type) { return; } // not created by this extension
|
||||||
|
const ch = marker.lastElementChild;
|
||||||
|
if (!ch) { return; }
|
||||||
|
const curfill = ch.getAttribute('fill');
|
||||||
|
const curstroke = ch.getAttribute('stroke');
|
||||||
|
if (curfill && curfill !== 'none') { ch.setAttribute('fill', color); }
|
||||||
|
if (curstroke && curstroke !== 'none') { ch.setAttribute('stroke', color); }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the main system creates or modifies an object.
|
||||||
|
* Its primary purpose is to create new markers for cloned objects.
|
||||||
|
* @param {Element} el
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const updateReferences = (el) => {
|
||||||
|
mtypes.forEach((pos) => {
|
||||||
|
const id = 'mkr_' + pos + '_' + el.id;
|
||||||
|
const markerName = 'marker-' + pos;
|
||||||
|
const marker = getLinked(el, markerName);
|
||||||
|
if (!marker || !marker.attributes.se_type) { return; } // not created by this extension
|
||||||
|
const url = el.getAttribute(markerName);
|
||||||
|
if (url) {
|
||||||
|
const len = el.id.length;
|
||||||
|
const linkid = url.substr(-len - 1, len);
|
||||||
|
if (el.id !== linkid) {
|
||||||
|
const val = $id(pos + '_marker').getAttribute('value');
|
||||||
|
addMarker(id, val);
|
||||||
|
svgCanvas.changeSelectedAttribute(markerName, 'url(#' + id + ')');
|
||||||
|
if (el.tagName === 'line' && pos === 'mid') { el = convertline(el); }
|
||||||
|
svgCanvas.call('changed', selElems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: svgEditor.i18next.t(`${name}:name`),
|
||||||
|
// The callback should be used to load the DOM with the appropriate UI items
|
||||||
|
callback() {
|
||||||
|
// Add the context panel and its handler(s)
|
||||||
|
const panelTemplate = document.createElement("template");
|
||||||
|
let innerHTML = '<div id="marker_panel">';
|
||||||
|
mtypes.forEach((pos) => {
|
||||||
|
innerHTML += `<se-list id="${pos}_marker_list_opts" title="tools.${pos}_marker_list_opts" label="" width="22px" height="22px">`;
|
||||||
|
Object.entries(markerTypes).forEach(([ marker, _mkr ]) => {
|
||||||
|
innerHTML += `<se-list-item id="mkr_${pos}_${marker}" value="${marker}" title="tools.mkr_${marker}" src="${marker}.svg" img-height="22px"></se-list-item>`;
|
||||||
|
});
|
||||||
|
innerHTML += '</se-list>';
|
||||||
|
});
|
||||||
|
innerHTML += '</div>';
|
||||||
|
// eslint-disable-next-line no-unsanitized/property
|
||||||
|
panelTemplate.innerHTML = innerHTML;
|
||||||
|
$id("tools_top").appendChild(panelTemplate.content.cloneNode(true));
|
||||||
|
mtypes.forEach((pos) => {
|
||||||
|
$id(`${pos}_marker_list_opts`).addEventListener('change', (evt) => {
|
||||||
|
setMarker(pos, evt.detail.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// don't display the panels on start
|
||||||
|
showPanel(false);
|
||||||
|
},
|
||||||
|
/* async */ addLangData ({ _importLocale, _lang }) {
|
||||||
|
return { data: strings.langList };
|
||||||
|
},
|
||||||
|
selectedChanged (opts) {
|
||||||
|
// Use this to update the current selected elements
|
||||||
|
selElems = opts.elems;
|
||||||
|
|
||||||
|
const markerElems = [ 'line', 'path', 'polyline', 'polygon' ];
|
||||||
|
|
||||||
|
let i = selElems.length;
|
||||||
|
while (i--) {
|
||||||
|
const elem = selElems[i];
|
||||||
|
if (elem && markerElems.includes(elem.tagName)) {
|
||||||
|
if (opts.selectedElement && !opts.multiselected) {
|
||||||
|
showPanel(true);
|
||||||
|
} else {
|
||||||
|
showPanel(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showPanel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
elementChanged (opts) {
|
||||||
|
const elem = opts.elems[0];
|
||||||
|
if (elem && (
|
||||||
|
elem.getAttribute('marker-start') ||
|
||||||
|
elem.getAttribute('marker-mid') ||
|
||||||
|
elem.getAttribute('marker-end')
|
||||||
|
)) {
|
||||||
|
colorChanged(elem);
|
||||||
|
updateReferences(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,46 @@
|
||||||
|
export default {
|
||||||
|
name: 'Markers',
|
||||||
|
langList: [
|
||||||
|
{ id: 'nomarker', title: 'No Marker' },
|
||||||
|
{ id: 'leftarrow', title: 'Left Arrow' },
|
||||||
|
{ id: 'rightarrow', title: 'Right Arrow' },
|
||||||
|
{ id: 'textmarker', title: 'Text Marker' },
|
||||||
|
{ id: 'forwardslash', title: 'Forward Slash' },
|
||||||
|
{ id: 'reverseslash', title: 'Reverse Slash' },
|
||||||
|
{ id: 'verticalslash', title: 'Vertical Slash' },
|
||||||
|
{ id: 'box', title: 'Box' },
|
||||||
|
{ id: 'star', title: 'Star' },
|
||||||
|
{ id: 'xmark', title: 'X' },
|
||||||
|
{ id: 'triangle', title: 'Triangle' },
|
||||||
|
{ id: 'mcircle', title: 'Circle' },
|
||||||
|
{ id: 'leftarrow_o', title: 'Open Left Arrow' },
|
||||||
|
{ id: 'rightarrow_o', title: 'Open Right Arrow' },
|
||||||
|
{ id: 'box_o', title: 'Open Box' },
|
||||||
|
{ id: 'star_o', title: 'Open Star' },
|
||||||
|
{ id: 'triangle_o', title: 'Open Triangle' },
|
||||||
|
{ id: 'mcircle_o', title: 'Open Circle' }
|
||||||
|
],
|
||||||
|
contextTools: [
|
||||||
|
{
|
||||||
|
title: 'Start marker',
|
||||||
|
label: 's'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Select start marker type'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Middle marker',
|
||||||
|
label: 'm'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Select mid marker type'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'End marker',
|
||||||
|
label: 'e'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Select end marker type'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
|
@ -0,0 +1,46 @@
|
||||||
|
export default {
|
||||||
|
name: '标记',
|
||||||
|
langList: [
|
||||||
|
{ id: 'nomarker', title: '无标记' },
|
||||||
|
{ id: 'leftarrow', title: '左箭头' },
|
||||||
|
{ id: 'rightarrow', title: '右箭头' },
|
||||||
|
{ id: 'textmarker', title: '文本' },
|
||||||
|
{ id: 'forwardslash', title: '斜杠' },
|
||||||
|
{ id: 'reverseslash', title: '反斜杠' },
|
||||||
|
{ id: 'verticalslash', title: '垂直线' },
|
||||||
|
{ id: 'box', title: '方块' },
|
||||||
|
{ id: 'star', title: '星形' },
|
||||||
|
{ id: 'xmark', title: 'X' },
|
||||||
|
{ id: 'triangle', title: '三角形' },
|
||||||
|
{ id: 'mcircle', title: '圆形' },
|
||||||
|
{ id: 'leftarrow_o', title: '左箭头(空心)' },
|
||||||
|
{ id: 'rightarrow_o', title: '右箭头(空心)' },
|
||||||
|
{ id: 'box_o', title: '方块(空心)' },
|
||||||
|
{ id: 'star_o', title: '星形(空心)' },
|
||||||
|
{ id: 'triangle_o', title: '三角形(空心)' },
|
||||||
|
{ id: 'mcircle_o', title: '圆形(空心)' }
|
||||||
|
],
|
||||||
|
contextTools: [
|
||||||
|
{
|
||||||
|
title: '起始标记',
|
||||||
|
label: 's'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '选择起始标记类型'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '中段标记',
|
||||||
|
label: 'm'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '选择中段标记类型'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '末端标记',
|
||||||
|
label: 'e'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '选择末端标记类型'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
|
@ -449,9 +449,6 @@ export default {
|
||||||
showPanel(false, "polygon");
|
showPanel(false, "polygon");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
elementChanged(_opts) {
|
|
||||||
// const elem = opts.elems[0];
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ const svgWhiteList_ = {
|
||||||
image: [ 'clip-path', 'clip-rule', 'filter', 'height', 'mask', 'opacity', 'requiredFeatures', 'systemLanguage', 'width', 'x', 'xlink:href', 'xlink:title', 'y' ],
|
image: [ 'clip-path', 'clip-rule', 'filter', 'height', 'mask', 'opacity', 'requiredFeatures', 'systemLanguage', 'width', 'x', 'xlink:href', 'xlink:title', 'y' ],
|
||||||
line: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'x1', 'x2', 'y1', 'y2' ],
|
line: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'x1', 'x2', 'y1', 'y2' ],
|
||||||
linearGradient: [ 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2' ],
|
linearGradient: [ 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2' ],
|
||||||
marker: [ 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox' ],
|
marker: [ 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'se_type', 'systemLanguage', 'viewBox' ],
|
||||||
mask: [ 'height', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y' ],
|
mask: [ 'height', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y' ],
|
||||||
metadata: [ ],
|
metadata: [ ],
|
||||||
path: [ 'clip-path', 'clip-rule', 'd', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage' ],
|
path: [ 'clip-path', 'clip-rule', 'd', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage' ],
|
||||||
|
|
Loading…
Reference in New Issue