move to standard linter for simpler configuration
This commit is contained in:
@@ -8,39 +8,41 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const name = "eyedropper";
|
||||
const name = 'eyedropper'
|
||||
|
||||
const loadExtensionTranslation = async function (svgEditor) {
|
||||
let translationModule;
|
||||
const lang = svgEditor.configObj.pref('lang');
|
||||
let translationModule
|
||||
const lang = svgEditor.configObj.pref('lang')
|
||||
try {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
translationModule = await import(`./locale/${lang}.js`);
|
||||
translationModule = await import(`./locale/${lang}.js`)
|
||||
} catch (_error) {
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
|
||||
translationModule = await import(`./locale/en.js`);
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`)
|
||||
translationModule = await import('./locale/en.js')
|
||||
}
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default);
|
||||
};
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default)
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
async init() {
|
||||
const svgEditor = this;
|
||||
const { svgCanvas } = svgEditor;
|
||||
await loadExtensionTranslation(svgEditor);
|
||||
const { ChangeElementCommand } = svgCanvas.history;
|
||||
async init () {
|
||||
const svgEditor = this
|
||||
const { svgCanvas } = svgEditor
|
||||
await loadExtensionTranslation(svgEditor)
|
||||
const { ChangeElementCommand } = svgCanvas.history
|
||||
// svgdoc = S.svgroot.parentNode.ownerDocument,
|
||||
const addToHistory = function (cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd); };
|
||||
const addToHistory = function (cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd) }
|
||||
const currentStyle = {
|
||||
fillPaint: 'red', fillOpacity: 1.0,
|
||||
strokePaint: 'black', strokeOpacity: 1.0,
|
||||
strokeWidth: 5, strokeDashArray: null,
|
||||
fillPaint: 'red',
|
||||
fillOpacity: 1.0,
|
||||
strokePaint: 'black',
|
||||
strokeOpacity: 1.0,
|
||||
strokeWidth: 5,
|
||||
strokeDashArray: null,
|
||||
opacity: 1.0,
|
||||
strokeLinecap: 'butt',
|
||||
strokeLinejoin: 'miter'
|
||||
};
|
||||
const { $id } = svgCanvas;
|
||||
}
|
||||
const { $id } = svgCanvas
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -49,79 +51,79 @@ export default {
|
||||
*/
|
||||
const getStyle = (opts) => {
|
||||
// if we are in eyedropper mode, we don't want to disable the eye-dropper tool
|
||||
const mode = svgCanvas.getMode();
|
||||
if (mode === 'eyedropper') { return; }
|
||||
const mode = svgCanvas.getMode()
|
||||
if (mode === 'eyedropper') { return }
|
||||
|
||||
const tool = $id('tool_eyedropper');
|
||||
const tool = $id('tool_eyedropper')
|
||||
// enable-eye-dropper if one element is selected
|
||||
let elem = null;
|
||||
let elem = null
|
||||
if (!opts.multiselected && opts.elems[0] &&
|
||||
![ 'svg', 'g', 'use' ].includes(opts.elems[0].nodeName)
|
||||
!['svg', 'g', 'use'].includes(opts.elems[0].nodeName)
|
||||
) {
|
||||
elem = opts.elems[0];
|
||||
tool.classList.remove('disabled');
|
||||
elem = opts.elems[0]
|
||||
tool.classList.remove('disabled')
|
||||
// grab the current style
|
||||
currentStyle.fillPaint = elem.getAttribute('fill') || 'black';
|
||||
currentStyle.fillOpacity = elem.getAttribute('fill-opacity') || 1.0;
|
||||
currentStyle.strokePaint = elem.getAttribute('stroke');
|
||||
currentStyle.strokeOpacity = elem.getAttribute('stroke-opacity') || 1.0;
|
||||
currentStyle.strokeWidth = elem.getAttribute('stroke-width');
|
||||
currentStyle.strokeDashArray = elem.getAttribute('stroke-dasharray');
|
||||
currentStyle.strokeLinecap = elem.getAttribute('stroke-linecap');
|
||||
currentStyle.strokeLinejoin = elem.getAttribute('stroke-linejoin');
|
||||
currentStyle.opacity = elem.getAttribute('opacity') || 1.0;
|
||||
currentStyle.fillPaint = elem.getAttribute('fill') || 'black'
|
||||
currentStyle.fillOpacity = elem.getAttribute('fill-opacity') || 1.0
|
||||
currentStyle.strokePaint = elem.getAttribute('stroke')
|
||||
currentStyle.strokeOpacity = elem.getAttribute('stroke-opacity') || 1.0
|
||||
currentStyle.strokeWidth = elem.getAttribute('stroke-width')
|
||||
currentStyle.strokeDashArray = elem.getAttribute('stroke-dasharray')
|
||||
currentStyle.strokeLinecap = elem.getAttribute('stroke-linecap')
|
||||
currentStyle.strokeLinejoin = elem.getAttribute('stroke-linejoin')
|
||||
currentStyle.opacity = elem.getAttribute('opacity') || 1.0
|
||||
// disable eye-dropper tool
|
||||
} else {
|
||||
tool.classList.add('disabled');
|
||||
tool.classList.add('disabled')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: svgEditor.i18next.t(`${name}:name`),
|
||||
callback() {
|
||||
callback () {
|
||||
// Add the button and its handler(s)
|
||||
const title = `${name}:buttons.0.title`;
|
||||
const key = `${name}:buttons.0.key`;
|
||||
const title = `${name}:buttons.0.title`
|
||||
const key = `${name}:buttons.0.key`
|
||||
const buttonTemplate = `
|
||||
<se-button id="tool_eyedropper" title="${title}" src="eye_dropper.svg" shortcut=${key}></se-button>
|
||||
`;
|
||||
svgCanvas.insertChildAtIndex($id('tools_left'), buttonTemplate, 12);
|
||||
$id('tool_eyedropper').addEventListener("click", () => {
|
||||
if (this.leftPanel.updateLeftPanel("tool_eyedropper")) {
|
||||
svgCanvas.setMode('eyedropper');
|
||||
`
|
||||
svgCanvas.insertChildAtIndex($id('tools_left'), buttonTemplate, 12)
|
||||
$id('tool_eyedropper').addEventListener('click', () => {
|
||||
if (this.leftPanel.updateLeftPanel('tool_eyedropper')) {
|
||||
svgCanvas.setMode('eyedropper')
|
||||
}
|
||||
});
|
||||
})
|
||||
},
|
||||
// if we have selected an element, grab its paint and enable the eye dropper button
|
||||
selectedChanged: getStyle,
|
||||
elementChanged: getStyle,
|
||||
mouseDown(opts) {
|
||||
const mode = svgCanvas.getMode();
|
||||
mouseDown (opts) {
|
||||
const mode = svgCanvas.getMode()
|
||||
if (mode === 'eyedropper') {
|
||||
const e = opts.event;
|
||||
const { target } = e;
|
||||
if (![ 'svg', 'g', 'use' ].includes(target.nodeName)) {
|
||||
const changes = {};
|
||||
const e = opts.event
|
||||
const { target } = e
|
||||
if (!['svg', 'g', 'use'].includes(target.nodeName)) {
|
||||
const changes = {}
|
||||
|
||||
const change = function (elem, attrname, newvalue) {
|
||||
changes[attrname] = elem.getAttribute(attrname);
|
||||
elem.setAttribute(attrname, newvalue);
|
||||
};
|
||||
changes[attrname] = elem.getAttribute(attrname)
|
||||
elem.setAttribute(attrname, newvalue)
|
||||
}
|
||||
|
||||
if (currentStyle.fillPaint) { change(target, 'fill', currentStyle.fillPaint); }
|
||||
if (currentStyle.fillOpacity) { change(target, 'fill-opacity', currentStyle.fillOpacity); }
|
||||
if (currentStyle.strokePaint) { change(target, 'stroke', currentStyle.strokePaint); }
|
||||
if (currentStyle.strokeOpacity) { change(target, 'stroke-opacity', currentStyle.strokeOpacity); }
|
||||
if (currentStyle.strokeWidth) { change(target, 'stroke-width', currentStyle.strokeWidth); }
|
||||
if (currentStyle.strokeDashArray) { change(target, 'stroke-dasharray', currentStyle.strokeDashArray); }
|
||||
if (currentStyle.opacity) { change(target, 'opacity', currentStyle.opacity); }
|
||||
if (currentStyle.strokeLinecap) { change(target, 'stroke-linecap', currentStyle.strokeLinecap); }
|
||||
if (currentStyle.strokeLinejoin) { change(target, 'stroke-linejoin', currentStyle.strokeLinejoin); }
|
||||
if (currentStyle.fillPaint) { change(target, 'fill', currentStyle.fillPaint) }
|
||||
if (currentStyle.fillOpacity) { change(target, 'fill-opacity', currentStyle.fillOpacity) }
|
||||
if (currentStyle.strokePaint) { change(target, 'stroke', currentStyle.strokePaint) }
|
||||
if (currentStyle.strokeOpacity) { change(target, 'stroke-opacity', currentStyle.strokeOpacity) }
|
||||
if (currentStyle.strokeWidth) { change(target, 'stroke-width', currentStyle.strokeWidth) }
|
||||
if (currentStyle.strokeDashArray) { change(target, 'stroke-dasharray', currentStyle.strokeDashArray) }
|
||||
if (currentStyle.opacity) { change(target, 'opacity', currentStyle.opacity) }
|
||||
if (currentStyle.strokeLinecap) { change(target, 'stroke-linecap', currentStyle.strokeLinecap) }
|
||||
if (currentStyle.strokeLinejoin) { change(target, 'stroke-linejoin', currentStyle.strokeLinejoin) }
|
||||
|
||||
addToHistory(new ChangeElementCommand(target, changes));
|
||||
addToHistory(new ChangeElementCommand(target, changes))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ export default {
|
||||
key: 'I'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ export default {
|
||||
key: 'I'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ export default {
|
||||
key: 'I'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,40 +7,39 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const name = "grid";
|
||||
const name = 'grid'
|
||||
|
||||
const loadExtensionTranslation = async function (svgEditor) {
|
||||
let translationModule;
|
||||
const lang = svgEditor.configObj.pref('lang');
|
||||
let translationModule
|
||||
const lang = svgEditor.configObj.pref('lang')
|
||||
try {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
translationModule = await import(`./locale/${lang}.js`);
|
||||
translationModule = await import(`./locale/${lang}.js`)
|
||||
} catch (_error) {
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
|
||||
translationModule = await import(`./locale/en.js`);
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`)
|
||||
translationModule = await import('./locale/en.js')
|
||||
}
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default);
|
||||
};
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default)
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
async init () {
|
||||
const svgEditor = this;
|
||||
await loadExtensionTranslation(svgEditor);
|
||||
const { svgCanvas } = svgEditor;
|
||||
const { $id, NS } = svgCanvas;
|
||||
const svgdoc = $id('svgcanvas').ownerDocument;
|
||||
const { assignAttributes } = svgCanvas;
|
||||
const hcanvas = document.createElement('canvas');
|
||||
const canvBG = $id('canvasBackground');
|
||||
const units = svgCanvas.getTypeMap(); // Assumes prior `init()` call on `units.js` module
|
||||
const intervals = [ 0.01, 0.1, 1, 10, 100, 1000 ];
|
||||
let showGrid = svgEditor.configObj.curConfig.showGrid || false;
|
||||
const svgEditor = this
|
||||
await loadExtensionTranslation(svgEditor)
|
||||
const { svgCanvas } = svgEditor
|
||||
const { $id, NS } = svgCanvas
|
||||
const svgdoc = $id('svgcanvas').ownerDocument
|
||||
const { assignAttributes } = svgCanvas
|
||||
const hcanvas = document.createElement('canvas')
|
||||
const canvBG = $id('canvasBackground')
|
||||
const units = svgCanvas.getTypeMap() // Assumes prior `init()` call on `units.js` module
|
||||
const intervals = [0.01, 0.1, 1, 10, 100, 1000]
|
||||
let showGrid = svgEditor.configObj.curConfig.showGrid || false
|
||||
|
||||
hcanvas.style.display = 'none';
|
||||
svgEditor.$svgEditor.appendChild(hcanvas);
|
||||
hcanvas.style.display = 'none'
|
||||
svgEditor.$svgEditor.appendChild(hcanvas)
|
||||
|
||||
const canvasGrid = svgdoc.createElementNS(NS.SVG, 'svg');
|
||||
const canvasGrid = svgdoc.createElementNS(NS.SVG, 'svg')
|
||||
assignAttributes(canvasGrid, {
|
||||
id: 'canvasGrid',
|
||||
width: '100%',
|
||||
@@ -49,11 +48,11 @@ export default {
|
||||
y: 0,
|
||||
overflow: 'visible',
|
||||
display: 'none'
|
||||
});
|
||||
canvBG.appendChild(canvasGrid);
|
||||
const gridDefs = svgdoc.createElementNS(NS.SVG, 'defs');
|
||||
})
|
||||
canvBG.appendChild(canvasGrid)
|
||||
const gridDefs = svgdoc.createElementNS(NS.SVG, 'defs')
|
||||
// grid-pattern
|
||||
const gridPattern = svgdoc.createElementNS(NS.SVG, 'pattern');
|
||||
const gridPattern = svgdoc.createElementNS(NS.SVG, 'pattern')
|
||||
assignAttributes(gridPattern, {
|
||||
id: 'gridpattern',
|
||||
patternUnits: 'userSpaceOnUse',
|
||||
@@ -61,21 +60,21 @@ export default {
|
||||
y: 0, // -(value.strokeWidth / 2), // position for strokewidth
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
})
|
||||
|
||||
const gridimg = svgdoc.createElementNS(NS.SVG, 'image');
|
||||
const gridimg = svgdoc.createElementNS(NS.SVG, 'image')
|
||||
assignAttributes(gridimg, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
gridPattern.append(gridimg);
|
||||
gridDefs.append(gridPattern);
|
||||
$id('canvasGrid').appendChild(gridDefs);
|
||||
})
|
||||
gridPattern.append(gridimg)
|
||||
gridDefs.append(gridPattern)
|
||||
$id('canvasGrid').appendChild(gridDefs)
|
||||
|
||||
// grid-box
|
||||
const gridBox = svgdoc.createElementNS(NS.SVG, 'rect');
|
||||
const gridBox = svgdoc.createElementNS(NS.SVG, 'rect')
|
||||
assignAttributes(gridBox, {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
@@ -85,8 +84,8 @@ export default {
|
||||
stroke: 'none',
|
||||
fill: 'url(#gridpattern)',
|
||||
style: 'pointer-events: none; display:visible;'
|
||||
});
|
||||
$id('canvasGrid').appendChild(gridBox);
|
||||
})
|
||||
$id('canvasGrid').appendChild(gridBox)
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -95,52 +94,52 @@ export default {
|
||||
*/
|
||||
const updateGrid = (zoom) => {
|
||||
// TODO: Try this with <line> elements, then compare performance difference
|
||||
const unit = units[svgEditor.configObj.curConfig.baseUnit]; // 1 = 1px
|
||||
const uMulti = unit * zoom;
|
||||
const unit = units[svgEditor.configObj.curConfig.baseUnit] // 1 = 1px
|
||||
const uMulti = unit * zoom
|
||||
// Calculate the main number interval
|
||||
const rawM = 100 / uMulti;
|
||||
let multi = 1;
|
||||
const rawM = 100 / uMulti
|
||||
let multi = 1
|
||||
intervals.some((num) => {
|
||||
multi = num;
|
||||
return rawM <= num;
|
||||
});
|
||||
const bigInt = multi * uMulti;
|
||||
multi = num
|
||||
return rawM <= num
|
||||
})
|
||||
const bigInt = multi * uMulti
|
||||
|
||||
// Set the canvas size to the width of the container
|
||||
hcanvas.width = bigInt;
|
||||
hcanvas.height = bigInt;
|
||||
const ctx = hcanvas.getContext('2d');
|
||||
const curD = 0.5;
|
||||
const part = bigInt / 10;
|
||||
hcanvas.width = bigInt
|
||||
hcanvas.height = bigInt
|
||||
const ctx = hcanvas.getContext('2d')
|
||||
const curD = 0.5
|
||||
const part = bigInt / 10
|
||||
|
||||
ctx.globalAlpha = 0.2;
|
||||
ctx.strokeStyle = svgEditor.configObj.curConfig.gridColor;
|
||||
ctx.globalAlpha = 0.2
|
||||
ctx.strokeStyle = svgEditor.configObj.curConfig.gridColor
|
||||
for (let i = 1; i < 10; i++) {
|
||||
const subD = Math.round(part * i) + 0.5;
|
||||
const subD = Math.round(part * i) + 0.5
|
||||
// const lineNum = (i % 2)?12:10;
|
||||
const lineNum = 0;
|
||||
ctx.moveTo(subD, bigInt);
|
||||
ctx.lineTo(subD, lineNum);
|
||||
ctx.moveTo(bigInt, subD);
|
||||
ctx.lineTo(lineNum, subD);
|
||||
const lineNum = 0
|
||||
ctx.moveTo(subD, bigInt)
|
||||
ctx.lineTo(subD, lineNum)
|
||||
ctx.moveTo(bigInt, subD)
|
||||
ctx.lineTo(lineNum, subD)
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.moveTo(curD, bigInt);
|
||||
ctx.lineTo(curD, 0);
|
||||
ctx.stroke()
|
||||
ctx.beginPath()
|
||||
ctx.globalAlpha = 0.5
|
||||
ctx.moveTo(curD, bigInt)
|
||||
ctx.lineTo(curD, 0)
|
||||
|
||||
ctx.moveTo(bigInt, curD);
|
||||
ctx.lineTo(0, curD);
|
||||
ctx.stroke();
|
||||
ctx.moveTo(bigInt, curD)
|
||||
ctx.lineTo(0, curD)
|
||||
ctx.stroke()
|
||||
|
||||
const datauri = hcanvas.toDataURL('image/png');
|
||||
gridimg.setAttribute('width', bigInt);
|
||||
gridimg.setAttribute('height', bigInt);
|
||||
gridimg.parentNode.setAttribute('width', bigInt);
|
||||
gridimg.parentNode.setAttribute('height', bigInt);
|
||||
svgCanvas.setHref(gridimg, datauri);
|
||||
};
|
||||
const datauri = hcanvas.toDataURL('image/png')
|
||||
gridimg.setAttribute('width', bigInt)
|
||||
gridimg.setAttribute('height', bigInt)
|
||||
gridimg.parentNode.setAttribute('width', bigInt)
|
||||
gridimg.parentNode.setAttribute('height', bigInt)
|
||||
svgCanvas.setHref(gridimg, datauri)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -148,34 +147,32 @@ export default {
|
||||
*/
|
||||
const gridUpdate = () => {
|
||||
if (showGrid) {
|
||||
updateGrid(svgCanvas.getZoom());
|
||||
updateGrid(svgCanvas.getZoom())
|
||||
}
|
||||
$id('canvasGrid').style.display = (showGrid) ? 'block' : 'none';
|
||||
$id('view_grid').pressed = showGrid;
|
||||
};
|
||||
$id('canvasGrid').style.display = (showGrid) ? 'block' : 'none'
|
||||
$id('view_grid').pressed = showGrid
|
||||
}
|
||||
return {
|
||||
name: svgEditor.i18next.t(`${name}:name`),
|
||||
zoomChanged (zoom) {
|
||||
if (showGrid) { updateGrid(zoom); }
|
||||
if (showGrid) { updateGrid(zoom) }
|
||||
},
|
||||
callback () {
|
||||
// Add the button and its handler(s)
|
||||
const buttonTemplate = document.createElement("template");
|
||||
const title = `${name}:buttons.0.title`;
|
||||
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
const buttonTemplate = document.createElement('template')
|
||||
const title = `${name}:buttons.0.title`
|
||||
buttonTemplate.innerHTML = `
|
||||
<se-button id="view_grid" title="${title}" src="grid.svg"></se-button>
|
||||
`;
|
||||
$id('editor_panel').append(buttonTemplate.content.cloneNode(true));
|
||||
$id('view_grid').addEventListener("click", () => {
|
||||
svgEditor.configObj.curConfig.showGrid = showGrid = !showGrid;
|
||||
gridUpdate();
|
||||
});
|
||||
`
|
||||
$id('editor_panel').append(buttonTemplate.content.cloneNode(true))
|
||||
$id('view_grid').addEventListener('click', () => {
|
||||
svgEditor.configObj.curConfig.showGrid = showGrid = !showGrid
|
||||
gridUpdate()
|
||||
})
|
||||
if (showGrid) {
|
||||
gridUpdate();
|
||||
gridUpdate()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export default {
|
||||
title: 'Show/Hide Grid'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export default {
|
||||
title: 'Afficher/Cacher Grille'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export default {
|
||||
title: '显示/隐藏网格'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,42 +13,40 @@
|
||||
* will show the user the point on the canvas that was clicked on.
|
||||
*/
|
||||
|
||||
const name = "helloworld";
|
||||
const name = 'helloworld'
|
||||
|
||||
const loadExtensionTranslation = async function (svgEditor) {
|
||||
let translationModule;
|
||||
const lang = svgEditor.configObj.pref('lang');
|
||||
let translationModule
|
||||
const lang = svgEditor.configObj.pref('lang')
|
||||
try {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
translationModule = await import(`./locale/${lang}.js`);
|
||||
translationModule = await import(`./locale/${lang}.js`)
|
||||
} catch (_error) {
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
|
||||
translationModule = await import(`./locale/en.js`);
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`)
|
||||
translationModule = await import('./locale/en.js')
|
||||
}
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default);
|
||||
};
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default)
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
async init ({ _importLocale }) {
|
||||
const svgEditor = this;
|
||||
await loadExtensionTranslation(svgEditor);
|
||||
const { svgCanvas } = svgEditor;
|
||||
const { $id } = svgCanvas;
|
||||
const svgEditor = this
|
||||
await loadExtensionTranslation(svgEditor)
|
||||
const { svgCanvas } = svgEditor
|
||||
const { $id } = svgCanvas
|
||||
return {
|
||||
name: svgEditor.i18next.t(`${name}:name`),
|
||||
callback() {
|
||||
callback () {
|
||||
// Add the button and its handler(s)
|
||||
const buttonTemplate = document.createElement("template");
|
||||
const title = `${name}:buttons.0.title`;
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
const buttonTemplate = document.createElement('template')
|
||||
const title = `${name}:buttons.0.title`
|
||||
buttonTemplate.innerHTML = `
|
||||
<se-button id="hello_world" title="${title}" src="hello_world.svg"></se-button>
|
||||
`;
|
||||
$id('tools_left').append(buttonTemplate.content.cloneNode(true));
|
||||
$id('hello_world').addEventListener("click", () => {
|
||||
svgCanvas.setMode('hello_world');
|
||||
});
|
||||
`
|
||||
$id('tools_left').append(buttonTemplate.content.cloneNode(true))
|
||||
$id('hello_world').addEventListener('click', () => {
|
||||
svgCanvas.setMode('hello_world')
|
||||
})
|
||||
},
|
||||
// This is triggered when the main mouse button is pressed down
|
||||
// on the editor canvas (not the tool panels)
|
||||
@@ -57,9 +55,9 @@ export default {
|
||||
if (svgCanvas.getMode() === 'hello_world') {
|
||||
// The returned object must include "started" with
|
||||
// a value of true in order for mouseUp to be triggered
|
||||
return { started: true };
|
||||
return { started: true }
|
||||
}
|
||||
return undefined;
|
||||
return undefined
|
||||
},
|
||||
|
||||
// This is triggered from anywhere, but "started" must have been set
|
||||
@@ -67,18 +65,18 @@ export default {
|
||||
mouseUp (opts) {
|
||||
// Check the mode on mouseup
|
||||
if (svgCanvas.getMode() === 'hello_world') {
|
||||
const zoom = svgCanvas.getZoom();
|
||||
const zoom = svgCanvas.getZoom()
|
||||
|
||||
// Get the actual coordinate by dividing by the zoom value
|
||||
const x = opts.mouse_x / zoom;
|
||||
const y = opts.mouse_y / zoom;
|
||||
const x = opts.mouse_x / zoom
|
||||
const y = opts.mouse_y / zoom
|
||||
|
||||
// We do our own formatting
|
||||
const text = svgEditor.i18next.t(`${name}:text`, { x, y });
|
||||
const text = svgEditor.i18next.t(`${name}:text`, { x, y })
|
||||
// Show the text using the custom alert function
|
||||
alert(text);
|
||||
alert(text)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ export default {
|
||||
title: "Say 'Hello World'"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ export default {
|
||||
title: "Dire 'Bonjour le Monde'"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ export default {
|
||||
title: "输出 'Hello World'"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-unsanitized/property */
|
||||
/* globals seConfirm */
|
||||
/**
|
||||
* @file ext-imagelib.js
|
||||
@@ -9,32 +8,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const name = "imagelib";
|
||||
const name = 'imagelib'
|
||||
|
||||
const loadExtensionTranslation = async function (svgEditor) {
|
||||
let translationModule;
|
||||
const lang = svgEditor.configObj.pref('lang');
|
||||
let translationModule
|
||||
const lang = svgEditor.configObj.pref('lang')
|
||||
try {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
translationModule = await import(`./locale/${lang}.js`);
|
||||
translationModule = await import(`./locale/${lang}.js`)
|
||||
} catch (_error) {
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
|
||||
translationModule = await import(`./locale/en.js`);
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`)
|
||||
translationModule = await import('./locale/en.js')
|
||||
}
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default);
|
||||
};
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default)
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
async init({ decode64, dropXMLInternalSubset }) {
|
||||
const svgEditor = this;
|
||||
const { $id } = svgEditor.svgCanvas;
|
||||
const { $svgEditor } = svgEditor;
|
||||
const { imgPath } = svgEditor.configObj.curConfig;
|
||||
async init ({ decode64, dropXMLInternalSubset }) {
|
||||
const svgEditor = this
|
||||
const { $id } = svgEditor.svgCanvas
|
||||
const { $svgEditor } = svgEditor
|
||||
const { imgPath } = svgEditor.configObj.curConfig
|
||||
|
||||
await loadExtensionTranslation(svgEditor);
|
||||
await loadExtensionTranslation(svgEditor)
|
||||
|
||||
const { svgCanvas } = svgEditor;
|
||||
const { svgCanvas } = svgEditor
|
||||
|
||||
const imgLibs = [
|
||||
{
|
||||
@@ -47,24 +45,24 @@ export default {
|
||||
url: 'https://ian.umces.edu/symbols/catalog/svgedit/album_chooser.php?svgedit=3',
|
||||
description: svgEditor.i18next.t(`${name}:imgLibs_1_description`)
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
const allowedImageLibOrigins = imgLibs.map(({ url }) => {
|
||||
try {
|
||||
return new URL(url).origin;
|
||||
return new URL(url).origin
|
||||
} catch (err) {
|
||||
return location.origin;
|
||||
return location.origin
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const closeBrowser = () => {
|
||||
$id("imgbrowse_holder").style.display = 'none';
|
||||
document.activeElement.blur(); // make sure focus is the body to correct issue #417
|
||||
};
|
||||
$id('imgbrowse_holder').style.display = 'none'
|
||||
document.activeElement.blur() // make sure focus is the body to correct issue #417
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
@@ -81,18 +79,18 @@ export default {
|
||||
id: svgCanvas.getNextId(),
|
||||
style: 'pointer-events:inherit'
|
||||
}
|
||||
});
|
||||
svgCanvas.clearSelection();
|
||||
svgCanvas.addToSelection([ newImage ]);
|
||||
svgCanvas.setImageURL(url);
|
||||
};
|
||||
})
|
||||
svgCanvas.clearSelection()
|
||||
svgCanvas.addToSelection([newImage])
|
||||
svgCanvas.setImageURL(url)
|
||||
}
|
||||
|
||||
const pending = {};
|
||||
const pending = {}
|
||||
|
||||
let mode = 's';
|
||||
let multiArr = [];
|
||||
let transferStopped = false;
|
||||
let preview; let submit;
|
||||
let mode = 's'
|
||||
let multiArr = []
|
||||
let transferStopped = false
|
||||
let preview; let submit
|
||||
|
||||
/**
|
||||
* Contains the SVG to insert.
|
||||
@@ -130,403 +128,401 @@ export default {
|
||||
* @param {ImageLibMetaMessage|ImageLibMessage|string} cfg.data String is deprecated when parsed to JSON `ImageLibMessage`
|
||||
* @returns {void}
|
||||
*/
|
||||
async function onMessage({ origin, data }) {
|
||||
let response = data;
|
||||
if (!response || ![ 'string', 'object' ].includes(typeof response)) {
|
||||
async function onMessage ({ origin, data }) {
|
||||
let response = data
|
||||
if (!response || !['string', 'object'].includes(typeof response)) {
|
||||
// Do nothing
|
||||
return;
|
||||
return
|
||||
}
|
||||
let id;
|
||||
let type;
|
||||
let id
|
||||
let type
|
||||
try {
|
||||
// Todo: This block can be removed (and the above check changed to
|
||||
// insist on an object) if embedAPI moves away from a string to
|
||||
// an object (if IE9 support not needed)
|
||||
response = typeof response === 'object' ? response : JSON.parse(response);
|
||||
response = typeof response === 'object' ? response : JSON.parse(response)
|
||||
if (response.namespace !== 'imagelib') {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (!allowedImageLibOrigins.includes('*') && !allowedImageLibOrigins.includes(origin)) {
|
||||
// Todo: Surface this error to user?
|
||||
console.error(`Origin ${origin} not whitelisted for posting to ${window.origin}`);
|
||||
return;
|
||||
console.error(`Origin ${origin} not whitelisted for posting to ${window.origin}`)
|
||||
return
|
||||
}
|
||||
const hasName = 'name' in response;
|
||||
const hasHref = 'href' in response;
|
||||
const hasName = 'name' in response
|
||||
const hasHref = 'href' in response
|
||||
|
||||
if (!hasName && transferStopped) {
|
||||
transferStopped = false;
|
||||
return;
|
||||
transferStopped = false
|
||||
return
|
||||
}
|
||||
|
||||
if (hasHref) {
|
||||
id = response.href;
|
||||
response = response.data;
|
||||
id = response.href
|
||||
response = response.data
|
||||
}
|
||||
|
||||
// Hide possible transfer dialog box
|
||||
if (document.querySelector('se-elix-alert-dialog')) {
|
||||
document.querySelector('se-elix-alert-dialog').remove();
|
||||
document.querySelector('se-elix-alert-dialog').remove()
|
||||
}
|
||||
type = hasName
|
||||
? 'meta'
|
||||
: response.charAt(0);
|
||||
: response.charAt(0)
|
||||
} catch (e) {
|
||||
// This block is for backward compatibility (for IAN and Openclipart);
|
||||
// should otherwise return
|
||||
if (typeof response === 'string') {
|
||||
const char1 = response.charAt(0);
|
||||
const char1 = response.charAt(0)
|
||||
|
||||
if (char1 !== '{' && transferStopped) {
|
||||
transferStopped = false;
|
||||
return;
|
||||
transferStopped = false
|
||||
return
|
||||
}
|
||||
|
||||
if (char1 === '|') {
|
||||
const secondpos = response.indexOf('|', 1);
|
||||
id = response.substr(1, secondpos - 1);
|
||||
response = response.substr(secondpos + 1);
|
||||
type = response.charAt(0);
|
||||
const secondpos = response.indexOf('|', 1)
|
||||
id = response.substr(1, secondpos - 1)
|
||||
response = response.substr(secondpos + 1)
|
||||
type = response.charAt(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let entry; let curMeta; let svgStr; let imgStr;
|
||||
let entry; let curMeta; let svgStr; let imgStr
|
||||
switch (type) {
|
||||
case 'meta': {
|
||||
case 'meta': {
|
||||
// Metadata
|
||||
transferStopped = false;
|
||||
curMeta = response;
|
||||
transferStopped = false
|
||||
curMeta = response
|
||||
|
||||
// Should be safe to add dynamic property as passed metadata
|
||||
pending[curMeta.id] = curMeta; // lgtm [js/remote-property-injection]
|
||||
// Should be safe to add dynamic property as passed metadata
|
||||
pending[curMeta.id] = curMeta // lgtm [js/remote-property-injection]
|
||||
|
||||
const name = (curMeta.name || 'file');
|
||||
const name = (curMeta.name || 'file')
|
||||
|
||||
const message = svgEditor.i18next.t('notification.retrieving').replace('%s', name);
|
||||
const message = svgEditor.i18next.t('notification.retrieving').replace('%s', name)
|
||||
|
||||
if (mode !== 'm') {
|
||||
await seConfirm(message);
|
||||
transferStopped = true;
|
||||
} else {
|
||||
entry = document.createElement('div');
|
||||
entry.textContent = message;
|
||||
entry.dataset.id = curMeta.id;
|
||||
preview.appendChild(entry);
|
||||
curMeta.entry = entry;
|
||||
if (mode !== 'm') {
|
||||
await seConfirm(message)
|
||||
transferStopped = true
|
||||
} else {
|
||||
entry = document.createElement('div')
|
||||
entry.textContent = message
|
||||
entry.dataset.id = curMeta.id
|
||||
preview.appendChild(entry)
|
||||
curMeta.entry = entry
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case '<':
|
||||
svgStr = true;
|
||||
break;
|
||||
case 'd': {
|
||||
if (response.startsWith('data:image/svg+xml')) {
|
||||
const pre = 'data:image/svg+xml;base64,';
|
||||
const src = response.substring(pre.length);
|
||||
response = decode64(src);
|
||||
svgStr = true;
|
||||
break;
|
||||
} else if (response.startsWith('data:image/')) {
|
||||
imgStr = true;
|
||||
break;
|
||||
case '<':
|
||||
svgStr = true
|
||||
break
|
||||
case 'd': {
|
||||
if (response.startsWith('data:image/svg+xml')) {
|
||||
const pre = 'data:image/svg+xml;base64,'
|
||||
const src = response.substring(pre.length)
|
||||
response = decode64(src)
|
||||
svgStr = true
|
||||
break
|
||||
} else if (response.startsWith('data:image/')) {
|
||||
imgStr = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else fall through
|
||||
default:
|
||||
// Else fall through
|
||||
default:
|
||||
// TODO: See if there's a way to base64 encode the binary data stream
|
||||
// const str = 'data:;base64,' + svgedit.utilities.encode64(response, true);
|
||||
|
||||
// Assume it's raw image data
|
||||
// importImage(str);
|
||||
// Assume it's raw image data
|
||||
// importImage(str);
|
||||
|
||||
// Don't give warning as postMessage may have been used by something else
|
||||
if (mode !== 'm') {
|
||||
closeBrowser();
|
||||
} else {
|
||||
pending[id].entry.remove();
|
||||
}
|
||||
// await alert('Unexpected data was returned: ' + response, function() {
|
||||
// if (mode !== 'm') {
|
||||
// closeBrowser();
|
||||
// } else {
|
||||
// pending[id].entry.remove();
|
||||
// }
|
||||
// });
|
||||
return;
|
||||
// Don't give warning as postMessage may have been used by something else
|
||||
if (mode !== 'm') {
|
||||
closeBrowser()
|
||||
} else {
|
||||
pending[id].entry.remove()
|
||||
}
|
||||
// await alert('Unexpected data was returned: ' + response, function() {
|
||||
// if (mode !== 'm') {
|
||||
// closeBrowser();
|
||||
// } else {
|
||||
// pending[id].entry.remove();
|
||||
// }
|
||||
// });
|
||||
return
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case 's':
|
||||
case 's':
|
||||
// Import one
|
||||
if (svgStr) {
|
||||
svgEditor.svgCanvas.importSvgString(response);
|
||||
} else if (imgStr) {
|
||||
importImage(response);
|
||||
}
|
||||
closeBrowser();
|
||||
break;
|
||||
case 'm': {
|
||||
if (svgStr) {
|
||||
svgEditor.svgCanvas.importSvgString(response)
|
||||
} else if (imgStr) {
|
||||
importImage(response)
|
||||
}
|
||||
closeBrowser()
|
||||
break
|
||||
case 'm': {
|
||||
// Import multiple
|
||||
multiArr.push([ (svgStr ? 'svg' : 'img'), response ]);
|
||||
curMeta = pending[id];
|
||||
let title;
|
||||
if (svgStr) {
|
||||
if (curMeta && curMeta.name) {
|
||||
title = curMeta.name;
|
||||
} else {
|
||||
multiArr.push([(svgStr ? 'svg' : 'img'), response])
|
||||
curMeta = pending[id]
|
||||
let title
|
||||
if (svgStr) {
|
||||
if (curMeta && curMeta.name) {
|
||||
title = curMeta.name
|
||||
} else {
|
||||
// Try to find a title
|
||||
// `dropXMLInternalSubset` is to help prevent the billion laughs attack
|
||||
const xml = new DOMParser().parseFromString(dropXMLInternalSubset(response), 'text/xml').documentElement; // lgtm [js/xml-bomb]
|
||||
title = xml.querySelector('title').textContent || '(SVG #' + response.length + ')';
|
||||
}
|
||||
if (curMeta) {
|
||||
Array.from(preview.children).forEach(function (element) {
|
||||
if (element.dataset.id === id) {
|
||||
if (curMeta.preview_url) {
|
||||
const img = document.createElement("img");
|
||||
img.src = curMeta.preview_url;
|
||||
const span = document.createElement("span");
|
||||
span.appendChild(img);
|
||||
element.append(span);
|
||||
} else {
|
||||
element.textContent = title;
|
||||
const xml = new DOMParser().parseFromString(dropXMLInternalSubset(response), 'text/xml').documentElement // lgtm [js/xml-bomb]
|
||||
title = xml.querySelector('title').textContent || '(SVG #' + response.length + ')'
|
||||
}
|
||||
if (curMeta) {
|
||||
Array.from(preview.children).forEach(function (element) {
|
||||
if (element.dataset.id === id) {
|
||||
if (curMeta.preview_url) {
|
||||
const img = document.createElement('img')
|
||||
img.src = curMeta.preview_url
|
||||
const span = document.createElement('span')
|
||||
span.appendChild(img)
|
||||
element.append(span)
|
||||
} else {
|
||||
element.textContent = title
|
||||
}
|
||||
submit.removeAttribute('disabled')
|
||||
}
|
||||
submit.removeAttribute('disabled');
|
||||
}
|
||||
});
|
||||
})
|
||||
} else {
|
||||
const div = document.createElement('div')
|
||||
div.textContent = title
|
||||
preview.appendChild(div)
|
||||
submit.removeAttribute('disabled')
|
||||
}
|
||||
} else {
|
||||
const div = document.createElement("div");
|
||||
div.textContent = title;
|
||||
preview.appendChild(div);
|
||||
submit.removeAttribute('disabled');
|
||||
}
|
||||
} else {
|
||||
if (curMeta && curMeta.preview_url) {
|
||||
title = curMeta.name || '';
|
||||
entry = document.createElement('span');
|
||||
const img = document.createElement("img");
|
||||
img.src = curMeta.preview_url;
|
||||
entry.appendChild(img);
|
||||
entry.appendChild(document.createTextNode(title));
|
||||
} else {
|
||||
entry = document.createElement("img");
|
||||
entry.src = response;
|
||||
}
|
||||
if (curMeta && curMeta.preview_url) {
|
||||
title = curMeta.name || ''
|
||||
entry = document.createElement('span')
|
||||
const img = document.createElement('img')
|
||||
img.src = curMeta.preview_url
|
||||
entry.appendChild(img)
|
||||
entry.appendChild(document.createTextNode(title))
|
||||
} else {
|
||||
entry = document.createElement('img')
|
||||
entry.src = response
|
||||
}
|
||||
|
||||
if (curMeta) {
|
||||
Array.from(preview.children).forEach(function (element) {
|
||||
if (element.dataset.id === id) {
|
||||
element.appendChild(entry);
|
||||
submit.removeAttribute('disabled');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const div = document.createElement("div");
|
||||
div.appendChild(entry);
|
||||
preview.appendChild(div);
|
||||
submit.removeAttribute('disabled');
|
||||
if (curMeta) {
|
||||
Array.from(preview.children).forEach(function (element) {
|
||||
if (element.dataset.id === id) {
|
||||
element.appendChild(entry)
|
||||
submit.removeAttribute('disabled')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const div = document.createElement('div')
|
||||
div.appendChild(entry)
|
||||
preview.appendChild(div)
|
||||
submit.removeAttribute('disabled')
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
} case 'o': {
|
||||
break
|
||||
} case 'o': {
|
||||
// Open
|
||||
if (!svgStr) { break; }
|
||||
closeBrowser();
|
||||
const ok = await svgEditor.openPrep();
|
||||
if (!ok) { return; }
|
||||
svgCanvas.clear();
|
||||
svgCanvas.setSvgString(response);
|
||||
// updateCanvas();
|
||||
break;
|
||||
}
|
||||
if (!svgStr) { break }
|
||||
closeBrowser()
|
||||
const ok = await svgEditor.openPrep()
|
||||
if (!ok) { return }
|
||||
svgCanvas.clear()
|
||||
svgCanvas.setSvgString(response)
|
||||
// updateCanvas();
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Receive `postMessage` data
|
||||
window.addEventListener('message', onMessage, true);
|
||||
window.addEventListener('message', onMessage, true)
|
||||
|
||||
const insertAfter = (referenceNode, newNode) => {
|
||||
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
|
||||
};
|
||||
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling)
|
||||
}
|
||||
|
||||
const toggleMultiLoop = () => {
|
||||
multiArr.forEach(function(item, i){
|
||||
const type = item[0];
|
||||
const data = item[1];
|
||||
multiArr.forEach(function (item, i) {
|
||||
const type = item[0]
|
||||
const data = item[1]
|
||||
if (type === 'svg') {
|
||||
svgCanvas.importSvgString(data);
|
||||
svgCanvas.importSvgString(data)
|
||||
} else {
|
||||
importImage(data);
|
||||
importImage(data)
|
||||
}
|
||||
svgCanvas.moveSelectedElements(i * 20, i * 20, false);
|
||||
});
|
||||
while (preview.firstChild)
|
||||
preview.removeChild(preview.firstChild);
|
||||
multiArr = [];
|
||||
$id("imgbrowse_holder").style.display = 'none';
|
||||
};
|
||||
svgCanvas.moveSelectedElements(i * 20, i * 20, false)
|
||||
})
|
||||
while (preview.firstChild) { preview.removeChild(preview.firstChild) }
|
||||
multiArr = []
|
||||
$id('imgbrowse_holder').style.display = 'none'
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} show
|
||||
* @returns {void}
|
||||
*/
|
||||
const toggleMulti = (show) => {
|
||||
$id('lib_framewrap').style.right = (show ? 200 : 10);
|
||||
$id('imglib_opts').style.right = (show ? 200 : 10);
|
||||
$id('lib_framewrap').style.right = (show ? 200 : 10)
|
||||
$id('imglib_opts').style.right = (show ? 200 : 10)
|
||||
if (!preview) {
|
||||
preview = document.createElement('div');
|
||||
preview.setAttribute('id', 'imglib_preview');
|
||||
preview.setAttribute('style', `position: absolute;top: 45px;right: 10px;width: 180px;bottom: 45px;background: #fff;overflow: auto;`);
|
||||
insertAfter($id('lib_framewrap'), preview);
|
||||
preview = document.createElement('div')
|
||||
preview.setAttribute('id', 'imglib_preview')
|
||||
preview.setAttribute('style', 'position: absolute;top: 45px;right: 10px;width: 180px;bottom: 45px;background: #fff;overflow: auto;')
|
||||
insertAfter($id('lib_framewrap'), preview)
|
||||
|
||||
submit = document.createElement('button');
|
||||
submit.setAttribute('content', 'Import selected');
|
||||
submit.setAttribute('disabled', true);
|
||||
submit.textContent = 'Import selected';
|
||||
submit.setAttribute('style', `position: absolute;bottom: 10px;right: -10px;`);
|
||||
$id('imgbrowse').appendChild(submit);
|
||||
submit.addEventListener('click', toggleMultiLoop);
|
||||
submit.addEventListener('touchend', toggleMultiLoop);
|
||||
submit = document.createElement('button')
|
||||
submit.setAttribute('content', 'Import selected')
|
||||
submit.setAttribute('disabled', true)
|
||||
submit.textContent = 'Import selected'
|
||||
submit.setAttribute('style', 'position: absolute;bottom: 10px;right: -10px;')
|
||||
$id('imgbrowse').appendChild(submit)
|
||||
submit.addEventListener('click', toggleMultiLoop)
|
||||
submit.addEventListener('touchend', toggleMultiLoop)
|
||||
}
|
||||
submit.style.display = (show) ? 'block' : 'none';
|
||||
preview.style.display = (show) ? 'block' : 'none';
|
||||
|
||||
};
|
||||
submit.style.display = (show) ? 'block' : 'none'
|
||||
preview.style.display = (show) ? 'block' : 'none'
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const showBrowser = () => {
|
||||
let browser = $id('imgbrowse');
|
||||
let browser = $id('imgbrowse')
|
||||
if (!browser) {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'imgbrowse_holder';
|
||||
div.innerHTML = '<div id=imgbrowse class=toolbar_button></div>';
|
||||
insertAfter($svgEditor, div);
|
||||
browser = $id('imgbrowse');
|
||||
const div = document.createElement('div')
|
||||
div.id = 'imgbrowse_holder'
|
||||
div.innerHTML = '<div id=imgbrowse class=toolbar_button></div>'
|
||||
insertAfter($svgEditor, div)
|
||||
browser = $id('imgbrowse')
|
||||
|
||||
const allLibs = svgEditor.i18next.t(`${name}:select_lib`);
|
||||
const allLibs = svgEditor.i18next.t(`${name}:select_lib`)
|
||||
|
||||
const divFrameWrap = document.createElement('div');
|
||||
divFrameWrap.id = 'lib_framewrap';
|
||||
const divFrameWrap = document.createElement('div')
|
||||
divFrameWrap.id = 'lib_framewrap'
|
||||
|
||||
const libOpts = document.createElement('ul');
|
||||
libOpts.id = 'imglib_opts';
|
||||
browser.append(libOpts);
|
||||
const frame = document.createElement('iframe');
|
||||
frame.src = "javascript:0";
|
||||
frame.style.display = 'none';
|
||||
divFrameWrap.append(frame);
|
||||
browser.prepend(divFrameWrap);
|
||||
const libOpts = document.createElement('ul')
|
||||
libOpts.id = 'imglib_opts'
|
||||
browser.append(libOpts)
|
||||
const frame = document.createElement('iframe')
|
||||
frame.src = 'javascript:0'
|
||||
frame.style.display = 'none'
|
||||
divFrameWrap.append(frame)
|
||||
browser.prepend(divFrameWrap)
|
||||
|
||||
const header = document.createElement('h1');
|
||||
browser.prepend(header);
|
||||
header.textContent = allLibs;
|
||||
header.setAttribute('style', `position: absolute;top: 0px;left: 0px;width: 100%;`);
|
||||
const header = document.createElement('h1')
|
||||
browser.prepend(header)
|
||||
header.textContent = allLibs
|
||||
header.setAttribute('style', 'position: absolute;top: 0px;left: 0px;width: 100%;')
|
||||
|
||||
const button = document.createElement('button');
|
||||
button.innerHTML = svgEditor.i18next.t('common.cancel');
|
||||
browser.appendChild(button);
|
||||
const button = document.createElement('button')
|
||||
button.innerHTML = svgEditor.i18next.t('common.cancel')
|
||||
browser.appendChild(button)
|
||||
button.addEventListener('click', function () {
|
||||
$id("imgbrowse_holder").style.display = 'none';
|
||||
});
|
||||
$id('imgbrowse_holder').style.display = 'none'
|
||||
})
|
||||
button.addEventListener('touchend', function () {
|
||||
$id("imgbrowse_holder").style.display = 'none';
|
||||
});
|
||||
button.setAttribute('style', `position: absolute;top: 5px;right: 10px;`);
|
||||
$id('imgbrowse_holder').style.display = 'none'
|
||||
})
|
||||
button.setAttribute('style', 'position: absolute;top: 5px;right: 10px;')
|
||||
|
||||
const leftBlock = document.createElement('span');
|
||||
leftBlock.setAttribute('style', `position: absolute;top: 5px;left: 10px;display: inline-flex;`);
|
||||
browser.appendChild(leftBlock);
|
||||
const leftBlock = document.createElement('span')
|
||||
leftBlock.setAttribute('style', 'position: absolute;top: 5px;left: 10px;display: inline-flex;')
|
||||
browser.appendChild(leftBlock)
|
||||
|
||||
const back = document.createElement('button');
|
||||
back.style.visibility = "hidden";
|
||||
back.innerHTML = `<img class="svg_icon" src="${imgPath}/library.svg" alt="icon" width="16" height="16" />` + svgEditor.i18next.t(`${name}:show_list`);
|
||||
leftBlock.appendChild(back);
|
||||
const back = document.createElement('button')
|
||||
back.style.visibility = 'hidden'
|
||||
back.innerHTML = `<img class="svg_icon" src="${imgPath}/library.svg" alt="icon" width="16" height="16" />` + svgEditor.i18next.t(`${name}:show_list`)
|
||||
leftBlock.appendChild(back)
|
||||
back.addEventListener('click', function () {
|
||||
frame.setAttribute('src', 'about:blank');
|
||||
frame.style.display = 'none';
|
||||
libOpts.style.display = 'block';
|
||||
header.textContent = allLibs;
|
||||
back.style.display = 'none';
|
||||
});
|
||||
frame.setAttribute('src', 'about:blank')
|
||||
frame.style.display = 'none'
|
||||
libOpts.style.display = 'block'
|
||||
header.textContent = allLibs
|
||||
back.style.display = 'none'
|
||||
})
|
||||
back.addEventListener('touchend', function () {
|
||||
frame.setAttribute('src', 'about:blank');
|
||||
frame.style.display = 'none';
|
||||
libOpts.style.display = 'block';
|
||||
header.textContent = allLibs;
|
||||
back.style.display = 'none';
|
||||
});
|
||||
back.setAttribute('style', `margin-right: 5px;`);
|
||||
back.style.display = 'none';
|
||||
frame.setAttribute('src', 'about:blank')
|
||||
frame.style.display = 'none'
|
||||
libOpts.style.display = 'block'
|
||||
header.textContent = allLibs
|
||||
back.style.display = 'none'
|
||||
})
|
||||
back.setAttribute('style', 'margin-right: 5px;')
|
||||
back.style.display = 'none'
|
||||
|
||||
const select = document.createElement('select');
|
||||
const select = document.createElement('select')
|
||||
select.innerHTML = '<select><option value=s>' +
|
||||
svgEditor.i18next.t(`${name}:import_single`) + '</option><option value=m>' +
|
||||
svgEditor.i18next.t(`${name}:import_multi`) + '</option><option value=o>' +
|
||||
svgEditor.i18next.t(`${name}:open`) + '</option>';
|
||||
leftBlock.appendChild(select);
|
||||
svgEditor.i18next.t(`${name}:open`) + '</option>'
|
||||
leftBlock.appendChild(select)
|
||||
select.addEventListener('change', function () {
|
||||
mode = this.value;
|
||||
mode = this.value
|
||||
switch (mode) {
|
||||
case 's':
|
||||
case 'o':
|
||||
toggleMulti(false);
|
||||
break;
|
||||
case 's':
|
||||
case 'o':
|
||||
toggleMulti(false)
|
||||
break
|
||||
|
||||
case 'm':
|
||||
case 'm':
|
||||
// Import multiple
|
||||
toggleMulti(true);
|
||||
break;
|
||||
toggleMulti(true)
|
||||
break
|
||||
}
|
||||
});
|
||||
select.setAttribute('style', `margin-top: 10px;`);
|
||||
})
|
||||
select.setAttribute('style', 'margin-top: 10px;')
|
||||
|
||||
imgLibs.forEach(function ({ name, url, description }) {
|
||||
const li = document.createElement('li');
|
||||
libOpts.appendChild(li);
|
||||
li.textContent = name;
|
||||
const li = document.createElement('li')
|
||||
libOpts.appendChild(li)
|
||||
li.textContent = name
|
||||
li.addEventListener('click', function () {
|
||||
frame.setAttribute('src', url);
|
||||
frame.style.display = 'block';
|
||||
header.textContent = name;
|
||||
libOpts.style.display = 'none';
|
||||
back.style.display = 'block';
|
||||
});
|
||||
frame.setAttribute('src', url)
|
||||
frame.style.display = 'block'
|
||||
header.textContent = name
|
||||
libOpts.style.display = 'none'
|
||||
back.style.display = 'block'
|
||||
})
|
||||
li.addEventListener('touchend', function () {
|
||||
frame.setAttribute('src', url);
|
||||
frame.style.display = 'block';
|
||||
header.textContent = name;
|
||||
libOpts.style.display = 'none';
|
||||
back.style.display = 'block';
|
||||
});
|
||||
const span = document.createElement("span");
|
||||
span.textContent = description;
|
||||
li.appendChild(span);
|
||||
});
|
||||
frame.setAttribute('src', url)
|
||||
frame.style.display = 'block'
|
||||
header.textContent = name
|
||||
libOpts.style.display = 'none'
|
||||
back.style.display = 'block'
|
||||
})
|
||||
const span = document.createElement('span')
|
||||
span.textContent = description
|
||||
li.appendChild(span)
|
||||
})
|
||||
} else {
|
||||
$id("imgbrowse_holder").style.display = 'block';
|
||||
$id('imgbrowse_holder').style.display = 'block'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
svgicons: 'ext-imagelib.xml',
|
||||
callback() {
|
||||
callback () {
|
||||
// Add the button and its handler(s)
|
||||
const buttonTemplate = document.createElement("template");
|
||||
const key = name + `:buttons.0.title`;
|
||||
const buttonTemplate = document.createElement('template')
|
||||
const key = name + ':buttons.0.title'
|
||||
buttonTemplate.innerHTML = `
|
||||
<se-menu-item id="tool_imagelib" label="${key}" src="library.svg"></se-menu-item>
|
||||
`;
|
||||
insertAfter($id('tool_export'), buttonTemplate.content.cloneNode(true));
|
||||
$id('tool_imagelib').addEventListener("click", () => {
|
||||
showBrowser();
|
||||
});
|
||||
`
|
||||
insertAfter($id('tool_export'), buttonTemplate.content.cloneNode(true))
|
||||
$id('tool_imagelib').addEventListener('click', () => {
|
||||
showBrowser()
|
||||
})
|
||||
|
||||
const style = document.createElement('style');
|
||||
const style = document.createElement('style')
|
||||
style.textContent = '#imgbrowse_holder {' +
|
||||
'position: absolute;' +
|
||||
'top: 0;' +
|
||||
@@ -599,9 +595,9 @@ export default {
|
||||
'width: 100%;' +
|
||||
'height: 100%;' +
|
||||
'border: 0;' +
|
||||
'}';
|
||||
document.head.appendChild(style);
|
||||
'}'
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
const atags = document.querySelectorAll('a');
|
||||
const atags = document.querySelectorAll('a')
|
||||
Array.prototype.forEach.call(atags, function (aEle) {
|
||||
aEle.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
const { href } = event.currentTarget;
|
||||
const target = window.parent;
|
||||
event.preventDefault()
|
||||
const { href } = event.currentTarget
|
||||
const target = window.parent
|
||||
const post = (message) => {
|
||||
// Todo: Make origin customizable as set by opening window
|
||||
// Todo: If dropping IE9, avoid stringifying
|
||||
target.postMessage(JSON.stringify({
|
||||
namespace: 'imagelib',
|
||||
...message
|
||||
}), '*');
|
||||
};
|
||||
}), '*')
|
||||
}
|
||||
// Convert Non-SVG images to data URL first
|
||||
// (this could also have been done server-side by the library)
|
||||
// Send metadata (also indicates file is about to be sent)
|
||||
post({
|
||||
name: event.currentTarget.textContent,
|
||||
id: href
|
||||
});
|
||||
})
|
||||
if (!href.includes('.svg')) {
|
||||
const img = new Image();
|
||||
const img = new Image()
|
||||
img.addEventListener('load', function () {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = this.width
|
||||
canvas.height = this.height
|
||||
// load the raster image into the canvas
|
||||
canvas.getContext('2d').drawImage(this, 0, 0);
|
||||
canvas.getContext('2d').drawImage(this, 0, 0)
|
||||
// retrieve the data: URL
|
||||
let data;
|
||||
let data
|
||||
try {
|
||||
data = canvas.toDataURL();
|
||||
data = canvas.toDataURL()
|
||||
} catch (err) {
|
||||
// This fails in Firefox with `file:///` URLs :(
|
||||
// Todo: This could use a generic alert library instead
|
||||
alert('Data URL conversion failed: ' + err);
|
||||
data = '';
|
||||
alert('Data URL conversion failed: ' + err)
|
||||
data = ''
|
||||
}
|
||||
post({ href, data });
|
||||
});
|
||||
img.src = href;
|
||||
post({ href, data })
|
||||
})
|
||||
img.src = href
|
||||
} else {
|
||||
fetch(href)
|
||||
.then( (r) => r.text())
|
||||
.then( (data) => {
|
||||
post({ href, data });
|
||||
return data;
|
||||
.then((r) => r.text())
|
||||
.then((data) => {
|
||||
post({ href, data })
|
||||
return data
|
||||
})
|
||||
.catch( (error) => console.error(error));
|
||||
.catch((error) => console.error(error))
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
return false
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ export default {
|
||||
imgLibs_0_description: 'Demonstration library for SVG-edit on this server',
|
||||
imgLibs_1_name: 'IAN Symbol Libraries',
|
||||
imgLibs_1_description: 'Free library of illustrations'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* eslint-disable node/no-unpublished-import */
|
||||
import { jml, body, nbsp } from 'jamilih';
|
||||
import $ from 'query-result';
|
||||
import { manipulation } from 'qr-manipulation';
|
||||
import { jml, body, nbsp } from 'jamilih'
|
||||
import $ from 'query-result'
|
||||
import { manipulation } from 'qr-manipulation'
|
||||
|
||||
manipulation($, jml);
|
||||
manipulation($, jml)
|
||||
|
||||
const baseAPIURL = 'https://openclipart.org/search/json/';
|
||||
const baseAPIURL = 'https://openclipart.org/search/json/'
|
||||
|
||||
const jsVoid = 'javascript: void(0);';
|
||||
const jsVoid = 'javascript: void(0);'
|
||||
|
||||
/**
|
||||
* Shows results after query submission.
|
||||
@@ -20,31 +20,35 @@ async function processResults (url) {
|
||||
* @returns {external:JamilihArray}
|
||||
*/
|
||||
function queryLink (query) {
|
||||
return [ 'a', {
|
||||
return ['a', {
|
||||
href: jsVoid,
|
||||
dataset: { value: query },
|
||||
$on: { click (e) {
|
||||
e.preventDefault();
|
||||
const { value } = this.dataset;
|
||||
$('#query')[0].$set(value);
|
||||
$('#openclipart')[0].$submit();
|
||||
} }
|
||||
}, [ query ] ];
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault()
|
||||
const { value } = this.dataset
|
||||
$('#query')[0].$set(value)
|
||||
$('#openclipart')[0].$submit()
|
||||
}
|
||||
}
|
||||
}, [query]]
|
||||
}
|
||||
|
||||
const r = await fetch(url);
|
||||
const json = await r.json();
|
||||
const r = await fetch(url)
|
||||
const json = await r.json()
|
||||
|
||||
if (!json || json.msg !== 'success') {
|
||||
// Todo: This could use a generic alert library instead
|
||||
alert('There was a problem downloading the results');
|
||||
return;
|
||||
alert('There was a problem downloading the results')
|
||||
return
|
||||
}
|
||||
const { payload, info: {
|
||||
results: numResults,
|
||||
pages,
|
||||
current_page: currentPage
|
||||
} } = json;
|
||||
const {
|
||||
payload, info: {
|
||||
results: numResults,
|
||||
pages,
|
||||
current_page: currentPage
|
||||
}
|
||||
} = json
|
||||
|
||||
// $('#page')[0].value = currentPage;
|
||||
// $('#page')[0].max = pages;
|
||||
@@ -57,19 +61,19 @@ async function processResults (url) {
|
||||
// }` object of relevance?
|
||||
// - No need for `tags` with `tags_array`
|
||||
// - `svg`'s: `png_thumb`, `png_full_lossy`, `png_2400px`
|
||||
const semiColonSep = '; ' + nbsp;
|
||||
const semiColonSep = '; ' + nbsp
|
||||
$('#results').jml('div', [
|
||||
[ 'span', [
|
||||
['span', [
|
||||
'Number of results: ',
|
||||
numResults
|
||||
] ],
|
||||
]],
|
||||
semiColonSep,
|
||||
[ 'span', [
|
||||
['span', [
|
||||
'page ',
|
||||
currentPage,
|
||||
' out of ',
|
||||
pages
|
||||
] ],
|
||||
]],
|
||||
...payload.map(({
|
||||
title, description, id,
|
||||
uploader, created,
|
||||
@@ -79,170 +83,174 @@ async function processResults (url) {
|
||||
downloaded_by: downloadedBy,
|
||||
total_favorites: totalFavorites
|
||||
}) => {
|
||||
const imgHW = '100px';
|
||||
const colonSep = ': ' + nbsp;
|
||||
return [ 'div', [
|
||||
[ 'button', { style: 'margin-right: 8px; border: 2px solid black;', dataset: { id, value: svgURL }, $on: {
|
||||
async click (e) {
|
||||
e.preventDefault();
|
||||
const { value: svgurl } = this.dataset;
|
||||
const post = (message) => {
|
||||
const imgHW = '100px'
|
||||
const colonSep = ': ' + nbsp
|
||||
return ['div', [
|
||||
['button', {
|
||||
style: 'margin-right: 8px; border: 2px solid black;',
|
||||
dataset: { id, value: svgURL },
|
||||
$on: {
|
||||
async click (e) {
|
||||
e.preventDefault()
|
||||
const { value: svgurl } = this.dataset
|
||||
const post = (message) => {
|
||||
// Todo: Make origin customizable as set by opening window
|
||||
// Todo: If dropping IE9, avoid stringifying
|
||||
window.parent.postMessage(JSON.stringify({
|
||||
namespace: 'imagelib',
|
||||
...message
|
||||
}), '*');
|
||||
};
|
||||
// Send metadata (also indicates file is about to be sent)
|
||||
post({
|
||||
name: title,
|
||||
id: svgurl
|
||||
});
|
||||
const result = await fetch(svgurl);
|
||||
const svg = await result.text();
|
||||
post({
|
||||
href: svgurl,
|
||||
data: svg
|
||||
});
|
||||
window.parent.postMessage(JSON.stringify({
|
||||
namespace: 'imagelib',
|
||||
...message
|
||||
}), '*')
|
||||
}
|
||||
// Send metadata (also indicates file is about to be sent)
|
||||
post({
|
||||
name: title,
|
||||
id: svgurl
|
||||
})
|
||||
const result = await fetch(svgurl)
|
||||
const svg = await result.text()
|
||||
post({
|
||||
href: svgurl,
|
||||
data: svg
|
||||
})
|
||||
}
|
||||
}
|
||||
} }, [
|
||||
}, [
|
||||
// If we wanted interactive versions despite security risk:
|
||||
// ['object', {data: svgURL, type: 'image/svg+xml'}]
|
||||
[ 'img', { src: svgURL, style: `width: ${imgHW}; height: ${imgHW};` } ]
|
||||
] ],
|
||||
[ 'b', [ title ] ],
|
||||
['img', { src: svgURL, style: `width: ${imgHW}; height: ${imgHW};` }]
|
||||
]],
|
||||
['b', [title]],
|
||||
' ',
|
||||
[ 'i', [ description ] ],
|
||||
['i', [description]],
|
||||
' ',
|
||||
[ 'span', [
|
||||
['span', [
|
||||
'(ID: ',
|
||||
[ 'a', {
|
||||
['a', {
|
||||
href: jsVoid,
|
||||
dataset: { value: id },
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
const { value } = this.dataset;
|
||||
$('#byids')[0].$set(value);
|
||||
$('#openclipart')[0].$submit();
|
||||
e.preventDefault()
|
||||
const { value } = this.dataset
|
||||
$('#byids')[0].$set(value)
|
||||
$('#openclipart')[0].$submit()
|
||||
}
|
||||
}
|
||||
}, [ id ] ],
|
||||
}, [id]],
|
||||
')'
|
||||
] ],
|
||||
]],
|
||||
' ',
|
||||
[ 'i', [
|
||||
[ 'a', {
|
||||
['i', [
|
||||
['a', {
|
||||
href: detailLink,
|
||||
target: '_blank'
|
||||
}, [ 'Details' ] ]
|
||||
] ],
|
||||
[ 'br' ],
|
||||
[ 'span', [
|
||||
[ 'u', [ 'Uploaded by' ] ], colonSep,
|
||||
}, ['Details']]
|
||||
]],
|
||||
['br'],
|
||||
['span', [
|
||||
['u', ['Uploaded by']], colonSep,
|
||||
queryLink(uploader),
|
||||
semiColonSep
|
||||
] ],
|
||||
[ 'span', [
|
||||
[ 'u', [ 'Download count' ] ], colonSep,
|
||||
]],
|
||||
['span', [
|
||||
['u', ['Download count']], colonSep,
|
||||
downloadedBy,
|
||||
semiColonSep
|
||||
] ],
|
||||
[ 'span', [
|
||||
[ 'u', [ 'Times used as favorite' ] ], colonSep,
|
||||
]],
|
||||
['span', [
|
||||
['u', ['Times used as favorite']], colonSep,
|
||||
totalFavorites,
|
||||
semiColonSep
|
||||
] ],
|
||||
[ 'span', [
|
||||
[ 'u', [ 'Created date' ] ], colonSep,
|
||||
]],
|
||||
['span', [
|
||||
['u', ['Created date']], colonSep,
|
||||
created
|
||||
] ],
|
||||
[ 'br' ],
|
||||
[ 'u', [ 'Tags' ] ], colonSep,
|
||||
]],
|
||||
['br'],
|
||||
['u', ['Tags']], colonSep,
|
||||
...tagsArray.map((tag) => {
|
||||
return [ 'span', [
|
||||
return ['span', [
|
||||
' ',
|
||||
queryLink(tag)
|
||||
] ];
|
||||
]]
|
||||
})
|
||||
] ];
|
||||
]]
|
||||
}),
|
||||
[ 'br' ], [ 'br' ],
|
||||
['br'], ['br'],
|
||||
(currentPage === 1 || pages <= 2
|
||||
? ''
|
||||
: [ 'span', [
|
||||
[ 'a', {
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
$('#page')[0].value = 1;
|
||||
$('#openclipart')[0].$submit();
|
||||
: ['span', [
|
||||
['a', {
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault()
|
||||
$('#page')[0].value = 1
|
||||
$('#openclipart')[0].$submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [ 'First' ] ],
|
||||
' '
|
||||
] ]
|
||||
}, ['First']],
|
||||
' '
|
||||
]]
|
||||
),
|
||||
(currentPage === 1
|
||||
? ''
|
||||
: [ 'span', [
|
||||
[ 'a', {
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
$('#page')[0].value = currentPage - 1;
|
||||
$('#openclipart')[0].$submit();
|
||||
: ['span', [
|
||||
['a', {
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault()
|
||||
$('#page')[0].value = currentPage - 1
|
||||
$('#openclipart')[0].$submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [ 'Prev' ] ],
|
||||
' '
|
||||
] ]
|
||||
}, ['Prev']],
|
||||
' '
|
||||
]]
|
||||
),
|
||||
(currentPage === pages
|
||||
? ''
|
||||
: [ 'span', [
|
||||
[ 'a', {
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
$('#page')[0].value = currentPage + 1;
|
||||
$('#openclipart')[0].$submit();
|
||||
: ['span', [
|
||||
['a', {
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault()
|
||||
$('#page')[0].value = currentPage + 1
|
||||
$('#openclipart')[0].$submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [ 'Next' ] ],
|
||||
' '
|
||||
] ]
|
||||
}, ['Next']],
|
||||
' '
|
||||
]]
|
||||
),
|
||||
(currentPage === pages || pages <= 2
|
||||
? ''
|
||||
: [ 'span', [
|
||||
[ 'a', {
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault();
|
||||
$('#page')[0].value = pages;
|
||||
$('#openclipart')[0].$submit();
|
||||
: ['span', [
|
||||
['a', {
|
||||
href: jsVoid,
|
||||
$on: {
|
||||
click (e) {
|
||||
e.preventDefault()
|
||||
$('#page')[0].value = pages
|
||||
$('#openclipart')[0].$submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [ 'Last' ] ],
|
||||
' '
|
||||
] ]
|
||||
}, ['Last']],
|
||||
' '
|
||||
]]
|
||||
)
|
||||
]);
|
||||
])
|
||||
}
|
||||
|
||||
jml('div', [
|
||||
[ 'style', [
|
||||
['style', [
|
||||
`.control {
|
||||
padding-top: 10px;
|
||||
}`
|
||||
] ],
|
||||
[ 'form', {
|
||||
]],
|
||||
['form', {
|
||||
id: 'openclipart',
|
||||
$custom: {
|
||||
async $submit () {
|
||||
@@ -250,95 +258,120 @@ jml('div', [
|
||||
[
|
||||
'query', 'sort', 'amount', 'page', 'byids'
|
||||
].forEach((prop) => {
|
||||
const { value } = $('#' + prop)[0];
|
||||
const { value } = $('#' + prop)[0]
|
||||
if (value) {
|
||||
url.searchParams.set(prop, value);
|
||||
url.searchParams.set(prop, value)
|
||||
}
|
||||
});
|
||||
await processResults(url);
|
||||
})
|
||||
await processResults(url)
|
||||
}
|
||||
},
|
||||
$on: {
|
||||
submit (e) {
|
||||
e.preventDefault();
|
||||
this.$submit();
|
||||
e.preventDefault()
|
||||
this.$submit()
|
||||
}
|
||||
}
|
||||
}, [
|
||||
// Todo: i18nize
|
||||
[ 'fieldset', [
|
||||
[ 'legend', [ 'Search terms' ] ],
|
||||
[ 'div', { class: 'control' }, [
|
||||
[ 'label', [
|
||||
['fieldset', [
|
||||
['legend', ['Search terms']],
|
||||
['div', { class: 'control' }, [
|
||||
['label', [
|
||||
'Query (Title, description, uploader, or tag): ',
|
||||
[ 'input', { id: 'query', name: 'query', placeholder: 'cat', $custom: {
|
||||
$set (value) {
|
||||
$('#byids')[0].value = '';
|
||||
this.value = value;
|
||||
['input', {
|
||||
id: 'query',
|
||||
name: 'query',
|
||||
placeholder: 'cat',
|
||||
$custom: {
|
||||
$set (value) {
|
||||
$('#byids')[0].value = ''
|
||||
this.value = value
|
||||
}
|
||||
},
|
||||
$on: {
|
||||
change () {
|
||||
$('#byids')[0].value = ''
|
||||
}
|
||||
}
|
||||
}, $on: {
|
||||
change () {
|
||||
$('#byids')[0].value = '';
|
||||
}
|
||||
} } ]
|
||||
] ]
|
||||
] ],
|
||||
[ 'br' ],
|
||||
}]
|
||||
]]
|
||||
]],
|
||||
['br'],
|
||||
' OR ',
|
||||
[ 'br' ],
|
||||
[ 'div', { class: 'control' }, [
|
||||
[ 'label', [
|
||||
['br'],
|
||||
['div', { class: 'control' }, [
|
||||
['label', [
|
||||
'IDs (single or comma-separated): ',
|
||||
[ 'input', { id: 'byids', name: 'ids', placeholder: '271380, 265741', $custom: {
|
||||
$set (value) {
|
||||
$('#query')[0].value = '';
|
||||
this.value = value;
|
||||
['input', {
|
||||
id: 'byids',
|
||||
name: 'ids',
|
||||
placeholder: '271380, 265741',
|
||||
$custom: {
|
||||
$set (value) {
|
||||
$('#query')[0].value = ''
|
||||
this.value = value
|
||||
}
|
||||
},
|
||||
$on: {
|
||||
change () {
|
||||
$('#query')[0].value = ''
|
||||
}
|
||||
}
|
||||
}, $on: {
|
||||
change () {
|
||||
$('#query')[0].value = '';
|
||||
}
|
||||
} } ]
|
||||
] ]
|
||||
] ]
|
||||
] ],
|
||||
[ 'fieldset', [
|
||||
[ 'legend', [ 'Configuring results' ] ],
|
||||
[ 'div', { class: 'control' }, [
|
||||
[ 'label', [
|
||||
}]
|
||||
]]
|
||||
]]
|
||||
]],
|
||||
['fieldset', [
|
||||
['legend', ['Configuring results']],
|
||||
['div', { class: 'control' }, [
|
||||
['label', [
|
||||
'Sort by: ',
|
||||
[ 'select', { id: 'sort' }, [
|
||||
['select', { id: 'sort' }, [
|
||||
// Todo: i18nize first values
|
||||
[ 'Date', 'date' ],
|
||||
[ 'Downloads', 'downloads' ],
|
||||
[ 'Favorited', 'favorites' ]
|
||||
].map(([ text, value = text ]) => {
|
||||
return [ 'option', { value }, [ text ] ];
|
||||
}) ]
|
||||
] ]
|
||||
] ],
|
||||
[ 'div', { class: 'control' }, [
|
||||
[ 'label', [
|
||||
['Date', 'date'],
|
||||
['Downloads', 'downloads'],
|
||||
['Favorited', 'favorites']
|
||||
].map(([text, value = text]) => {
|
||||
return ['option', { value }, [text]]
|
||||
})]
|
||||
]]
|
||||
]],
|
||||
['div', { class: 'control' }, [
|
||||
['label', [
|
||||
'Results per page: ',
|
||||
[ 'input', {
|
||||
id: 'amount', name: 'amount', value: 10,
|
||||
type: 'number', min: 1, max: 200, step: 1, pattern: '\\d+' } ]
|
||||
] ]
|
||||
] ],
|
||||
[ 'div', { class: 'control' }, [
|
||||
[ 'label', [
|
||||
['input', {
|
||||
id: 'amount',
|
||||
name: 'amount',
|
||||
value: 10,
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 200,
|
||||
step: 1,
|
||||
pattern: '\\d+'
|
||||
}]
|
||||
]]
|
||||
]],
|
||||
['div', { class: 'control' }, [
|
||||
['label', [
|
||||
'Page number: ',
|
||||
[ 'input', {
|
||||
['input', {
|
||||
// max: 1, // We'll change this based on available results
|
||||
id: 'page', name: 'page', value: 1, style: 'width: 40px;',
|
||||
type: 'number', min: 1, step: 1, pattern: '\\d+'
|
||||
} ]
|
||||
] ]
|
||||
] ]
|
||||
] ],
|
||||
[ 'div', { class: 'control' }, [
|
||||
[ 'input', { type: 'submit' } ]
|
||||
] ]
|
||||
] ],
|
||||
[ 'div', { id: 'results' } ]
|
||||
], body);
|
||||
id: 'page',
|
||||
name: 'page',
|
||||
value: 1,
|
||||
style: 'width: 40px;',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
step: 1,
|
||||
pattern: '\\d+'
|
||||
}]
|
||||
]]
|
||||
]]
|
||||
]],
|
||||
['div', { class: 'control' }, [
|
||||
['input', { type: 'submit' }]
|
||||
]]
|
||||
]],
|
||||
['div', { id: 'results' }]
|
||||
], body)
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
export default {
|
||||
name: 'markers',
|
||||
async init () {
|
||||
const svgEditor = this;
|
||||
const { svgCanvas } = svgEditor;
|
||||
const { BatchCommand, RemoveElementCommand, InsertElementCommand } = svgCanvas.history;
|
||||
const { $id, addSVGElemensFromJson: addElem } = svgCanvas;
|
||||
const mtypes = [ 'start', 'mid', 'end' ];
|
||||
const markerElems = [ 'line', 'path', 'polyline', 'polygon' ];
|
||||
const svgEditor = this
|
||||
const { svgCanvas } = svgEditor
|
||||
const { BatchCommand, RemoveElementCommand, InsertElementCommand } = svgCanvas.history
|
||||
const { $id, addSVGElemensFromJson: addElem } = svgCanvas
|
||||
const mtypes = ['start', 'mid', 'end']
|
||||
const markerElems = ['line', 'path', 'polyline', 'polygon']
|
||||
|
||||
// note - to add additional marker types add them below with a unique id
|
||||
// and add the associated icon(s) to marker-icons.svg
|
||||
@@ -55,25 +55,25 @@ export default {
|
||||
};
|
||||
|
||||
// duplicate shapes to support unfilled (open) marker types with an _o suffix
|
||||
[ 'leftarrow', 'rightarrow', 'box', 'mcircle' ].forEach((v) => {
|
||||
markerTypes[v + '_o'] = markerTypes[v];
|
||||
});
|
||||
['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(/\(#(.*)\)/);
|
||||
const getLinked = (elem, attr) => {
|
||||
const str = elem.getAttribute(attr)
|
||||
if (!str) { return null }
|
||||
const m = str.match(/\(#(.*)\)/)
|
||||
// "url(#mkr_end_svg_1)" would give m[1] = "mkr_end_svg_1"
|
||||
if (!m || m.length !== 2) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
return svgCanvas.getElem(m[1]);
|
||||
};
|
||||
return svgCanvas.getElem(m[1])
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles context tool panel off/on.
|
||||
@@ -81,18 +81,18 @@ export default {
|
||||
* @returns {void}
|
||||
*/
|
||||
const showPanel = (on, elem) => {
|
||||
$id('marker_panel').style.display = (on) ? 'block' : 'none';
|
||||
$id('marker_panel').style.display = (on) ? 'block' : 'none'
|
||||
if (on && elem) {
|
||||
mtypes.forEach((pos) => {
|
||||
const marker = getLinked(elem, 'marker-' + pos);
|
||||
const marker = getLinked(elem, 'marker-' + pos)
|
||||
if (marker?.attributes?.se_type) {
|
||||
$id(`${pos}_marker_list_opts`).setAttribute('value', marker.attributes.se_type.value);
|
||||
$id(`${pos}_marker_list_opts`).setAttribute('value', marker.attributes.se_type.value)
|
||||
} else {
|
||||
$id(`${pos}_marker_list_opts`).setAttribute('value', 'nomarker');
|
||||
$id(`${pos}_marker_list_opts`).setAttribute('value', 'nomarker')
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
@@ -100,22 +100,22 @@ export default {
|
||||
* @returns {SVGMarkerElement}
|
||||
*/
|
||||
const addMarker = (id, seType) => {
|
||||
const selElems = svgCanvas.getSelectedElements();
|
||||
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;
|
||||
const selElems = svgCanvas.getSelectedElements()
|
||||
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;
|
||||
console.error(`unknown marker type: ${seType}`)
|
||||
return undefined
|
||||
}
|
||||
|
||||
// create a generic marker
|
||||
@@ -128,27 +128,27 @@ export default {
|
||||
style: 'pointer-events:none',
|
||||
se_type: seType
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const mel = addElem(markerTypes[seType]);
|
||||
const mel = addElem(markerTypes[seType])
|
||||
const fillcolor = (seType.substr(-2) === '_o')
|
||||
? 'none'
|
||||
: color;
|
||||
: color
|
||||
|
||||
mel.setAttribute('fill', fillcolor);
|
||||
mel.setAttribute('stroke', color);
|
||||
mel.setAttribute('stroke-width', strokeWidth);
|
||||
marker.append(mel);
|
||||
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);
|
||||
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;
|
||||
};
|
||||
return marker
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} elem
|
||||
@@ -157,16 +157,16 @@ export default {
|
||||
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; }
|
||||
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 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 midPt = (' ' + ((x1 + x2) / 2) + ',' + ((y1 + y2) / 2) + ' ')
|
||||
const pline = addElem({
|
||||
element: 'polyline',
|
||||
attr: {
|
||||
@@ -176,53 +176,53 @@ export default {
|
||||
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 nam = 'marker-' + pos
|
||||
const m = elem.getAttribute(nam)
|
||||
if (m) { pline.setAttribute(nam, elem.getAttribute(nam)) }
|
||||
})
|
||||
|
||||
const batchCmd = new BatchCommand();
|
||||
batchCmd.addSubCommand(new RemoveElementCommand(elem, elem.parentNode));
|
||||
batchCmd.addSubCommand(new InsertElementCommand(pline));
|
||||
const batchCmd = new BatchCommand()
|
||||
batchCmd.addSubCommand(new RemoveElementCommand(elem, elem.parentNode))
|
||||
batchCmd.addSubCommand(new InsertElementCommand(pline))
|
||||
|
||||
elem.insertAdjacentElement('afterend', pline);
|
||||
elem.remove();
|
||||
svgCanvas.clearSelection();
|
||||
pline.id = id;
|
||||
svgCanvas.addToSelection([ pline ]);
|
||||
svgCanvas.addCommandToHistory(batchCmd);
|
||||
return pline;
|
||||
};
|
||||
elem.insertAdjacentElement('afterend', pline)
|
||||
elem.remove()
|
||||
svgCanvas.clearSelection()
|
||||
pline.id = id
|
||||
svgCanvas.addToSelection([pline])
|
||||
svgCanvas.addCommandToHistory(batchCmd)
|
||||
return pline
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const setMarker = (pos, markerType) => {
|
||||
const selElems = svgCanvas.getSelectedElements();
|
||||
if (selElems.length === 0) 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'; }
|
||||
const selElems = svgCanvas.getSelectedElements()
|
||||
if (selElems.length === 0) 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;
|
||||
svgCanvas.call('changed', selElems)
|
||||
return
|
||||
}
|
||||
// Set marker on element
|
||||
const id = 'mkr_' + pos + '_' + el.id;
|
||||
addMarker(id, val);
|
||||
svgCanvas.changeSelectedAttribute(markerName, 'url(#' + id + ')');
|
||||
const id = 'mkr_' + pos + '_' + el.id
|
||||
addMarker(id, val)
|
||||
svgCanvas.changeSelectedAttribute(markerName, 'url(#' + id + ')')
|
||||
if (el.tagName === 'line' && pos === 'mid') {
|
||||
convertline(el);
|
||||
convertline(el)
|
||||
}
|
||||
svgCanvas.call('changed', selElems);
|
||||
};
|
||||
svgCanvas.call('changed', selElems)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the main system modifies an object. This routine changes
|
||||
@@ -231,20 +231,20 @@ export default {
|
||||
* @returns {void}
|
||||
*/
|
||||
const colorChanged = (elem) => {
|
||||
const color = elem.getAttribute('stroke');
|
||||
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); }
|
||||
});
|
||||
};
|
||||
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.
|
||||
@@ -253,78 +253,77 @@ export default {
|
||||
* @returns {void}
|
||||
*/
|
||||
const updateReferences = (el) => {
|
||||
const selElems = svgCanvas.getSelectedElements();
|
||||
const selElems = svgCanvas.getSelectedElements()
|
||||
mtypes.forEach((pos) => {
|
||||
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);
|
||||
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);
|
||||
const len = el.id.length
|
||||
const linkid = url.substr(-len - 1, len)
|
||||
if (el.id !== linkid) {
|
||||
const newMarkerId = 'mkr_' + pos + '_' + el.id;
|
||||
addMarker(newMarkerId, marker.attributes.se_type.value);
|
||||
svgCanvas.changeSelectedAttribute(markerName, 'url(#' + newMarkerId + ')');
|
||||
svgCanvas.call('changed', selElems);
|
||||
const newMarkerId = 'mkr_' + pos + '_' + el.id
|
||||
addMarker(newMarkerId, marker.attributes.se_type.value)
|
||||
svgCanvas.changeSelectedAttribute(markerName, 'url(#' + newMarkerId + ')')
|
||||
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() {
|
||||
callback () {
|
||||
// Add the context panel and its handler(s)
|
||||
const panelTemplate = document.createElement("template");
|
||||
const panelTemplate = document.createElement('template')
|
||||
// create the marker panel
|
||||
let innerHTML = '<div id="marker_panel">';
|
||||
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));
|
||||
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>'
|
||||
panelTemplate.innerHTML = innerHTML
|
||||
$id('tools_top').appendChild(panelTemplate.content.cloneNode(true))
|
||||
// don't display the panels on start
|
||||
showPanel(false);
|
||||
showPanel(false)
|
||||
mtypes.forEach((pos) => {
|
||||
$id(`${pos}_marker_list_opts`).addEventListener('change', (evt) => {
|
||||
setMarker(pos, evt.detail.value);
|
||||
});
|
||||
});
|
||||
setMarker(pos, evt.detail.value)
|
||||
})
|
||||
})
|
||||
},
|
||||
selectedChanged (opts) {
|
||||
// Use this to update the current selected elements
|
||||
if (opts.elems.length === 0) showPanel(false);
|
||||
opts.elems.forEach( (elem) => {
|
||||
if (opts.elems.length === 0) showPanel(false)
|
||||
opts.elems.forEach((elem) => {
|
||||
if (elem && markerElems.includes(elem.tagName)) {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
showPanel(true, elem);
|
||||
showPanel(true, elem)
|
||||
} else {
|
||||
showPanel(false);
|
||||
showPanel(false)
|
||||
}
|
||||
} else {
|
||||
showPanel(false);
|
||||
showPanel(false)
|
||||
}
|
||||
});
|
||||
})
|
||||
},
|
||||
elementChanged (opts) {
|
||||
const elem = opts.elems[0];
|
||||
const elem = opts.elems[0]
|
||||
if (elem && (
|
||||
elem.getAttribute('marker-start') ||
|
||||
elem.getAttribute('marker-mid') ||
|
||||
elem.getAttribute('marker-end')
|
||||
)) {
|
||||
colorChanged(elem);
|
||||
updateReferences(elem);
|
||||
colorChanged(elem)
|
||||
updateReferences(elem)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,4 +43,4 @@ export default {
|
||||
title: 'Select end marker type'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,4 +43,4 @@ export default {
|
||||
title: '选择末端标记类型'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,66 +15,65 @@
|
||||
* @listens module:svgcanvas.SvgCanvas#event:saved
|
||||
* @returns {void}
|
||||
*/
|
||||
import { fileOpen, fileSave } from 'browser-fs-access';
|
||||
import { fileOpen, fileSave } from 'browser-fs-access'
|
||||
|
||||
const name = "opensave";
|
||||
let handle = null;
|
||||
const name = 'opensave'
|
||||
let handle = null
|
||||
|
||||
const loadExtensionTranslation = async function (svgEditor) {
|
||||
let translationModule;
|
||||
const lang = svgEditor.configObj.pref('lang');
|
||||
let translationModule
|
||||
const lang = svgEditor.configObj.pref('lang')
|
||||
try {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
translationModule = await import(`./locale/${lang}.js`);
|
||||
translationModule = await import(`./locale/${lang}.js`)
|
||||
} catch (_error) {
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
|
||||
translationModule = await import(`./locale/en.js`);
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`)
|
||||
translationModule = await import('./locale/en.js')
|
||||
}
|
||||
svgEditor.i18next.addResourceBundle(lang, 'translation', translationModule.default, true, true);
|
||||
};
|
||||
svgEditor.i18next.addResourceBundle(lang, 'translation', translationModule.default, true, true)
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
async init(_S) {
|
||||
const svgEditor = this;
|
||||
const { svgCanvas } = svgEditor;
|
||||
const { $id } = svgCanvas;
|
||||
await loadExtensionTranslation(svgEditor);
|
||||
async init (_S) {
|
||||
const svgEditor = this
|
||||
const { svgCanvas } = svgEditor
|
||||
const { $id } = svgCanvas
|
||||
await loadExtensionTranslation(svgEditor)
|
||||
/**
|
||||
* @param {Event} e
|
||||
* @returns {void}
|
||||
*/
|
||||
const importImage = (e) => {
|
||||
$id('se-prompt-dialog').title = this.i18next.t('notification.loadingImage');
|
||||
$id('se-prompt-dialog').setAttribute('close', false);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
const file = (e.type === 'drop') ? e.dataTransfer.files[0] : e.currentTarget.files[0];
|
||||
$id('se-prompt-dialog').title = this.i18next.t('notification.loadingImage')
|
||||
$id('se-prompt-dialog').setAttribute('close', false)
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
const file = (e.type === 'drop') ? e.dataTransfer.files[0] : e.currentTarget.files[0]
|
||||
if (!file) {
|
||||
$id('se-prompt-dialog').setAttribute('close', true);
|
||||
return;
|
||||
$id('se-prompt-dialog').setAttribute('close', true)
|
||||
return
|
||||
}
|
||||
|
||||
if (!file.type.includes('image')) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
// Detected an image
|
||||
// svg handling
|
||||
let reader;
|
||||
let reader
|
||||
if (file.type.includes('svg')) {
|
||||
reader = new FileReader();
|
||||
reader.onloadend = (ev) => {
|
||||
const newElement = this.svgCanvas.importSvgString(ev.target.result, true);
|
||||
this.svgCanvas.alignSelectedElements('m', 'page');
|
||||
this.svgCanvas.alignSelectedElements('c', 'page');
|
||||
reader = new FileReader()
|
||||
reader.onloadend = (ev) => {
|
||||
const newElement = this.svgCanvas.importSvgString(ev.target.result, true)
|
||||
this.svgCanvas.alignSelectedElements('m', 'page')
|
||||
this.svgCanvas.alignSelectedElements('c', 'page')
|
||||
// highlight imported element, otherwise we get strange empty selectbox
|
||||
this.svgCanvas.selectOnly([ newElement ]);
|
||||
$id('se-prompt-dialog').setAttribute('close', true);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
this.svgCanvas.selectOnly([newElement])
|
||||
$id('se-prompt-dialog').setAttribute('close', true)
|
||||
}
|
||||
reader.readAsText(file)
|
||||
} else {
|
||||
// bitmap handling
|
||||
reader = new FileReader();
|
||||
reader = new FileReader()
|
||||
reader.onloadend = function ({ target: { result } }) {
|
||||
/**
|
||||
* Insert the new image until we know its dimensions.
|
||||
@@ -93,54 +92,54 @@ export default {
|
||||
id: this.svgCanvas.getNextId(),
|
||||
style: 'pointer-events:inherit'
|
||||
}
|
||||
});
|
||||
this.svgCanvas.setHref(newImage, result);
|
||||
this.svgCanvas.selectOnly([ newImage ]);
|
||||
this.svgCanvas.alignSelectedElements('m', 'page');
|
||||
this.svgCanvas.alignSelectedElements('c', 'page');
|
||||
this.topPanel.updateContextPanel();
|
||||
$id('se-prompt-dialog').setAttribute('close', true);
|
||||
};
|
||||
})
|
||||
this.svgCanvas.setHref(newImage, result)
|
||||
this.svgCanvas.selectOnly([newImage])
|
||||
this.svgCanvas.alignSelectedElements('m', 'page')
|
||||
this.svgCanvas.alignSelectedElements('c', 'page')
|
||||
this.topPanel.updateContextPanel()
|
||||
$id('se-prompt-dialog').setAttribute('close', true)
|
||||
}
|
||||
// create dummy img so we know the default dimensions
|
||||
let imgWidth = 100;
|
||||
let imgHeight = 100;
|
||||
const img = new Image();
|
||||
img.style.opacity = 0;
|
||||
let imgWidth = 100
|
||||
let imgHeight = 100
|
||||
const img = new Image()
|
||||
img.style.opacity = 0
|
||||
img.addEventListener('load', () => {
|
||||
imgWidth = img.offsetWidth || img.naturalWidth || img.width;
|
||||
imgHeight = img.offsetHeight || img.naturalHeight || img.height;
|
||||
insertNewImage(imgWidth, imgHeight);
|
||||
});
|
||||
img.src = result;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
imgWidth = img.offsetWidth || img.naturalWidth || img.width
|
||||
imgHeight = img.offsetHeight || img.naturalHeight || img.height
|
||||
insertNewImage(imgWidth, imgHeight)
|
||||
})
|
||||
img.src = result
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
};
|
||||
}
|
||||
// create an input with type file to open the filesystem dialog
|
||||
const imgImport = document.createElement('input');
|
||||
imgImport.type="file";
|
||||
imgImport.addEventListener('change', importImage);
|
||||
const imgImport = document.createElement('input')
|
||||
imgImport.type = 'file'
|
||||
imgImport.addEventListener('change', importImage)
|
||||
// dropping a svg file will import it in the svg as well
|
||||
this.workarea.addEventListener('drop', importImage);
|
||||
this.workarea.addEventListener('drop', importImage)
|
||||
/**
|
||||
* @fires module:svgcanvas.SvgCanvas#event:ext_onNewDocument
|
||||
* @returns {void}
|
||||
*/
|
||||
const clickClear = async function () {
|
||||
const [ x, y ] = svgEditor.configObj.curConfig.dimensions;
|
||||
const ok = await seConfirm(svgEditor.i18next.t('notification.QwantToClear'));
|
||||
if (ok === "Cancel") {
|
||||
return;
|
||||
const [x, y] = svgEditor.configObj.curConfig.dimensions
|
||||
const ok = await seConfirm(svgEditor.i18next.t('notification.QwantToClear'))
|
||||
if (ok === 'Cancel') {
|
||||
return
|
||||
}
|
||||
svgEditor.leftPanel.clickSelect();
|
||||
svgEditor.svgCanvas.clear();
|
||||
svgEditor.svgCanvas.setResolution(x, y);
|
||||
svgEditor.updateCanvas(true);
|
||||
svgEditor.zoomImage();
|
||||
svgEditor.layersPanel.populateLayers();
|
||||
svgEditor.topPanel.updateContextPanel();
|
||||
svgEditor.svgCanvas.runExtensions("onNewDocument");
|
||||
};
|
||||
svgEditor.leftPanel.clickSelect()
|
||||
svgEditor.svgCanvas.clear()
|
||||
svgEditor.svgCanvas.setResolution(x, y)
|
||||
svgEditor.updateCanvas(true)
|
||||
svgEditor.zoomImage()
|
||||
svgEditor.layersPanel.populateLayers()
|
||||
svgEditor.topPanel.updateContextPanel()
|
||||
svgEditor.svgCanvas.runExtensions('onNewDocument')
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, this.editor.svgCanvas.open() is a no-op. It is up to an extension
|
||||
@@ -150,113 +149,113 @@ export default {
|
||||
*/
|
||||
const clickOpen = async function () {
|
||||
// ask user before clearing an unsaved SVG
|
||||
const response = await svgEditor.openPrep();
|
||||
if (response === 'Cancel') { return; }
|
||||
svgCanvas.clear();
|
||||
const response = await svgEditor.openPrep()
|
||||
if (response === 'Cancel') { return }
|
||||
svgCanvas.clear()
|
||||
try {
|
||||
const blob = await fileOpen({
|
||||
mimeTypes: [ 'image/*' ]
|
||||
});
|
||||
const svgContent = await blob.text();
|
||||
await svgEditor.loadSvgString(svgContent);
|
||||
svgEditor.updateCanvas();
|
||||
mimeTypes: ['image/*']
|
||||
})
|
||||
const svgContent = await blob.text()
|
||||
await svgEditor.loadSvgString(svgContent)
|
||||
svgEditor.updateCanvas()
|
||||
} catch (err) {
|
||||
if (err.name !== 'AbortError') {
|
||||
return console.error(err);
|
||||
return console.error(err)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
|
||||
const byteCharacters = atob(b64Data);
|
||||
const byteArrays = [];
|
||||
const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => {
|
||||
const byteCharacters = atob(b64Data)
|
||||
const byteArrays = []
|
||||
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
||||
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
||||
const byteNumbers = new Array(slice.length);
|
||||
const slice = byteCharacters.slice(offset, offset + sliceSize)
|
||||
const byteNumbers = new Array(slice.length)
|
||||
for (let i = 0; i < slice.length; i++) {
|
||||
byteNumbers[i] = slice.charCodeAt(i);
|
||||
byteNumbers[i] = slice.charCodeAt(i)
|
||||
}
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
byteArrays.push(byteArray);
|
||||
const byteArray = new Uint8Array(byteNumbers)
|
||||
byteArrays.push(byteArray)
|
||||
}
|
||||
const blob = new Blob(byteArrays, { type: contentType });
|
||||
return blob;
|
||||
};
|
||||
const blob = new Blob(byteArrays, { type: contentType })
|
||||
return blob
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const clickSave = async function (type, _) {
|
||||
const $editorDialog = $id("se-svg-editor-dialog");
|
||||
const editingsource = $editorDialog.getAttribute("dialog") === "open";
|
||||
const $editorDialog = $id('se-svg-editor-dialog')
|
||||
const editingsource = $editorDialog.getAttribute('dialog') === 'open'
|
||||
if (editingsource) {
|
||||
svgEditor.saveSourceEditor();
|
||||
svgEditor.saveSourceEditor()
|
||||
} else {
|
||||
// In the future, more options can be provided here
|
||||
const saveOpts = {
|
||||
images: svgEditor.configObj.pref("img_save"),
|
||||
images: svgEditor.configObj.pref('img_save'),
|
||||
round_digits: 6
|
||||
};
|
||||
}
|
||||
// remove the selected outline before serializing
|
||||
svgCanvas.clearSelection();
|
||||
svgCanvas.clearSelection()
|
||||
// Update save options if provided
|
||||
if (saveOpts) {
|
||||
const saveOptions = svgCanvas.mergeDeep(svgCanvas.getSvgOption(), saveOpts);
|
||||
for (const [ key, value ] of Object.entries(saveOptions)) {
|
||||
svgCanvas.setSvgOption(key, value);
|
||||
const saveOptions = svgCanvas.mergeDeep(svgCanvas.getSvgOption(), saveOpts)
|
||||
for (const [key, value] of Object.entries(saveOptions)) {
|
||||
svgCanvas.setSvgOption(key, value)
|
||||
}
|
||||
}
|
||||
svgCanvas.setSvgOption('apply', true);
|
||||
svgCanvas.setSvgOption('apply', true)
|
||||
|
||||
// no need for doctype, see https://jwatt.org/svg/authoring/#doctype-declaration
|
||||
const svg = '<?xml version="1.0"?>\n' + svgCanvas.svgCanvasToString();
|
||||
const b64Data = svgCanvas.encode64(svg);
|
||||
const blob = b64toBlob(b64Data, 'image/svg+xml');
|
||||
const svg = '<?xml version="1.0"?>\n' + svgCanvas.svgCanvasToString()
|
||||
const b64Data = svgCanvas.encode64(svg)
|
||||
const blob = b64toBlob(b64Data, 'image/svg+xml')
|
||||
try {
|
||||
if(type === "save" && handle !== null) {
|
||||
const throwIfExistingHandleNotGood = false;
|
||||
if (type === 'save' && handle !== null) {
|
||||
const throwIfExistingHandleNotGood = false
|
||||
handle = await fileSave(blob, {
|
||||
fileName: 'icon.svg',
|
||||
extensions: [ '.svg' ]
|
||||
}, handle, throwIfExistingHandleNotGood);
|
||||
extensions: ['.svg']
|
||||
}, handle, throwIfExistingHandleNotGood)
|
||||
} else {
|
||||
handle = await fileSave(blob, {
|
||||
fileName: 'icon.svg',
|
||||
extensions: [ '.svg' ]
|
||||
});
|
||||
extensions: ['.svg']
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.name !== 'AbortError') {
|
||||
return console.error(err);
|
||||
return console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: svgEditor.i18next.t(`${name}:name`),
|
||||
// The callback should be used to load the DOM with the appropriate UI items
|
||||
callback() {
|
||||
callback () {
|
||||
const buttonTemplate = `
|
||||
<se-menu-item id="tool_clear" label="opensave.new_doc" shortcut="N" src="new.svg"></se-menu-item>`;
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), buttonTemplate, 0);
|
||||
const openButtonTemplate = `<se-menu-item id="tool_open" label="opensave.open_image_doc" src="open.svg"></se-menu-item>`;
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), openButtonTemplate, 1);
|
||||
const saveButtonTemplate = `<se-menu-item id="tool_save" label="opensave.save_doc" shortcut="S" src="saveImg.svg"></se-menu-item>`;
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), saveButtonTemplate, 2);
|
||||
const saveAsButtonTemplate = `<se-menu-item id="tool_save_as" label="opensave.save_as_doc" src="saveImg.svg"></se-menu-item>`;
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), saveAsButtonTemplate, 3);
|
||||
const importButtonTemplate = `<se-menu-item id="tool_import" label="tools.import_doc" src="importImg.svg"></se-menu-item>`;
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), importButtonTemplate, 4);
|
||||
<se-menu-item id="tool_clear" label="opensave.new_doc" shortcut="N" src="new.svg"></se-menu-item>`
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), buttonTemplate, 0)
|
||||
const openButtonTemplate = '<se-menu-item id="tool_open" label="opensave.open_image_doc" src="open.svg"></se-menu-item>'
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), openButtonTemplate, 1)
|
||||
const saveButtonTemplate = '<se-menu-item id="tool_save" label="opensave.save_doc" shortcut="S" src="saveImg.svg"></se-menu-item>'
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), saveButtonTemplate, 2)
|
||||
const saveAsButtonTemplate = '<se-menu-item id="tool_save_as" label="opensave.save_as_doc" src="saveImg.svg"></se-menu-item>'
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), saveAsButtonTemplate, 3)
|
||||
const importButtonTemplate = '<se-menu-item id="tool_import" label="tools.import_doc" src="importImg.svg"></se-menu-item>'
|
||||
svgCanvas.insertChildAtIndex($id('main_button'), importButtonTemplate, 4)
|
||||
|
||||
// handler
|
||||
$id("tool_clear").addEventListener("click", clickClear.bind(this));
|
||||
$id("tool_open").addEventListener("click", clickOpen.bind(this));
|
||||
$id("tool_save").addEventListener("click", clickSave.bind(this, "save"));
|
||||
$id("tool_save_as").addEventListener("click", clickSave.bind(this, "saveas"));
|
||||
$id("tool_import").addEventListener("click", () => imgImport.click());
|
||||
$id('tool_clear').addEventListener('click', clickClear.bind(this))
|
||||
$id('tool_open').addEventListener('click', clickOpen.bind(this))
|
||||
$id('tool_save').addEventListener('click', clickSave.bind(this, 'save'))
|
||||
$id('tool_save_as').addEventListener('click', clickSave.bind(this, 'saveas'))
|
||||
$id('tool_import').addEventListener('click', () => imgImport.click())
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export default {
|
||||
save_doc: 'Save SVG',
|
||||
save_as_doc: 'Save as SVG'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export default {
|
||||
save_doc: 'Enregistrer l\'image',
|
||||
save_as_doc: 'Enregistrer en tant qu\'image'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export default {
|
||||
save_doc: '保存图像',
|
||||
save_as_doc: '另存为图像'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,95 +3,95 @@
|
||||
// MIT License.
|
||||
// can't use npm version as the dragmove is different.
|
||||
|
||||
let _loaded = false;
|
||||
const _callbacks = [];
|
||||
const _isTouch = window.ontouchstart !== undefined;
|
||||
let _loaded = false
|
||||
const _callbacks = []
|
||||
const _isTouch = window.ontouchstart !== undefined
|
||||
|
||||
export const dragmove = function(target, handler, parent, onStart, onEnd, onDrag) {
|
||||
export const dragmove = function (target, handler, parent, onStart, onEnd, onDrag) {
|
||||
// Register a global event to capture mouse moves (once).
|
||||
if (!_loaded) {
|
||||
document.addEventListener(_isTouch ? "touchmove" : "mousemove", function(e) {
|
||||
let c = e;
|
||||
document.addEventListener(_isTouch ? 'touchmove' : 'mousemove', function (e) {
|
||||
let c = e
|
||||
if (e.touches) {
|
||||
c = e.touches[0];
|
||||
c = e.touches[0]
|
||||
}
|
||||
|
||||
// On mouse move, dispatch the coords to all registered callbacks.
|
||||
for (let i = 0; i < _callbacks.length; i++) {
|
||||
_callbacks[i](c.clientX, c.clientY);
|
||||
_callbacks[i](c.clientX, c.clientY)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
_loaded = true;
|
||||
let isMoving = false; let hasStarted = false;
|
||||
let startX = 0; let startY = 0; let lastX = 0; let lastY = 0;
|
||||
_loaded = true
|
||||
let isMoving = false; let hasStarted = false
|
||||
let startX = 0; let startY = 0; let lastX = 0; let lastY = 0
|
||||
|
||||
// On the first click and hold, record the offset of the pointer in relation
|
||||
// to the point of click inside the element.
|
||||
handler.addEventListener(_isTouch ? "touchstart" : "mousedown", function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (target.dataset.dragEnabled === "false") {
|
||||
return;
|
||||
handler.addEventListener(_isTouch ? 'touchstart' : 'mousedown', function (e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (target.dataset.dragEnabled === 'false') {
|
||||
return
|
||||
}
|
||||
|
||||
let c = e;
|
||||
let c = e
|
||||
if (e.touches) {
|
||||
c = e.touches[0];
|
||||
c = e.touches[0]
|
||||
}
|
||||
|
||||
isMoving = true;
|
||||
startX = target.offsetLeft - c.clientX;
|
||||
startY = target.offsetTop - c.clientY;
|
||||
});
|
||||
isMoving = true
|
||||
startX = target.offsetLeft - c.clientX
|
||||
startY = target.offsetTop - c.clientY
|
||||
})
|
||||
|
||||
// On leaving click, stop moving.
|
||||
document.addEventListener(_isTouch ? "touchend" : "mouseup", function() {
|
||||
document.addEventListener(_isTouch ? 'touchend' : 'mouseup', function () {
|
||||
if (onEnd && hasStarted) {
|
||||
onEnd(target, parent, parseInt(target.style.left), parseInt(target.style.top));
|
||||
onEnd(target, parent, parseInt(target.style.left), parseInt(target.style.top))
|
||||
}
|
||||
|
||||
isMoving = false;
|
||||
hasStarted = false;
|
||||
});
|
||||
isMoving = false
|
||||
hasStarted = false
|
||||
})
|
||||
|
||||
// On leaving click, stop moving.
|
||||
document.addEventListener(_isTouch ? "touchmove" : "mousemove", function() {
|
||||
document.addEventListener(_isTouch ? 'touchmove' : 'mousemove', function () {
|
||||
if (onDrag && hasStarted) {
|
||||
onDrag(target, parseInt(target.style.left), parseInt(target.style.top));
|
||||
onDrag(target, parseInt(target.style.left), parseInt(target.style.top))
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Register mouse-move callback to move the element.
|
||||
_callbacks.push(function move(x, y) {
|
||||
_callbacks.push(function move (x, y) {
|
||||
if (!isMoving) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (!hasStarted) {
|
||||
hasStarted = true;
|
||||
hasStarted = true
|
||||
if (onStart) {
|
||||
onStart(target, lastX, lastY);
|
||||
onStart(target, lastX, lastY)
|
||||
}
|
||||
}
|
||||
|
||||
lastX = x + startX;
|
||||
lastY = y + startY;
|
||||
lastX = x + startX
|
||||
lastY = y + startY
|
||||
|
||||
// If boundary checking is on, don't let the element cross the viewport.
|
||||
if (target.dataset.dragBoundary === "true") {
|
||||
if (target.dataset.dragBoundary === 'true') {
|
||||
if (lastX < 1 || lastX >= window.innerWidth - target.offsetWidth) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (lastY < 1 || lastY >= window.innerHeight - target.offsetHeight) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
target.style.left = lastX + "px";
|
||||
target.style.top = lastY + "px";
|
||||
});
|
||||
};
|
||||
target.style.left = lastX + 'px'
|
||||
target.style.top = lastY + 'px'
|
||||
})
|
||||
}
|
||||
|
||||
export { dragmove as default };
|
||||
export { dragmove as default }
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
* @copyright 2013 James Sacksteder
|
||||
*
|
||||
*/
|
||||
import { dragmove } from './dragmove/dragmove.js';
|
||||
import { dragmove } from './dragmove/dragmove.js'
|
||||
|
||||
export default {
|
||||
name: 'overview_window',
|
||||
init ({ _$ }) {
|
||||
const svgEditor = this;
|
||||
const { $id } = svgEditor.svgCanvas;
|
||||
const overviewWindowGlobals = {};
|
||||
const svgEditor = this
|
||||
const { $id } = svgEditor.svgCanvas
|
||||
const overviewWindowGlobals = {}
|
||||
|
||||
// Define and insert the base html element.
|
||||
const propsWindowHtml =
|
||||
@@ -29,129 +29,128 @@ export default {
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
$id("sidepanel_content").insertAdjacentHTML( 'beforeend', propsWindowHtml );
|
||||
'</div>'
|
||||
$id('sidepanel_content').insertAdjacentHTML('beforeend', propsWindowHtml)
|
||||
|
||||
// Define dynamic animation of the view box.
|
||||
const updateViewBox = () => {
|
||||
const { workarea } = svgEditor;
|
||||
const portHeight = parseFloat(getComputedStyle(workarea, null).height.replace("px", ""));
|
||||
const portWidth = parseFloat(getComputedStyle(workarea, null).width.replace("px", ""));
|
||||
const portX = workarea.scrollLeft;
|
||||
const portY = workarea.scrollTop;
|
||||
const windowWidth = parseFloat(getComputedStyle($id("svgcanvas"), null).width.replace("px", ""));
|
||||
const windowHeight = parseFloat(getComputedStyle($id("svgcanvas"), null).height.replace("px", ""));
|
||||
const overviewWidth = parseFloat(getComputedStyle($id("overviewMiniView"), null).width.replace("px", ""));
|
||||
const overviewHeight = parseFloat(getComputedStyle($id("overviewMiniView"), null).height.replace("px", ""));
|
||||
const { workarea } = svgEditor
|
||||
const portHeight = parseFloat(getComputedStyle(workarea, null).height.replace('px', ''))
|
||||
const portWidth = parseFloat(getComputedStyle(workarea, null).width.replace('px', ''))
|
||||
const portX = workarea.scrollLeft
|
||||
const portY = workarea.scrollTop
|
||||
const windowWidth = parseFloat(getComputedStyle($id('svgcanvas'), null).width.replace('px', ''))
|
||||
const windowHeight = parseFloat(getComputedStyle($id('svgcanvas'), null).height.replace('px', ''))
|
||||
const overviewWidth = parseFloat(getComputedStyle($id('overviewMiniView'), null).width.replace('px', ''))
|
||||
const overviewHeight = parseFloat(getComputedStyle($id('overviewMiniView'), null).height.replace('px', ''))
|
||||
|
||||
const viewBoxX = portX / windowWidth * overviewWidth;
|
||||
const viewBoxY = portY / windowHeight * overviewHeight;
|
||||
const viewBoxWidth = portWidth / windowWidth * overviewWidth;
|
||||
const viewBoxHeight = portHeight / windowHeight * overviewHeight;
|
||||
const viewBoxX = portX / windowWidth * overviewWidth
|
||||
const viewBoxY = portY / windowHeight * overviewHeight
|
||||
const viewBoxWidth = portWidth / windowWidth * overviewWidth
|
||||
const viewBoxHeight = portHeight / windowHeight * overviewHeight
|
||||
|
||||
$id("overview_window_view_box").style.minWidth = viewBoxWidth + 'px';
|
||||
$id("overview_window_view_box").style.minHeight = viewBoxHeight + 'px';
|
||||
$id("overview_window_view_box").style.top = viewBoxY + 'px';
|
||||
$id("overview_window_view_box").style.left = viewBoxX + 'px';
|
||||
};
|
||||
$id('overview_window_view_box').style.minWidth = viewBoxWidth + 'px'
|
||||
$id('overview_window_view_box').style.minHeight = viewBoxHeight + 'px'
|
||||
$id('overview_window_view_box').style.top = viewBoxY + 'px'
|
||||
$id('overview_window_view_box').style.left = viewBoxX + 'px'
|
||||
}
|
||||
$id('workarea').addEventListener('scroll', () => {
|
||||
if (!(overviewWindowGlobals.viewBoxDragging)) {
|
||||
updateViewBox();
|
||||
updateViewBox()
|
||||
}
|
||||
});
|
||||
$id('workarea').addEventListener('resize', updateViewBox);
|
||||
updateViewBox();
|
||||
})
|
||||
$id('workarea').addEventListener('resize', updateViewBox)
|
||||
updateViewBox()
|
||||
|
||||
// Compensate for changes in zoom and canvas size.
|
||||
const updateViewDimensions = function () {
|
||||
const viewWidth = parseFloat(getComputedStyle($id("svgroot"), null).width.replace("px", ""));
|
||||
const viewHeight = parseFloat(getComputedStyle($id("svgroot"), null).height.replace("px", ""));
|
||||
const viewWidth = parseFloat(getComputedStyle($id('svgroot'), null).width.replace('px', ''))
|
||||
const viewHeight = parseFloat(getComputedStyle($id('svgroot'), null).height.replace('px', ''))
|
||||
|
||||
const viewX = 640;
|
||||
const viewY = 480;
|
||||
const viewX = 640
|
||||
const viewY = 480
|
||||
|
||||
const svgWidthOld = parseFloat(getComputedStyle($id("overviewMiniView"), null).width.replace("px", ""));
|
||||
const svgHeightNew = viewHeight / viewWidth * svgWidthOld;
|
||||
$id('overviewMiniView').setAttribute('viewBox', viewX + ' ' + viewY + ' ' + viewWidth + ' ' + viewHeight);
|
||||
$id('overviewMiniView').setAttribute('height', svgHeightNew);
|
||||
updateViewBox();
|
||||
};
|
||||
updateViewDimensions();
|
||||
const svgWidthOld = parseFloat(getComputedStyle($id('overviewMiniView'), null).width.replace('px', ''))
|
||||
const svgHeightNew = viewHeight / viewWidth * svgWidthOld
|
||||
$id('overviewMiniView').setAttribute('viewBox', viewX + ' ' + viewY + ' ' + viewWidth + ' ' + viewHeight)
|
||||
$id('overviewMiniView').setAttribute('height', svgHeightNew)
|
||||
updateViewBox()
|
||||
}
|
||||
updateViewDimensions()
|
||||
|
||||
// Set up the overview window as a controller for the view port.
|
||||
overviewWindowGlobals.viewBoxDragging = false;
|
||||
overviewWindowGlobals.viewBoxDragging = false
|
||||
const updateViewPortFromViewBox = function () {
|
||||
const windowWidth = parseFloat(getComputedStyle($id("svgcanvas"), null).width.replace("px", ""));
|
||||
const windowHeight = parseFloat(getComputedStyle($id("svgcanvas"), null).height.replace("px", ""));
|
||||
const overviewWidth = parseFloat(getComputedStyle($id("overviewMiniView"), null).width.replace("px", ""));
|
||||
const overviewHeight = parseFloat(getComputedStyle($id("overviewMiniView"), null).height.replace("px", ""));
|
||||
const viewBoxX = parseFloat(getComputedStyle($id("overview_window_view_box"), null).getPropertyValue('left').replace("px", ""));
|
||||
const viewBoxY = parseFloat(getComputedStyle($id("overview_window_view_box"), null).getPropertyValue('top').replace("px", ""));
|
||||
const windowWidth = parseFloat(getComputedStyle($id('svgcanvas'), null).width.replace('px', ''))
|
||||
const windowHeight = parseFloat(getComputedStyle($id('svgcanvas'), null).height.replace('px', ''))
|
||||
const overviewWidth = parseFloat(getComputedStyle($id('overviewMiniView'), null).width.replace('px', ''))
|
||||
const overviewHeight = parseFloat(getComputedStyle($id('overviewMiniView'), null).height.replace('px', ''))
|
||||
const viewBoxX = parseFloat(getComputedStyle($id('overview_window_view_box'), null).getPropertyValue('left').replace('px', ''))
|
||||
const viewBoxY = parseFloat(getComputedStyle($id('overview_window_view_box'), null).getPropertyValue('top').replace('px', ''))
|
||||
|
||||
|
||||
const portX = viewBoxX / overviewWidth * windowWidth;
|
||||
const portY = viewBoxY / overviewHeight * windowHeight;
|
||||
$id('workarea').scrollLeft = portX;
|
||||
$id('workarea').scrollTop = portY;
|
||||
};
|
||||
const portX = viewBoxX / overviewWidth * windowWidth
|
||||
const portY = viewBoxY / overviewHeight * windowHeight
|
||||
$id('workarea').scrollLeft = portX
|
||||
$id('workarea').scrollTop = portY
|
||||
}
|
||||
const onStart = () => {
|
||||
overviewWindowGlobals.viewBoxDragging = true;
|
||||
updateViewPortFromViewBox();
|
||||
};
|
||||
overviewWindowGlobals.viewBoxDragging = true
|
||||
updateViewPortFromViewBox()
|
||||
}
|
||||
const onEnd = (el, parent, _x, _y) => {
|
||||
if((el.offsetLeft + el.offsetWidth) > parseFloat(getComputedStyle(parent, null).width.replace("px", ""))){
|
||||
el.style.left = (parseFloat(getComputedStyle(parent, null).width.replace("px", "")) - el.offsetWidth) + 'px';
|
||||
} else if(el.offsetLeft < 0){
|
||||
el.style.left = "0px";
|
||||
if ((el.offsetLeft + el.offsetWidth) > parseFloat(getComputedStyle(parent, null).width.replace('px', ''))) {
|
||||
el.style.left = (parseFloat(getComputedStyle(parent, null).width.replace('px', '')) - el.offsetWidth) + 'px'
|
||||
} else if (el.offsetLeft < 0) {
|
||||
el.style.left = '0px'
|
||||
}
|
||||
if((el.offsetTop + el.offsetHeight) > parseFloat(getComputedStyle(parent, null).height.replace("px", ""))){
|
||||
el.style.top = (parseFloat(getComputedStyle(parent, null).height.replace("px", "")) - el.offsetHeight) + 'px';
|
||||
} else if(el.offsetTop < 0){
|
||||
el.style.top = "0px";
|
||||
if ((el.offsetTop + el.offsetHeight) > parseFloat(getComputedStyle(parent, null).height.replace('px', ''))) {
|
||||
el.style.top = (parseFloat(getComputedStyle(parent, null).height.replace('px', '')) - el.offsetHeight) + 'px'
|
||||
} else if (el.offsetTop < 0) {
|
||||
el.style.top = '0px'
|
||||
}
|
||||
overviewWindowGlobals.viewBoxDragging = false;
|
||||
updateViewPortFromViewBox();
|
||||
};
|
||||
overviewWindowGlobals.viewBoxDragging = false
|
||||
updateViewPortFromViewBox()
|
||||
}
|
||||
const onDrag = function () {
|
||||
updateViewPortFromViewBox();
|
||||
};
|
||||
const dragElem = document.querySelector("#overview_window_view_box");
|
||||
const parentElem = document.querySelector("#overviewMiniView");
|
||||
dragmove(dragElem, dragElem, parentElem, onStart, onEnd, onDrag);
|
||||
updateViewPortFromViewBox()
|
||||
}
|
||||
const dragElem = document.querySelector('#overview_window_view_box')
|
||||
const parentElem = document.querySelector('#overviewMiniView')
|
||||
dragmove(dragElem, dragElem, parentElem, onStart, onEnd, onDrag)
|
||||
|
||||
$id("overviewMiniView").addEventListener("click", (evt) => {
|
||||
$id('overviewMiniView').addEventListener('click', (evt) => {
|
||||
// Firefox doesn't support evt.offsetX and evt.offsetY.
|
||||
const mouseX = (evt.offsetX || evt.originalEvent.layerX);
|
||||
const mouseY = (evt.offsetY || evt.originalEvent.layerY);
|
||||
const overviewWidth = parseFloat(getComputedStyle($id("overviewMiniView"), null).width.replace("px", ""));
|
||||
const overviewHeight = parseFloat(getComputedStyle($id("overviewMiniView"), null).height.replace("px", ""));
|
||||
const viewBoxWidth = parseFloat(getComputedStyle($id("overview_window_view_box"), null).getPropertyValue('min-width').replace("px", ""));
|
||||
const viewBoxHeight = parseFloat(getComputedStyle($id("overview_window_view_box"), null).getPropertyValue('min-height').replace("px", ""));
|
||||
const mouseX = (evt.offsetX || evt.originalEvent.layerX)
|
||||
const mouseY = (evt.offsetY || evt.originalEvent.layerY)
|
||||
const overviewWidth = parseFloat(getComputedStyle($id('overviewMiniView'), null).width.replace('px', ''))
|
||||
const overviewHeight = parseFloat(getComputedStyle($id('overviewMiniView'), null).height.replace('px', ''))
|
||||
const viewBoxWidth = parseFloat(getComputedStyle($id('overview_window_view_box'), null).getPropertyValue('min-width').replace('px', ''))
|
||||
const viewBoxHeight = parseFloat(getComputedStyle($id('overview_window_view_box'), null).getPropertyValue('min-height').replace('px', ''))
|
||||
|
||||
let viewBoxX = mouseX - 0.5 * viewBoxWidth;
|
||||
let viewBoxY = mouseY - 0.5 * viewBoxHeight;
|
||||
let viewBoxX = mouseX - 0.5 * viewBoxWidth
|
||||
let viewBoxY = mouseY - 0.5 * viewBoxHeight
|
||||
// deal with constraints
|
||||
if (viewBoxX < 0) {
|
||||
viewBoxX = 0;
|
||||
viewBoxX = 0
|
||||
}
|
||||
if (viewBoxY < 0) {
|
||||
viewBoxY = 0;
|
||||
viewBoxY = 0
|
||||
}
|
||||
if (viewBoxX + viewBoxWidth > overviewWidth) {
|
||||
viewBoxX = overviewWidth - viewBoxWidth;
|
||||
viewBoxX = overviewWidth - viewBoxWidth
|
||||
}
|
||||
if (viewBoxY + viewBoxHeight > overviewHeight) {
|
||||
viewBoxY = overviewHeight - viewBoxHeight;
|
||||
viewBoxY = overviewHeight - viewBoxHeight
|
||||
}
|
||||
$id("overview_window_view_box").style.top = viewBoxY + 'px';
|
||||
$id("overview_window_view_box").style.left = viewBoxX + 'px';
|
||||
updateViewPortFromViewBox();
|
||||
});
|
||||
$id('overview_window_view_box').style.top = viewBoxY + 'px'
|
||||
$id('overview_window_view_box').style.left = viewBoxX + 'px'
|
||||
updateViewPortFromViewBox()
|
||||
})
|
||||
|
||||
return {
|
||||
name: 'overview window',
|
||||
canvasUpdated: updateViewDimensions,
|
||||
workareaResized: updateViewBox
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,71 +10,69 @@
|
||||
This is a very basic SVG-Edit extension to let tablet/mobile devices pan without problem
|
||||
*/
|
||||
|
||||
const name = "panning";
|
||||
const name = 'panning'
|
||||
|
||||
const loadExtensionTranslation = async function (svgEditor) {
|
||||
let translationModule;
|
||||
const lang = svgEditor.configObj.pref('lang');
|
||||
let translationModule
|
||||
const lang = svgEditor.configObj.pref('lang')
|
||||
try {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
translationModule = await import(`./locale/${lang}.js`);
|
||||
translationModule = await import(`./locale/${lang}.js`)
|
||||
} catch (_error) {
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
|
||||
translationModule = await import(`./locale/en.js`);
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`)
|
||||
translationModule = await import('./locale/en.js')
|
||||
}
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default);
|
||||
};
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default)
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
async init() {
|
||||
const svgEditor = this;
|
||||
await loadExtensionTranslation(svgEditor);
|
||||
async init () {
|
||||
const svgEditor = this
|
||||
await loadExtensionTranslation(svgEditor)
|
||||
const {
|
||||
svgCanvas
|
||||
} = svgEditor;
|
||||
} = svgEditor
|
||||
const {
|
||||
$id
|
||||
} = svgCanvas;
|
||||
} = svgCanvas
|
||||
const insertAfter = (referenceNode, newNode) => {
|
||||
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
|
||||
};
|
||||
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling)
|
||||
}
|
||||
return {
|
||||
name: svgEditor.i18next.t(`${name}:name`),
|
||||
callback() {
|
||||
const btitle = `${name}:buttons.0.title`;
|
||||
callback () {
|
||||
const btitle = `${name}:buttons.0.title`
|
||||
// Add the button and its handler(s)
|
||||
const buttonTemplate = document.createElement("template");
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
const buttonTemplate = document.createElement('template')
|
||||
buttonTemplate.innerHTML = `
|
||||
<se-button id="ext-panning" title="${btitle}" src="panning.svg"></se-button>
|
||||
`;
|
||||
insertAfter($id('tool_zoom'), buttonTemplate.content.cloneNode(true));
|
||||
$id('ext-panning').addEventListener("click", () => {
|
||||
if (this.leftPanel.updateLeftPanel("ext-panning")) {
|
||||
svgCanvas.setMode('ext-panning');
|
||||
`
|
||||
insertAfter($id('tool_zoom'), buttonTemplate.content.cloneNode(true))
|
||||
$id('ext-panning').addEventListener('click', () => {
|
||||
if (this.leftPanel.updateLeftPanel('ext-panning')) {
|
||||
svgCanvas.setMode('ext-panning')
|
||||
}
|
||||
});
|
||||
})
|
||||
},
|
||||
mouseDown() {
|
||||
mouseDown () {
|
||||
if (svgCanvas.getMode() === 'ext-panning') {
|
||||
svgEditor.setPanning(true);
|
||||
svgEditor.setPanning(true)
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return undefined
|
||||
},
|
||||
mouseUp() {
|
||||
mouseUp () {
|
||||
if (svgCanvas.getMode() === 'ext-panning') {
|
||||
svgEditor.setPanning(false);
|
||||
svgEditor.setPanning(false)
|
||||
return {
|
||||
keep: false,
|
||||
element: null
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export default {
|
||||
title: 'Panning'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ export default {
|
||||
title: '移动'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,33 +8,32 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const name = "polystar";
|
||||
const name = 'polystar'
|
||||
|
||||
const loadExtensionTranslation = async function (svgEditor) {
|
||||
let translationModule;
|
||||
const lang = svgEditor.configObj.pref('lang');
|
||||
let translationModule
|
||||
const lang = svgEditor.configObj.pref('lang')
|
||||
try {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
translationModule = await import(`./locale/${lang}.js`);
|
||||
translationModule = await import(`./locale/${lang}.js`)
|
||||
} catch (_error) {
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
|
||||
translationModule = await import(`./locale/en.js`);
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`)
|
||||
translationModule = await import('./locale/en.js')
|
||||
}
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default);
|
||||
};
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default)
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
async init() {
|
||||
const svgEditor = this;
|
||||
const { svgCanvas } = svgEditor;
|
||||
const { ChangeElementCommand } = svgCanvas.history;
|
||||
const addToHistory = function (cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd); };
|
||||
const { $id } = svgCanvas;
|
||||
let selElems;
|
||||
let started;
|
||||
let newFO;
|
||||
await loadExtensionTranslation(svgEditor);
|
||||
async init () {
|
||||
const svgEditor = this
|
||||
const { svgCanvas } = svgEditor
|
||||
const { ChangeElementCommand } = svgCanvas.history
|
||||
const addToHistory = function (cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd) }
|
||||
const { $id } = svgCanvas
|
||||
let selElems
|
||||
let started
|
||||
let newFO
|
||||
await loadExtensionTranslation(svgEditor)
|
||||
|
||||
/**
|
||||
* @param {boolean} on true=display
|
||||
@@ -43,11 +42,11 @@ export default {
|
||||
*/
|
||||
const showPanel = (on, tool) => {
|
||||
if (on) {
|
||||
$id(`${tool}_panel`).style.removeProperty('display');
|
||||
$id(`${tool}_panel`).style.removeProperty('display')
|
||||
} else {
|
||||
$id(`${tool}_panel`).style.display = 'none';
|
||||
$id(`${tool}_panel`).style.display = 'none'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -56,66 +55,65 @@ export default {
|
||||
* @returns {void}
|
||||
*/
|
||||
const setAttr = (attr, val) => {
|
||||
svgCanvas.changeSelectedAttribute(attr, val);
|
||||
svgCanvas.call("changed", selElems);
|
||||
};
|
||||
svgCanvas.changeSelectedAttribute(attr, val)
|
||||
svgCanvas.call('changed', selElems)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Float} n angle
|
||||
* @return {Float} cotangeante
|
||||
*/
|
||||
const cot = (n) => 1 / Math.tan(n);
|
||||
const cot = (n) => 1 / Math.tan(n)
|
||||
|
||||
/**
|
||||
* @param {Float} n angle
|
||||
* @returns {Float} sec
|
||||
*/
|
||||
const sec = (n) => 1 / Math.cos(n);
|
||||
const sec = (n) => 1 / Math.cos(n)
|
||||
|
||||
return {
|
||||
name: svgEditor.i18next.t(`${name}:name`),
|
||||
// The callback should be used to load the DOM with the appropriate UI items
|
||||
callback() {
|
||||
callback () {
|
||||
// Add the button and its handler(s)
|
||||
// Note: the star extension needs to be loaded before the polygon extension
|
||||
const fbtitle = `${name}:title`;
|
||||
const title_star = `${name}:buttons.0.title`;
|
||||
const title_polygon = `${name}:buttons.1.title`;
|
||||
const fbtitle = `${name}:title`
|
||||
const titleStar = `${name}:buttons.0.title`
|
||||
const titlePolygon = `${name}:buttons.1.title`
|
||||
const buttonTemplate = `
|
||||
<se-flyingbutton id="tools_polygon" title="${fbtitle}">
|
||||
<se-button id="tool_star" title="${title_star}" src="star.svg">
|
||||
<se-button id="tool_star" title="${titleStar}" src="star.svg">
|
||||
</se-button>
|
||||
<se-button id="tool_polygon" title="${title_polygon}" src="polygon.svg">
|
||||
<se-button id="tool_polygon" title="${titlePolygon}" src="polygon.svg">
|
||||
</se-button>
|
||||
</se-flyingbutton>
|
||||
`;
|
||||
svgCanvas.insertChildAtIndex($id('tools_left'), buttonTemplate, 10);
|
||||
`
|
||||
svgCanvas.insertChildAtIndex($id('tools_left'), buttonTemplate, 10)
|
||||
// handler
|
||||
$id("tool_star").addEventListener("click", () => {
|
||||
if (this.leftPanel.updateLeftPanel("tool_star")) {
|
||||
svgCanvas.setMode("star");
|
||||
showPanel(true, "star");
|
||||
showPanel(false, "polygon");
|
||||
$id('tool_star').addEventListener('click', () => {
|
||||
if (this.leftPanel.updateLeftPanel('tool_star')) {
|
||||
svgCanvas.setMode('star')
|
||||
showPanel(true, 'star')
|
||||
showPanel(false, 'polygon')
|
||||
}
|
||||
});
|
||||
$id("tool_polygon").addEventListener("click", () => {
|
||||
if (this.leftPanel.updateLeftPanel("tool_polygon")) {
|
||||
svgCanvas.setMode("polygon");
|
||||
showPanel(true, "polygon");
|
||||
showPanel(false, "star");
|
||||
})
|
||||
$id('tool_polygon').addEventListener('click', () => {
|
||||
if (this.leftPanel.updateLeftPanel('tool_polygon')) {
|
||||
svgCanvas.setMode('polygon')
|
||||
showPanel(true, 'polygon')
|
||||
showPanel(false, 'star')
|
||||
}
|
||||
});
|
||||
const label0 = `${name}:contextTools.0.label`;
|
||||
const title0 = `${name}:contextTools.0.title`;
|
||||
const label1 = `${name}:contextTools.1.label`;
|
||||
const title1 = `${name}:contextTools.1.title`;
|
||||
const label2 = `${name}:contextTools.2.label`;
|
||||
const title2 = `${name}:contextTools.2.title`;
|
||||
const label3 = `${name}:contextTools.3.label`;
|
||||
const title3 = `${name}:contextTools.3.title`;
|
||||
})
|
||||
const label0 = `${name}:contextTools.0.label`
|
||||
const title0 = `${name}:contextTools.0.title`
|
||||
const label1 = `${name}:contextTools.1.label`
|
||||
const title1 = `${name}:contextTools.1.title`
|
||||
const label2 = `${name}:contextTools.2.label`
|
||||
const title2 = `${name}:contextTools.2.title`
|
||||
const label3 = `${name}:contextTools.3.label`
|
||||
const title3 = `${name}:contextTools.3.title`
|
||||
// Add the context panel and its handler(s)
|
||||
const panelTemplate = document.createElement("template");
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
const panelTemplate = document.createElement('template')
|
||||
panelTemplate.innerHTML = `
|
||||
<div id="star_panel">
|
||||
<se-spin-input id="starNumPoints" label="${label0}" min=1 step=1 value=5 title="${title0}">
|
||||
@@ -129,324 +127,324 @@ export default {
|
||||
<se-spin-input size="3" id="polySides" min=1 step=1 value=5 label="${label3}" title="${title3}">
|
||||
</se-spin-input>
|
||||
</div>
|
||||
`;
|
||||
//add handlers for the panel
|
||||
$id("tools_top").appendChild(panelTemplate.content.cloneNode(true));
|
||||
`
|
||||
// add handlers for the panel
|
||||
$id('tools_top').appendChild(panelTemplate.content.cloneNode(true))
|
||||
// don't display the panels on start
|
||||
showPanel(false, "star");
|
||||
showPanel(false, "polygon");
|
||||
$id("starNumPoints").addEventListener("change", (event) => {
|
||||
setAttr("point", event.target.value);
|
||||
const orient = 'point';
|
||||
const point = event.target.value;
|
||||
let i = selElems.length;
|
||||
showPanel(false, 'star')
|
||||
showPanel(false, 'polygon')
|
||||
$id('starNumPoints').addEventListener('change', (event) => {
|
||||
setAttr('point', event.target.value)
|
||||
const orient = 'point'
|
||||
const point = event.target.value
|
||||
let i = selElems.length
|
||||
while (i--) {
|
||||
const elem = selElems[i];
|
||||
const elem = selElems[i]
|
||||
if (elem.hasAttribute('r')) {
|
||||
const oldPoint = elem.getAttribute('point');
|
||||
const oldPoints = elem.getAttribute('points');
|
||||
const radialshift = elem.getAttribute('radialshift');
|
||||
let xpos = 0;
|
||||
let ypos = 0;
|
||||
const oldPoint = elem.getAttribute('point')
|
||||
const oldPoints = elem.getAttribute('points')
|
||||
const radialshift = elem.getAttribute('radialshift')
|
||||
let xpos = 0
|
||||
let ypos = 0
|
||||
if (elem.points) {
|
||||
const list = elem.points;
|
||||
const len = list.numberOfItems;
|
||||
const list = elem.points
|
||||
const len = list.numberOfItems
|
||||
for (let i = 0; i < len; ++i) {
|
||||
const pt = list.getItem(i);
|
||||
xpos += parseFloat(pt.x);
|
||||
ypos += parseFloat(pt.y);
|
||||
const pt = list.getItem(i)
|
||||
xpos += parseFloat(pt.x)
|
||||
ypos += parseFloat(pt.y)
|
||||
}
|
||||
const cx = xpos / len;
|
||||
const cy = ypos / len;
|
||||
const circumradius = Number(elem.getAttribute('r'));
|
||||
const inradius = circumradius / elem.getAttribute('starRadiusMultiplier');
|
||||
const cx = xpos / len
|
||||
const cy = ypos / len
|
||||
const circumradius = Number(elem.getAttribute('r'))
|
||||
const inradius = circumradius / elem.getAttribute('starRadiusMultiplier')
|
||||
|
||||
let polyPoints = "";
|
||||
let polyPoints = ''
|
||||
for (let s = 0; point >= s; s++) {
|
||||
let angle = 2.0 * Math.PI * (s / point);
|
||||
if (orient === "point") {
|
||||
angle -= Math.PI / 2;
|
||||
} else if (orient === "edge") {
|
||||
angle = angle + Math.PI / point - Math.PI / 2;
|
||||
let angle = 2.0 * Math.PI * (s / point)
|
||||
if (orient === 'point') {
|
||||
angle -= Math.PI / 2
|
||||
} else if (orient === 'edge') {
|
||||
angle = angle + Math.PI / point - Math.PI / 2
|
||||
}
|
||||
|
||||
let x = circumradius * Math.cos(angle) + cx;
|
||||
let y = circumradius * Math.sin(angle) + cy;
|
||||
let x = circumradius * Math.cos(angle) + cx
|
||||
let y = circumradius * Math.sin(angle) + cy
|
||||
|
||||
polyPoints += x + "," + y + " ";
|
||||
polyPoints += x + ',' + y + ' '
|
||||
|
||||
if (!isNaN(inradius)) {
|
||||
angle = 2.0 * Math.PI * (s / point) + Math.PI / point;
|
||||
if (orient === "point") {
|
||||
angle -= Math.PI / 2;
|
||||
} else if (orient === "edge") {
|
||||
angle = angle + Math.PI / point - Math.PI / 2;
|
||||
angle = 2.0 * Math.PI * (s / point) + Math.PI / point
|
||||
if (orient === 'point') {
|
||||
angle -= Math.PI / 2
|
||||
} else if (orient === 'edge') {
|
||||
angle = angle + Math.PI / point - Math.PI / 2
|
||||
}
|
||||
angle += radialshift;
|
||||
angle += radialshift
|
||||
|
||||
x = inradius * Math.cos(angle) + cx;
|
||||
y = inradius * Math.sin(angle) + cy;
|
||||
x = inradius * Math.cos(angle) + cx
|
||||
y = inradius * Math.sin(angle) + cy
|
||||
|
||||
polyPoints += x + "," + y + " ";
|
||||
polyPoints += x + ',' + y + ' '
|
||||
}
|
||||
}
|
||||
elem.setAttribute("points", polyPoints);
|
||||
addToHistory(new ChangeElementCommand(elem, { 'point': oldPoint, 'points': oldPoints }));
|
||||
elem.setAttribute('points', polyPoints)
|
||||
addToHistory(new ChangeElementCommand(elem, { point: oldPoint, points: oldPoints }))
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
$id("RadiusMultiplier").addEventListener("change", (event) => {
|
||||
setAttr("starRadiusMultiplier", event.target.value);
|
||||
});
|
||||
$id("radialShift").addEventListener("change", (event) => {
|
||||
setAttr("radialshift", event.target.value);
|
||||
});
|
||||
$id("polySides").addEventListener("change", (event) => {
|
||||
setAttr("sides", event.target.value);
|
||||
const sides = event.target.value;
|
||||
let i = selElems.length;
|
||||
})
|
||||
$id('RadiusMultiplier').addEventListener('change', (event) => {
|
||||
setAttr('starRadiusMultiplier', event.target.value)
|
||||
})
|
||||
$id('radialShift').addEventListener('change', (event) => {
|
||||
setAttr('radialshift', event.target.value)
|
||||
})
|
||||
$id('polySides').addEventListener('change', (event) => {
|
||||
setAttr('sides', event.target.value)
|
||||
const sides = event.target.value
|
||||
let i = selElems.length
|
||||
while (i--) {
|
||||
const elem = selElems[i];
|
||||
const elem = selElems[i]
|
||||
if (elem.hasAttribute('edge')) {
|
||||
const oldSides = elem.getAttribute('sides');
|
||||
const oldPoints = elem.getAttribute('points');
|
||||
let xpos = 0;
|
||||
let ypos = 0;
|
||||
const oldSides = elem.getAttribute('sides')
|
||||
const oldPoints = elem.getAttribute('points')
|
||||
let xpos = 0
|
||||
let ypos = 0
|
||||
if (elem.points) {
|
||||
const list = elem.points;
|
||||
const len = list.numberOfItems;
|
||||
const list = elem.points
|
||||
const len = list.numberOfItems
|
||||
for (let i = 0; i < len; ++i) {
|
||||
const pt = list.getItem(i);
|
||||
xpos += parseFloat(pt.x);
|
||||
ypos += parseFloat(pt.y);
|
||||
const pt = list.getItem(i)
|
||||
xpos += parseFloat(pt.x)
|
||||
ypos += parseFloat(pt.y)
|
||||
}
|
||||
const cx = xpos / len;
|
||||
const cy = ypos / len;
|
||||
const edg = elem.getAttribute('edge');
|
||||
const inradius = (edg / 2) * cot(Math.PI / sides);
|
||||
const circumradius = inradius * sec(Math.PI / sides);
|
||||
let points = "";
|
||||
const cx = xpos / len
|
||||
const cy = ypos / len
|
||||
const edg = elem.getAttribute('edge')
|
||||
const inradius = (edg / 2) * cot(Math.PI / sides)
|
||||
const circumradius = inradius * sec(Math.PI / sides)
|
||||
let points = ''
|
||||
for (let s = 0; sides >= s; s++) {
|
||||
const angle = (2.0 * Math.PI * s) / sides;
|
||||
const x = circumradius * Math.cos(angle) + cx;
|
||||
const y = circumradius * Math.sin(angle) + cy;
|
||||
points += x + "," + y + " ";
|
||||
const angle = (2.0 * Math.PI * s) / sides
|
||||
const x = circumradius * Math.cos(angle) + cx
|
||||
const y = circumradius * Math.sin(angle) + cy
|
||||
points += x + ',' + y + ' '
|
||||
}
|
||||
elem.setAttribute("points", points);
|
||||
addToHistory(new ChangeElementCommand(elem, { 'sides': oldSides, 'points': oldPoints }));
|
||||
elem.setAttribute('points', points)
|
||||
addToHistory(new ChangeElementCommand(elem, { sides: oldSides, points: oldPoints }))
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
},
|
||||
mouseDown(opts) {
|
||||
if (svgCanvas.getMode() === "star") {
|
||||
const rgb = svgCanvas.getColor("fill");
|
||||
const sRgb = svgCanvas.getColor("stroke");
|
||||
const sWidth = svgCanvas.getStrokeWidth();
|
||||
started = true;
|
||||
mouseDown (opts) {
|
||||
if (svgCanvas.getMode() === 'star') {
|
||||
const rgb = svgCanvas.getColor('fill')
|
||||
const sRgb = svgCanvas.getColor('stroke')
|
||||
const sWidth = svgCanvas.getStrokeWidth()
|
||||
started = true
|
||||
newFO = svgCanvas.addSVGElemensFromJson({
|
||||
element: "polygon",
|
||||
element: 'polygon',
|
||||
attr: {
|
||||
cx: opts.start_x,
|
||||
cy: opts.start_y,
|
||||
id: svgCanvas.getNextId(),
|
||||
shape: "star",
|
||||
point: $id("starNumPoints").value,
|
||||
shape: 'star',
|
||||
point: $id('starNumPoints').value,
|
||||
r: 0,
|
||||
radialshift: $id("radialShift").value,
|
||||
radialshift: $id('radialShift').value,
|
||||
r2: 0,
|
||||
orient: "point",
|
||||
orient: 'point',
|
||||
fill: rgb,
|
||||
strokecolor: sRgb,
|
||||
strokeWidth: sWidth
|
||||
}
|
||||
});
|
||||
})
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
}
|
||||
if (svgCanvas.getMode() === "polygon") {
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
// const e = opts.event;
|
||||
const rgb = svgCanvas.getColor("fill");
|
||||
const rgb = svgCanvas.getColor('fill')
|
||||
// const ccRgbEl = rgb.substring(1, rgb.length);
|
||||
const sRgb = svgCanvas.getColor("stroke");
|
||||
const sRgb = svgCanvas.getColor('stroke')
|
||||
// ccSRgbEl = sRgb.substring(1, rgb.length);
|
||||
const sWidth = svgCanvas.getStrokeWidth();
|
||||
started = true;
|
||||
const sWidth = svgCanvas.getStrokeWidth()
|
||||
started = true
|
||||
newFO = svgCanvas.addSVGElemensFromJson({
|
||||
element: "polygon",
|
||||
element: 'polygon',
|
||||
attr: {
|
||||
cx: opts.start_x,
|
||||
cy: opts.start_y,
|
||||
id: svgCanvas.getNextId(),
|
||||
shape: "regularPoly",
|
||||
sides: $id("polySides").value,
|
||||
orient: "x",
|
||||
shape: 'regularPoly',
|
||||
sides: $id('polySides').value,
|
||||
orient: 'x',
|
||||
edge: 0,
|
||||
fill: rgb,
|
||||
strokecolor: sRgb,
|
||||
strokeWidth: sWidth
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return undefined
|
||||
},
|
||||
mouseMove(opts) {
|
||||
mouseMove (opts) {
|
||||
if (!started) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
if (svgCanvas.getMode() === "star") {
|
||||
const cx = Number(newFO.getAttribute("cx"));
|
||||
const cy = Number(newFO.getAttribute("cy"));
|
||||
const point = Number(newFO.getAttribute("point"));
|
||||
const orient = newFO.getAttribute("orient");
|
||||
const fill = newFO.getAttribute("fill");
|
||||
const strokecolor = newFO.getAttribute("strokecolor");
|
||||
const strokeWidth = Number(newFO.getAttribute("strokeWidth"));
|
||||
const radialshift = Number(newFO.getAttribute("radialshift"));
|
||||
if (svgCanvas.getMode() === 'star') {
|
||||
const cx = Number(newFO.getAttribute('cx'))
|
||||
const cy = Number(newFO.getAttribute('cy'))
|
||||
const point = Number(newFO.getAttribute('point'))
|
||||
const orient = newFO.getAttribute('orient')
|
||||
const fill = newFO.getAttribute('fill')
|
||||
const strokecolor = newFO.getAttribute('strokecolor')
|
||||
const strokeWidth = Number(newFO.getAttribute('strokeWidth'))
|
||||
const radialshift = Number(newFO.getAttribute('radialshift'))
|
||||
|
||||
let x = opts.mouse_x;
|
||||
let y = opts.mouse_y;
|
||||
let x = opts.mouse_x
|
||||
let y = opts.mouse_y
|
||||
|
||||
const circumradius =
|
||||
Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) / 1.5;
|
||||
const RadiusMultiplier = document.getElementById("RadiusMultiplier").value;
|
||||
Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) / 1.5
|
||||
const RadiusMultiplier = document.getElementById('RadiusMultiplier').value
|
||||
const inradius =
|
||||
circumradius / RadiusMultiplier;
|
||||
newFO.setAttribute("r", circumradius);
|
||||
newFO.setAttribute("r2", inradius);
|
||||
newFO.setAttribute('starRadiusMultiplier', RadiusMultiplier);
|
||||
circumradius / RadiusMultiplier
|
||||
newFO.setAttribute('r', circumradius)
|
||||
newFO.setAttribute('r2', inradius)
|
||||
newFO.setAttribute('starRadiusMultiplier', RadiusMultiplier)
|
||||
|
||||
let polyPoints = "";
|
||||
let polyPoints = ''
|
||||
for (let s = 0; point >= s; s++) {
|
||||
let angle = 2.0 * Math.PI * (s / point);
|
||||
if (orient === "point") {
|
||||
angle -= Math.PI / 2;
|
||||
} else if (orient === "edge") {
|
||||
angle = angle + Math.PI / point - Math.PI / 2;
|
||||
let angle = 2.0 * Math.PI * (s / point)
|
||||
if (orient === 'point') {
|
||||
angle -= Math.PI / 2
|
||||
} else if (orient === 'edge') {
|
||||
angle = angle + Math.PI / point - Math.PI / 2
|
||||
}
|
||||
|
||||
x = circumradius * Math.cos(angle) + cx;
|
||||
y = circumradius * Math.sin(angle) + cy;
|
||||
x = circumradius * Math.cos(angle) + cx
|
||||
y = circumradius * Math.sin(angle) + cy
|
||||
|
||||
polyPoints += x + "," + y + " ";
|
||||
polyPoints += x + ',' + y + ' '
|
||||
|
||||
if (!isNaN(inradius)) {
|
||||
angle = 2.0 * Math.PI * (s / point) + Math.PI / point;
|
||||
if (orient === "point") {
|
||||
angle -= Math.PI / 2;
|
||||
} else if (orient === "edge") {
|
||||
angle = angle + Math.PI / point - Math.PI / 2;
|
||||
angle = 2.0 * Math.PI * (s / point) + Math.PI / point
|
||||
if (orient === 'point') {
|
||||
angle -= Math.PI / 2
|
||||
} else if (orient === 'edge') {
|
||||
angle = angle + Math.PI / point - Math.PI / 2
|
||||
}
|
||||
angle += radialshift;
|
||||
angle += radialshift
|
||||
|
||||
x = inradius * Math.cos(angle) + cx;
|
||||
y = inradius * Math.sin(angle) + cy;
|
||||
x = inradius * Math.cos(angle) + cx
|
||||
y = inradius * Math.sin(angle) + cy
|
||||
|
||||
polyPoints += x + "," + y + " ";
|
||||
polyPoints += x + ',' + y + ' '
|
||||
}
|
||||
}
|
||||
newFO.setAttribute("points", polyPoints);
|
||||
newFO.setAttribute("fill", fill);
|
||||
newFO.setAttribute("stroke", strokecolor);
|
||||
newFO.setAttribute("stroke-width", strokeWidth);
|
||||
/* const shape = */ newFO.getAttribute("shape");
|
||||
newFO.setAttribute('points', polyPoints)
|
||||
newFO.setAttribute('fill', fill)
|
||||
newFO.setAttribute('stroke', strokecolor)
|
||||
newFO.setAttribute('stroke-width', strokeWidth)
|
||||
/* const shape = */ newFO.getAttribute('shape')
|
||||
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
}
|
||||
if (svgCanvas.getMode() === "polygon") {
|
||||
const cx = Number(newFO.getAttribute("cx"));
|
||||
const cy = Number(newFO.getAttribute("cy"));
|
||||
const sides = Number(newFO.getAttribute("sides"));
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
const cx = Number(newFO.getAttribute('cx'))
|
||||
const cy = Number(newFO.getAttribute('cy'))
|
||||
const sides = Number(newFO.getAttribute('sides'))
|
||||
// const orient = newFO.getAttribute('orient');
|
||||
const fill = newFO.getAttribute("fill");
|
||||
const strokecolor = newFO.getAttribute("strokecolor");
|
||||
const strokeWidth = Number(newFO.getAttribute("strokeWidth"));
|
||||
const fill = newFO.getAttribute('fill')
|
||||
const strokecolor = newFO.getAttribute('strokecolor')
|
||||
const strokeWidth = Number(newFO.getAttribute('strokeWidth'))
|
||||
|
||||
let x = opts.mouse_x;
|
||||
let y = opts.mouse_y;
|
||||
let x = opts.mouse_x
|
||||
let y = opts.mouse_y
|
||||
|
||||
const edg =
|
||||
Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) / 1.5;
|
||||
newFO.setAttribute("edge", edg);
|
||||
Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) / 1.5
|
||||
newFO.setAttribute('edge', edg)
|
||||
|
||||
const inradius = (edg / 2) * cot(Math.PI / sides);
|
||||
const circumradius = inradius * sec(Math.PI / sides);
|
||||
let points = "";
|
||||
const inradius = (edg / 2) * cot(Math.PI / sides)
|
||||
const circumradius = inradius * sec(Math.PI / sides)
|
||||
let points = ''
|
||||
for (let s = 0; sides >= s; s++) {
|
||||
const angle = (2.0 * Math.PI * s) / sides;
|
||||
x = circumradius * Math.cos(angle) + cx;
|
||||
y = circumradius * Math.sin(angle) + cy;
|
||||
const angle = (2.0 * Math.PI * s) / sides
|
||||
x = circumradius * Math.cos(angle) + cx
|
||||
y = circumradius * Math.sin(angle) + cy
|
||||
|
||||
points += x + "," + y + " ";
|
||||
points += x + ',' + y + ' '
|
||||
}
|
||||
|
||||
// const poly = newFO.createElementNS(NS.SVG, 'polygon');
|
||||
newFO.setAttribute("points", points);
|
||||
newFO.setAttribute("fill", fill);
|
||||
newFO.setAttribute("stroke", strokecolor);
|
||||
newFO.setAttribute("stroke-width", strokeWidth);
|
||||
newFO.setAttribute('points', points)
|
||||
newFO.setAttribute('fill', fill)
|
||||
newFO.setAttribute('stroke', strokecolor)
|
||||
newFO.setAttribute('stroke-width', strokeWidth)
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return undefined
|
||||
},
|
||||
mouseUp() {
|
||||
if (svgCanvas.getMode() === "star") {
|
||||
const r = newFO.getAttribute("r");
|
||||
mouseUp () {
|
||||
if (svgCanvas.getMode() === 'star') {
|
||||
const r = newFO.getAttribute('r')
|
||||
return {
|
||||
keep: r !== "0",
|
||||
keep: r !== '0',
|
||||
element: newFO
|
||||
};
|
||||
}
|
||||
}
|
||||
if (svgCanvas.getMode() === "polygon") {
|
||||
const edge = newFO.getAttribute("edge");
|
||||
const keep = edge !== "0";
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
const edge = newFO.getAttribute('edge')
|
||||
const keep = edge !== '0'
|
||||
// svgCanvas.addToSelection([newFO], true);
|
||||
return {
|
||||
keep,
|
||||
element: newFO
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return undefined
|
||||
},
|
||||
selectedChanged(opts) {
|
||||
selectedChanged (opts) {
|
||||
// Use this to update the current selected elements
|
||||
selElems = opts.elems;
|
||||
selElems = opts.elems
|
||||
|
||||
let i = selElems.length;
|
||||
let i = selElems.length
|
||||
while (i--) {
|
||||
const elem = selElems[i];
|
||||
if (elem && elem.getAttribute("shape") === "star") {
|
||||
const elem = selElems[i]
|
||||
if (elem && elem.getAttribute('shape') === 'star') {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
$id("starNumPoints").value = elem.getAttribute("point");
|
||||
$id("radialShift").value = elem.getAttribute("radialshift");
|
||||
showPanel(true, "star");
|
||||
$id('starNumPoints').value = elem.getAttribute('point')
|
||||
$id('radialShift').value = elem.getAttribute('radialshift')
|
||||
showPanel(true, 'star')
|
||||
} else {
|
||||
showPanel(false, "star");
|
||||
showPanel(false, 'star')
|
||||
}
|
||||
} else if (elem && elem.getAttribute("shape") === "regularPoly") {
|
||||
} else if (elem && elem.getAttribute('shape') === 'regularPoly') {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
$id("polySides").value = elem.getAttribute("sides");
|
||||
showPanel(true, "polygon");
|
||||
$id('polySides').value = elem.getAttribute('sides')
|
||||
showPanel(true, 'polygon')
|
||||
} else {
|
||||
showPanel(false, "polygon");
|
||||
showPanel(false, 'polygon')
|
||||
}
|
||||
} else {
|
||||
showPanel(false, "star");
|
||||
showPanel(false, "polygon");
|
||||
showPanel(false, 'star')
|
||||
showPanel(false, 'polygon')
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,4 +27,4 @@ export default {
|
||||
label: 'sides'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,4 +27,4 @@ export default {
|
||||
label: 'côtés'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,4 +27,4 @@ export default {
|
||||
label: '边数'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,37 +6,36 @@
|
||||
* @copyright 2010 Christian Tzurcanu, 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
const name = "shapes";
|
||||
const name = 'shapes'
|
||||
|
||||
const loadExtensionTranslation = async function (svgEditor) {
|
||||
let translationModule;
|
||||
const lang = svgEditor.configObj.pref('lang');
|
||||
let translationModule
|
||||
const lang = svgEditor.configObj.pref('lang')
|
||||
try {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
translationModule = await import(`./locale/${lang}.js`);
|
||||
translationModule = await import(`./locale/${lang}.js`)
|
||||
} catch (_error) {
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`);
|
||||
translationModule = await import(`./locale/en.js`);
|
||||
console.warn(`Missing translation (${lang}) for ${name} - using 'en'`)
|
||||
translationModule = await import('./locale/en.js')
|
||||
}
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default);
|
||||
};
|
||||
svgEditor.i18next.addResourceBundle(lang, name, translationModule.default)
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
async init () {
|
||||
const svgEditor = this;
|
||||
const canv = svgEditor.svgCanvas;
|
||||
const { $id } = canv;
|
||||
const svgroot = canv.getSvgRoot();
|
||||
let lastBBox = {};
|
||||
await loadExtensionTranslation(svgEditor);
|
||||
const svgEditor = this
|
||||
const canv = svgEditor.svgCanvas
|
||||
const { $id } = canv
|
||||
const svgroot = canv.getSvgRoot()
|
||||
let lastBBox = {}
|
||||
await loadExtensionTranslation(svgEditor)
|
||||
|
||||
const modeId = 'shapelib';
|
||||
const startClientPos = {};
|
||||
const modeId = 'shapelib'
|
||||
const startClientPos = {}
|
||||
|
||||
let curShape;
|
||||
let startX;
|
||||
let startY;
|
||||
let curShape
|
||||
let startX
|
||||
let startY
|
||||
|
||||
return {
|
||||
callback () {
|
||||
@@ -44,28 +43,28 @@ export default {
|
||||
const buttonTemplate = `
|
||||
<se-explorerbutton id="tool_shapelib" title="${svgEditor.i18next.t(`${name}:buttons.0.title`)}" lib="./extensions/ext-shapes/shapelib/"
|
||||
src="shapelib.svg"></se-explorerbutton>
|
||||
`;
|
||||
canv.insertChildAtIndex($id('tools_left'), buttonTemplate, 9);
|
||||
$id('tool_shapelib').addEventListener("click", () => {
|
||||
if (this.leftPanel.updateLeftPanel("tool_shapelib")) {
|
||||
canv.setMode(modeId);
|
||||
`
|
||||
canv.insertChildAtIndex($id('tools_left'), buttonTemplate, 9)
|
||||
$id('tool_shapelib').addEventListener('click', () => {
|
||||
if (this.leftPanel.updateLeftPanel('tool_shapelib')) {
|
||||
canv.setMode(modeId)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
mouseDown (opts) {
|
||||
const mode = canv.getMode();
|
||||
if (mode !== modeId) { return undefined; }
|
||||
const mode = canv.getMode()
|
||||
if (mode !== modeId) { return undefined }
|
||||
|
||||
const currentD = document.getElementById('tool_shapelib').dataset.draw;
|
||||
startX = opts.start_x;
|
||||
const x = startX;
|
||||
startY = opts.start_y;
|
||||
const y = startY;
|
||||
const curStyle = canv.getStyle();
|
||||
const currentD = document.getElementById('tool_shapelib').dataset.draw
|
||||
startX = opts.start_x
|
||||
const x = startX
|
||||
startY = opts.start_y
|
||||
const y = startY
|
||||
const curStyle = canv.getStyle()
|
||||
|
||||
startClientPos.x = opts.event.clientX;
|
||||
startClientPos.y = opts.event.clientY;
|
||||
startClientPos.x = opts.event.clientX
|
||||
startClientPos.y = opts.event.clientY
|
||||
|
||||
curShape = canv.addSVGElemensFromJson({
|
||||
element: 'path',
|
||||
@@ -76,87 +75,87 @@ export default {
|
||||
opacity: curStyle.opacity / 2,
|
||||
style: 'pointer-events:none'
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
curShape.setAttribute('transform', 'translate(' + x + ',' + y + ') scale(0.005) translate(' + -x + ',' + -y + ')');
|
||||
curShape.setAttribute('transform', 'translate(' + x + ',' + y + ') scale(0.005) translate(' + -x + ',' + -y + ')')
|
||||
|
||||
canv.recalculateDimensions(curShape);
|
||||
canv.recalculateDimensions(curShape)
|
||||
|
||||
lastBBox = curShape.getBBox();
|
||||
lastBBox = curShape.getBBox()
|
||||
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
},
|
||||
mouseMove (opts) {
|
||||
const mode = canv.getMode();
|
||||
if (mode !== modeId) { return; }
|
||||
const mode = canv.getMode()
|
||||
if (mode !== modeId) { return }
|
||||
|
||||
const zoom = canv.getZoom();
|
||||
const evt = opts.event;
|
||||
const zoom = canv.getZoom()
|
||||
const evt = opts.event
|
||||
|
||||
const x = opts.mouse_x / zoom;
|
||||
const y = opts.mouse_y / zoom;
|
||||
const x = opts.mouse_x / zoom
|
||||
const y = opts.mouse_y / zoom
|
||||
|
||||
const tlist = curShape.transform.baseVal;
|
||||
const box = curShape.getBBox();
|
||||
const left = box.x; const top = box.y;
|
||||
const tlist = curShape.transform.baseVal
|
||||
const box = curShape.getBBox()
|
||||
const left = box.x; const top = box.y
|
||||
|
||||
const newbox = {
|
||||
x: Math.min(startX, x),
|
||||
y: Math.min(startY, y),
|
||||
width: Math.abs(x - startX),
|
||||
height: Math.abs(y - startY)
|
||||
};
|
||||
}
|
||||
|
||||
let sx = (newbox.width / lastBBox.width) || 1;
|
||||
let sy = (newbox.height / lastBBox.height) || 1;
|
||||
let sx = (newbox.width / lastBBox.width) || 1
|
||||
let sy = (newbox.height / lastBBox.height) || 1
|
||||
|
||||
// Not perfect, but mostly works...
|
||||
let tx = 0;
|
||||
let tx = 0
|
||||
if (x < startX) {
|
||||
tx = lastBBox.width;
|
||||
tx = lastBBox.width
|
||||
}
|
||||
let ty = 0;
|
||||
let ty = 0
|
||||
if (y < startY) {
|
||||
ty = lastBBox.height;
|
||||
ty = lastBBox.height
|
||||
}
|
||||
|
||||
// update the transform list with translate,scale,translate
|
||||
const translateOrigin = svgroot.createSVGTransform();
|
||||
const scale = svgroot.createSVGTransform();
|
||||
const translateBack = svgroot.createSVGTransform();
|
||||
const translateOrigin = svgroot.createSVGTransform()
|
||||
const scale = svgroot.createSVGTransform()
|
||||
const translateBack = svgroot.createSVGTransform()
|
||||
|
||||
translateOrigin.setTranslate(-(left + tx), -(top + ty));
|
||||
translateOrigin.setTranslate(-(left + tx), -(top + ty))
|
||||
if (!evt.shiftKey) {
|
||||
const max = Math.min(Math.abs(sx), Math.abs(sy));
|
||||
const max = Math.min(Math.abs(sx), Math.abs(sy))
|
||||
|
||||
sx = max * (sx < 0 ? -1 : 1);
|
||||
sy = max * (sy < 0 ? -1 : 1);
|
||||
sx = max * (sx < 0 ? -1 : 1)
|
||||
sy = max * (sy < 0 ? -1 : 1)
|
||||
}
|
||||
scale.setScale(sx, sy);
|
||||
scale.setScale(sx, sy)
|
||||
|
||||
translateBack.setTranslate(left + tx, top + ty);
|
||||
tlist.appendItem(translateBack);
|
||||
tlist.appendItem(scale);
|
||||
tlist.appendItem(translateOrigin);
|
||||
translateBack.setTranslate(left + tx, top + ty)
|
||||
tlist.appendItem(translateBack)
|
||||
tlist.appendItem(scale)
|
||||
tlist.appendItem(translateOrigin)
|
||||
|
||||
canv.recalculateDimensions(curShape);
|
||||
canv.recalculateDimensions(curShape)
|
||||
|
||||
lastBBox = curShape.getBBox();
|
||||
lastBBox = curShape.getBBox()
|
||||
},
|
||||
mouseUp (opts) {
|
||||
const mode = canv.getMode();
|
||||
if (mode !== modeId) { return undefined; }
|
||||
const mode = canv.getMode()
|
||||
if (mode !== modeId) { return undefined }
|
||||
|
||||
const keepObject = (opts.event.clientX !== startClientPos.x && opts.event.clientY !== startClientPos.y);
|
||||
const keepObject = (opts.event.clientX !== startClientPos.x && opts.event.clientY !== startClientPos.y)
|
||||
|
||||
return {
|
||||
keep: keepObject,
|
||||
element: curShape,
|
||||
started: false
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ export default {
|
||||
title: 'Shape library'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ export default {
|
||||
title: "Bibliothèque d'images"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ export default {
|
||||
title: '图元库'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,23 +18,23 @@
|
||||
* @todo We might provide control of storage settings through the UI besides the
|
||||
* initial (or URL-forced) dialog. *
|
||||
*/
|
||||
import './storageDialog.js';
|
||||
import './storageDialog.js'
|
||||
|
||||
/**
|
||||
* Expire the storage cookie.
|
||||
* @returns {void}
|
||||
*/
|
||||
const removeStoragePrefCookie = () => {
|
||||
expireCookie('svgeditstore');
|
||||
};
|
||||
expireCookie('svgeditstore')
|
||||
}
|
||||
/**
|
||||
* Set the cookie to expire.
|
||||
* @param {string} cookie
|
||||
* @returns {void}
|
||||
*/
|
||||
const expireCookie = (cookie) => {
|
||||
document.cookie = encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
};
|
||||
document.cookie = encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT'
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace `storagePrompt` parameter within URL.
|
||||
@@ -43,22 +43,22 @@ const expireCookie = (cookie) => {
|
||||
* @todo Replace the string manipulation with `searchParams.set`
|
||||
*/
|
||||
const replaceStoragePrompt = (val) => {
|
||||
val = val ? 'storagePrompt=' + val : '';
|
||||
const loc = top.location; // Allow this to work with the embedded editor as well
|
||||
val = val ? 'storagePrompt=' + val : ''
|
||||
const loc = top.location // Allow this to work with the embedded editor as well
|
||||
if (loc.href.includes('storagePrompt=')) {
|
||||
loc.href = loc.href.replace(/([&?])storagePrompt=[^&]*(&?)/, function (n0, n1, amp) {
|
||||
return (val ? n1 : '') + val + (!val && amp ? n1 : (amp || ''));
|
||||
});
|
||||
return (val ? n1 : '') + val + (!val && amp ? n1 : (amp || ''))
|
||||
})
|
||||
} else {
|
||||
loc.href += (loc.href.includes('?') ? '&' : '?') + val;
|
||||
loc.href += (loc.href.includes('?') ? '&' : '?') + val
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'storage',
|
||||
init () {
|
||||
const svgEditor = this;
|
||||
const { svgCanvas, storage } = svgEditor;
|
||||
const svgEditor = this
|
||||
const { svgCanvas, storage } = svgEditor
|
||||
|
||||
// We could empty any already-set data for users when they decline storage,
|
||||
// but it would be a risk for users who wanted to store but accidentally
|
||||
@@ -77,50 +77,50 @@ export default {
|
||||
// the "noStorageOnLoad" config setting to true in svgedit-config-*.js.
|
||||
noStorageOnLoad,
|
||||
forceStorage
|
||||
} = svgEditor.configObj.curConfig;
|
||||
} = svgEditor.configObj.curConfig
|
||||
|
||||
// storageDialog added to DOM
|
||||
const storageBox = document.createElement('se-storage-dialog');
|
||||
storageBox.setAttribute('id', 'se-storage-dialog');
|
||||
svgEditor.$container.append(storageBox);
|
||||
storageBox.init(svgEditor.i18next);
|
||||
const storageBox = document.createElement('se-storage-dialog')
|
||||
storageBox.setAttribute('id', 'se-storage-dialog')
|
||||
svgEditor.$container.append(storageBox)
|
||||
storageBox.init(svgEditor.i18next)
|
||||
|
||||
// manage the change in the storageDialog
|
||||
|
||||
storageBox.addEventListener('change', (e) => {
|
||||
storageBox.setAttribute('dialog', 'close');
|
||||
storageBox.setAttribute('dialog', 'close')
|
||||
if (e?.detail?.trigger === 'ok') {
|
||||
if (e?.detail?.select !== 'noPrefsOrContent') {
|
||||
const storagePrompt = new URL(top.location).searchParams.get('storagePrompt');
|
||||
document.cookie = 'svgeditstore=' + encodeURIComponent(e.detail.select) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';
|
||||
const storagePrompt = new URL(top.location).searchParams.get('storagePrompt')
|
||||
document.cookie = 'svgeditstore=' + encodeURIComponent(e.detail.select) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'
|
||||
if (storagePrompt === 'true' && e?.detail?.checkbox) {
|
||||
replaceStoragePrompt();
|
||||
return;
|
||||
replaceStoragePrompt()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
removeStoragePrefCookie();
|
||||
removeStoragePrefCookie()
|
||||
if (svgEditor.configObj.curConfig.emptyStorageOnDecline && e?.detail?.checkbox) {
|
||||
this.setSvgContentStorage('');
|
||||
this.setSvgContentStorage('')
|
||||
Object.keys(svgEditor.curPrefs).forEach((name) => {
|
||||
name = 'svg-edit-' + name;
|
||||
name = 'svg-edit-' + name
|
||||
if (svgEditor.storage) {
|
||||
svgEditor.storage.removeItem(name);
|
||||
svgEditor.storage.removeItem(name)
|
||||
}
|
||||
expireCookie(name);
|
||||
});
|
||||
expireCookie(name)
|
||||
})
|
||||
}
|
||||
if (e?.detail?.select && e?.detail?.checkbox) {
|
||||
replaceStoragePrompt('false');
|
||||
return;
|
||||
replaceStoragePrompt('false')
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if (e?.detail?.trigger === 'cancel') {
|
||||
removeStoragePrefCookie();
|
||||
removeStoragePrefCookie()
|
||||
}
|
||||
setupBeforeUnloadListener();
|
||||
svgEditor.storagePromptState = 'closed';
|
||||
svgEditor.updateCanvas(true);
|
||||
});
|
||||
setupBeforeUnloadListener()
|
||||
svgEditor.storagePromptState = 'closed'
|
||||
svgEditor.updateCanvas(true)
|
||||
})
|
||||
|
||||
/**
|
||||
* Sets SVG content as a string with "svgedit-" and the current
|
||||
@@ -130,11 +130,11 @@ export default {
|
||||
*/
|
||||
function setSvgContentStorage (val) {
|
||||
if (storage) {
|
||||
const name = 'svgedit-' + svgEditor.configObj.curConfig.canvasName;
|
||||
const name = 'svgedit-' + svgEditor.configObj.curConfig.canvasName
|
||||
if (!val) {
|
||||
storage.removeItem(name);
|
||||
storage.removeItem(name)
|
||||
} else {
|
||||
storage.setItem(name, val);
|
||||
storage.setItem(name, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,46 +152,46 @@ export default {
|
||||
window.addEventListener('beforeunload', function () {
|
||||
// Don't save anything unless the user opted in to storage
|
||||
if (!(/(?:^|;\s*)svgeditstore=(?:prefsAndContent|prefsOnly)/).test(document.cookie)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if ((/(?:^|;\s*)svgeditstore=prefsAndContent/).test(document.cookie)) {
|
||||
setSvgContentStorage(svgCanvas.getSvgString());
|
||||
setSvgContentStorage(svgCanvas.getSvgString())
|
||||
}
|
||||
|
||||
svgEditor.setConfig({ no_save_warning: true }); // No need for explicit saving at all once storage is on
|
||||
svgEditor.setConfig({ no_save_warning: true }) // No need for explicit saving at all once storage is on
|
||||
// svgEditor.showSaveWarning = false;
|
||||
|
||||
const { curPrefs } = svgEditor.configObj;
|
||||
const { curPrefs } = svgEditor.configObj
|
||||
|
||||
Object.entries(curPrefs).forEach(([ key, val ]) => {
|
||||
const store = (val !== undefined);
|
||||
key = 'svg-edit-' + key;
|
||||
Object.entries(curPrefs).forEach(([key, val]) => {
|
||||
const store = (val !== undefined)
|
||||
key = 'svg-edit-' + key
|
||||
if (!store) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (storage) {
|
||||
storage.setItem(key, val);
|
||||
storage.setItem(key, val)
|
||||
} else if (window.widget) {
|
||||
window.widget.setPreferenceForKey(val, key);
|
||||
window.widget.setPreferenceForKey(val, key)
|
||||
} else {
|
||||
val = encodeURIComponent(val);
|
||||
document.cookie = encodeURIComponent(key) + '=' + val + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';
|
||||
val = encodeURIComponent(val)
|
||||
document.cookie = encodeURIComponent(key) + '=' + val + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let loaded = false;
|
||||
let loaded = false
|
||||
return {
|
||||
name: 'storage',
|
||||
callback () {
|
||||
const storagePrompt = new URL(top.location).searchParams.get('storagePrompt');
|
||||
const storagePrompt = new URL(top.location).searchParams.get('storagePrompt')
|
||||
// No need to run this one-time dialog again just because the user
|
||||
// changes the language
|
||||
if (loaded) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
loaded = true;
|
||||
loaded = true
|
||||
|
||||
// Note that the following can load even if "noStorageOnLoad" is
|
||||
// set to false; to avoid any chance of storage, avoid this
|
||||
@@ -213,17 +213,17 @@ export default {
|
||||
)
|
||||
// ...then show the storage prompt.
|
||||
)) {
|
||||
const options = Boolean(storage);
|
||||
const options = Boolean(storage)
|
||||
// Open select-with-checkbox dialog
|
||||
// From svg-editor.js
|
||||
svgEditor.storagePromptState = 'waiting';
|
||||
const $storageDialog = document.getElementById('se-storage-dialog');
|
||||
$storageDialog.setAttribute('dialog', 'open');
|
||||
$storageDialog.setAttribute('storage', options);
|
||||
svgEditor.storagePromptState = 'waiting'
|
||||
const $storageDialog = document.getElementById('se-storage-dialog')
|
||||
$storageDialog.setAttribute('dialog', 'open')
|
||||
$storageDialog.setAttribute('storage', options)
|
||||
} else if (!noStorageOnLoad || forceStorage) {
|
||||
setupBeforeUnloadListener();
|
||||
setupBeforeUnloadListener()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ export default {
|
||||
storageNoPrefs: 'Do not store my preferences locally',
|
||||
rememberLabel: 'Remember this choice?',
|
||||
rememberTooltip: 'If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again.'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ export default {
|
||||
storageNoPrefs: 'Do not store my preferences locally',
|
||||
rememberLabel: 'Remember this choice?',
|
||||
rememberTooltip: 'If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again.'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ export default {
|
||||
storageNoPrefs: 'Do not store my preferences locally',
|
||||
rememberLabel: 'Remember this choice?',
|
||||
rememberTooltip: "Si vous choisissez de désactiver le stockage en mémorisant le choix, l'URL va changer afin que la question ne vous soit plus reposée."
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,4 +8,4 @@ export default {
|
||||
storageNoPrefs: '本地不保存配置参数',
|
||||
rememberLabel: '记住选择?',
|
||||
rememberTooltip: '如果您勾选记住选择,将不再弹出本窗口.'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import storageDialogHTML from './storageDialog.html';
|
||||
import storageDialogHTML from './storageDialog.html'
|
||||
|
||||
const template = document.createElement('template');
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
template.innerHTML = storageDialogHTML;
|
||||
const template = document.createElement('template')
|
||||
template.innerHTML = storageDialogHTML
|
||||
/**
|
||||
* @class SeStorageDialog
|
||||
*/
|
||||
@@ -11,39 +10,42 @@ export class SeStorageDialog extends HTMLElement {
|
||||
* @function constructor
|
||||
*/
|
||||
constructor () {
|
||||
super();
|
||||
super()
|
||||
// create the shadowDom and insert the template
|
||||
this._shadowRoot = this.attachShadow({ mode: 'open' });
|
||||
this._shadowRoot.append(template.content.cloneNode(true));
|
||||
this.$dialog = this._shadowRoot.querySelector('#dialog_box');
|
||||
this.$storage = this._shadowRoot.querySelector('#js-storage');
|
||||
this.$okBtn = this._shadowRoot.querySelector('#storage_ok');
|
||||
this.$cancelBtn = this._shadowRoot.querySelector('#storage_cancel');
|
||||
this.$storageInput = this._shadowRoot.querySelector('#se-storage-pref');
|
||||
this.$rememberInput = this._shadowRoot.querySelector('#se-remember');
|
||||
this._shadowRoot = this.attachShadow({ mode: 'open' })
|
||||
this._shadowRoot.append(template.content.cloneNode(true))
|
||||
this.$dialog = this._shadowRoot.querySelector('#dialog_box')
|
||||
this.$storage = this._shadowRoot.querySelector('#js-storage')
|
||||
this.$okBtn = this._shadowRoot.querySelector('#storage_ok')
|
||||
this.$cancelBtn = this._shadowRoot.querySelector('#storage_cancel')
|
||||
this.$storageInput = this._shadowRoot.querySelector('#se-storage-pref')
|
||||
this.$rememberInput = this._shadowRoot.querySelector('#se-remember')
|
||||
}
|
||||
|
||||
/**
|
||||
* @function init
|
||||
* @param {any} name
|
||||
* @returns {void}
|
||||
*/
|
||||
init (i18next) {
|
||||
this.setAttribute('common-ok', i18next.t('common.ok'));
|
||||
this.setAttribute('common-cancel', i18next.t('common.cancel'));
|
||||
this.setAttribute('notify-editor_pref_msg', i18next.t('notification.editorPreferencesMsg'));
|
||||
this.setAttribute('properties-prefs_and_content', i18next.t('properties.prefs_and_content'));
|
||||
this.setAttribute('properties-prefs_only', i18next.t('properties.prefs_only'));
|
||||
this.setAttribute('properties-no_prefs_or_content', i18next.t('properties.no_prefs_or_content'));
|
||||
this.setAttribute('tools-remember_this_choice', i18next.t('tools.remember_this_choice'));
|
||||
this.setAttribute('tools-remember_this_choice_title', i18next.t('tools.remember_this_choice_title'));
|
||||
this.setAttribute('common-ok', i18next.t('common.ok'))
|
||||
this.setAttribute('common-cancel', i18next.t('common.cancel'))
|
||||
this.setAttribute('notify-editor_pref_msg', i18next.t('notification.editorPreferencesMsg'))
|
||||
this.setAttribute('properties-prefs_and_content', i18next.t('properties.prefs_and_content'))
|
||||
this.setAttribute('properties-prefs_only', i18next.t('properties.prefs_only'))
|
||||
this.setAttribute('properties-no_prefs_or_content', i18next.t('properties.no_prefs_or_content'))
|
||||
this.setAttribute('tools-remember_this_choice', i18next.t('tools.remember_this_choice'))
|
||||
this.setAttribute('tools-remember_this_choice_title', i18next.t('tools.remember_this_choice_title'))
|
||||
}
|
||||
|
||||
/**
|
||||
* @function observedAttributes
|
||||
* @returns {any} observed
|
||||
*/
|
||||
static get observedAttributes () {
|
||||
return [ 'dialog', 'storage', 'common-ok', 'common-cancel', 'notify-editor_pref_msg', 'properties-prefs_and_content', 'tools-remember_this_choice', 'tools-remember_this_choice_title', 'properties-prefs_only', 'properties-no_prefs_or_content' ];
|
||||
return ['dialog', 'storage', 'common-ok', 'common-cancel', 'notify-editor_pref_msg', 'properties-prefs_and_content', 'tools-remember_this_choice', 'tools-remember_this_choice_title', 'properties-prefs_only', 'properties-no_prefs_or_content']
|
||||
}
|
||||
|
||||
/**
|
||||
* @function attributeChangedCallback
|
||||
* @param {string} name
|
||||
@@ -52,87 +54,93 @@ export class SeStorageDialog extends HTMLElement {
|
||||
* @returns {void}
|
||||
*/
|
||||
attributeChangedCallback (name, oldValue, newValue) {
|
||||
let node;
|
||||
let node
|
||||
switch (name) {
|
||||
case 'dialog':
|
||||
if (newValue === 'open') {
|
||||
this.$dialog.open();
|
||||
} else {
|
||||
this.$dialog.close();
|
||||
}
|
||||
break;
|
||||
case 'storage':
|
||||
if (newValue === 'true') {
|
||||
this.$storageInput.options[0].disabled = false;
|
||||
} else {
|
||||
this.$storageInput.options[0].disabled = true;
|
||||
}
|
||||
break;
|
||||
case 'common-ok':
|
||||
this.$okBtn.textContent = newValue;
|
||||
break;
|
||||
case 'common-cancel':
|
||||
this.$cancelBtn.textContent = newValue;
|
||||
break;
|
||||
case 'notify-editor_pref_msg':
|
||||
node = this._shadowRoot.querySelector('#notificationNote');
|
||||
node.textContent = newValue;
|
||||
break;
|
||||
case 'properties-prefs_and_content':
|
||||
node = this._shadowRoot.querySelector('#prefsAndContent');
|
||||
node.textContent = newValue;
|
||||
break;
|
||||
case 'properties-prefs_only':
|
||||
node = this._shadowRoot.querySelector('#prefsOnly');
|
||||
node.textContent = newValue;
|
||||
break;
|
||||
case 'properties-no_prefs_or_content':
|
||||
node = this._shadowRoot.querySelector('#noPrefsOrContent');
|
||||
node.textContent = newValue;
|
||||
break;
|
||||
case 'tools-remember_this_choice':
|
||||
node = this._shadowRoot.querySelector('#se-remember-title');
|
||||
node.prepend(newValue);
|
||||
break;
|
||||
case 'tools-remember_this_choice_title':
|
||||
node = this._shadowRoot.querySelector('#se-remember-title');
|
||||
node.setAttribute('title', newValue);
|
||||
break;
|
||||
default:
|
||||
case 'dialog':
|
||||
if (newValue === 'open') {
|
||||
this.$dialog.open()
|
||||
} else {
|
||||
this.$dialog.close()
|
||||
}
|
||||
break
|
||||
case 'storage':
|
||||
if (newValue === 'true') {
|
||||
this.$storageInput.options[0].disabled = false
|
||||
} else {
|
||||
this.$storageInput.options[0].disabled = true
|
||||
}
|
||||
break
|
||||
case 'common-ok':
|
||||
this.$okBtn.textContent = newValue
|
||||
break
|
||||
case 'common-cancel':
|
||||
this.$cancelBtn.textContent = newValue
|
||||
break
|
||||
case 'notify-editor_pref_msg':
|
||||
node = this._shadowRoot.querySelector('#notificationNote')
|
||||
node.textContent = newValue
|
||||
break
|
||||
case 'properties-prefs_and_content':
|
||||
node = this._shadowRoot.querySelector('#prefsAndContent')
|
||||
node.textContent = newValue
|
||||
break
|
||||
case 'properties-prefs_only':
|
||||
node = this._shadowRoot.querySelector('#prefsOnly')
|
||||
node.textContent = newValue
|
||||
break
|
||||
case 'properties-no_prefs_or_content':
|
||||
node = this._shadowRoot.querySelector('#noPrefsOrContent')
|
||||
node.textContent = newValue
|
||||
break
|
||||
case 'tools-remember_this_choice':
|
||||
node = this._shadowRoot.querySelector('#se-remember-title')
|
||||
node.prepend(newValue)
|
||||
break
|
||||
case 'tools-remember_this_choice_title':
|
||||
node = this._shadowRoot.querySelector('#se-remember-title')
|
||||
node.setAttribute('title', newValue)
|
||||
break
|
||||
default:
|
||||
// super.attributeChangedCallback(name, oldValue, newValue);
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @function get
|
||||
* @returns {any}
|
||||
*/
|
||||
get dialog () {
|
||||
return this.getAttribute('dialog');
|
||||
return this.getAttribute('dialog')
|
||||
}
|
||||
|
||||
/**
|
||||
* @function set
|
||||
* @returns {void}
|
||||
*/
|
||||
set dialog (value) {
|
||||
this.setAttribute('dialog', value);
|
||||
this.setAttribute('dialog', value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @function connectedCallback
|
||||
* @returns {void}
|
||||
*/
|
||||
connectedCallback () {
|
||||
const onSubmitHandler = (e, action) => {
|
||||
const triggerEvent = new CustomEvent('change', { detail: {
|
||||
trigger: action,
|
||||
select: this.$storageInput.value,
|
||||
checkbox: this.$rememberInput.checked
|
||||
} });
|
||||
this.dispatchEvent(triggerEvent);
|
||||
};
|
||||
this.$okBtn.addEventListener('click', (evt) => onSubmitHandler(evt, 'ok'));
|
||||
this.$cancelBtn.addEventListener('click', (evt) => onSubmitHandler(evt, 'cancel'));
|
||||
const triggerEvent = new CustomEvent('change', {
|
||||
detail: {
|
||||
trigger: action,
|
||||
select: this.$storageInput.value,
|
||||
checkbox: this.$rememberInput.checked
|
||||
}
|
||||
})
|
||||
this.dispatchEvent(triggerEvent)
|
||||
}
|
||||
this.$okBtn.addEventListener('click', (evt) => onSubmitHandler(evt, 'ok'))
|
||||
this.$cancelBtn.addEventListener('click', (evt) => onSubmitHandler(evt, 'cancel'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets SVG content as a string with "svgedit-" and the current
|
||||
* canvas name as namespace.
|
||||
@@ -141,15 +149,15 @@ export class SeStorageDialog extends HTMLElement {
|
||||
*/
|
||||
setSvgContentStorage (val) {
|
||||
if (this.storage) {
|
||||
const name = 'svgedit-' + this.configObj.curConfig.canvasName;
|
||||
const name = 'svgedit-' + this.configObj.curConfig.canvasName
|
||||
if (!val) {
|
||||
this.storage.removeItem(name);
|
||||
this.storage.removeItem(name)
|
||||
} else {
|
||||
this.storage.setItem(name, val);
|
||||
this.storage.setItem(name, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
customElements.define('se-storage-dialog', SeStorageDialog);
|
||||
customElements.define('se-storage-dialog', SeStorageDialog)
|
||||
|
||||
Reference in New Issue
Block a user